From 5e473f287772edb7deeda4765ada064fd0939a6a Mon Sep 17 00:00:00 2001 From: Wei Dai Date: Thu, 23 Sep 2021 02:04:20 -0700 Subject: [PATCH] Initial commit. --- .clang-format | 26 + .devcontainer/Dockerfile | 8 + .gitignore | 618 +++ .pre-commit-config.yaml | 15 + CODE_OF_CONDUCT.md | 9 + CONTRIBUTING.md | 29 + ISSUES.md | 27 + LICENSE | 21 + NOTICE | 15 + README.md | 111 + SECURITY.md | 53 + adapter/CMakeLists.txt | 166 + adapter/adapter.cpp | 259 ++ adapter/config.h.in | 10 + adapter/convert.cpp | 240 + adapter/convert.h | 110 + adapter/fileops.cpp | 532 +++ adapter/fileops.h | 276 ++ adapter/generate.cpp | 488 ++ adapter/generate.h | 107 + adapter/utils.cpp | 382 ++ adapter/utils.h | 403 ++ device/CMakeLists.txt | 300 ++ device/CMakeSettings.json | 85 + device/app_manifest.json | 13 + device/bench/CMakeLists.txt | 14 + device/bench/bench_asym.c | 147 + device/bench/bench_common.h | 124 + device/bench/bench_ifft.c | 146 + device/bench/bench_index_map.c | 73 + device/bench/bench_ntt.c | 144 + device/bench/bench_sample.c | 227 + device/bench/bench_sym.c | 157 + device/bench/main.c | 53 + device/lib/CMakeLists.txt | 24 + device/lib/ckks_asym.c | 304 ++ device/lib/ckks_asym.h | 132 + device/lib/ckks_common.c | 374 ++ device/lib/ckks_common.h | 282 ++ device/lib/ckks_sym.c | 314 ++ device/lib/ckks_sym.h | 129 + device/lib/defines.h | 541 +++ device/lib/fft.c | 212 + device/lib/fft.h | 109 + device/lib/fileops.c | 378 ++ device/lib/fileops.h | 245 + device/lib/intt.c | 378 ++ device/lib/intt.h | 47 + device/lib/modulo.h | 188 + device/lib/modulus.c | 54 + device/lib/modulus.h | 52 + device/lib/network.c | 125 + device/lib/network.h | 45 + device/lib/ntt.c | 239 + device/lib/ntt.h | 87 + device/lib/parameters.c | 177 + device/lib/parameters.h | 135 + device/lib/polymodarith.h | 102 + device/lib/polymodmult.c | 103 + device/lib/polymodmult.h | 32 + device/lib/rng.c | 18 + device/lib/rng.h | 105 + device/lib/sample.c | 357 ++ device/lib/sample.h | 356 ++ device/lib/sdk_config.h | 5009 +++++++++++++++++++++ device/lib/seal_embedded.c | 233 + device/lib/seal_embedded.h | 153 + device/lib/shake256/CMakeLists.txt | 18 + device/lib/shake256/cgmanifest.json | 32 + device/lib/shake256/fips202.c | 130 + device/lib/shake256/fips202.h | 14 + device/lib/shake256/keccakf1600.asm | 739 +++ device/lib/shake256/keccakf1600.c | 321 ++ device/lib/shake256/keccakf1600.h | 17 + device/lib/timer.c | 398 ++ device/lib/timer.h | 80 + device/lib/uint_arith.c | 19 + device/lib/uint_arith.h | 84 + device/lib/uintmodarith.h | 340 ++ device/lib/uintops.h | 102 + device/lib/user_defines.h | 208 + device/lib/util_print.h | 892 ++++ device/linker.ld | 80 + device/scripts/app_manifest_sphere_a7.txt | 13 + device/scripts/app_manifest_sphere_m4.txt | 13 + device/scripts/commands_sphere_a7.gdb | 4 + device/scripts/commands_sphere_m4.gdb | 3 + device/scripts/gdb_launch_sphere_a7.sh | 33 + device/scripts/gdb_launch_sphere_m4.sh | 32 + device/scripts/minirc_sphere_m4.dfl | 8 + device/scripts/ocd_launch_sphere_m4.sh | 39 + device/scripts/sphere_a7_launch_cl.sh | 56 + device/scripts/sphere_m4_launch_cl.sh | 50 + device/test/CMakeLists.txt | 20 + device/test/api_tests.c | 141 + device/test/applibs_versions.h | 36 + device/test/ckks_tests_asym.c | 326 ++ device/test/ckks_tests_common.c | 237 + device/test/ckks_tests_common.h | 145 + device/test/ckks_tests_encode.c | 97 + device/test/ckks_tests_sym.c | 214 + device/test/fft_tests.c | 350 ++ device/test/main.c | 126 + device/test/modulo_tests.c | 239 + device/test/network_tests.c | 105 + device/test/ntt_tests.c | 314 ++ device/test/sample_tests.c | 236 + device/test/test_common.h | 352 ++ device/test/uintmodarith_tests.c | 209 + device/test/uintops_tests.c | 156 + pipelines/device_docker.yml | 40 + pipelines/device_local.yml | 87 + tools/scripts/clang-format-all.sh | 14 + 113 files changed, 23596 insertions(+) create mode 100644 .clang-format create mode 100644 .devcontainer/Dockerfile create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 ISSUES.md create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 adapter/CMakeLists.txt create mode 100644 adapter/adapter.cpp create mode 100644 adapter/config.h.in create mode 100644 adapter/convert.cpp create mode 100644 adapter/convert.h create mode 100644 adapter/fileops.cpp create mode 100644 adapter/fileops.h create mode 100644 adapter/generate.cpp create mode 100644 adapter/generate.h create mode 100644 adapter/utils.cpp create mode 100644 adapter/utils.h create mode 100644 device/CMakeLists.txt create mode 100644 device/CMakeSettings.json create mode 100644 device/app_manifest.json create mode 100644 device/bench/CMakeLists.txt create mode 100644 device/bench/bench_asym.c create mode 100644 device/bench/bench_common.h create mode 100644 device/bench/bench_ifft.c create mode 100644 device/bench/bench_index_map.c create mode 100644 device/bench/bench_ntt.c create mode 100644 device/bench/bench_sample.c create mode 100644 device/bench/bench_sym.c create mode 100644 device/bench/main.c create mode 100644 device/lib/CMakeLists.txt create mode 100644 device/lib/ckks_asym.c create mode 100644 device/lib/ckks_asym.h create mode 100644 device/lib/ckks_common.c create mode 100644 device/lib/ckks_common.h create mode 100644 device/lib/ckks_sym.c create mode 100644 device/lib/ckks_sym.h create mode 100644 device/lib/defines.h create mode 100644 device/lib/fft.c create mode 100644 device/lib/fft.h create mode 100644 device/lib/fileops.c create mode 100644 device/lib/fileops.h create mode 100644 device/lib/intt.c create mode 100644 device/lib/intt.h create mode 100644 device/lib/modulo.h create mode 100644 device/lib/modulus.c create mode 100644 device/lib/modulus.h create mode 100644 device/lib/network.c create mode 100644 device/lib/network.h create mode 100644 device/lib/ntt.c create mode 100644 device/lib/ntt.h create mode 100644 device/lib/parameters.c create mode 100644 device/lib/parameters.h create mode 100644 device/lib/polymodarith.h create mode 100644 device/lib/polymodmult.c create mode 100644 device/lib/polymodmult.h create mode 100644 device/lib/rng.c create mode 100644 device/lib/rng.h create mode 100644 device/lib/sample.c create mode 100644 device/lib/sample.h create mode 100644 device/lib/sdk_config.h create mode 100644 device/lib/seal_embedded.c create mode 100644 device/lib/seal_embedded.h create mode 100644 device/lib/shake256/CMakeLists.txt create mode 100644 device/lib/shake256/cgmanifest.json create mode 100644 device/lib/shake256/fips202.c create mode 100644 device/lib/shake256/fips202.h create mode 100644 device/lib/shake256/keccakf1600.asm create mode 100644 device/lib/shake256/keccakf1600.c create mode 100644 device/lib/shake256/keccakf1600.h create mode 100644 device/lib/timer.c create mode 100644 device/lib/timer.h create mode 100644 device/lib/uint_arith.c create mode 100644 device/lib/uint_arith.h create mode 100644 device/lib/uintmodarith.h create mode 100644 device/lib/uintops.h create mode 100644 device/lib/user_defines.h create mode 100644 device/lib/util_print.h create mode 100644 device/linker.ld create mode 100644 device/scripts/app_manifest_sphere_a7.txt create mode 100644 device/scripts/app_manifest_sphere_m4.txt create mode 100644 device/scripts/commands_sphere_a7.gdb create mode 100644 device/scripts/commands_sphere_m4.gdb create mode 100644 device/scripts/gdb_launch_sphere_a7.sh create mode 100644 device/scripts/gdb_launch_sphere_m4.sh create mode 100644 device/scripts/minirc_sphere_m4.dfl create mode 100644 device/scripts/ocd_launch_sphere_m4.sh create mode 100644 device/scripts/sphere_a7_launch_cl.sh create mode 100644 device/scripts/sphere_m4_launch_cl.sh create mode 100644 device/test/CMakeLists.txt create mode 100644 device/test/api_tests.c create mode 100644 device/test/applibs_versions.h create mode 100644 device/test/ckks_tests_asym.c create mode 100644 device/test/ckks_tests_common.c create mode 100644 device/test/ckks_tests_common.h create mode 100644 device/test/ckks_tests_encode.c create mode 100644 device/test/ckks_tests_sym.c create mode 100644 device/test/fft_tests.c create mode 100644 device/test/main.c create mode 100644 device/test/modulo_tests.c create mode 100644 device/test/network_tests.c create mode 100644 device/test/ntt_tests.c create mode 100644 device/test/sample_tests.c create mode 100644 device/test/test_common.h create mode 100644 device/test/uintmodarith_tests.c create mode 100644 device/test/uintops_tests.c create mode 100644 pipelines/device_docker.yml create mode 100644 pipelines/device_local.yml create mode 100644 tools/scripts/clang-format-all.sh diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..bbf331e --- /dev/null +++ b/.clang-format @@ -0,0 +1,26 @@ +--- +Language: Cpp +BasedOnStyle: Google +AllowShortLoopsOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: false +AlignConsecutiveAssignments: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterEnum: false + AfterFunction: true + AfterStruct: true + AfterCaseLabel: false + BeforeElse: true + AfterControlStatement: true +BinPackParameters: true +ColumnLimit: 100 +DerivePointerAlignment: false +IncludeBlocks: Regroup +IndentPPDirectives: None +IndentWidth: 4 +IndentGotoLabels: false +PointerAlignment: Right +TabWidth: 4 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..7c2885a --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,8 @@ +FROM mcr.microsoft.com/azurespheresdk:latest AS dev + +FROM dev AS build +COPY ./device /src/ +WORKDIR /out +RUN cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="/opt/azurespheresdk/CMakeFiles/AzureSphereToolchain.cmake" \ + -DAZURE_SPHERE_TARGET_API_SET="latest-lts" -DCMAKE_BUILD_TYPE="Release" -DSE_BUILD_LOCAL=OFF -DSE_BUILD_TYPE="Tests" -DSE_BUILD_M4=OFF "/src" +ENTRYPOINT [ "ninja" ] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9537622 --- /dev/null +++ b/.gitignore @@ -0,0 +1,618 @@ +################################ +#### SEAL-Embedded Specific #### +################################ + +**/.vscode +*.dat +**/config.h +**/adapter_output_data +# device/lib/kri_data/* +# adpater/thirdparty +# **/data/ + +# Configuration +!cmake/*.cmake.in +!cmake/*.cmake +cmake/* +# CMakeSettings.json + +# Build +bin/ +build/ +*.build +env +**/thirdparty/ + +######################################################################################## +#### The following are based on templates from https://github.com/github/gitignore. #### +######################################################################################## + +*.pc +.config +*.args.json +# You Complete Me +.ycm_extra_conf.py +# Vim +.vimrc +.lvimrc +.local_vimrc + +#### Archives #### +# It's better to unpack these files and commit the raw source because +# git has its own built in compression methods. +*.7z +*.jar +*.rar +*.zip +*.gz +*.gzip +*.tgz +*.bzip +*.bzip2 +*.bz2 +*.xz +*.lzma +*.cab +*.xar +# Packing-only formats +*.iso +*.tar +# Package management formats +*.dmg +*.xpi +*.gem +*.egg +*.deb +*.rpm +*.msi +*.msm +*.msp +*.txz + +#### Backup #### +*.bak +*.gho +*.ori +*.orig +*.tmp + +#### Git #### +*.patch +*.diff + +#### Windows #### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db +# Dump file +*.stackdump +# Folder config file +[Dd]esktop.ini +# Recycle Bin used on file shares +$RECYCLE.BIN/ +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp +# Windows shortcuts +*.lnk + +#### Linux #### +*~ +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* +# KDE directory preferences +.directory +# Linux trash folder which might appear on any partition or disk +.Trash-* +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +#### macOS #### +# General +.DS_Store +.AppleDouble +.LSOverride +# Icon must end with two \r +Icon +# Thumbnails +._* +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +#### Autotools #### +# http://www.gnu.org/software/automake +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +.deps/ +.dirstamp +# http://www.gnu.org/software/autoconf +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 +# https://www.gnu.org/software/libtool/ +/ltmain.sh +# http://www.gnu.org/software/texinfo +/texinfo.tex +# http://www.gnu.org/software/m4/ +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile + +#### C/C++ #### +# Prerequisites +*.d +# Compiled Object files +*.slo +*.lo +*.ko +*.o +*.obj +*.elf +# Linker output +*.lik +*.map +*.exp +# Precompiled Headers +*.gch +*.pch +# Compiled Static libraries +*.lai +*.la +*.a +*.lib +# Compiled Dynamic libraries +*.so +*.so.* +*.dylib +*.dll +# Kernel Module Compile Results +*.mod* +*.smod +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +#### CMake #### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +CPackConfig.cmake +CPackSourceConfig.cmake + +#### Gradle #### +.gradle +**/build/ +!src/**/build/ +# Ignore Gradle GUI config +gradle-app.setting +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar +# Cache of project +.gradletasknamecache +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +#### Visual Studio Code #### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace +# Local History for Visual Studio Code +.history/ + +#### Visual Stduio #### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..158f3ee --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.0.0 + hooks: + - id: check-added-large-files + - id: trailing-whitespace + - id: end-of-file-fixer + - repo: local + hooks: + - id: clang-format + name: clang-format + entry: clang-format + language: system + files: \.(c|cpp|h)$ + args: ["-i"] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f9ba8cf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..441801a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Contributing + +This project welcomes contributions and suggestions. +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). +Simply follow the instructions provided by the bot. +You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +### Pull Requests + +Submit pull requrests to **branch *contrib***. +Pull requests to any other branch will not be accepted. + +When you submit a pull request, a CLA bot will automatically determine whether you need to **provide a CLA** and decorate the PR appropriately (e.g., status check, comment). +Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +### Formatting + +SEAL-Embedded uses a customized `.clang-format` configuration for C/C++ code styling. +A script `tools/scripts/clang-format-all.sh` is provided to easily format all C/C++ sources and headers in the `adapter`, `device`, and `example` directories. +To ensure the code is properly formatted before making a pull request, we highly recommend using [pre-commit](https://pre-commit.com/). +Note that the repository includes a `.pre-commit-config.yaml` that describes the appropriate formatting checks. + +Documentation are mostly written in GitHub-flavored Markdown. +A line break is required after each full sentence. diff --git a/ISSUES.md b/ISSUES.md new file mode 100644 index 0000000..2bcd92c --- /dev/null +++ b/ISSUES.md @@ -0,0 +1,27 @@ +# Issues + +## Technical questions + +The best way to get help with technical questions is on [StackOverflow](https://stackoverflow.com/questions/tagged/seal) using the `[seal]` tag. +To contact the Microsoft SEAL team directly, please email [sealcrypto@microsoft.com](mailto:sealcrypto@microsoft.com). + +## Bug reports + +We appreciate community efforts to find and fix bugs and issues in Microsoft SEAL. +If you believe you have found a bug or want to report some other issue, please +do so on [GitHub](https://github.com/Microsoft/SEAL-Embedded/issues). To help others +determine what the problem may be, we provide a helpful script that collects +relevant system information that you can submit with the bug report (see below). + +### System information + +To collect system information for an improved bug report, please run +``` +make -C tools system_info +``` +This will result in a file `system_info.tar.gz` to be generated, which you can +optionally attach with your bug report. + +## Critical security issues + +For reporting critical security issues, see [SECURITY.md](SECURITY.md). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ + MIT License + + 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 diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..5dff7f3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,15 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION + +This software incorporates components from the projects listed below. The original copyright notices +and the licenses under which Microsoft received such components are set forth below and are provided for +informational purposes only. Microsoft reserves all rights not expressly granted herein, whether by +implication, estoppel or otherwise. + +This software includes parts of the pqm4 library (https://github.com/mupq/pqm4). +The code from pqm4 library used in SEAL-Embedded is licensed under CC0 Universal, version 1.0. You can find a copy of this license at https://creativecommons.org/publicdomain/zero/1.0/legalcode + +This software includes parts of the Microsoft SEAL library (https://github.com/microsoft/SEAL). +The Kyber library is licensed under the MIT license. You can find a copy of this license at https://github.com/microsoft/SEAL/blob/main/LICENSE. + +This software includes parts of the Nordic Semiconductor nRF5 SDK (https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download). +The nRF5 SDK is licensed under the nRF5 SDK license. You can find a copy of this license at https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v12.x.x/doc/12.3.0/nRF5_Nordic_license.txt. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6ab97f --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# SEAL-Embedded + +SEAL-Embedded is an open-source ([MIT licensed](LICENSE)) homomorphic encryption toolset for embedded devices developed by the Cryptography and Privacy Reserach Group at Microsoft. +It implements the designs of CKKS-style encoding and encryption with small code and memory footprint that is published in [this peer-reviewed paper](https://tches.iacr.org/index.php/TCHES/article/view/8991). +SEAL-Embedded is written in C and C++ and primarily designed for building high-level applications on [Azure Sphere](https://azure.microsoft.com/en-us/services/azure-sphere/)'s ARM A7 processor. +To enable wider experiments by developers and researchers, SEAL-Embedded includes configurations that target ARM M4 as well. + +## Acknowledgments + +The majority of SEAL-Embedded was developed by [Deepika Natarajan](https://github.com/dnat112) from University of Michigan during an internship at Microsoft, despite not presented in the Git history. + +## Introduction + +Homomorphic encryption allows computation on encryption data without decryption. +To understand what homomorphic encryption and Microsoft SEAL are, we refer readers to [the Introduction section of Microsoft SEAL](https://github.com/microsoft/SEAL#introduction). +Typically, a client generates a secret key and public keys; data are encrypted with the secret key or public keys; encrypted data are sent to a untrusted server for computation; encrypted results are returned to the client who then decrypts the results. +SEAL-Embedded enables embedded devices to encrypt data; it *does not* perform key generation, computation on encrypted data, or decryption. + +SEAL-Embedded consists of mainly two components: a device library ([`device/lib`](device/lib)) to build an application that encrypts data and an adapter application ([`adapter`](adapter)) for compability with Microsoft SEAL. +The simplest workflow is described as follows: +1. run adapter to generate a secret key and public keys; +1. build an application with device library and create an image of the application together with public keys for a target device; +1. run adapter on an remote server to receive and serialize encrypted data to Microsoft-SEAL-compatible format; +1. build an application with Microsoft SEAL to compute on encrypted data; +1. decrypt encrypted results with Microsoft SEAL on a safe device that has the secret key. + +## Warning + +This library is research code and is not yet intended for production use. +Use at your own risk. +Holding secret key on device's storage for asymmetric encryption is extremely dangerous, regardless of whether the storage type is persistent or not. +Use public key (symmetric encryption), or consult a security expert. +See more information related to [SECURITY](SECURITY.md). + +## Building SEAL-Embedded + +On all platforms SEAL-Embedded is built with CMake. +We recommend using out-of-source build although in-source build works. +Below we give instructions for how to configure and build SEAL-Embedded components. + +### SEAL-Embedded Adapter + +The adapter ([`adapter`](adapter)) application automatically downloads and builds Microsoft SEAL as a dependency. +System requirements are listed in [SEAL/README.md](https://github.com/microsoft/SEAL/blob/main/README.md#requirements). + +On Unix-like systems: +```powershell +cd adapter +cmake -S . -B build +cmake --build build +./build/bin/se_adapter # run adapter +``` + +On Windows, following [this guide](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-160), use Visual Studio 2019 to open [`adapter/CMakeLists.txt`](adapter/CMakeLists.txt) as a CMake project. + +### SEAL-Embedded Device Library + +The device library comes with optional executables: unit tests ([`device/test`](device/test)) and benchmark ([`device/bench`](device/bench)). +The library itself, unit tests, and benchmark can be built on a native system (Linux or macOS) with same development environment listed in SEAL-Embedded adapter. +```powershell +cd adapter +cmake -S . -B build -DSE_BUILD_LOCAL=ON +cmake --build build +``` +This by default builds a library and unit tests. +The following options are configurable for custom builds. + +| CMake option | Values | Information | +| ------------ | ------ | ------------ | +| SE_BUILD_LOCAL | **ON** / OFF | Set to `OFF` to target an embedded device. | +| SE_BUILD_M4 | ON / **OFF** | Set to `ON` to target ARM M4, `OFF` to target ARM A7 on Azure Sphere. | +| SE_M4_IS_SPHERE | ON / **OFF** | Set to `ON` to target the real-time core on Azure Sphere, `OFF` to target other ARM M4 processors. | +| CMAKE_BUILD_TYPE | **Debug**
Release
MinSizeRel
RelWithDebInfo | Set to `Release` for the best run-time performance and the smallest code size. | +| SE_BUILD_TYPE | **Tests**
Bench
Lib | Set to `Bench` to build instead the benchmark executable, to `Lib` to build a library only. | + +To target an embedded device, the specific SDK for the target device is usually required. +Either the unit tests or the benchmark can be built and imaged on the device. +Using Azure Sphere as an example, please follow [this guide](https://docs.microsoft.com/en-us/azure-sphere/install/qs-blink-application?tabs=windows%2Ccliv2beta&pivots=visual-studio). +**Note: SEAL-Embedded Adapter must be built and executed first to generate required key and precomputation files.** +Ultimately, the users can develop their own application following [this guide](https://docs.microsoft.com/en-us/azure-sphere/install/qs-real-time-application?tabs=windows%2Ccliv2beta&pivots=visual-studio) and statically link to SEAL-Embedded device library. + +For targeting ARM M4 on Azure Sphere or other development boards, please follow related documents and tutorials. +For the convinience of verifying ARM M4 compatibility, we also provide [`device/lib/sdk_config.h`](device/lib/sdk_config.h) for an easier deployment on Nordic Semiconductor nRF5* series. + +## Contributing + +The main purpose of open sourcing SEAL-Embedded is to enable developers to build privacy-enhancing applications or services. +We welcome any suggestions or contributions to improve the usebility, efficiency, and quality of SEAL-Embedded. +To keep the development active, the `main` branch will always include the most recent changes, beyond the lastest release tag. +For contributing to Microsoft SEAL, please see [CONTRIBUTING](CONTRIBUTING.md). + +## Citing SEAL-Embedded + +To cite SEAL-Embedded in academic papers, please use the following BibTeX entries. + +```tex + @Article{sealembedded, + title = {{SEAL}-Embedded: A Homomorphic Encryption Library for the Internet of Things}, + author = {Deepika Natarajan and Wei Dai}, + journal = {{IACR} Transactions on Cryptographic Hardware and Embedded Systems}, + publisher = {Ruhr-Universit{\"a}t Bochum}, + year = 2021, + month = jul, + pages = {756--779}, + valume = 2021, + number = 3, + doi = {10.46586/tches.v2021.i3.756-779}, + issn = {2569-2925}, + note = {\url{https://tches.iacr.org/index.php/TCHES/article/view/8991}}, + } +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..e5b7bbc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,53 @@ +## Correct Use of SEAL-Embedded + +SEAL-Embedded supports encryption with either a secret key (symmetric) or a public key (asymmetric). +A secret key is stored in device flash RAM, and is vulnerable against attacker with physiscal access to the deivce. +Once a secret key is leaked, all previous data encrypted with the secret key or a public key generated by the secret key are at risk. +Deployments of SEAL-Embedded should avoid using symmetric encryption unless has been reviewed by security and cryptography experts. + +## Correct Use of Microsoft SEAL + +Homomorphic encryption schemes have various and often unexpected security models that may be surprising even to cryptography experts. +In particular, decryptions of Microsoft SEAL ciphertexts should be treated as private information only available to the secret key owner, as sharing decryptions of ciphertexts may in some cases lead to leaking the secret key. +If it is absolutely necessary to share information about the decryption of a ciphertext, for example when building a protocol of some kind, the number of bits shared should be kept to a minimum, and secret keys should be rotated regularly. +Commercial applications of Microsoft SEAL should be carefully reviewed by cryptography experts who are familiar with homomorphic encryption security models. + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). + + \ No newline at end of file diff --git a/adapter/CMakeLists.txt b/adapter/CMakeLists.txt new file mode 100644 index 0000000..1126b20 --- /dev/null +++ b/adapter/CMakeLists.txt @@ -0,0 +1,166 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +cmake_minimum_required(VERSION 3.13) + +# [option] CMAKE_BUILD_TYPE (default: "Release") +# Build in one of the following modes: Release, Debug, MiniSizeRel, or RelWithDebInfo. +# Most generators recognize these and can set the compiler flags accordingly. We set +# the build type here before creating the project to prevent the CMake generator from +# overriding our default of "Release". +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "Release" "Debug" "MinSizeRel" "RelWithDebInfo") +endif() +message(STATUS "Build type (CMAKE_BUILD_TYPE): ${CMAKE_BUILD_TYPE}") + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + # In Debug mode, enable extra compiler flags. + include(CheckCXXCompilerFlag) + # For easier adding of CXX compiler flags + function(se_enable_cxx_compiler_flag_if_supported flag) + string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" flag_already_set) + if(flag_already_set EQUAL -1) + message(STATUS "Adding CXX compiler flag: ${flag} ...") + check_cxx_compiler_flag("${flag}" flag_supported) + if(flag_supported) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE) + endif() + unset(flag_supported CACHE) + endif() + endfunction() + if(NOT MSVC AND SEAL_DEBUG) + se_enable_cxx_compiler_flag_if_supported("-Wall") + se_enable_cxx_compiler_flag_if_supported("-Wextra") + se_enable_cxx_compiler_flag_if_supported("-Wconversion") + se_enable_cxx_compiler_flag_if_supported("-Wshadow") + se_enable_cxx_compiler_flag_if_supported("-pedantic") + endif() +endif() + +project(SEAL_Embedded_Adapter VERSION 1.0.0 LANGUAGES CXX) + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) + cmake_policy(SET CMP0077 NEW) +endif() + +# [option] SE_ADAPTER_USE_CXX17_OPTION_STR (default: ON) +# Use C++17, use C++14 otherwise. An error will be thrown if SEAL_USE_CXX17 is ON but SE_ADAPTER_USE_CXX17 is OFF. +set(SE_ADAPTER_USE_CXX17_OPTION_STR "Use C++17") +option(SE_ADAPTER_USE_CXX17 ${SE_ADAPTER_USE_CXX17_OPTION_STR} ON) +message(STATUS "SE_ADAPTER_USE_CXX17: ${SE_ADAPTER_USE_CXX17}") + +# Enable security-related compile options (MSVC only) +set(SE_ADAPTER_SECURE_COMPILE_OPTIONS_OPTION_STR "Enable Control Flow Guard and Spectre mitigations (MSVC only)") +option(SE_ADAPTER_SECURE_COMPILE_OPTIONS ${SE_ADAPTER_SECURE_COMPILE_OPTIONS_OPTION_STR} OFF) +mark_as_advanced(SE_ADAPTER_SECURE_COMPILE_OPTIONS) + +# Source tree +set(SE_ADAPTER_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}) +set(SE_ADAPTER_CONFIG_IN_FILENAME ${CMAKE_CURRENT_LIST_DIR}/config.h.in) +set(SE_ADAPTER_THIRDPARTY_DIR ${CMAKE_CURRENT_LIST_DIR}/thirdparty) + +# Build tree +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${OUTLIB_PATH}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${OUTLIB_PATH}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +set(SE_ADAPTER_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(SE_ADAPTER_CONFIG_H_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +######################### +# External Dependencies # +######################### + +# [option] SE_ADAPTER_BUILD_DEPS (default: ON) +# Download and build missing dependencies, throw error if disabled. +set(SE_ADAPTER_BUILD_DEPS_OPTION_STR "Automatically download and build unmet dependencies") +option(SE_ADAPTER_BUILD_DEPS ${SE_ADAPTER_BUILD_DEPS_OPTION_STR} ON) +message(STATUS "SE_ADAPTER_BUILD_DEPS: ${SE_ADAPTER_BUILD_DEPS}") + +if(SE_ADAPTER_BUILD_DEPS) + include(FetchContent) + mark_as_advanced(FETCHCONTENT_BASE_DIR) + mark_as_advanced(FETCHCONTENT_FULLY_DISCONNECTED) + mark_as_advanced(FETCHCONTENT_UPDATES_DISCONNECTED) + mark_as_advanced(FETCHCONTENT_QUIET) +endif() + +# Microsoft SEAL +if(SE_ADAPTER_BUILD_DEPS) + message(STATUS "Microsoft SEAL: download ...") + set(SE_ADAPTER_FETCHCONTENT_BASE_DIR_OLD ${FETCHCONTENT_BASE_DIR}) + set(FETCHCONTENT_BASE_DIR ${SE_ADAPTER_THIRDPARTY_DIR} CACHE STRING "" FORCE) + FetchContent_Declare( + seal + GIT_REPOSITORY https://github.com/microsoft/SEAL.git + GIT_TAG 6bfac481aae4057ea887d42eb0f24371e9b4c4f9 # 3.7.1 + ) + FetchContent_GetProperties(seal) + + if(NOT seal_POPULATED) + FetchContent_Populate(seal) + set(SEAL_USE_CXX17 ON) + set(SEAL_BUILD_DEPS ON) + mark_as_advanced(FETCHCONTENT_SOURCE_DIR_SEAL) + mark_as_advanced(FETCHCONTENT_UPDATES_DISCONNECTED_SEAL) + message(STATUS ${seal_SOURCE_DIR}) + add_subdirectory( + ${seal_SOURCE_DIR} + EXCLUDE_FROM_ALL) + endif() + set(FETCHCONTENT_BASE_DIR ${SE_ADAPTER_FETCHCONTENT_BASE_DIR_OLD} CACHE STRING "" FORCE) + unset(SE_ADAPTER_FETCHCONTENT_BASE_DIR_OLD) +else() + find_package(SEAL 3.7 QUIET REQUIRED) + if(NOT SEAL_FOUND) + message(FATAL_ERROR "Microsoft SEAL: not found") + else() + message(STATUS "Microsoft SEAL: found") + endif() +endif() +if(NOT SE_ADAPTER_USE_CXX17 AND SEAL_USE_CXX17) + message(FATAL_ERROR "CXX standards mismatch: SE_ADAPTER_USE_CXX17 is OFF, SEAL_USE_CXX17 is ON") +endif() + +################# +# Configuration # +################# + +# Set a directory to save output files generated by SEAL-Embedded Adapter +set(SE_ADAPTER_FILE_OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/../device CACHE STRING "File output directory" FORCE) +message(STATUS "File output directory: ${SE_ADAPTER_FILE_OUTPUT_DIR}") +if(NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/adapter_output_data) + make_directory(${SE_ADAPTER_FILE_OUTPUT_DIR}/adapter_output_data) +endif() + +# Create the config file +configure_file(${SE_ADAPTER_CONFIG_IN_FILENAME} ${SE_ADAPTER_CONFIG_H_FILENAME}) + +##################################### +# SEAL-Embedded Adapter C++ library # +##################################### + +# TODO: factorize adapter and build a C++ library + +#################################### +# SEAL-Embedded Adapter Executable # +#################################### + +add_executable(se_adapter) +target_sources(se_adapter + PRIVATE + adapter.cpp + fileops.cpp + generate.cpp + convert.cpp + utils.cpp +) +target_include_directories(se_adapter PUBLIC $) +if(TARGET SEAL::seal) + target_link_libraries(se_adapter PRIVATE SEAL::seal) +elseif(TARGET SEAL::seal_shared) + target_link_libraries(se_adapter PRIVATE SEAL::seal_shared) +else() + message(FATAL_ERROR "Cannot find target SEAL::seal or SEAL::seal_shared") +endif() diff --git a/adapter/adapter.cpp b/adapter/adapter.cpp new file mode 100644 index 0000000..f9a15e3 --- /dev/null +++ b/adapter/adapter.cpp @@ -0,0 +1,259 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file adapter.cpp +*/ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "convert.h" +#include "fileops.h" +#include "generate.h" +#include "seal/seal.h" +#include "utils.h" + +using namespace std; +using namespace seal; +using namespace seal::util; + +// -- Set this to the path to directory to save keys, indices, and roots +static string save_dir_path = string(SE_ADAPTER_FILE_OUTPUT_DIR) + "/adapter_output_data/"; +// -- Set these to the paths to the output of running seal-embedded +// using api calls (see /device/test/api_tests.c for an example) +static string ct_str_file_path_asym = string(SE_ADAPTER_FILE_OUTPUT_DIR) + "/out_asym_api_tests"; +static string ct_str_file_path_sym = string(SE_ADAPTER_FILE_OUTPUT_DIR) + "/out_sym_api_tests"; + +void verify_ciphertexts_30bitprime_default(string dirpath, size_t degree, SEALContext &context, + bool symm_enc, string ct_str_file_path, + string sk_binfilename = "") +{ + // size_t n = degree; + // EncryptionParameters parms(scheme_type::ckks); + // SEALContext context = setup_seale_30bitprime_default(degree, parms); + auto &parms = context.key_context_data()->parms(); + size_t n = parms.poly_modulus_degree(); + + size_t print_size = 8; + assert(print_size <= n); + + // -- We don't need to generate any new keys, but we need the format + // to be compatible with SEAL. Use keygen to create the secret key + // object, then load the value of our particular secret key. + KeyGenerator keygen(context); + Evaluator evaluator(context); + CKKSEncoder encoder(context); + size_t slot_count = encoder.slot_count(); + // double scale = (double)(n * n); + // double scale = pow(2, 40); + // double scale = pow(2, 30); + double scale = (n > 1024) ? pow(2, 25) : pow(2, 20); + cout << "\nNumber of slots: " << slot_count << "\n" << endl; + + SecretKey sk = keygen.secret_key(); + if (sk_binfilename.size() == 0) + { + sk_binfilename = dirpath + "sk_" + to_string(degree) + ".dat"; + } + sk_bin_file_load(sk_binfilename, context, sk); + Decryptor decryptor(context, sk); + + Ciphertext ct; + PublicKey pk; + { + // -- Set up ciphertext + vector test = {1.0, 2.0, 3.0}; + Plaintext pt; + encoder.encode(test, scale, pt); + if (symm_enc) + { + Encryptor encryptor(context, sk); + encryptor.encrypt_symmetric(pt, ct); + } + else + { + keygen.create_public_key(pk); + PublicKeyWrapper pk_wr; + pk_wr.pk = &pk; + pk_wr.is_ntt = pk.data().is_ntt_form(); + + // -- We know this should start out being true + assert(pk_wr.is_ntt == true); + + bool incl_sp = 1; // Need to read back the special prime + pk_bin_file_load(dirpath, context, pk_wr, incl_sp); + print_pk("pk", pk_wr, 8, incl_sp); + + pk_to_non_ntt_form(context, pk_wr); + print_pk("pk", pk_wr, 8, incl_sp); + + pk_to_ntt_form(context, pk_wr); + print_pk("pk", pk_wr, 8, incl_sp); + + Encryptor encryptor(context, pk); + encryptor.encrypt(pt, ct); + } + } + + streampos filepos = 0; + size_t nfailures = 0; + try + { + size_t ntest_stop = 9; + for (size_t ntest = 0; ntest < ntest_stop; ntest++) + { + cout << "---------------------------------------------" << endl; + cout << " Test # " << ntest << endl; + cout << "---------------------------------------------" << endl; + + // -- Find the expected message from the string file. Comment out if not + // needed. + vector values_orig(slot_count, 0); + cout << "Reading values from file..." << endl; + filepos = poly_string_file_load(ct_str_file_path, values_orig, 1, filepos); + + // -- Uncomment to check against expected plaintext output (assuming + // corresponding lines in seal_embedded.c are also uncommented) + // vector plaintext_vals(slot_count * 2, 0); + // cout << "Reading plaintext values from file..." << endl; + // filepos = poly_string_file_load(ct_str_file_path, plaintext_vals, 1, filepos); + // print_poly("\npt ", plaintext_vals, print_size); + + // -- Uncomment to check against expected plaintext output (assuming + // corresponding lines in seal_embedded.c are also uncommented) + // vector plaintext_error_vals(slot_count * 2, 0); + // cout << "Reading plaintext error values from file..." << endl; + // filepos = + // poly_string_file_load(ct_str_file_path, plaintext_error_vals, 1, filepos); + // print_poly("\npte ", plaintext_error_vals, print_size); + + // -- Read in the ciphertext from the string file + cout << "Reading ciphertexts from file..." << endl; + filepos = ct_string_file_load(ct_str_file_path, context, evaluator, ct, filepos); + cout << "encrypted size: " << ct.size() << endl; + + // -- Decrypt and decode the ciphertext + Plaintext pt_d; + decryptor.decrypt(ct, pt_d); + print_poly("\n(ntt) pt_d ", pt_d.data(), print_size); + pt_to_non_ntt_form(context, pt_d); + print_poly("\n pt_d ", pt_d.data(), print_size); + pt_to_ntt_form(context, pt_d); + + vector msg_d(slot_count, 0); + encoder.decode(pt_d, msg_d); + + print_poly("\nmsg_d ", msg_d, print_size); + cout << endl; + + // -- Uncomment to check against expected plaintext output (assuming + // corresponding lines in seal_embedded.c are also uncommented) + // int precision = 4; + // print_poly("values_orig", values_orig, print_size, precision); + // bool are_equal = are_equal_poly(msg_d, values_orig, slot_count); + // assert(are_equal); + // if (!are_equal) nfailures++; + // Plaintext pt_test; + // encoder.encode(values_orig, scale, pt_test); + // print_poly("pt_test", get_pt_arr_ptr(pt_test), print_size); + } + } catch (...) + { + cout << "In adapter, verify_ciphertexts. "; + cout << "Something went wrong or end of file reached!" << endl; + throw; + } + + cout << "Done running tests."; + if (nfailures) { cout << " " << nfailures << " tests did not pass." << endl; } + else + { + cout << " All tests passed!! :) :)" << endl; + } +} + +// TODO: make this non-main. Need to have a different main? +int main() +{ + cout << "Parameters: degree 4096, prime bit-lengths: {30, 30, 30, 19}, ntt_form, " + "scale = pow(2, 25)" + << endl; + size_t degree = 4096; + EncryptionParameters parms(scheme_type::ckks); + SEALContext context = setup_seale_30bitprime_default(degree, parms); + bool sk_generated = false, pk_generated = false; + + string sk_fpath = save_dir_path + "sk_" + to_string(degree) + ".dat"; + // string str_sk_fpath = save_dir_path + "str_sk_" + to_string(degree) + ".h"; + string str_sk_fpath = save_dir_path + "str_sk.h"; + string seal_sk_fpath = save_dir_path + "sk_" + to_string(degree) + "_seal" + ".dat"; + string seal_pk_fpath = save_dir_path + "pk_" + to_string(degree) + "_seal" + ".dat"; + + SecretKey sk; + + // string err_msg1 = "This option is not yet supported. Please choose another + // option."; + string err_msg2 = "This is not a valid option choice. Please choose a valid option."; + + while (1) + { + cout << "\nChoose an action:\n"; + cout << " 0) Quit\n"; + cout << " 1) Generate all objects\n"; + cout << " 2) Verify ciphertexts\n"; + cout << " 3) Generate secret key, public key\n"; + cout << " 4) Generate IFFT roots\n"; + cout << " 5) Generate fast (a.k.a. \"lazy\") NTT roots\n"; + cout << " 6) Generate fast (a.k.a. \"lazy\") INTT roots\n"; + cout << " 7) Generate regular NTT roots\n"; + cout << " 8) Generate regular INTT roots\n"; + cout << " 9) Generate index map\n"; + int option; + cin >> option; + + bool is_sym = true; // TODO: Make this command line settable + string ct_str_file_path = is_sym ? ct_str_file_path_sym : ct_str_file_path_asym; + + bool use_seal_sk_fpath = true; + switch (option) + { + case 0: exit(0); + case 2: + verify_ciphertexts_30bitprime_default(save_dir_path, degree, context, is_sym, + ct_str_file_path); + break; + case 1: [[fallthrough]]; + case 3: + cout << "Generating secret key..." << endl; + gen_save_secret_key(sk_fpath, str_sk_fpath, seal_sk_fpath, context); + cout << "Generating public key..." << endl; + gen_save_public_key(save_dir_path, seal_pk_fpath, sk_fpath, seal_sk_fpath, context, + use_seal_sk_fpath); + if (option != 1) break; + case 4: + gen_save_ifft_roots(save_dir_path, context, 0, 1); + if (option != 1) break; + case 5: + gen_save_ntt_roots(save_dir_path, context, 1, 0, 0, 1); + if (option != 1) break; + case 6: + gen_save_ntt_roots(save_dir_path, context, 1, 1, 0, 1); + if (option != 1) break; + case 7: + gen_save_ntt_roots(save_dir_path, context, 0, 0, 0, 1); + if (option != 1) break; + case 8: + gen_save_ntt_roots(save_dir_path, context, 0, 1, 0, 1); + if (option != 1) break; + case 9: gen_save_index_map(save_dir_path, context, 0); break; + default: cout << err_msg2 << endl; break; + } + exit(0); + } + return 0; +} diff --git a/adapter/config.h.in b/adapter/config.h.in new file mode 100644 index 0000000..869624f --- /dev/null +++ b/adapter/config.h.in @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file config.h +*/ + +#pragma once + +#define SE_ADAPTER_FILE_OUTPUT_DIR "@SE_ADAPTER_FILE_OUTPUT_DIR@" diff --git a/adapter/convert.cpp b/adapter/convert.cpp new file mode 100644 index 0000000..0937a47 --- /dev/null +++ b/adapter/convert.cpp @@ -0,0 +1,240 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file convert.cpp +*/ + +#include "convert.h" + +#include + +#include "seal/seal.h" +#include "utils.h" + +using namespace std; +using namespace seal; +using namespace seal::util; + +void sk_to_ntt_form(const SEALContext &context, SecretKey &sk) +{ + bool is_ntt = sk.data().is_ntt_form(); + if (is_ntt == true) return; + + // -- This is kind of clunky but we need to do it this way + auto &parms_id = (sk.parms_id() == parms_id_zero) ? context.key_parms_id() : sk.parms_id(); + + auto sk_context_data_ptr = context.get_context_data(parms_id); + auto &parms = sk_context_data_ptr->parms(); + auto &coeff_modulus = parms.coeff_modulus(); + size_t n = parms.poly_modulus_degree(); + size_t coeff_modulus_size = coeff_modulus.size(); + auto ntt_tables_ptr = sk_context_data_ptr->small_ntt_tables(); + + RNSIter sk_iter(sk.data().data(), n); + ntt_negacyclic_harvey(sk_iter, coeff_modulus_size, ntt_tables_ptr); + + sk.data().parms_id() = context.key_parms_id(); + assert(sk.data().is_ntt_form() == true); +} + +void sk_to_non_ntt_form(const SEALContext &context, SecretKey &sk) +{ + bool is_ntt = sk.data().is_ntt_form(); + if (is_ntt == false) return; + + // -- This is kind of clunky but we need to do it this way + auto &parms_id = (sk.parms_id() == parms_id_zero) ? context.key_parms_id() : sk.parms_id(); + + auto sk_context_data_ptr = context.get_context_data(parms_id); + auto &parms = sk_context_data_ptr->parms(); + auto &coeff_modulus = parms.coeff_modulus(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t n = parms.poly_modulus_degree(); + auto ntt_tables_ptr = sk_context_data_ptr->small_ntt_tables(); + + assert(sk_context_data_ptr == context.key_context_data()); + + RNSIter sk_itr(sk.data().data(), n); + inverse_ntt_negacyclic_harvey(sk_itr, coeff_modulus_size, ntt_tables_ptr); + + sk.data().parms_id() = parms_id_zero; + assert(sk.data().is_ntt_form() == false); +} + +void pk_to_ntt_form(const SEALContext &context, PublicKeyWrapper &pk_wr) +{ + if (pk_wr.is_ntt == true) return; + + auto pk_context_data_ptr = context.get_context_data(pk_wr.pk->parms_id()); + auto &parms = pk_context_data_ptr->parms(); + auto &coeff_modulus = parms.coeff_modulus(); + size_t n = parms.poly_modulus_degree(); + size_t coeff_modulus_size = coeff_modulus.size(); + auto ntt_tables_ptr = pk_context_data_ptr->small_ntt_tables(); + + assert(pk_context_data_ptr == context.key_context_data()); + assert(pk_wr.pk->data().size() == 2); + + PolyIter pk_iter(pk_wr.pk->data().data(), n, coeff_modulus_size); + ntt_negacyclic_harvey(pk_iter, pk_wr.pk->data().size(), ntt_tables_ptr); + + pk_wr.is_ntt = true; + // assert(pk_wr.pk->data().is_ntt_form() == true); + // -- Note: We cannot use pk_wr.pk->data().is_ntt_form() directly because this value + // is not + // directly changeable for ciphertexts (and therefore public keys). It is only + // changeable by calling evaluator.transform_to_ntt(), but this function + // throws an exception when you try to give it a public key. +} + +void pk_to_non_ntt_form(const SEALContext &context, PublicKeyWrapper &pk_wr) +{ + if (pk_wr.is_ntt == false) return; + + auto pk_context_data_ptr = context.get_context_data(pk_wr.pk->parms_id()); + auto &parms = pk_context_data_ptr->parms(); + auto &coeff_modulus = parms.coeff_modulus(); + size_t n = parms.poly_modulus_degree(); + size_t coeff_modulus_size = coeff_modulus.size(); + auto ntt_tables_ptr = pk_context_data_ptr->small_ntt_tables(); + + assert(pk_context_data_ptr == context.key_context_data()); + assert(pk_wr.pk->data().size() == 2); + + PolyIter pk_iter(pk_wr.pk->data().data(), n, coeff_modulus_size); + inverse_ntt_negacyclic_harvey(pk_iter, pk_wr.pk->data().size(), ntt_tables_ptr); + + pk_wr.is_ntt = false; + // assert(pk_wr.pk->data().is_ntt_form() == false); + // -- Note: We cannot use pk_wr.pk->data().is_ntt_form() directly because this value + // is not + // directly changeable for ciphertexts (and therefore public keys). It is only + // changeable by calling evaluator.transform_to_ntt(), but this function + // throws an exception when you try to give it a public key +} + +void ct_to_ntt_form(Evaluator &evaluator, Ciphertext &c_in) +{ + if (c_in.is_ntt_form() == true) return; + evaluator.transform_to_ntt_inplace(c_in); + assert(c_in.is_ntt_form() == true); +} + +void ct_to_non_ntt_form(Evaluator &evaluator, Ciphertext &c_in) +{ + if (c_in.is_ntt_form() == false) return; + evaluator.transform_from_ntt_inplace(c_in); + assert(c_in.is_ntt_form() == false); +} + +void pt_to_ntt_form(const SEALContext &context, Plaintext &pt) +{ + bool is_ntt = pt.is_ntt_form(); + if (is_ntt == true) return; + + // auto context_data_ptr = context.key_context_data(); + auto context_data_ptr = context.first_context_data(); + auto &parms = context_data_ptr->parms(); + auto &coeff_modulus = parms.coeff_modulus(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t n = parms.poly_modulus_degree(); + auto ntt_tables_ptr = context_data_ptr->small_ntt_tables(); + + RNSIter plaintext(pt.data(), n); + ntt_negacyclic_harvey(plaintext, coeff_modulus_size, ntt_tables_ptr); + + pt.parms_id() = context_data_ptr->parms_id(); + assert(pt.is_ntt_form() == true); +} + +void pt_to_non_ntt_form(const SEALContext &context, Plaintext &pt) +{ + bool is_ntt = pt.is_ntt_form(); + if (is_ntt == false) return; + + auto context_data_ptr = context.first_context_data(); + auto &parms = context_data_ptr->parms(); + auto &coeff_modulus = parms.coeff_modulus(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t n = parms.poly_modulus_degree(); + auto ntt_tables_ptr = context_data_ptr->small_ntt_tables(); + + RNSIter plaintext(pt.data(), n); + inverse_ntt_negacyclic_harvey(plaintext, coeff_modulus_size, ntt_tables_ptr); + + pt.parms_id() = parms_id_zero; + assert(pt.is_ntt_form() == false); +} + +void compare_sk(const SEALContext &context, SecretKey &sk1, SecretKey &sk2, bool incl_sp, + bool should_match) +{ + assert(sk1.data().is_ntt_form() && sk2.data().is_ntt_form()); + + size_t print_size = 8; + + for (int i = 0; i < 2; i++) + { + print_sk_compare("sk2", sk2, "sk1", sk1, context, print_size, incl_sp); + + if (should_match) + cout << "(Above: Values should match)" << endl; + else + cout << "(Above: Values should differ)" << endl; + + bool match_result = same_sk(sk2, sk1, context, incl_sp); + assert(match_result == should_match); + + if (i == 0) + { + sk_to_non_ntt_form(context, sk2); + sk_to_non_ntt_form(context, sk1); + } + else + { + sk_to_ntt_form(context, sk2); + sk_to_ntt_form(context, sk1); + } + } +} + +void compare_pk(const SEALContext &context, PublicKeyWrapper &pk1_wr, PublicKeyWrapper &pk2_wr, + bool incl_sp, bool should_match) +{ + assert(pk1_wr.is_ntt && pk2_wr.is_ntt); + + size_t print_size = 8; + bool compare_both_forms = 1; // Compare both forms just in case + + for (int i = 0; i < 2; i++) + { + print_pk_compare("pk2", pk2_wr, "pk1", pk1_wr, print_size, incl_sp); + + if (should_match) + cout << "(Above: These should be the same)" << endl; + else + cout << "(Above: These should be different)" << endl; + + bool match_result = same_pk(pk1_wr, pk2_wr, incl_sp); + assert(match_result == should_match); + + if (compare_both_forms) + { + if (i == 0) + { + pk_to_non_ntt_form(context, pk2_wr); + pk_to_non_ntt_form(context, pk1_wr); + } + else + { + pk_to_ntt_form(context, pk2_wr); + pk_to_ntt_form(context, pk1_wr); + } + } + else + { + break; + } + } +} diff --git a/adapter/convert.h b/adapter/convert.h new file mode 100644 index 0000000..2f8eb17 --- /dev/null +++ b/adapter/convert.h @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file convert.h + +@brief Functions to convert secret keys, public keys, and ciphertexts to NTT or non-NTT +form, and compare secret key and public key instances. +*/ + +#pragma once + +#include + +#include "generate.h" +#include "seal/seal.h" + +/** +Converts a secret key to NTT form. + +@param[in] context SEAL context +@param[in,out] sk Secret key to convert +*/ +void sk_to_ntt_form(const seal::SEALContext &context, seal::SecretKey &sk); + +/** +Converts a secret key to non-NTT form. + +@param[in] context SEAL context +@param[in,out] sk Secret key to convert +*/ +void sk_to_non_ntt_form(const seal::SEALContext &context, seal::SecretKey &sk); + +/** +Converts a public key to NTT form. + +@param[in] context SEAL context +@param[in,out] pk_wr Wrapper containing public key to convert +*/ +void pk_to_ntt_form(const seal::SEALContext &context, PublicKeyWrapper &pk_wr); + +/** +Converts a public key to non-NTT form. + +@param[in] context SEAL context +@param[in,out] pk_wr Wrapper containing public key to convert +*/ +void pk_to_non_ntt_form(const seal::SEALContext &context, PublicKeyWrapper &pk_wr); + +/** +Converts a ciphertext to NTT form. + +@param[in] evaluator SEAL evaluator +@param[in,out] c_in Ciphertext to convert +*/ +void ct_to_ntt_form(seal::Evaluator &evaluator, seal::Ciphertext &c_in); + +/** +Converts a ciphertext to non-NTT form. + +@param[in] evaluator SEAL evaluator +@param[in,out] c_in Ciphertext to convert +*/ +void ct_to_non_ntt_form(seal::Evaluator &evaluator, seal::Ciphertext &c_in); + +/** +Converts a plaintext to NTT form. + +@param[in] context SEAL context +@param[in,out] pt Plaintext to convert +*/ +void pt_to_ntt_form(const seal::SEALContext &context, seal::Plaintext &pt); + +/** +Converts a plaintext to non-NTT form. + +@param[in] context SEAL context +@param[in,out] pt Plaintext to convert +*/ +void pt_to_non_ntt_form(const seal::SEALContext &context, seal::Plaintext &pt); + +/** +Compares two secret key instances to see if they match. + +Note: This function may modify SecretKey instances to compare NTT and non-NTT forms of the +secret key, but should revert all changes before returning. + +@param[in] context SEAL context +@param[in] sk1 Secret key 1 +@param[in] sk2 Secret key 2 +@param[in] incl_sp If true, compares "special prime" component of secret keys +@param[in] should_match If true, throws an error if sk1 != equal sk2 (debugging only) +*/ +void compare_sk(const seal::SEALContext &context, seal::SecretKey &sk1, seal::SecretKey &sk2, + bool incl_sp, bool should_match); + +/** +Compares two public key instances to see if they match. + +Note: This function may modify PublicKeyWrapper instances to compare NTT and non-NTT forms +of the public key, but should revert all changes before returning. + +@param[in] context SEAL context +@param[in] pk1_wr Public key 1 wrapper +@param[in] pk2_wr Public key 2 wrapper +@param[in] incl_sp If true, compares "special prime" component of public keys +@param[in] should_match If true, throws an error if pk1 != pk2 (debugging only) +*/ +void compare_pk(const seal::SEALContext &context, PublicKeyWrapper &pk1_wr, + PublicKeyWrapper &pk2_wr, bool incl_sp, bool should_match); diff --git a/adapter/fileops.cpp b/adapter/fileops.cpp new file mode 100644 index 0000000..0560b80 --- /dev/null +++ b/adapter/fileops.cpp @@ -0,0 +1,532 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fileops.cpp +*/ + +#include "fileops.h" + +#include +#include +#include + +#include "convert.h" +#include "seal/seal.h" +#include "utils.h" + +using namespace std; +using namespace seal; +using namespace seal::util; + +// #define HIGH_BYTE_FIRST // big endian +// #define DEBUG_EASYMOD // See: SEAL-Embedded for explanation + +// =============================================================== +// Binary file save/load +// (SEAL-Embedded format) +// =============================================================== + +void sk_bin_file_save(string fpath, string str_fpath, const SEALContext &context, + bool use_str_fpath, SecretKey &sk) +{ + auto &sk_parms = context.key_context_data()->parms(); + size_t n = sk_parms.poly_modulus_degree(); + auto sk_ptr = get_sk_arr_ptr(sk); + bool is_ntt = sk.data().is_ntt_form(); + + if (is_ntt) + { + sk_to_non_ntt_form(context, sk); + assert(sk.data().is_ntt_form() == false); + } + + fstream outfile(fpath.c_str(), ios::out | ios::binary | ios::trunc); + fstream outfile2; + if (use_str_fpath) + { + outfile2.open(str_fpath.c_str(), ios::out | ios::trunc); + outfile2 << "#pragma once\n\n#include \"defines.h\"\n\n"; + outfile2 << "#if defined(SE_DATA_FROM_CODE_COPY) || " + "defined(SE_DATA_FROM_CODE_DIRECT)\n"; + outfile2 << "\n#include \n\n"; + // -- # bytes = n vals * (2 bits / val) * (1 byte / 8 bits) + size_t nbytes = n / 4; + outfile2 << "#ifdef SE_DATA_FROM_CODE_COPY\nconst\n#endif" << endl; + outfile2 << "// -- Secret key for polynomial ring degree = " << n << "\n"; + outfile2 << "uint8_t secret_key[" << nbytes << "] = { "; + } + for (size_t i = 0; i < n; i += 4) + { + uint8_t byte = 0; + for (size_t j = 0; ((i + j) < n) && (j < 4); j++) + { + uint64_t sk_val = sk_ptr[i + j]; + if (i < 8) cout << "sk_val: " << sk_val << endl; +#ifdef DEBUG_EASYMOD + // -- Mapping: 0 --> 0, 1 --> 1, q-1 --> 2 + uint8_t val = (sk_val > 1) ? 2 : (uint8_t)sk_val; +#else + // -- Mapping: q-1 --> 0, 0 --> 1, 1 --> 2 + uint8_t val = (sk_val > 1) ? 0 : (uint8_t)(sk_val + 1); + // if (i < 8) cout << "val: " << (uint64_t)val << endl; +#endif + byte |= val << (6 - 2 * j); + } + // TODO: make this more compact? + outfile.put(static_cast(byte)); + if (use_str_fpath) + { + // cout << "byte: " << to_string(byte) << endl; + string next_str = ((i + 4) < n) ? ", " : "};\n"; + outfile2 << to_string(byte) + next_str; + if (!(i % 64) && i) outfile2 << "\n"; + } + } + outfile.close(); + if (use_str_fpath) + { + outfile2 << "#endif" << endl; + outfile2.close(); + } + + if (is_ntt) + { + sk_to_ntt_form(context, sk); + assert(sk.data().is_ntt_form() == true); + } +} + +void sk_bin_file_load(string fpath, const SEALContext &context, SecretKey &sk) +{ + auto &sk_parms = context.key_context_data()->parms(); + auto &coeff_modulus = sk_parms.coeff_modulus(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t n = sk_parms.poly_modulus_degree(); + auto sk_ptr = get_sk_arr_ptr(sk); + bool is_ntt = sk.data().is_ntt_form(); + + // -- Make sure sk is not in NTT form + if (is_ntt) + { + sk_to_non_ntt_form(context, sk); + assert(sk.data().is_ntt_form() == false); + } + + // print_poly("sk_before ", sk_ptr, 8); + fstream outfile(fpath.c_str(), ios::in | ios::binary); + for (size_t i = 0; i < n; i += 4) + { + char byte; + outfile.get(byte); + // cout << "byte[" << i << "]: " << hex << unsigned(byte & 0xFF) << dec << endl; + for (size_t j = 0; ((i + j) < n) && (j < 4); j++) + { + uint8_t val = ((uint8_t)byte >> (6 - 2 * j)) & 0b11; + // cout << "val[" << j << "]: " << hex << unsigned(val) << dec << endl; +#ifdef DEBUG_EASYMOD + // -- Mapping: 0 --> 0, 1 --> 1, 2 --> q-1 + if (val > 1) + { + for (size_t k = 0; k < coeff_modulus_size; k++) + { + sk_ptr[(i + j) + k * n] = coeff_modulus[k].value() - 1; + } + } + else + { + for (size_t k = 0; k < coeff_modulus_size; k++) { sk_ptr[(i + j) + k * n] = val; } + } +#else + // -- Mapping: 0 --> q-1, 1 --> 0, 2 --> 1 + if (val > 0) + { + for (size_t k = 0; k < coeff_modulus_size; k++) + { + sk_ptr[(i + j) + k * n] = val - 1; + } + } + else + { + for (size_t k = 0; k < coeff_modulus_size; k++) + { + sk_ptr[(i + j) + k * n] = coeff_modulus[k].value() - 1; + } + } +#endif + } + } + outfile.close(); + // print_poly("sk_after ", sk_ptr, n*4); + // print_poly("sk_after ", sk_ptr, 8); + // print_poly("sk_after ", sk_ptr + n, 8); + // print_poly("sk_after ", sk_ptr + 2 * n, 8); + // print_poly("sk_after ", sk_ptr + 3 * n, 8); + + // -- Convert sk back to NTT form + if (is_ntt) + { + sk_to_ntt_form(context, sk); + assert(sk.data().is_ntt_form() == true); + } +} + +void pk_bin_file_save(string dirpath, const SEALContext &context, PublicKeyWrapper &pk_wr, + bool incl_sp, bool append) +{ + bool large_modulus = 0; + bool is_ntt = pk_wr.is_ntt; + assert(is_ntt); // Make sure we start out in ntt form + assert(pk_wr.pk); + + bool incl_sp_sf = false; // No need to include the special prime for the headers + + size_t n = pk_wr.pk->data().poly_modulus_degree(); + size_t coeff_modulus_size = pk_wr.pk->data().coeff_modulus_size(); + assert(coeff_modulus_size >= 2); + + string fpath3 = dirpath + "str_pk_addr_array.h"; + fstream outfile3(fpath3.c_str(), ios::out | ios::trunc); + + outfile3 << "#pragma once\n\n#include \"defines.h\"\n\n"; + outfile3 << "#if defined(SE_DATA_FROM_CODE_COPY) || defined(SE_DATA_FROM_CODE_DIRECT)\n\n"; + + stringstream pk_addr_str; + + size_t string_file_coeff_modulus_size = + incl_sp_sf ? coeff_modulus_size : coeff_modulus_size - 1; + pk_addr_str << "ZZ* pk_prime_addr[" << string_file_coeff_modulus_size << "][2] = \n{\n"; + + for (size_t outer = 0; outer < 2; outer++) + { + assert(pk_wr.pk->data().size() == 2); + assert(sizeof(pk_wr.pk->data().data()[0]) == sizeof(uint64_t)); + + for (size_t t = 0; t < coeff_modulus_size; t++) + { + for (size_t k = 0; k < 2; k++) // pk0, pk1 + { + string fpath_common = "pk" + to_string(k) + "_"; + if (pk_wr.is_ntt) fpath_common += "ntt_"; + + auto coeff_modulus = context.key_context_data()->parms().coeff_modulus()[t].value(); + fpath_common += to_string(n) + "_" + to_string(coeff_modulus); + + string fpath = dirpath + fpath_common + ".dat"; + string fpath2 = dirpath + "str_" + fpath_common + ".h"; + cout << "writing to files: " << fpath << ", " << fpath2 << endl; + + if (outer == 0 && t < string_file_coeff_modulus_size) + { + outfile3 << " #include \"str_" << fpath_common + ".h\"" << endl; + } + + auto open_mode_type = ios::out | ios::binary | (append ? ios::app : ios::trunc); + fstream outfile(fpath.c_str(), open_mode_type); + + fstream outfile2(fpath2.c_str(), ios::out | ios::trunc); + size_t num_ZZ_elements = n; + outfile2 << "#pragma once\n\n#include \"defines.h\"\n\n"; + outfile2 << "#if defined(SE_DATA_FROM_CODE_COPY) || " + "defined(SE_DATA_FROM_CODE_DIRECT)\n"; + outfile2 << "#ifdef SE_DATA_FROM_CODE_COPY\nconst\n#endif" << endl; + outfile2 << "ZZ pk" + to_string(k) + "_prime" << to_string(t); + outfile2 << "[" << num_ZZ_elements << "] = { \n"; + + uint64_t *ptr = get_pk_arr_ptr(pk_wr, k); + for (size_t i = 0; i < n; i++) + { + uint64_t data = ptr[i + t * n]; + size_t stop_j = (large_modulus) ? 8 : 4; + for (size_t j = 0; j < stop_j; j++) // write one byte at a time + { + // -- & with 0xFF to prevent 'byte' from being sign extended +#ifdef HIGH_BYTE_FIRST + uint8_t byte = (data >> (56 - 8 * j)) & 0xFF; +#else + uint8_t byte = (data >> (8 * j)) & 0xFF; + outfile.put((char)byte); +#endif + } + + // -- Write to string file + uint32_t val = data & 0xFFFFFFFF; + string next_str = ((i + 1) < n) ? ", " : "};\n"; + outfile2 << hex << "0x" << (large_modulus ? data : val) << next_str; + size_t row_break = large_modulus ? 4 : 8; + if (!(i % row_break)) outfile2 << "\n"; + } + outfile.close(); + outfile2 << "#endif" << endl; + outfile2.close(); + } + // TODO: get rid of special prime stuff since this must be included as + // part of the three primes... + // -- No need to include special prime in string files + if (outer == 0 && t < string_file_coeff_modulus_size) + { + pk_addr_str << " {&(pk0_prime" << to_string(t) << "[0]),"; + pk_addr_str << " &(pk1_prime" << to_string(t) << "[0])}"; + if (t == string_file_coeff_modulus_size - 1) + pk_addr_str << "\n};" << endl; + else + pk_addr_str << "," << endl; + } + if (!incl_sp && t == (coeff_modulus_size - 2)) break; + } + + if (pk_wr.is_ntt) // Convert and write in non-ntt form + { + pk_to_non_ntt_form(context, pk_wr); + assert(pk_wr.is_ntt == false); + } + if (outer == 0) { outfile3 << "\n"; } + } + + outfile3 << pk_addr_str.str(); + outfile3 << "#endif" << endl; + outfile3.close(); + + if (is_ntt) + { + pk_to_ntt_form(context, pk_wr); + assert(pk_wr.is_ntt == true); + } +} + +void pk_bin_file_load(string dirpath, const SEALContext &context, PublicKeyWrapper &pk_wr, + bool incl_sp) +{ + bool is_ntt = pk_wr.is_ntt; + bool large_modulus = 0; + assert(pk_wr.pk); + + if (is_ntt) + { + pk_to_non_ntt_form(context, pk_wr); + assert(pk_wr.is_ntt == false); + } + + assert(pk_wr.pk->data().size() == 2); + assert(sizeof(pk_wr.pk->data().data()[0]) == sizeof(uint64_t)); + + size_t n = pk_wr.pk->data().poly_modulus_degree(); + size_t coeff_modulus_size = pk_wr.pk->data().coeff_modulus_size(); + assert(coeff_modulus_size >= 2); + + for (size_t t = 0; t < coeff_modulus_size; t++) + { + for (size_t k = 0; k < 2; k++) + { + string fpath = dirpath + "pk" + to_string(k) + "_"; + if (pk_wr.is_ntt) fpath += "ntt_"; + + auto coeff_modulus = context.key_context_data()->parms().coeff_modulus()[t].value(); + fpath += to_string(n) + "_" + to_string(coeff_modulus) + ".dat"; + cout << "reading from file: " << fpath << endl; + + fstream infile(fpath.c_str(), ios::in | ios::binary); + + uint64_t *ptr = get_pk_arr_ptr(pk_wr, k); + for (size_t i = 0; i < n; i++) // # of elements in pk0, pk1 + { + uint64_t data = 0; + size_t stop_j = (large_modulus) ? 8 : 4; + for (size_t j = 0; j < stop_j; j++) // write one byte at a time + { + char byte; + infile.get(byte); + + // -- & with 0xFF to prevent 'byte' from being sign extended +#ifdef HIGH_BYTE_FIRST + data <<= 8; + data |= (byte & 0xFF); +#else + data |= ((uint64_t)(byte & 0xFF) << 8 * j); +#endif + } + ptr[i + t * n] = data; + } + infile.close(); + } + if (!incl_sp && t == (coeff_modulus_size - 2)) break; + } + + if (is_ntt) + { + pk_to_ntt_form(context, pk_wr); + assert(pk_wr.is_ntt == true); + } +} + +// ============================================================== +// Binary file save/load +// (SEAL format) +// ============================================================== + +void sk_seal_save(string fpath, SecretKey &sk, bool compress) +{ + string action = "Saving secret key to file at \"" + fpath + "\""; + + fstream file(fpath, fstream::binary | fstream::out | fstream::trunc); + exit_on_err_file(file, action, 0); + + cerr << action << " ..." << endl; + if (compress) + sk.save(file, compr_mode_type::zstd); + else + sk.save(file, compr_mode_type::none); + file.close(); +} + +void sk_seal_load(string fpath, const SEALContext &context, SecretKey &sk) +{ + string action = "Loading secret key from file at \"" + fpath + "\""; + + fstream file(fpath, fstream::binary | fstream::in); + exit_on_err_file(file, action, 1); + + cerr << action << " ..." << endl; + sk.load(context, file); + file.close(); +} + +void pk_seal_save(string fpath, PublicKey &pk, bool compress) +{ + string action = "Saving public key to file at \"" + fpath + "\""; + + fstream file(fpath, fstream::binary | fstream::out | fstream::trunc); + exit_on_err_file(file, action, 0); + + cerr << action << " ..." << endl; + if (compress) + pk.save(file, compr_mode_type::zstd); + else + pk.save(file, compr_mode_type::none); + file.close(); +} + +void pk_seal_load(string fpath, const SEALContext &context, PublicKey &pk) +{ + string action = "Loading public key from file at \"" + fpath + "\""; + + fstream file(fpath, fstream::binary | fstream::in); + exit_on_err_file(file, action, 1); + cerr << action << " ..." << endl; + pk.load(context, file); + file.close(); +} + +// ============================================================== +// String file save/load +// (Mainly for debugging) +// ============================================================== + +streampos sk_string_file_load(string fpath, const SEALContext &context, SecretKey &sk) +{ + auto &sk_parms = context.key_context_data()->parms(); + auto &coeff_modulus = sk_parms.coeff_modulus(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t n = sk_parms.poly_modulus_degree(); + auto sk_ptr = get_sk_arr_ptr(sk); + bool is_ntt = sk.data().is_ntt_form(); + + // -- Make sure sk is not in NTT form + // -- Note: this shouldn't run if we don't generate sk using keygenerator + if (is_ntt) + { + sk_to_non_ntt_form(context, sk); + assert(sk.data().is_ntt_form() == false); + } + + auto filepos = poly_string_file_load(fpath, sk_ptr, 1); + + for (size_t i = 0; i < n; i++) + { + uint64_t val = sk_ptr[i]; +#ifdef DEBUG_EASYMOD + if (val > 1) + { + for (size_t j = 0; j < coeff_modulus_size; j++) + { + sk_ptr[i + j * n] = coeff_modulus[j].value() - 1; + } + } + else + { + for (size_t j = 0; j < coeff_modulus_size; j++) { sk_ptr[i + j * n] = val; } + } +#else + if (val > 0) + { + for (size_t j = 0; j < coeff_modulus_size; j++) { sk_ptr[i + j * n] = val - 1; } + } + else + { + for (size_t j = 0; j < coeff_modulus_size; j++) + { + sk_ptr[i + j * n] = coeff_modulus[j].value() - 1; + } + } +#endif + } + + // -- Convert back to NTT form if necessary + if (is_ntt) + { + sk_to_ntt_form(context, sk); + assert(sk.data().is_ntt_form() == true); + } + + return filepos; +} + +streampos ct_string_file_load(string fpath, const SEALContext &context, Evaluator &evaluator, + Ciphertext &ct, streampos filepos_in) +{ + auto &ct_parms = context.first_context_data()->parms(); + auto &ct_parms_id = context.first_parms_id(); + size_t coeff_modulus_size = ct_parms.coeff_modulus().size(); + size_t n = ct_parms.poly_modulus_degree(); + bool is_ntt = ct.is_ntt_form(); + + assert(is_ntt); + assert(coeff_modulus_size == 3); + + // -- Ciphertext has two components + ct.resize(context, ct_parms_id, 2); + + // -- Make sure ciphertext is in NTT form + if (!is_ntt) + { + ct_to_non_ntt_form(evaluator, ct); + assert(ct.is_ntt_form() == false); + } + + vector ct_temp_1p(2 * n); + + streampos filepos = filepos_in; + for (size_t j = 0; j < coeff_modulus_size; j++) + { + // -- Load in two components at a time + filepos = poly_string_file_load(fpath, ct_temp_1p, 2, filepos); + auto ct_ptr = get_ct_arr_ptr(ct); + + // -- Set c0 and c1 terms + for (size_t i = 0; i < n; i++) + { + ct_ptr[i + j * n] = ct_temp_1p[i]; + ct_ptr[i + j * n + coeff_modulus_size * n] = ct_temp_1p[i + n]; + } + } + print_ct(ct, 8); + + // -- Convert ciphertext back to non-NTT form if necessary + if (!is_ntt) + { + ct_to_ntt_form(evaluator, ct); + assert(ct.is_ntt_form() == true); + } + + return filepos; +} diff --git a/adapter/fileops.h b/adapter/fileops.h new file mode 100644 index 0000000..767aab0 --- /dev/null +++ b/adapter/fileops.h @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fileops.h + +@brief Functions to load and save keys and ciphertexts and other objects to files. +*/ + +#pragma once + +#include +#include +#include + +#include "seal/seal.h" +#include "utils.h" + +// ----------------------------------------------------- +// ---------------- Utility functions ------------------ +// ----------------------------------------------------- + +/** +Returns the number of bytes in a file + +@param[in] file File to check +*/ +inline std::size_t size_of_file(std::fstream &file) +{ + file.seekg(0, std::ios::end); + std::size_t size = file.tellg(); + file.seekg(0, std::ios::beg); + return size; +} + +/** +Exits if a file is not open or on file failure. Optionally checks that the file is not +empty. If failure is detected, closes file and exits. + +@param[in] file File to check +@param[in] msg Message to print on exit +@param[in] check_size If true, checks that the file is not empty +*/ +inline void exit_on_err_file(std::fstream &file, std::string msg, bool check_size) +{ + if (!file.is_open()) + { + std::cerr << "Error: File is not open." << std::endl; + goto close_and_exit; + } + if (file.fail()) + { + std::cerr << "Error: File failed." << std::endl; + goto close_and_exit; + } + if (check_size && !size_of_file(file)) + { + std::cerr << "Error: File is empty." << std::endl; + goto close_and_exit; + } + return; + +close_and_exit: + file.close(); + exit_on_err(1, msg); +} + +// =============================================================== +// Binary file save/load +// (SEAL-Embedded format) +// =============================================================== + +/** +Saves the secret key to a binary file. Optionally also creates a code file containing the +hard-coded values of the secret key. This file may then be compiled with the SEAL-Embedded +library. + +Note: This stores the secret key in **compressed** form. + +Note: This function may modify sk in order to write to file, but should revert all changes +before returning. + +@param[in] fpath Path to file to save secret key values in binary form +@param[in] str_fpath Path to file to hard-code secret key bytes in a code file for +SEAL-Embedded +@param[in] context SEAL context +@param[in] use_str_fpath If true, hard codes secret key bytes to file at str_fpath +@param[in] sk Secret key instance +*/ +void sk_bin_file_save(std::string fpath, std::string str_fpath, const seal::SEALContext &context, + bool use_str_fpath, seal::SecretKey &sk); + +// TODO: this currently includes the extra prime... +/** +Loads the secret key from a binary file generating using 'sk_bin_file_save'. + +Note: This assumes the secret key was saved in **compressed** form. + +@param[in] fpath Path to file to load the secret key values, stored in binary form +@param[in] context SEAL context +@param[out] sk Secret key instance +*/ +void sk_bin_file_load(std::string fpath, const seal::SEALContext &context, seal::SecretKey &sk); + +void pk_bin_file_save(std::string dirpath, const seal::SEALContext &context, + PublicKeyWrapper &pk_wr, bool incl_sp, bool append = 0); + +/** +Loads a public key from a SEAL-Embedded-formatted binary file. + +@param[in] dirpath Path to directory containing public key file +@param[in] context SEAL context +@param[in, out] pk_wr Public key wrapper instance +@param[in] inc_sp If true, reads in bytes for special prime as well +*/ +void pk_bin_file_load(std::string dirpath, const seal::SEALContext &context, + PublicKeyWrapper &pk_wr, bool incl_special_prime); + +// ============================================================== +// Binary file save/load +// (SEAL format) +// ============================================================== + +/** +Saves a secret key to a binary file in SEAL form. + +@param[in] fpath Binary file to save the secret key +@param[in] sk Secret key instance +@param[in] compress If true, compresses the secret key w/ zstd before saving +*/ +void sk_seal_save(std::string fpath, seal::SecretKey &sk, bool compress = true); + +/** +Loads a secret key from a SEAL-formatted binary file. + +@param[in] fpath Binary file containing secret key in SEAL form +@param[in] context SEAL context +@param[out] sk Secret key instance +*/ +void sk_seal_load(std::string fpath, const seal::SEALContext &context, seal::SecretKey &sk); + +/** +Saves a public key to a binary file in SEAL form. + +@param[in] fpath Binary file to save the public key +@param[in] pk Public key instance +@param[in] compress If true, compresses the public key w/ zstd before saving +*/ +void pk_seal_save(std::string fpath, seal::PublicKey &pk, bool compress = true); + +/** +Loads a public key from a SEAL-formatted binary file. + +@param[in] fpath Binary file containing public key in SEAL form +@param[in] context SEAL context +@param[out] pk Public key instance +*/ +void pk_seal_load(std::string fpath, const seal::SEALContext &context, seal::PublicKey &pk); + +// ============================================================== +// std::string file save/load +// (Mainly for debugging) +// ============================================================== + +/** + */ +std::streampos sk_string_file_load(std::string fpath, const seal::SEALContext &context, + seal::SecretKey &sk); + +// This is not memory efficient at all +// File should be in the following format: +// This is just for testing, so can be slow +// File must follow a format similar to: +// ct0 : { x, x, x, x, x} +// ct1 : { x, x, x, x, x} +// ct0 : { x, x, x, x, x} --> w.r.t. next prime +// ct1 : { x, x, x, x, x} --> w.r.t. next prime +// ... + +/** +This will call poly_string_file_load +*/ +std::streampos ct_string_file_load(std::string fpath, const seal::SEALContext &context, + seal::Evaluator &evaluator, seal::Ciphertext &ct, + std::streampos filepos_in = 0); + +// This is just for testing, so can be slow +// std::string must follow a format similar to: +// Example file: +// ct0 : { x, x, x, x, x} +// ct1 : { x, x, x, x, x} +// ct0 : { x, x, x, x, x} +// ct1 : { x, x, x, x, x} +// ... + +/** +@returns End position of ftell after reading 'num_elements' type-T elements of 'invec' +starting from pos +*/ +template +std::streampos poly_string_file_load(std::string fname, T *invec, std::size_t ncomponents, + std::streampos pos = 0) +{ + std::vector vec_temp; + std::fstream infile(fname.c_str(), std::ios::in); + std::cout << std::endl << "opening file at: " << fname << std::endl << std::endl; + assert(infile.is_open()); + + char ch; + std::size_t idx = 0; + + // -- In case we have multiple lines in a file + if (pos) { infile.seekg(pos); } + + while ((idx < ncomponents) && infile.get(ch)) + { + // -- First, find the opening bracket + if (ch != '{') continue; + + vec_temp.clear(); + for (std::size_t i = 0; !infile.eof(); i++) + { + std::string val; + + // -- Read in a single byte + infile >> val; + + // -- Break when we find the closing bracket + if (val.find("}") != std::string::npos) break; + + val.erase(remove(val.begin(), val.end(), ','), val.end()); + + T val_temp; + if (std::is_same::value) + { + val_temp = static_cast(std::stod(val.c_str(), 0)); + } + else if (std::is_same::value) + { + val_temp = static_cast(std::strtoull(val.c_str(), 0, 10)); + } + else if (std::is_same::value) + { + val_temp = static_cast(std::strtoll(val.c_str(), 0, 10)); + } + else + { + std::cout << "Error! Type not accounted for." << std::endl; + exit(0); + } + + // std::cout << "idx: " << i << " value: " << val_temp << std::endl; + + // -- Store found values in a vector + vec_temp.push_back(val_temp); + } + + // -- Copy read elements into input vector at specific location + std::size_t N = vec_temp.size(); + // print_poly("vec_temp", vec_temp, N); + for (std::size_t i = 0; i < N; i++) { invec[i + idx * N] = vec_temp[i]; } + idx++; + } + std::streampos curr_pos = infile.tellg(); + assert(infile.is_open()); + infile.close(); + std::cout << "curr_pos: " << curr_pos << std::endl; + return curr_pos; +} + +template +std::streampos poly_string_file_load(std::string fname, std::vector &invec, + std::size_t ncomponents, std::streampos pos = 0) +{ + return poly_string_file_load(fname, &(invec[0]), ncomponents, pos); +} diff --git a/adapter/generate.cpp b/adapter/generate.cpp new file mode 100644 index 0000000..a996896 --- /dev/null +++ b/adapter/generate.cpp @@ -0,0 +1,488 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file generate.cpp +*/ + +#include "generate.h" + +#include +#include +#include +#include + +#include "convert.h" +#include "fileops.h" +#include "seal/seal.h" +#include "utils.h" + +using namespace std; +using namespace seal; +using namespace seal::util; + +void gen_save_secret_key(string sk_fpath, string str_sk_fpath, string seal_sk_fpath, + const SEALContext &context) +{ + KeyGenerator keygen(context); + SecretKey sk1 = keygen.secret_key(); + assert(sk1.data().is_ntt_form()); + + sk_bin_file_save(sk_fpath, str_sk_fpath, context, true, sk1); + sk_seal_save(seal_sk_fpath, sk1); + + // -- Check to make sure we can read back secret key correctly. + // -- First, generate a new secret key. This should be the same as the + // old secret key since it will be created from keygen's copy. + bool incl_sp = 1; // include special prime + SecretKey sk2 = sk1; + compare_sk(context, sk1, sk2, incl_sp, true); + + // -- Next, clear sk2 so we can read in values from file. + clear_sk(context, sk2); // overwrite internal keygen copy of sk + compare_sk(context, sk1, sk2, incl_sp, false); + + // -- Finally, read in the secret key from file. The objects should now match. + cout << "\nAbout to read secret key from binary file at \" " << sk_fpath << "\" ..." << endl; + sk_bin_file_load(sk_fpath, context, sk2); + compare_sk(context, sk1, sk2, incl_sp, true); +} + +void gen_save_public_key(string dirpath, string seal_pk_fpath, string sk_fpath, + string seal_sk_fpath, const SEALContext &context, bool use_seal_sk_fpath) +{ + SecretKey sk; + if (use_seal_sk_fpath) + sk_seal_load(seal_sk_fpath, context, sk); + else + { + auto &sk_parms = context.key_context_data()->parms(); + auto &coeff_modulus = sk_parms.coeff_modulus(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t n = sk_parms.poly_modulus_degree(); + sk.data().resize(mul_safe(n, coeff_modulus_size)); + sk_bin_file_load(sk_fpath, context, sk); + sk.data().parms_id() = context.key_parms_id(); + } + + KeyGenerator keygen(context, sk); + + PublicKey pk1; + PublicKeyWrapper pk1_wr; + keygen.create_public_key(pk1); + pk1_wr.pk = &pk1; + pk1_wr.is_ntt = pk1.data().is_ntt_form(); // Must manually track this + assert(pk1_wr.is_ntt == true); // Should start out being true + pk_seal_save(seal_pk_fpath, pk1); + + // -- Write the public key, prime by prime, across multiple files + // Make sure to write special prime to file as well. + bool incl_sp = 1; + pk_bin_file_save(dirpath, context, pk1_wr, incl_sp); + + // -- Check to make sure we can read back public key correctly. -- + + // -- Instantiate new pk. Special prime will not be the same throughout + PublicKey pk2; + PublicKeyWrapper pk2_wr; + keygen.create_public_key(pk2); // A new public key + pk2_wr.pk = &pk2; + pk2_wr.is_ntt = pk2.data().is_ntt_form(); // Must manually track this + assert(pk2_wr.is_ntt == true); // Should start out being true + + // -- At this point, the public keys should be different + compare_pk(context, pk1_wr, pk2_wr, incl_sp, false); + + // -- Read in saved public key from file + pk_bin_file_load(dirpath, context, pk2_wr, incl_sp); + compare_pk(context, pk1_wr, pk2_wr, incl_sp, true); +} + +void gen_save_ifft_roots(string dirpath, const SEALContext &context, bool high_byte_first, + bool string_roots) +{ + size_t n = context.key_context_data()->parms().poly_modulus_degree(); + vector> ifft_roots(n); + + shared_ptr complex_roots; + complex_roots = make_shared(ComplexRoots(2 * n, MemoryPoolHandle::Global())); + + int logn = static_cast(log2(n)); + bool better_order = 1; + if (better_order) + { + for (size_t i = 0; i < n; i++) + { + ifft_roots[i] = + conj(complex_roots->get_root(static_cast(reverse_bits(i - 1, logn) + 1))); + } + } + else + { + for (size_t i = 0; i < n; i++) + { + ifft_roots[i] = + conj(complex_roots->get_root(static_cast(reverse_bits(i, logn)))); + } + } + + string fname = dirpath + "ifft_roots_" + to_string(n) + +".dat"; + fstream outfile(fname.c_str(), ios::out | ios::binary | ios::trunc); + + fstream outfile2; + if (string_roots) + { + string fname2 = dirpath + "str_ifft_roots.h"; + // string fname2 = dirpath + "str_ifft_roots_" + to_string(n) + ".h"; + outfile2.open(fname2, ios::out | ios::trunc); + size_t num_uint64_elements = n * 2; // real part + imag part + outfile2 << "#pragma once\n\n#include \"defines.h\"\n\n#include \n\n"; + outfile2 << "#if defined(SE_DATA_FROM_CODE_COPY) || " + "defined(SE_DATA_FROM_CODE_DIRECT)\n"; + outfile2 << "#ifdef SE_IFFT_LOAD_FULL\n"; + outfile2 << "#ifdef SE_DATA_FROM_CODE_COPY\nconst\n#endif" << endl; + outfile2 << "// -- IFFT roots for polynomial ring degree = " << n << "\n"; + outfile2 << "uint64_t ifft_roots_save[" << num_uint64_elements << "] = { "; + } + for (size_t i = 0; i < n; i++) + { + // -- Debugging info + // cout << "ifft_roots[" << i << "]: " << "real: " << real(ifft_roots[i]); + // cout << " imag: " << imag(ifft_roots[i]) << endl; + + for (size_t k = 0; k < 2; k++) + { + double data_d = (!k) ? real(ifft_roots[i]) : imag(ifft_roots[i]); + uint64_t data = *((uint64_t *)(&data_d)); // we can only shift an int type + + // -- Debugging info + // if (i < 5) cout << "data_d: " << data_d << endl; + // if (i < 5) cout << "data : " << data << endl; + + for (size_t j = 0; j < 8; j++) // write one byte at a time + { + size_t shift_amt = (high_byte_first) ? 7 - j : j; + uint8_t byte = (data >> (8 * shift_amt)) & 0xFF; + outfile.put(static_cast(byte)); + } + if (string_roots) + { + // cout << "data string: " << to_string(data) << endl; + string next_str = (((i + 1) < n) || !k) ? ", " : "};\n"; + outfile2 << to_string(data) + "ULL" << next_str; + if (!(i % 64) && i && k) outfile2 << "\n"; + } + } + } + outfile.close(); + if (string_roots) + { + outfile2 << "\n#endif\n#endif" << endl; + outfile2.close(); + } +} + +void gen_save_ntt_roots_header(string dirpath, const SEALContext &context, bool inverse) +{ + auto &key_parms = context.key_context_data()->parms(); + size_t n = key_parms.poly_modulus_degree(); + size_t coeff_modulus_size = key_parms.coeff_modulus().size(); + assert(coeff_modulus_size >= 2); + + string fpath = dirpath + "str_"; + if (inverse) fpath += "i"; + fpath += "ntt_roots_addr_array.h"; + fstream outfile(fpath.c_str(), ios::out | ios::trunc); + + bool incl_sp_sf = false; // No need to include the special prime for the headers + + // const bool large_primes = false; // Assume this + + outfile << "#pragma once\n\n#include \"defines.h\"\n\n"; + outfile << "#if defined(SE_DATA_FROM_CODE_COPY) || defined(SE_DATA_FROM_CODE_DIRECT)\n\n"; + outfile << "#include \n\n"; + + stringstream pk_addr_str; + + size_t string_file_coeff_modulus_size = + incl_sp_sf ? coeff_modulus_size : coeff_modulus_size - 1; + + for (size_t outer = 0; outer < 2; outer++) + { + if (inverse) + { + if (outer == 0) + outfile << "#ifdef SE_INTT_REG\n"; + else + outfile << "#elif defined(SE_INTT_FAST)\n"; + } + else + { + if (outer == 0) + outfile << "#ifdef SE_NTT_REG\n"; + else + outfile << "#elif defined(SE_NTT_FAST)\n"; + } + for (size_t t = 0; t < string_file_coeff_modulus_size; t++) + { + string fpath_common = "roots_"; + auto coeff_modulus = context.key_context_data()->parms().coeff_modulus()[t].value(); + fpath_common += to_string(n) + "_" + to_string(coeff_modulus); + + string fpath = dirpath + "str_"; + if (inverse) fpath += "i"; + fpath += "ntt" + fpath_common + ".h"; + cout << "writing to file: " << fpath << endl; + + outfile << " #include \"str_"; + if (inverse) outfile << "i"; + outfile << "ntt_"; + if (outer) outfile << "fast_"; + outfile << fpath_common + ".h\"" << endl; + } + if (outer == 1) outfile << "#endif\n"; + } + + outfile << "\nZZ* "; + if (inverse) outfile << "i"; + outfile << "ntt_roots_addr[" << string_file_coeff_modulus_size << "] =\n{\n"; + + // -- No need to include special prime in string files + for (size_t t = 0; t < string_file_coeff_modulus_size; t++) + { + outfile << " &(((ZZ*)("; + if (inverse) outfile << "i"; + outfile << "ntt_roots_save_prime" << to_string(t) << "))[0])"; + if (t == string_file_coeff_modulus_size - 1) + outfile << "\n};" << endl; + else + outfile << "," << endl; + } + outfile << "\n#endif\n"; + outfile.close(); +} + +void gen_save_ntt_roots(string dirpath, const SEALContext &context, bool lazy, bool inverse, + bool high_byte_first, bool string_roots) +{ + auto ntt_tables_ptr = context.key_context_data()->small_ntt_tables(); + auto &key_parms = context.key_context_data()->parms(); + size_t n = key_parms.poly_modulus_degree(); + + const bool large_primes = false; + for (size_t mod_loop = 0; mod_loop < key_parms.coeff_modulus().size(); mod_loop++) + { + const NTTTables *tables = &(ntt_tables_ptr[mod_loop]); + Modulus modulus = tables->modulus(); + MultiplyUIntModOperand inv_n = tables->inv_degree_modulo(); + MultiplyUIntModOperand w_n_minus_1 = tables->get_from_inv_root_powers(n - 1); + MultiplyUIntModOperand inv_n_w; + inv_n_w.set(multiply_uint_mod(inv_n.operand, w_n_minus_1, modulus), modulus); + + // -- Debugging + cout << "inv_n.operand : " << inv_n.operand << endl; + cout << "inv_n.quotient : large: " << inv_n.quotient; + cout << " small: " << upper32(inv_n.quotient) << endl; + cout << "inv_n_w.operand: " << inv_n_w.operand << endl; + cout << "inv_n.quotient : large: " << inv_n_w.quotient; + cout << " small: " << upper32(inv_n_w.quotient) << endl; + + // -- Create file + string fname = dirpath; + fname += (!inverse) ? "ntt" : "intt"; + // if (lazy) fname += "_lazy"; + if (lazy) fname += "_fast"; + fname += "_roots_" + to_string(n) + "_" + to_string(modulus.value()) + ".dat"; + fstream outfile(fname.c_str(), ios::out | ios::binary | ios::trunc); + cout << "Writing to " << fname << endl; + + fstream outfile2; + if (string_roots) + { + string fname2 = dirpath; + fname2 += (!inverse) ? "str_ntt" : "str_intt"; + // if (lazy) fname2 += "_lazy"; + if (lazy) fname2 += "_fast"; + fname2 += "_roots_" + to_string(n) + "_" + to_string(modulus.value()) + ".h"; + outfile2.open(fname2, ios::out | ios::trunc); + size_t num_elements = lazy ? n * 2 : n; // real part + imag part + outfile2 << "#pragma once\n\n#include \"defines.h\"\n\n"; + outfile2 << "#if defined(SE_DATA_FROM_CODE_COPY) || " + "defined(SE_DATA_FROM_CODE_DIRECT)\n"; + outfile2 << "#include \n\n"; // uint32_t + if (lazy) + { + if (inverse) + outfile2 << "#ifdef SE_INTT_FAST\n"; + else + outfile2 << "#ifdef SE_NTT_FAST\n"; + } + else + { + if (inverse) + outfile2 << "#ifdef SE_INTT_REG\n"; + else + outfile2 << "#ifdef SE_NTT_REG\n"; + } + outfile2 << "#ifdef SE_DATA_FROM_CODE_COPY\nconst\n#endif" << endl; + // outfile2 << "uint64_t "; + outfile2 << "ZZ "; + if (inverse) outfile2 << "i"; + outfile2 << "ntt_"; + // if (lazy) outfile2 << "fast_"; + outfile2 << "roots_save_prime" << to_string(mod_loop); + outfile2 << "[" << num_elements << "] = { "; + } + + for (size_t i = 0; i < n; i++) + { + // -- Quotient stores floor(operand * 2^64 / q). + // If SEAL-Embedded is using uint32_t datatypes, we actually + // need floor(operand * 2^32 / q). This happens to be + // floor(quotient / 2^32), i.e. the upper 32-bits of quotient. + // (In this case, operand = root and quotient = root_prime.) + const MultiplyUIntModOperand w = + (!inverse) ? tables->get_from_root_powers(i) : tables->get_from_inv_root_powers(i); + + // -- Debugging info + if (i < 5) + { + cout << "root[" << i << "]: operand = " << w.operand << " , quotient = "; + if (large_primes) + cout << w.quotient << endl; + else + cout << upper32(w.quotient) << endl; + } + + for (size_t k = 0; k < 1 + static_cast(lazy); k++) + { + uint64_t data = (!k) ? w.operand : w.quotient; + if (k && !large_primes) { data = static_cast(upper32(data)); } + size_t primesize = large_primes ? 8 : 4; // size in bytes + for (size_t j = 0; j < primesize; j++) // write one byte at a time + { + size_t shift_amt = high_byte_first ? primesize - 1 - j : j; + uint8_t byte = (data >> (8 * shift_amt)) & 0xFF; + outfile.put(static_cast(byte)); + } + if (string_roots) + { + string next_str = (((i + 1) < n) || (!k && lazy)) ? ", " : "};\n"; + string ulong_str = large_primes ? "ULL" : ""; + outfile2 << to_string(data) + ulong_str << next_str; + if (!(i % 64) && i && !k) outfile2 << "\n"; + } + } + } + outfile.close(); + if (string_roots) + { + outfile2 << "\n#endif\n#endif\n" << endl; + outfile2.close(); + } + } + gen_save_ntt_roots_header(dirpath, context, inverse); +} + +/** +Helper function for gen_save_index_map. Generates the bit-reversed value of the input. +Requires numbits to be at most 16. + +@param[in] input Value to be bit-reversed +@param[in] numbits Number of bits to reverse +*/ +static size_t index_map_bit_rev(size_t input, size_t numbits) +{ + assert(numbits <= 16); + size_t t = (((input & 0xaaaa) >> 1) | ((input & 0x5555) << 1)); + t = (((t & 0xcccc) >> 2) | ((t & 0x3333) << 2)); + t = (((t & 0xf0f0) >> 4) | ((t & 0x0f0f) << 4)); + t = (((t & 0xff00) >> 8) | ((t & 0x00ff) << 8)); + return numbits == 0 ? 0 : t >> (16 - (size_t)(numbits)); +} + +void gen_save_index_map(string dirpath, const SEALContext &context, bool high_byte_first) +{ + auto &key_parms = context.key_context_data()->parms(); + size_t n = key_parms.poly_modulus_degree(); + uint64_t m = (uint64_t)n * 2; // m = 2n + size_t slot_count = n / 2; // slot_count = n/2 + size_t logn = static_cast(log2(n)); // number of bits to represent n + + // -- If n > 16384, cannot use uint16_t + assert(n < 16384); + + // -- 3 generates a multiplicative group mod 2^n with order n/2 + // (Note: 5 would work as well, but 3 matches SEAL and SEAL-Embedded) + uint64_t gen = 3; + // uint64_t gen = 5; + uint64_t pos = 1; + + uint16_t *index_map = (uint16_t *)calloc(n, sizeof(uint16_t)); + for (size_t i = 0; i < n / 2; i++) + { + // -- We want index1 + index2 to equal n-1 + size_t index1 = ((size_t)pos - 1) / 2; + size_t index2 = n - index1 - 1; + + // -- Merge index mapping step w/ bitrev step req. for later application of + // IFFT/NTT + index_map[i] = (uint16_t)index_map_bit_rev(index1, logn); + index_map[i + slot_count] = (uint16_t)index_map_bit_rev(index2, logn); + + // -- Next root + pos *= gen; + + // -- Since m is a power of 2, m-1 sets all bits less significant than the '1' + // bit in the value m. A bit-wise 'and' with (m-1) is therefore essentially + // a reduction moduluo m. Ex: m = 4 = 0100, m-1 = 3 = 0011 --> if pos = 21 + // = 10101: 21 % 4 = 1 = 10101 & 0011 + pos &= (m - 1); + } + + // -- Save to binary file + { + string fname = dirpath + "index_map_" + to_string(n) + ".dat"; + fstream outfile(fname.c_str(), ios::out | ios::binary | ios::trunc); + cout << "Writing to " << fname << endl; + for (size_t i = 0; i < n; i++) + { + for (size_t j = 0; j < 2; j++) // write one byte at a time + { + size_t shift_amt = (high_byte_first) ? 1 - j : j; + uint8_t byte = (index_map[i] >> (8 * shift_amt)) & 0xFF; + outfile.put(static_cast(byte)); + } + } + outfile.close(); + } + + // -- Hard-code in code file + { + string fname = dirpath + "str_index_map.h"; + fstream outfile(fname.c_str(), ios::out | ios::binary | ios::trunc); + cout << "Writing to " << fname << endl; + + outfile << "#pragma once\n\n#include \"defines.h\"\n\n"; + outfile << "#if defined(SE_DATA_FROM_CODE_COPY) || " + "defined(SE_DATA_FROM_CODE_DIRECT)\n"; + outfile << "#if defined(SE_INDEX_MAP_LOAD) || " + "defined(SE_INDEX_MAP_LOAD_PERSIST)\n"; + outfile << "#include \n\n"; // uint64_t ? + outfile << "#ifdef SE_DATA_FROM_CODE_COPY\nconst\n#endif" << endl; + outfile << "// -- index map indices for polynomial ring degree = " << n << "\n"; + outfile << "uint32_t index_map_store[" << n / 2 << "] = { "; + uint32_t *index_map_str_save = (uint32_t *)index_map; + + size_t i_stop = n / 2; // half since we are storing as uint32 instead of uint16 + for (size_t i = 0; i < i_stop; i++) + { + string next_str = ((i + 1) < i_stop) ? ", " : "};\n"; + outfile << "0x" << hex << index_map[i] << next_str; + if (!(i % 13)) outfile << "\n"; + } + outfile << "\n#endif\n#endif" << endl; + outfile.close(); + } + free(index_map); +} diff --git a/adapter/generate.h b/adapter/generate.h new file mode 100644 index 0000000..10f63e3 --- /dev/null +++ b/adapter/generate.h @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file generate.h + +@brief Functions to generate secret keys, public keys, ifft/ntt roots, and index map (i.e. +pi-inverse) values. Useful for generating objects to load onto a SEAL-Embedded device. +*/ + +#pragma once + +#include "seal/seal.h" + +/** +Wrapper struct for Public key. Required to keep track of whether public key is in NTT +form. + +@param pk Pointer to public key +@param is_ntt If true, pk is in NTT form +*/ +typedef struct PublicKeyWrapper +{ + seal::PublicKey *pk; + bool is_ntt; +} PublicKeyWrapper; + +/** +Returns the upper 32 bits of a 64-bit value +*/ +static inline std::uint32_t upper32(std::uint64_t val) +{ + return (val >> 32) & 0xFFFFFFFF; +} + +/** +Generates and saves a secret key to file. + +@param[in] sk_fpath Path to file to store the secret key in SEAL-Embedded form +@param[in] str_sk_fpath Path to code file to hard-code the values of the secret key +@param[in] seal_sk_fpath Path to file to store the secret key in SEAL form +@param[in] context SEAL context +*/ +void gen_save_secret_key(std::string sk_fpath, std::string str_sk_fpath, std::string seal_sk_fpath, + const seal::SEALContext &context); + +/** +Generates and saves a public key to file. + +In order to generate the public key, the key generator must read in the secret +key from file. This file may either be: 1) the file created using +SEAL-Embedded's adapter save functionality (SEAL-Embedded form). In this case, +set use_seal_sk_fpath to 0 and set sk_fpath to the path to the secret key. 2) +the file created using SEAL's stream save functionality (SEAL form). In this +case, set use_seal_sk_fpath to 1 and set seal_sk_fpath to the path to the secret +key. + +@param[in] dirpath Path to the directory to store file containing public key +in SEAL-Embedded form +@param[in] seal_pk_fpath Path to file to store public key +@param[in] sk_fpath Path to file storing secret key in SEAL-Embedded form +@param[in] seal_sk_fpath Path to file storing secret key in SEAL form +@param[in] context SEAL context +@param[in] use_seal_sk_fpath If true, use seal_sk_fpath to load secret key. Else, use +sk_fpath +*/ +void gen_save_public_key(std::string dirpath, std::string seal_pk_fpath, std::string sk_fpath, + std::string seal_sk_fpath, const seal::SEALContext &context, + bool use_seal_sk_fpath); + +/** +Generates and saves the IFFT roots to file. + +@param[in] dirpath Path to the directory to store file containing public key in +SEAL-Embedded form +@param[in] context SEAL context +@param[in] high_byte_first If true, saves in (??)endian form +@param[in] string_roots +*/ +void gen_save_ifft_roots(std::string dirpath, const seal::SEALContext &context, + bool high_byte_first, bool string_roots); + +/** +Generates and saves the NTT roots to file. + +@param[in] dirpath Path to directory to store file containing NTT roots in +SEAL-Embedded form +@param[in] context SEAL context +@param[in] lazy If true, generates "fast" NTT roots (false = regular roots) +@param[in] inverse If true, generates inverse NTT roots (false = forward roots) +@param[in] high_byte_first If true, saves in (??)endian form // TODO: +@param[in] string_roots +*/ +void gen_save_ntt_roots(std::string dirpath, const seal::SEALContext &context, bool lazy, + bool inverse, bool high_byte_first, bool string_roots); + +/** +Generates and saves the index map (i.e. pi-inverse) to file. Also generates a code file +with hard-coded index map values. + +@param[in] dirpath Path to directory to store file containing NTT roots in +SEAL-Embedded form +@param[in] context SEAL context +@param[in] high_byte_first If true, saves in (??)endian form // TODO: +*/ +void gen_save_index_map(std::string dirpath, const seal::SEALContext &context, + bool high_byte_first); diff --git a/adapter/utils.cpp b/adapter/utils.cpp new file mode 100644 index 0000000..49e54b9 --- /dev/null +++ b/adapter/utils.cpp @@ -0,0 +1,382 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file utils.cpp +*/ + +#include "utils.h" + +#include +#include +#include + +#include "convert.h" +#include "seal/seal.h" + +using namespace std; +using namespace seal; +using namespace seal::util; + +// ---------------- Setup ------------------ + +void set_modulus_testing_values(size_t nprimes, vector &vec) +{ + vec.clear(); + + // -- Note: We create the vector in reverse first + switch (nprimes) + { + case 8: vec.push_back(Modulus(1073643521)); [[fallthrough]]; + case 7: vec.push_back(Modulus(1073479681)); [[fallthrough]]; + case 6: vec.push_back(Modulus(1073184769)); [[fallthrough]]; + case 5: vec.push_back(Modulus(1073053697)); [[fallthrough]]; + case 4: vec.push_back(Modulus(1072857089)); [[fallthrough]]; + case 3: vec.push_back(Modulus(1072496641)); [[fallthrough]]; + case 2: vec.push_back(Modulus(1071513601)); [[fallthrough]]; + case 1: vec.push_back(Modulus(1071415297)); [[fallthrough]]; + default: break; + } + reverse(vec.begin(), vec.end()); +} + +SEALContext setup_seale_custom(size_t degree, const vector &moduli, + EncryptionParameters &parms) +{ + for (size_t i = 0; i < moduli.size() - 1; i++) + { + // -- Required for compatibility with SEAL-embedded. + assert(moduli[i].bit_count() <= 30); + } + parms.set_poly_modulus_degree(degree); + parms.set_coeff_modulus(moduli); + SEALContext context(parms); + print_parameters(context); + cout << endl; + return context; +} + +SEALContext setup_seale_30bitprime_default(size_t degree, EncryptionParameters &parms) +{ + int n_30bit_primes; + int special_prime_bitcount; + switch (degree) + { + case 2048: + n_30bit_primes = 1; + special_prime_bitcount = 24; + break; + case 4096: + n_30bit_primes = 3; + special_prime_bitcount = 19; + break; + case 8192: + n_30bit_primes = 6; + special_prime_bitcount = 38; + break; + default: throw runtime_error("Please use a different setup function"); break; + } + vector moduli; + set_modulus_testing_values(n_30bit_primes, moduli); + vector special_prime = CoeffModulus::Create(degree, {special_prime_bitcount}); + moduli.push_back(special_prime[0]); + return setup_seale_custom(degree, moduli, parms); +} + +SEALContext setup_seal_api(size_t degree, const vector &bit_lengths, + EncryptionParameters &parms) +{ + return setup_seale_custom(degree, CoeffModulus::Create(degree, bit_lengths), parms); +} + +// ---------------- Size functions ------------------ + +size_t get_sk_num_bytes(const SecretKey &sk, const SEALContext &context, bool incl_sp) +{ + auto &sk_parms = context.key_context_data()->parms(); + auto &coeff_modulus = sk_parms.coeff_modulus(); + size_t n = sk_parms.poly_modulus_degree(); + size_t coeff_modulus_size = coeff_modulus.size(); + size_t type_size = sizeof(sk.data().data()[0]); + + size_t num_primes = incl_sp ? coeff_modulus_size : coeff_modulus_size - 1; + return n * num_primes * type_size; +} + +size_t get_pk_num_bytes(const PublicKey &pk, bool incl_sp) +{ + size_t n = pk.data().poly_modulus_degree(); + size_t coeff_modulus_size = pk.data().coeff_modulus_size(); + size_t type_size = sizeof(pk.data().data()[0]); + size_t num_components = pk.data().size(); + assert(num_components == 2); + + size_t num_primes = incl_sp ? coeff_modulus_size : coeff_modulus_size - 1; + return n * num_primes * type_size * 2; +} + +// ---------------- Data pointers ------------------ + +uint64_t *get_pt_arr_ptr(Plaintext &pt) +{ + return reinterpret_cast(pt.data()); +} + +uint64_t *get_ct_arr_ptr(Ciphertext &ct, bool second_element) +{ + if (second_element) + { + size_t n = ct.poly_modulus_degree(); + size_t coeff_modulus_size = ct.coeff_modulus_size(); + return reinterpret_cast(&(ct[n * coeff_modulus_size])); + } + else + { + return reinterpret_cast(ct.data()); + } +} + +uint64_t *get_sk_arr_ptr(SecretKey &sk) +{ + return reinterpret_cast(sk.data().data()); +} + +uint64_t *get_pk_arr_ptr(PublicKey &pk, bool second_element) +{ + if (second_element) + { + size_t n = pk.data().poly_modulus_degree(); + size_t coeff_modulus_size = pk.data().coeff_modulus_size(); + return reinterpret_cast(&(pk.data()[n * coeff_modulus_size])); + } + else + { + return reinterpret_cast(pk.data().data()); + } +} + +uint64_t *get_pk_arr_ptr(const PublicKeyWrapper &pk_wr, bool second_element) +{ + return get_pk_arr_ptr(*(pk_wr.pk), second_element); +} + +// ---------------- Clearing functions ------------------ + +void clear_pk(PublicKey &pk) +{ + bool incl_special_prime = true; + size_t num_bytes = get_pk_num_bytes(pk, incl_special_prime); + memset(reinterpret_cast(get_pk_arr_ptr(pk)), 0, num_bytes); +} + +void clear_sk(const SEALContext &context, SecretKey &sk) +{ + bool incl_special_prime = true; + size_t num_bytes = get_sk_num_bytes(sk, context, incl_special_prime); + memset(reinterpret_cast(get_sk_arr_ptr(sk)), 0, num_bytes); +} + +// ---------------- Comparison ------------------ + +bool same_pk(const PublicKeyWrapper &pk1_wr, const PublicKeyWrapper &pk2_wr, bool compare_sp) +{ + assert(pk1_wr.pk); + assert(pk2_wr.pk); + assert(pk1_wr.pk->data().data() == get_pk_arr_ptr(pk1_wr, 0)); + assert(pk2_wr.pk->data().data() == get_pk_arr_ptr(pk2_wr, 0)); + + if (pk1_wr.is_ntt != pk2_wr.is_ntt) return false; + + auto &data1 = pk1_wr.pk->data(); // This is actually the backing ciphertext + auto &data2 = pk2_wr.pk->data(); // This is actually the backing ciphertext + + // -- Compare polynomial degree + size_t n1 = data1.poly_modulus_degree(); + size_t n2 = data2.poly_modulus_degree(); + if (n1 != n2) return false; + + // -- Compare number of primes + size_t coeff_modulus_size1 = data1.coeff_modulus_size(); + size_t coeff_modulus_size2 = data2.coeff_modulus_size(); + if (coeff_modulus_size1 != coeff_modulus_size2) return false; + + // -- Compare number of components + size_t num_components1 = data1.size(); + size_t num_components2 = data2.size(); + if (num_components1 != num_components2) return false; + + size_t num_bytes = get_pk_num_bytes(*(pk1_wr.pk), compare_sp) / 2; + cout << "num bytes: " << num_bytes << endl; + + bool same_pk1 = !memcmp(reinterpret_cast(data1.data()), + reinterpret_cast(data2.data()), num_bytes); + + bool same_pk2 = !memcmp(reinterpret_cast(get_pk_arr_ptr(pk1_wr, 1)), + reinterpret_cast(get_pk_arr_ptr(pk2_wr, 1)), num_bytes); + + // -- For debugging + /* + if (!same_pk1) + { + auto data1_ptr = reinterpret_cast(data1.data()); + auto data2_ptr = reinterpret_cast(data2.data()); + for (size_t i = 0; i < num_bytes; i++) + { + const char d1 = data1_ptr[i]; + const char d2 = data2_ptr[i]; + if (d1 != d2) + { + cout << "mismatched index: " << i << endl; + cout << "d1: " << d1 << endl; + cout << "d2: " << d2 << endl; + break; + } + } + } + */ + + return same_pk1 && same_pk2; +} + +bool same_sk(const SecretKey &sk1, const SecretKey &sk2, const SEALContext &context, + bool compare_sp) +{ + bool sk1_is_ntt = sk1.data().is_ntt_form(); + bool sk2_is_ntt = sk2.data().is_ntt_form(); + + if (sk1_is_ntt != sk2_is_ntt) + { + cout << "secret keys are not in the same form " << endl; + return false; + } + + auto &data1 = sk1.data(); // This is actually the backing plaintext + auto &data2 = sk2.data(); // This is actually the backing plaintext + + size_t num_bytes1 = get_sk_num_bytes(sk1, context); + size_t num_bytes2 = get_sk_num_bytes(sk2, context); + assert(num_bytes1 == num_bytes2); + + if (compare_sp) { return data1 == data2; } + else + { + return (!memcmp(reinterpret_cast(data1.data()), + reinterpret_cast(data2.data()), num_bytes1)); + } +} + +// ---------------- Printing ------------------ + +void print_all_moduli(EncryptionParameters &parms) +{ + cout << "Primes: " << endl; + for (size_t i = 0; i < parms.coeff_modulus().size(); i++) + { + cout << " coeff_modulus[" << i << "]: "; + cout << parms.coeff_modulus()[i].value() << endl; + } +} + +void print_ct(Ciphertext &ct, size_t print_size) +{ + // total mod size >= 2, so ct_mod_size >=1 + size_t ct_mod_size = ct.coeff_modulus_size(); + size_t ct_size = ct.size(); // should be 2 for freshly encrypted + size_t n = ct.poly_modulus_degree(); + bool is_ntt = ct.is_ntt_form(); + assert(ct_mod_size >= 1); + assert(ct_size >= 2); + + string ct_name_base = is_ntt ? "(ntt) ct" : " ct"; + cout << endl; + for (size_t i = 0; i < ct_size; i++) + { + string ct_name_base_i = ct_name_base + to_string(i) + "["; + for (size_t j = 0; j < ct_mod_size; j++) + { + string ct_name_ij = ct_name_base_i + to_string(j) + "]"; + size_t offset = n * (ct_mod_size * i + j); + print_poly(ct_name_ij, get_ct_arr_ptr(ct) + offset, print_size); + } + } +} + +void print_pk(string name, PublicKeyWrapper &pk_wr, size_t print_size, bool print_sp) +{ + size_t n = pk_wr.pk->data().poly_modulus_degree(); + size_t nprimes = pk_wr.pk->data().coeff_modulus_size(); + bool is_ntt = pk_wr.is_ntt; + assert(nprimes >= 2); + assert(pk_wr.pk->data().size() == 2); + + string base = is_ntt ? "(ntt form) " : "(regular form) "; + cout << endl; + for (size_t t = 0; t < nprimes; t++) + { + for (size_t k = 0; k < 2; k++) + { + string pk_name_kt = base + name; + pk_name_kt += "[" + to_string(k) + "][" + to_string(t) + "]"; + + print_poly(pk_name_kt, get_pk_arr_ptr(pk_wr, k) + (t * n), print_size); + } + if (!print_sp && t == (nprimes - 2)) break; + } +} + +void print_sk_compare(string name1, SecretKey &sk1, string name2, SecretKey &sk2, + const SEALContext &context, size_t print_size, bool print_sp) +{ + auto &parms_id1 = (sk1.parms_id() == parms_id_zero) ? context.key_parms_id() : sk1.parms_id(); + auto sk1_context_data_ptr = context.get_context_data(parms_id1); + auto &parms1 = sk1_context_data_ptr->parms(); + size_t n = parms1.poly_modulus_degree(); + size_t nprimes = parms1.coeff_modulus().size(); + bool is_ntt = sk1.data().is_ntt_form(); + assert(nprimes >= 2); + + auto &parms_id2 = (sk2.parms_id() == parms_id_zero) ? context.key_parms_id() : sk2.parms_id(); + auto sk2_context_data_ptr = context.get_context_data(parms_id2); + auto &parms2 = sk2_context_data_ptr->parms(); + assert(n == parms2.poly_modulus_degree()); + assert(nprimes == parms2.coeff_modulus().size()); + assert(is_ntt == sk2.data().is_ntt_form()); + + string base = is_ntt ? "(ntt form) " : "(regular form) "; + cout << endl << endl; + for (size_t t = 0; t < nprimes; t++) + { + string idx = "[" + to_string(t) + "]"; + print_poly(base + name1 + idx, get_sk_arr_ptr(sk1) + t * n, print_size); + print_poly(base + name2 + idx, get_sk_arr_ptr(sk2) + t * n, print_size); + if (!print_sp && t == (nprimes - 2)) break; + } +} + +void print_pk_compare(string name1, const PublicKeyWrapper &pk1_wr, string name2, + const PublicKeyWrapper &pk2_wr, size_t print_size, bool print_sp) +{ + size_t n = pk1_wr.pk->data().poly_modulus_degree(); + size_t nprimes = pk1_wr.pk->data().coeff_modulus_size(); + bool is_ntt = pk1_wr.is_ntt; + + assert(pk1_wr.pk->data().size() == 2); + assert(pk1_wr.pk->data().size() == pk2_wr.pk->data().size()); + assert(nprimes >= 2); // minimum is 2 + assert(nprimes == pk2_wr.pk->data().coeff_modulus_size()); + assert(n == pk2_wr.pk->data().poly_modulus_degree()); + assert(is_ntt == pk2_wr.is_ntt); + + string base = is_ntt ? "(ntt form) " : "(regular form) "; + cout << endl << endl; + for (size_t t = 0; t < nprimes; t++) + { + for (size_t k = 0; k < 2; k++) + { + string idx = "[" + to_string(k) + "][" + to_string(t) + "]"; + print_poly(base + name1 + idx, get_pk_arr_ptr(pk1_wr, k) + (t * n), print_size); + print_poly(base + name2 + idx, get_pk_arr_ptr(pk2_wr, k) + (t * n), print_size); + } + if (!print_sp && t == (nprimes - 2)) break; + } +} diff --git a/adapter/utils.h b/adapter/utils.h new file mode 100644 index 0000000..5e0bf18 --- /dev/null +++ b/adapter/utils.h @@ -0,0 +1,403 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file utils.h + +@brief Various adapter utility functions. +*/ + +#pragma once + +#include +#include +#include + +#include "generate.h" +#include "seal/seal.h" + +/** +Exits the program when an error is detected. + +@param[in] err If = 0, returns. Else, exits +@param[in] msg Message to print if an error is detected +*/ +inline void exit_on_err(int err, std::string msg) +{ + if (!err) return; + std::cerr << "Error: " << msg << "." << std::endl; + std::cerr << "Error value: " << err << std::endl; + exit(1); +} + +// ----------------------------------------- +// ---------------- Setup ------------------ +// ----------------------------------------- +/** +Helper function for setup_seal. Sets default SEAL-Embedded seal::Modulus values for testing. + +@param[in] nprimes Number of primes to set +@param[in,out] vec Vector of moduli +*/ +void set_modulus_testing_values(std::size_t nprimes, std::vector &vec); + +/** +Creates SEAL context based on custom-chosen moduli. Verifies compatibility with +SEAL-Embedded (i.e., all moduli other than the special prime must be <= 30 bits). + +@param[in] degree Polynomial ring degree +@param[in] moduli Moduli +@param[in,out] parms Encryption parameters object to set +@returns SEAL context object +*/ +seal::SEALContext setup_seale_custom(std::size_t degree, const std::vector &moduli, + seal::EncryptionParameters &parms); + +/** +Creates SEAL context based on default moduli for degree for SEAL-Embedded. Verifies +compatibility with SEAL-Embedded (i.e., all moduli other than the special prime must be <= +30 bits). + +For a degree of 2048, chooses 1 30-bit prime + 1 24-bit special prime. +For a degree of 4096, chooses 3 30-bit primes + 1 19-bit special prime. +For a degree of 8192, chooses 6 30-bit primes + 1 38-bit special prime. +Throws an error for all other degree choices. + +@param[in] degree Polynomial ring degree +@param[in,out] parms Encryption parameters object to set +@returns SEAL context object +*/ +seal::SEALContext setup_seale_30bitprime_default(std::size_t degree, + seal::EncryptionParameters &parms); + +/** +Creates SEAL context based on parameters, using the SEAL api to choose the seal::Modulus values +based on provided bit-lengths. + +@param[in] degree Polynomial ring degree +@param[in] bit_lengths Vector of bit lengths for moduli +@param[in,out] parms Encryption parameters object to set +@returns SEAL context object +*/ +seal::SEALContext setup_seal_api(std::size_t degree, const std::vector &bit_lengths, + seal::EncryptionParameters &parms); + +// -------------------------------------------------- +// ---------------- Size functions ------------------ +// -------------------------------------------------- +/** +Returns the number of bytes in a (fully expanded) secret key instance + +@param[in] sk Secret key instance +@param[in] context SEAL context +@param[in] incl_sp If true, includes the special prime bytes in the count +*/ +std::size_t get_sk_num_bytes(const seal::SecretKey &sk, const seal::SEALContext &context, + bool incl_sp = true); + +/** +Returns the number of bytes in a public key instance + +@param[in] pk Public key instance +@param[in] incl_sp If true, includes the special prime bytes in the count +*/ +std::size_t get_pk_num_bytes(const seal::PublicKey &pk, bool incl_sp = true); + +// ------------------------------------------------- +// ---------------- Data pointers ------------------ +// ------------------------------------------------- +/** +Returns a pointer to the plaintext data array + +@param[in] pt Plaintext instance +@returns A pointer to the plaintext data array +*/ +uint64_t *get_pt_arr_ptr(seal::Plaintext &pt); + +/** +Returns a pointer to the ciphertext data array + +@param[in] ct Ciphertext instance +@param[in] second_element If true, returns pointer starting at second (c1) element +@returns A pointer to the ciphertext data array +*/ +uint64_t *get_ct_arr_ptr(seal::Ciphertext &ct, bool second_element = false); + +/** +Returns a pointer to the secret key data array + +@param[in] sk Secret key instance +@returns A pointer to the secret key data array +*/ +uint64_t *get_sk_arr_ptr(seal::SecretKey &sk); + +/** +Returns a pointer to the public key data array + +@param[in] pk Public key instance +@param[in] second_element If true, returns pointer starting at second element +@returns A pointer to the public key data array +*/ +uint64_t *get_pk_arr_ptr(seal::PublicKey &pk, bool second_element = false); + +/** +Returns a pointer to the public key data array + +@param[in] pk_wr Public key wrapper instance +@param[in] second_element If true, returns pointer starting at second element +@returns A pointer to the public key data array +*/ +uint64_t *get_pk_arr_ptr(const PublicKeyWrapper &pk_wr, bool second_element = false); + +// ------------------------------------------------------ +// ---------------- Clearing functions ------------------ +// ------------------------------------------------------ +/** +Sets all bytes in a public key instance to 0 (including special prime bytes). + +@param[in,out] pk Public key instance +*/ +void clear_pk(const seal::SEALContext &context, seal::PublicKey &pk); + +/** +Sets all bytes in a (fully expanded) secret key instance to 0 (including special prime +bytes). + +@param[in] context SEAL context +@param[in,out] sk Secret key instance +*/ +void clear_sk(const seal::SEALContext &context, seal::SecretKey &sk); + +// ---------------------------------------------- +// ---------------- Comparison ------------------ +// ---------------------------------------------- +/** +Compares two public key objects to see if they have the same value. + +@param[in] pk1_wr First public key wrapper instance +@param[in] pk2_wr Second public key wrapper instance +@param[in] compare_sp If true, compares special prime bytes as well +@returns True if both objects have the same values, false otherwise +*/ +bool same_pk(const PublicKeyWrapper &pk1_wr, const PublicKeyWrapper &pk2_wr, bool compare_sp); + +/** +Compares two secret key objects to see if they have the same value. + +@param[in] sk1 First secret key wrapper instance +@param[in] sk2 Second secret key wrapper instance +@param[in] context SEAL context +@param[in] compare_sp If true, compares special prime bytes as well +@returns True if both objects have the same values, false otherwise +*/ +bool same_sk(const seal::SecretKey &sk1, const seal::SecretKey &sk2, + const seal::SEALContext &context, bool compare_sp); + +/** +Compares two polynomial objects to see if they have the same value. + +@param[in] a Object 1 +@param[in] b Object 2 +@param[in] n Number of elements in object +@param[in] diff Maximum allowed difference between values (only applicable for objects + with non-integer values) +@returns True if a and b have the same values, False otherwise +*/ +template +bool are_equal_poly(T *a, T *b, std::size_t n, double diff = 0.4) +{ + bool is_error = false; + if (std::is_same::value) + { + for (std::size_t i = 0; i < n; i++) + { + double abs_val = fabs(a[i] - b[i]); + if (abs_val >= diff) + { + std::streamsize ss = std::cout.precision(); // save original precision + std::cout << std::setprecision(9); + std::cout << "a[" << i << "]: " << a[i] << std::endl; + std::cout << "b[" << i << "]: " << b[i] << std::endl; + std::cout.precision(ss); + } + if (abs_val >= diff) is_error = true; + assert(!is_error); + } + } + else if (std::is_same::value) + { + is_error = memcmp(a, b, n * sizeof(T)); + assert(!is_error); + } + else + { + std::cout << "Error: Have not considered that compare poly option" << std::endl; + exit(0); + } + return !is_error; +} + +/** +TODO:? +Compares the first element of two polynomial objects to see if they have the same value. + +@param[in] a Object 1 +@param[in] b Object 2 +@param[in] n Number of elements in object +@param[in] diff Maximum allowed difference between values (only applicable for objects + with non-integer values) +@returns True if a and b have the same values, False otherwise +*/ +template +bool are_equal_poly(std::vector &a, std::vector &b, std::size_t s_in, double diff = 0.4) +{ + std::size_t s = s_in; + if (s == 0) s = b.size(); + return are_equal_poly(&(a[0]), &(b[0]), s, diff); +} + +// -------------------------------------------- +// ---------------- Printing ------------------ +// -------------------------------------------- +/** +Prints all encryption moduli to stdout. + +@param[in] parms Encryption parameters +*/ +void print_all_moduli(seal::EncryptionParameters &parms); + +/** +Prints a ciphertext to stdout. + +@param[in] ct Ciphertext instance +@param[in] print_size # of elements of each component to print for debug +*/ +void print_ct(seal::Ciphertext &ct, std::size_t print_size); + +/** +Prints a public key to stdout. + +@param[in] name Name of public key instance +@param[in] pk_wr Public key wrapper instance +@param[in] print_size # of elements of each component to print for debug +@param[in] print_sp If true, also prints the special prime +*/ +void print_pk(std::string name, PublicKeyWrapper &pk_wr, std::size_t print_size, bool print_sp); + +/** +Prints two secret key objects to stdout for manual comparison. + +@param[in] name1 Name for first secret key object +@param[in] sk1 First secret key wrapper object +@param[in] name2 Name for second secret key object +@param[in] sk2 Second secret key wrapper object +@param[in] context SEAL context +@param[in] print_size # of elements of each component to print for debug +@param[in] print_sp If true, also prints special prime bytes +*/ +void print_sk_compare(std::string name1, seal::SecretKey &sk1, std::string name2, + seal::SecretKey &sk2, const seal::SEALContext &context, + std::size_t print_size, bool print_sp); + +/** +Prints two public key objects to stdout for manual comparison. + +@param[in] name1 Name for first public key object +@param[in] pk1_wr First public key wrapper object +@param[in] name2 Name for second public key object +@param[in] pk2_wr Second public key wrapper object +@param[in] print_size Number of elements of each component to print for debug +@param[in] print_sp If true, also prints special prime bytes +*/ +void print_pk_compare(std::string name1, const PublicKeyWrapper &pk1_wr, std::string name2, + const PublicKeyWrapper &pk2_wr, std::size_t print_size, bool print_sp); + +/** +Prints a polynomial object to stdout. + +@param[in] pname Name of object +@param[in] poly Polynomial object +@param[in] print_size Number of elements of each component to print for debug +@param[in] prec Precision with which to print polynomial values +*/ +template +void print_poly(std::string pname, T *poly, std::size_t print_size, int prec = 2) +{ + std::streamsize ss = std::cout.precision(); // save original precision + bool is_double = std::is_same::value; + if (is_double) std::cout << std::setprecision(prec); + + std::cout << pname << " : { "; + for (std::size_t i = 0; i < print_size; i++) + { + std::cout << poly[i]; + if (i < (print_size - 1)) std::cout << ", "; + } + std::cout << " }" << std::endl; + + if (is_double) std::cout.precision(ss); // restore precision +} + +/** +TODO:? +Prints the first element of a polynomial object to stdout. + +@param[in] pname Name of object +@param[in] poly Polynomial object +@param[in] print_size Number of elements of each component to print for debug +@param[in] prec Precision with which to print polynomial values +*/ +template +void print_poly(std::string pname, std::vector &poly, std::size_t psize, int prec = 2) +{ + print_poly(pname, &(poly[0]), psize, prec); +} + +/** +Overloaded << operator for params_id. Prints the `parms_id' to std::ostream. +(Note: This is modified from SEAL/native/examples/examples.h.) +*/ +inline std::ostream &operator<<(std::ostream &stream, seal::parms_id_type parms_id) +{ + // Save the formatting information for std::cout. + std::ios old_fmt(nullptr); + old_fmt.copyfmt(std::cout); + + stream << std::hex << std::setfill('0') << std::setw(16) << parms_id[0] << " " << std::setw(16) + << parms_id[1] << " " << std::setw(16) << parms_id[2] << " " << std::setw(16) + << parms_id[3] << " "; + + // Restore the old std::cout formatting. + std::cout.copyfmt(old_fmt); + + return stream; +} + +/** +Prints the parameters in a SEALContext to stdout. Modified from +SEAL/native/examples/examples.h. + +@param[in] context SEAL context +*/ +inline void print_parameters(const seal::SEALContext &context) +{ + auto &context_data = *context.key_context_data(); + + std::cout << "/" << std::endl; + std::cout << "| Encryption parameters :" << std::endl; + std::cout << "| poly_modulus_degree: " << context_data.parms().poly_modulus_degree() + << std::endl; + + // Print the size of the true (product) coefficient seal::Modulus. + std::cout << "| coeff_modulus size: "; + std::cout << context_data.total_coeff_modulus_bit_count() << " ("; + auto coeff_modulus = context_data.parms().coeff_modulus(); + std::size_t coeff_modulus_size = coeff_modulus.size(); + for (std::size_t i = 0; i < coeff_modulus_size - 1; i++) + { + std::cout << coeff_modulus[i].bit_count() << " + "; + } + std::cout << coeff_modulus.back().bit_count(); + std::cout << ") bits" << std::endl; + std::cout << "\\" << std::endl; +} diff --git a/device/CMakeLists.txt b/device/CMakeLists.txt new file mode 100644 index 0000000..11be47c --- /dev/null +++ b/device/CMakeLists.txt @@ -0,0 +1,300 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +cmake_minimum_required (VERSION 3.13) + +##################################################################### +# Start of configuration section # +##################################################################### + +# -- 1. Local or on device +option(SE_BUILD_LOCAL "Build for native instead of device" ON) + +# -- 2. If !local, m4 or a7 (on = m4) +option(SE_BUILD_M4 "Build for M4 instead of for A7" OFF) + +# -- 3. If m4, azure sphere or nrf (on = sphere) +option(SE_M4_IS_SPHERE "Use Azure Sphere m4" OFF) + +# -- This needs to be above the 'project' line +# -- Set these paths with paths for your setup +if(NOT SE_BUILD_LOCAL) + if(SE_BUILD_M4 AND SE_M4_IS_SPHERE) + # -- This path may need to be absolute + set(ARM_GNU_PATH "/home/d/gcc-arm-none-eabi-10.3-2021.07/bin") + set(CMAKE_TOOLCHAIN_FILE "/opt/azurespheresdk/CMakeFiles/AzureSphereRTCoreToolchain.cmake") + # set(CMAKE_C_COMPILER "/opt/azurespheresdk/Sysroots/10/tools/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-musleabi/arm-poky-linux-musleabi-gcc") + else() + set(CMAKE_TOOLCHAIN_FILE "/opt/azurespheresdk/CMakeFiles/AzureSphereToolchain.cmake") + endif() +endif() + +project(SEAL_EMBEDDED VERSION 1.0.0 LANGUAGES C ASM) + +################# +# Configuration # +################# + +# Always build position-independent-code +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Set the directory that stores output files generated by SEAL-Embedded Adapter +set(SE_ADAPTER_FILE_OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/adapter_output_data CACHE STRING "Adapter file directory" FORCE) +message(STATUS "Adapter file directory: ${SE_ADAPTER_FILE_OUTPUT_DIR}") +if(NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/sk_4096.dat OR + NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/index_map_4096.dat OR + NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/ifft_roots_4096.dat OR + NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_roots_4096_1071415297.dat OR + NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_roots_4096_1071513601.dat OR + NOT EXISTS ${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_roots_4096_1072496641.dat) + message(FATAL_ERROR "Run adapter first to generate key and precomputation data") +endif() + +# -- 4. Build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "Release" "Debug" "MinSizeRel" "RelWithDebInfo") +endif() +message(STATUS "Build type (CMAKE_BUILD_TYPE): ${CMAKE_BUILD_TYPE}") + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + # In Debug mode, enable extra compiler flags. + include(CheckCXXCompilerFlag) + # For easier adding of CXX compiler flags + function(se_enable_cxx_compiler_flag_if_supported flag) + string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" flag_already_set) + if(flag_already_set EQUAL -1) + message(STATUS "Adding CXX compiler flag: ${flag} ...") + check_cxx_compiler_flag("${flag}" flag_supported) + if(flag_supported) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE) + endif() + unset(flag_supported CACHE) + endif() + endfunction() + if(NOT MSVC AND SEAL_DEBUG) + se_enable_cxx_compiler_flag_if_supported("-Wall") + se_enable_cxx_compiler_flag_if_supported("-Wextra") + se_enable_cxx_compiler_flag_if_supported("-Wconversion") + se_enable_cxx_compiler_flag_if_supported("-Wshadow") + se_enable_cxx_compiler_flag_if_supported("-pedantic") + endif() +endif() + +# -- 5. Executable (tests or bench) or just library +if(NOT SE_BUILD_TYPE) + set(SE_BUILD_TYPE "Tests" CACHE STRING "SEAL-Embedded build type" FORCE) + set_property(CACHE SE_BUILD_TYPE PROPERTY + STRINGS "Tests" "Bench" "Lib") +endif() +message(STATUS "Build type (SE_BUILD_TYPE): ${SE_BUILD_TYPE}") + +if(NOT SE_BUILD_LOCAL AND (NOT SE_BUILD_M4 OR SE_M4_IS_SPHERE)) + azsphere_configure_tools(TOOLS_REVISION "21.07") + + # -- This means we are targetting one of the sphere targets + # -- 6. Make sure to update these lines to latest versions + if(SE_BUILD_M4) + # -- 7. Set this path to the path to the OS_HAL folder on your setup + set(OS_HAL_DIR_PATH "../../mt3620_m4_software/MT3620_M4_Sample_Code/OS_HAL") + + # -- 8. Set this path to the path to the Driver path in your setup + set(MT3620_M4_DRIVER_PATH "../../mt3620_m4_software/MT3620_M4_Driver") + + set(MT3620_M4_BSP_PATH "../../mt3620_m4_software/MT3620_M4_BSP") + else() + azsphere_configure_api(TARGET_API_SET "10") + endif() +endif() + +if(NOT SE_BUILD_LOCAL AND NOT SE_BUILD_M4) + # -- This means we are targetting the sphere a7 + # -- 9. Uncomment files corresponding to the desired config. + set(RESOURCE_FILES + # -- Secret key + "${SE_ADAPTER_FILE_OUTPUT_DIR}/sk_4096.dat" + + # -- Public key + "${SE_ADAPTER_FILE_OUTPUT_DIR}/pk0_ntt_4096_1071415297.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/pk0_ntt_4096_1071513601.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/pk0_ntt_4096_1072496641.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/pk1_ntt_4096_1071415297.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/pk1_ntt_4096_1071513601.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/pk1_ntt_4096_1072496641.dat" + + # -- Index map + "${SE_ADAPTER_FILE_OUTPUT_DIR}/index_map_4096.dat" + + # -- IFFT Roots + "${SE_ADAPTER_FILE_OUTPUT_DIR}/ifft_roots_4096.dat" + + # -- Regular NTT roots + "${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_roots_4096_1071415297.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_roots_4096_1071513601.dat" + "${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_roots_4096_1072496641.dat" + + # -- "Fast" NTT roots + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_fast_roots_4096_1071415297.dat" + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_fast_roots_4096_1071513601.dat" + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/ntt_fast_roots_4096_1072496641.dat" + + # ---------------------------------------- + # -- The following are for testing only -- + # ---------------------------------------- + + # -- FFT Roots + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/fft_roots_4096.dat" + + # -- Regular INTT roots + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/intt_roots_4096_1071415297.dat" + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/intt_roots_4096_1071513601.dat" + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/intt_roots_4096_1072496641.dat" + + # -- "Fast" INTT roots + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/intt_fast_roots_4096_1071415297.dat" + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/intt_fast_roots_4096_1071513601.dat" + # "${SE_ADAPTER_FILE_OUTPUT_DIR}/intt_fast_roots_4096_1072496641.dat" + ) +endif() + +if(NOT SE_BUILD_LOCAL AND SE_BUILD_M4 AND SE_M4_IS_SPHERE) # On Sphere M4 + add_compile_definitions(OSAI_BARE_METAL) + add_compile_definitions(OSAI_ENABLE_DMA) + + # When place CODE_REGION in FLASH instead of TCM, please enable this definition: + add_compile_definitions(M4_ENABLE_XIP_FLASH) + add_link_options(-specs=nano.specs -specs=nosys.specs) + # add_link_options(-specs=nosys.specs) + + set(SPHERE_M4_SRC_FILES + "${OS_HAL_DIR_PATH}/src/os_hal_uart.c" + "${OS_HAL_DIR_PATH}/src/os_hal_gpio.c" + "${OS_HAL_DIR_PATH}/src/os_hal_dma.c" + "${OS_HAL_DIR_PATH}/src/os_hal_gpt.c" + "${MT3620_M4_BSP_PATH}/printf/printf.c" + ) + + # add_subdirectory(../../../MT3620_M4_Driver ./lib/MT3620_M4_Driver) + add_subdirectory(${MT3620_M4_DRIVER_PATH} ./MT3620_M4_Driver) +else() + set(SPHERE_M4_SRC_FILES "") +endif() + + +##################################################################### +# End of configuration section # +##################################################################### + +# -- CMPLX requires C11 +set(C_STANDARD_REQUIRED ON) +set(CMAKE_C_STANDARD 11) + +set_property(CACHE SE_BUILD_TYPE PROPERTY STRINGS "Lib" "Bench" "Tests") +message(STATUS "BUILD LOCAL (SE_BUILD_LOCAL): ${SE_BUILD_LOCAL}") +message(STATUS "CMAKE build type (CMAKE_BUILD_TYPE): ${CMAKE_BUILD_TYPE}") +message(STATUS "SEAL-Embedded build type (SE_BUILD_TYPE): ${SE_BUILD_TYPE}") + + +if(NOT SE_BUILD_LOCAL) + message(STATUS "BUILD M4 (SE_BUILD_M4): ${SE_BUILD_M4}") + if(SE_BUILD_M4) + message(STATUS "IS SPHERE M4 (SE_M4_IS_SPHERE): ${SE_M4_IS_SPHERE}") + endif() +endif() + +if(SE_BUILD_LOCAL) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_RUNTIME_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +endif() + +set(SE_LIB_SOURCE_FILES ${SPHERE_M4_SRC_FILES}) +set(SE_BENCH_SOURCE_FILES ${SPHERE_M4_SRC_FILES}) +set(SE_TESTS_SOURCE_FILES ${SPHERE_M4_SRC_FILES}) + +add_subdirectory(lib) +add_subdirectory(bench) +add_subdirectory(test) + +set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp") + +# -- Set up the library +add_library(seal_embedded STATIC ${SE_LIB_SOURCE_FILES}) +target_include_directories(seal_embedded PUBLIC $) +target_include_directories(seal_embedded PUBLIC ${SE_ADAPTER_FILE_OUTPUT_DIR}) + +if(NOT SE_BUILD_LOCAL AND SE_BUILD_M4 AND SE_M4_IS_SPHERE) + target_include_directories(seal_embedded PUBLIC ${OS_HAL_DIR_PATH}/inc) + target_include_directories(seal_embedded PUBLIC ${MT3620_M4_DRIVER_PATH}/MHAL/inc) + target_include_directories(seal_embedded PUBLIC ${MT3620_M4_BSP_PATH}/printf) + target_link_libraries(seal_embedded PUBLIC MT3620_M4_Driver) + target_link_libraries(seal_embedded PRIVATE m) + set_target_properties(seal_embedded PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) +else() + target_link_libraries(seal_embedded PRIVATE m) +endif() + +set_target_properties(seal_embedded PROPERTIES VERSION ${SEAL_EMBEDDED_VERSION}) +set_target_properties(seal_embedded PROPERTIES sealembedded seal_embedded-${SEAL_EMBEDDED_VERSION_MAJOR}.${SEAL_EMBEDDED_VERSION_MINOR}) + +# -- Always build the library. Optionally build the the executable. +if(SE_BUILD_TYPE STREQUAL "Bench") + if(SE_BUILD_LOCAL) + add_executable(seal_embedded_bench ${SE_BENCH_SOURCE_FILES}) + target_link_libraries(seal_embedded_bench PRIVATE m gcc_s c seal_embedded) + else() + add_executable(${PROJECT_NAME} ${SE_BENCH_SOURCE_FILES}) + if(SE_BUILD_M4) + target_link_libraries(${PROJECT_NAME} PRIVATE MT3620_M4_Driver c m seal_embedded) + else() + target_link_libraries(${PROJECT_NAME} PRIVATE applibs gcc_s c curl seal_embedded) + endif() + endif() +elseif(SE_BUILD_TYPE STREQUAL "Tests") + if(SE_BUILD_LOCAL) + add_executable(seal_embedded_tests ${SE_TESTS_SOURCE_FILES}) + target_link_libraries(seal_embedded_tests PRIVATE m gcc_s c seal_embedded) + else() + add_executable(${PROJECT_NAME} ${SE_TESTS_SOURCE_FILES}) + if(SE_BUILD_M4) + target_include_directories(${PROJECT_NAME} PUBLIC ${MT3620_M4_BSP_PATH}/printf) + target_link_libraries(${PROJECT_NAME} PUBLIC MT3620_M4_Driver c m seal_embedded) + else() + target_link_libraries(${PROJECT_NAME} PRIVATE applibs gcc_s c curl seal_embedded) + endif() + endif() +else() + message(STATUS "No executable will be built") +endif() + +# -- Create an image on an Azure Sphere device +if(NOT SE_BUILD_LOCAL AND NOT (SE_BUILD_M4 AND NOT SE_M4_IS_SPHERE)) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) + azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES ${RESOURCE_FILES}) +endif() + +# ------------------------------- +# --- Set defines for library --- +# ------------------------------- +add_definitions(-DSE_DATA_PATH="${SE_ADAPTER_FILE_OUTPUT_DIR}") +string(LENGTH "${SE_ADAPTER_FILE_OUTPUT_DIR}" SE_ADAPTER_FILE_OUTPUT_DIR_LEN) +add_definitions(-DSE_DATA_PATH_LEN=${SE_ADAPTER_FILE_OUTPUT_DIR_LEN}) + +if(NOT SE_BUILD_LOCAL) + if(SE_BUILD_M4) + if(SE_M4_IS_SPHERE) + add_definitions(-DSE_ON_SPHERE_M4) + else() + add_definitions(-DSE_ON_NRF5) + endif() + else() + add_definitions(-DSE_ON_SPHERE_A7) + add_definitions(-DSE_USE_ASM_ARITH) + endif() +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Release") + add_definitions(-DSE_UNDEF_ASSERT_FORCE) +endif() diff --git a/device/CMakeSettings.json b/device/CMakeSettings.json new file mode 100644 index 0000000..4b60593 --- /dev/null +++ b/device/CMakeSettings.json @@ -0,0 +1,85 @@ +{ + "environments": [ + { + "environment": "AzureSphere" + } + ], + "configurations": [ + { + "name": "ARM-Debug-Sphere-A7", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ + "AzureSphere" + ], + "buildRoot": "${projectDir}\\out\\${name}", + "installRoot": "${projectDir}\\install\\${name}", + "cmakeToolchain": "${env.AzureSphereDefaultSDKDir}CMakeFiles\\AzureSphereToolchain.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [ + { + "name": "AZURE_SPHERE_TARGET_API_SET", + "value": "latest-lts" + } + ] + }, + { + "name": "ARM-Release-Sphere-A7", + "generator": "Ninja", + "configurationType": "Release", + "inheritEnvironments": [ + "AzureSphere" + ], + "buildRoot": "${projectDir}\\out\\${name}", + "installRoot": "${projectDir}\\install\\${name}", + "cmakeToolchain": "${env.AzureSphereDefaultSDKDir}CMakeFiles\\AzureSphereToolchain.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [ + { + "name": "AZURE_SPHERE_TARGET_API_SET", + "value": "latest-lts" + } + ] + }, + { + "name": "ARM-Debug-Sphere-M4", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ + "AzureSphere" + ], + "buildRoot": "${projectDir}\\out\\${name}", + "installRoot": "${projectDir}\\install\\${name}", + "cmakeToolchain": "${env.AzureSphereDefaultSDKDir}CMakeFiles\\AzureSphereRTCoreToolchain.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [ + { + "name": "ARM_GNU_PATH", + "value": "${env.DefaultArmToolsetPath}" + } + ] + }, + { + "name": "ARM-Release-Sphere-M4", + "generator": "Ninja", + "configurationType": "Release", + "inheritEnvironments": [ + "AzureSphere" + ], + "buildRoot": "${projectDir}\\out\\${name}", + "installRoot": "${projectDir}\\install\\${name}", + "cmakeToolchain": "${env.AzureSphereDefaultSDKDir}CMakeFiles\\AzureSphereRTCoreToolchain.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [ + { + "name": "ARM_GNU_PATH", + "value": "${env.DefaultArmToolsetPath}" + } + ] + } + ] +} diff --git a/device/app_manifest.json b/device/app_manifest.json new file mode 100644 index 0000000..5d5023c --- /dev/null +++ b/device/app_manifest.json @@ -0,0 +1,13 @@ +{ + "SchemaVersion": 1, + "Name": "SealEmbeddedApp", + "ComponentId": "5a4b43d1-a3b0-4f9f-a9c9-30a4cc52a2f2", + "EntryPoint": "/bin/app", + "CmdArgs": [], + "Capabilities": { + "Gpio": [ 9 ], + "AllowedApplicationConnections": [], + "AllowedConnections": [ "www.neverssl.com", "neverssl.com", "httpstat.us"] + }, + "ApplicationType": "Default" +} diff --git a/device/bench/CMakeLists.txt b/device/bench/CMakeLists.txt new file mode 100644 index 0000000..09f6a85 --- /dev/null +++ b/device/bench/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +set(SE_BENCH_SOURCE_FILES ${SE_BENCH_SOURCE_FILES} + ${CMAKE_CURRENT_LIST_DIR}/bench_sym.c + ${CMAKE_CURRENT_LIST_DIR}/bench_asym.c + ${CMAKE_CURRENT_LIST_DIR}/bench_ntt.c + ${CMAKE_CURRENT_LIST_DIR}/bench_ifft.c + ${CMAKE_CURRENT_LIST_DIR}/bench_sample.c + ${CMAKE_CURRENT_LIST_DIR}/bench_index_map.c + ${CMAKE_CURRENT_LIST_DIR}/main.c +) + +set(SE_BENCH_SOURCE_FILES ${SE_BENCH_SOURCE_FILES} PARENT_SCOPE) diff --git a/device/bench/bench_asym.c b/device/bench/bench_asym.c new file mode 100644 index 0000000..e558692 --- /dev/null +++ b/device/bench/bench_asym.c @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_asym.c +*/ + +#include "defines.h" +#ifdef SE_ENABLE_TIMERS + +#include + +#include "bench_common.h" +#include "ckks_asym.h" +#include "ckks_common.h" +#include "ckks_sym.h" +#include "parameters.h" +#include "timer.h" + +// -- Configuration +// #define SE_BENCH_ENCODE +// #define SE_BENCH_SAMPLE +// #define SE_BENCH_ENCRYPT +#define SE_BENCH_FULL + +// -- Sanity check +#if !defined(SE_BENCH_ENCODE) && !defined(SE_BENCH_SAMPLE) && !defined(SE_BENCH_ENCRYPT) && \ + !defined(SE_BENCH_FULL) +#define SE_BENCH_ENCODE +#endif + +void bench_asym(void) +{ + // -- Config -- +#ifdef SE_USE_MALLOC + size_t n = 4096; + size_t nprimes = 3; +#else + size_t n = SE_DEGREE_N; + size_t nprimes = SE_NPRIMES; +#endif + double scale = (n > 1024) ? pow(2, 25) : pow(2, 20); + // ------------ + + Parms parms; + parms.scale = scale; + parms.pk_from_file = 1; + parms.is_asymmetric = 1; + parms.sample_s = 0; + +#ifdef SE_USE_MALLOC + print_ckks_mempool_size(n, 0); + ZZ *mempool = ckks_mempool_setup_asym(n); +#else + print_ckks_mempool_size(); + ZZ mempool_local[MEMPOOL_SIZE]; + ZZ *mempool = &(mempool_local[0]); +#endif + + // -- Get pointers + SE_PTRS se_ptrs_local; + ckks_set_ptrs_asym(n, mempool, &se_ptrs_local); + double complex *conj_vals = se_ptrs_local.conj_vals; + int64_t *conj_vals_int = se_ptrs_local.conj_vals_int_ptr; + double complex *ifft_roots = se_ptrs_local.ifft_roots; + ZZ *pk_c0 = se_ptrs_local.c0_ptr; + ZZ *pk_c1 = se_ptrs_local.c1_ptr; + uint16_t *index_map = se_ptrs_local.index_map_ptr; + ZZ *ntt_roots = se_ptrs_local.ntt_roots_ptr; + ZZ *ntt_u_e1_pte = se_ptrs_local.ntt_pte_ptr; + ZZ *u = se_ptrs_local.ternary; + flpt *v = se_ptrs_local.values; + int8_t *e1 = se_ptrs_local.e1_ptr; + size_t vlen = n / 2; + + SE_PRNG prng; + + // -- Set up parameters and index_map if applicable + ckks_setup(n, nprimes, index_map, &parms); + + const char *bench_name = "Asymmetric_Encryption"; + print_bench_banner(bench_name, &parms); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + t_curr = 0; + se_assert(parms.nprimes >= 1); + gen_flpt_quarter_poly(v, -10, vlen); + + // -- Begin encode-encrypt sequence +#if defined(SE_BENCH_ENCODE) || defined(SE_BENCH_FULL) + reset_start_timer(&timer); +#endif + // -- First, encode base + ckks_encode_base(&parms, v, vlen, index_map, ifft_roots, conj_vals); + +#ifdef SE_BENCH_ENCODE + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#elif defined(SE_BENCH_SAMPLE) + reset_start_timer(&timer); +#endif + // -- Sample u, e0, e1. Store e0 directly as part of conj_vals_int + ckks_asym_init(&parms, NULL, &prng, conj_vals_int, u, e1); + +#if defined(SE_BENCH_SAMPLE) || defined(SE_BENCH_FULL) + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#endif + + for (size_t i = 0; i < parms.nprimes; i++) + { +#if defined(SE_BENCH_ENCRYPT) || defined(SE_BENCH_FULL) + reset_start_timer(&timer); +#endif + ckks_encode_encrypt_asym(&parms, conj_vals_int, u, e1, ntt_roots, pk_c0, pk_c1, 0, + ntt_u_e1_pte, + 0); // TODO: THIS INTERFACE MIGHT BE WRONG + +#if defined(SE_BENCH_ENCRYPT) || defined(SE_BENCH_FULL) + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#endif + + if (b_itr && (i + 1) == parms.nprimes) + set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + + // -- We need to execute these printf calls to ensure the compiler does not + // optimize away generation of c0 and c1 polynomials. + print_poly_full("c0 ", pk_c0, n); + print_poly_full("c1 ", pk_c1, n); + if ((i + 1) < parms.nprimes) ckks_next_prime_asym(&parms, u); + } + } + + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); + print_bench_banner(bench_name, &parms); + +#ifdef SE_USE_MALLOC + free(mempool); + delete_parameters(&parms); +#endif +} +#endif diff --git a/device/bench/bench_common.h b/device/bench/bench_common.h new file mode 100644 index 0000000..184d324 --- /dev/null +++ b/device/bench/bench_common.h @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_common.h +*/ + +#pragma once + +#include "defines.h" +#include "modulo.h" // barrett_reduce +#include "sample.h" // random_zz +#include "util_print.h" // print functions, printf + +/* +Generate "random" uint +Note: random_zz is already defined in sample.h +*/ +static inline ZZ random_zzq(Modulus *q) +{ + return barrett_reduce(random_zz(), q); +} + +static inline ZZ random_zz_half(void) +{ + // -- Don't do this. It doesn't work. + // ZZ result; + // getrandom((void*)&result, sizeof(result)/2, 0); + return random_zz() & 0xFFFF; +} + +static inline ZZ random_zz_quarter(void) +{ + return random_zz() & 0xFF; +} + +// Generate "random" double +static inline double gen_double_half(int64_t div) +{ + return (double)random_zz_half() / (double)div; +} + +// "Random" uint poly +static inline void random_zz_poly(ZZ *poly, size_t n) +{ + for (size_t i = 0; i < n; i++) { poly[i] = random_zz(); } +} + +static inline void random_zzq_poly(ZZ *poly, size_t n, Modulus *q) +{ + for (size_t i = 0; i < n; i++) { poly[i] = random_zzq(q); } +} + +// "Random" double complex +static inline void gen_double_complex_half_vec(double complex *vec, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + vec[i] = (double complex)_complex(gen_double_half(div), gen_double_half(div)); + } +} + +static inline double gen_double_quarter(int64_t div) +{ + return (double)random_zz_quarter() / (double)div; +} + +static inline flpt gen_flpt_quarter(int64_t div) +{ + return (flpt)gen_double_quarter(div); +} + +// "Random" flpt +static inline void gen_flpt_quarter_poly(flpt *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_flpt_quarter(div); +} + +static inline void print_bench_banner(const char *benchmark_name, const Parms *parms) +{ +#if defined(SE_ON_NRF5) && defined(SE_NRF5_UART_PRINTF_ENABLED) + return; // Printing banner on the NRF5 via serial doesn't work well. Just + // don't do it. +#endif + printf("***************************************************\n"); + printf("Running Benchmark: %s\n", benchmark_name); + if (parms) + { + printf("n: %zu, nprimes: %zu, scale: %0.2f\n", parms->coeff_count, parms->nprimes, + parms->scale); + print_config(!parms->is_asymmetric); + } + printf("***************************************************\n"); +} + +static inline void set_time_vals(float time_curr, float *time_total, float *time_min, + float *time_max) +{ + se_assert(time_total); + time_total[0] += time_curr; + if (time_min && ((time_curr < time_min[0]) || (time_min[0] == 0))) time_min[0] = time_curr; + if (time_max && (time_curr > time_max[0])) time_max[0] = time_curr; +} + +static inline void print_time_vals(const char *name, float time_curr, size_t num_runs, + float *time_total, float *time_min, float *time_max) +{ + printf("\n\n"); + for (size_t k = 0; k < 3; k++) + { + printf("-- Runtimes out of %zu runs (%s) --\n", num_runs, name); + printf("curr runtime (us) = %0.2f\n", time_curr); + if (num_runs) printf("avg runtime (us) = %0.2f\n", time_total[0] / (float)num_runs); + printf("max runtime (us) = %0.2f\n", time_max[0]); + printf("min runtime (us) = %0.2f\n", time_min[0]); + } +} + +static inline void set_print_time_vals(const char *name, float time_curr, size_t num_runs, + float *time_total, float *time_min, float *time_max) +{ + set_time_vals(time_curr, time_total, time_min, time_max); + print_time_vals(name, time_curr, num_runs, time_total, time_min, time_max); +} diff --git a/device/bench/bench_ifft.c b/device/bench/bench_ifft.c new file mode 100644 index 0000000..a64e986 --- /dev/null +++ b/device/bench/bench_ifft.c @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_ifft.h +*/ + +#include "defines.h" + +#ifdef SE_ENABLE_TIMERS + +#include +#include +#include + +#include "bench_common.h" +#include "ckks_common.h" +#include "fft.h" +#include "fileops.h" +#include "parameters.h" +#include "timer.h" +#include "util_print.h" + +// -- Configuration +// #define SE_BENCH_IFFT_ROOTS +// #define SE_BENCH_IFFT_COMP +#define SE_BENCH_IFFT_FULL + +// -- Sanity check +#if !defined(SE_BENCH_IFFT_ROOTS) && !defined(SE_BENCH_IFFT_COMP) && !defined(SE_BENCH_IFFT_FULL) +#define SE_BENCH_IFFT_ROOTS +#endif + +#ifndef SE_USE_MALLOC +#if defined(SE_IFFT_LOAD_FULL) || defined(SE_IFFT_ONE_SHOT) +#define IFFT_TEST_ROOTS_MEM SE_DEGREE_N +#else +#define IFFT_TEST_ROOTS_MEM 0 +#endif +#endif + +void bench_ifft(void) +{ + // ================================ + // Configuration + // ================================ + // PolySizeType n = 16; +#ifdef SE_USE_MALLOC + PolySizeType n = 4096; +#else + PolySizeType n = SE_DEGREE_N; +#endif + // ================================ + size_t logn = get_log2(n); + + size_t size_mult = sizeof(double complex) / sizeof(ZZ); + size_t vec_size = n * size_mult; +#ifdef SE_IFFT_OTF + size_t ifft_roots_size = 0; +#else + size_t ifft_roots_size = n * size_mult; +#endif + + size_t mempool_size = vec_size + ifft_roots_size; +#ifdef SE_USE_MALLOC + ZZ *mempool = calloc(mempool_size, sizeof(ZZ)); +#else + ZZ mempool[SE_DEGREE_N + IFFT_TEST_ROOTS_MEM]; +#endif + + double complex *vec = (double complex *)mempool; + +#ifdef SE_IFFT_OTF + double complex *ifft_roots = 0; +#else + double complex *ifft_roots = (double complex *)&(mempool[vec_size]); +#endif + + Parms parms; + ckks_setup(n, 1, NULL, &parms); + +#ifdef SE_BENCH_IFFT_ROOTS + const char *bench_name = "inverse fft (timing roots load/gen)"; +#elif defined(SE_BENCH_IFFT_COMP) + const char *bench_name = "inverse fft (timing computation)"; +#else + const char *bench_name = "inverse fft (timing roots + computation)"; +#endif + print_bench_banner(bench_name, &parms); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0; + volatile float t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + t_curr = 0; + gen_double_complex_half_vec(vec, pow(10, 6), n); + +#if defined(SE_IFFT_LOAD_FULL) + se_secure_zero_memset(ifft_roots, n * sizeof(double complex)); +#endif + +#if defined(SE_BENCH_IFFT_ROOTS) || defined(SE_BENCH_IFFT_FULL) + reset_start_timer(&timer); +#endif + // -- Load or generate the roots -- +#ifdef SE_IFFT_LOAD_FULL + se_assert(ifft_roots); + load_ifft_roots(n, ifft_roots); +#endif + +#ifdef SE_BENCH_IFFT_ROOTS + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#elif defined(SE_BENCH_IFFT_COMP) + reset_start_timer(&timer); +#endif + + // -- Ifft computation -- + ifft_inpl(vec, n, logn, ifft_roots); + +#if defined(SE_BENCH_IFFT_COMP) || defined(SE_BENCH_IFFT_FULL) + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#endif + + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_double_complex_full("ifft(vec)", vec, n); + } + + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); + print_bench_banner(bench_name, &parms); + +#ifdef SE_USE_MALLOC + free(mempool); +#endif + delete_parameters(&parms); +} +#endif + +#ifndef SE_USE_MALLOC +#ifdef IFFT_TEST_ROOTS_MEM +#undef IFFT_TEST_ROOTS_MEM +#endif +#endif diff --git a/device/bench/bench_index_map.c b/device/bench/bench_index_map.c new file mode 100644 index 0000000..80b82dd --- /dev/null +++ b/device/bench/bench_index_map.c @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_index_map.c +*/ + +#include "defines.h" + +#if defined(SE_ENABLE_TIMERS) +#include +#include + +#include "bench_common.h" +#include "ckks_common.h" +#include "fileops.h" +#include "parameters.h" +#include "timer.h" +#include "util_print.h" + +void bench_index_map(void) +{ +#ifdef SE_USE_MALLOC + PolySizeType n = 4096; + uint16_t *vec = calloc(n, sizeof(uint16_t)); +#else + PolySizeType n = SE_DEGREE_N; + uint16_t vec[SE_DEGREE_N]; +#endif + uint16_t *index_map = &(vec[0]); + + Parms parms; + ckks_setup(n, 1, NULL, &parms); + + const char *bench_name = "index map"; + print_bench_banner(bench_name, &parms); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + se_secure_zero_memset(index_map, n * sizeof(uint16_t)); + reset_start_timer(&timer); + +#if defined(SE_INDEX_MAP_PERSIST) || defined(SE_INDEX_MAP_OTF) + ckks_calc_index_map(&parms, index_map); +#elif defined(SE_INDEX_MAP_LOAD) || defined(SE_INDEX_MAP_LOAD_PERSIST) + load_index_map(&parms, index_map); +#endif + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + fflush(stdout); + + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + + fflush(stdout); + print_poly_uint16_full("indices", index_map, n); + fflush(stdout); + } + fflush(stdout); + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); + + fflush(stdout); + print_bench_banner(bench_name, &parms); + +#ifdef SE_USE_MALLOC + free(vec); +#endif + delete_parameters(&parms); +} +#endif diff --git a/device/bench/bench_ntt.c b/device/bench/bench_ntt.c new file mode 100644 index 0000000..13ef849 --- /dev/null +++ b/device/bench/bench_ntt.c @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_ntt.c +*/ + +#include "defines.h" + +#if defined(SE_ENABLE_TIMERS) + +#include +#include + +#include "bench_common.h" +#include "ckks_common.h" +#include "fileops.h" +#include "ntt.h" +#include "parameters.h" +#include "timer.h" +#include "util_print.h" + +// -- Configuration +// #define SE_BENCH_NTT_ROOTS +// #define SE_BENCH_NTT_COMP +#define SE_BENCH_NTT_FULL + +// -- Sanity check +#if !defined(SE_BENCH_NTT_ROOTS) && !defined(SE_BENCH_NTT_COMP) && !defined(SE_BENCH_NTT_FULL) +#define SE_BENCH_NTT_ROOTS +#endif + +#ifndef SE_USE_MALLOC +#ifdef SE_NTT_FAST +#define NTT_TESTS_ROOTS_MEM 2 * SE_DEGREE_N +#elif defined(SE_NTT_REG) || defined(SE_NTT_ONE_SHOT) +#define NTT_TESTS_ROOTS_MEM SE_DEGREE_N +#else +#define NTT_TESTS_ROOTS_MEM 0 +#endif +#endif + +void bench_ntt(void) +{ +#ifdef SE_USE_MALLOC + PolySizeType n = 4096; +#else + PolySizeType n = SE_DEGREE_N; +#endif + + size_t vec_size = n; + +#ifdef SE_USE_MALLOC + +#ifdef SE_NTT_FAST + size_t ntt_roots_size = 2 * n; +#elif defined(SE_NTT_OTF) + size_t ntt_roots_size = 0; +#else + size_t ntt_roots_size = n; +#endif + + size_t mempool_size = vec_size + ntt_roots_size; + ZZ *mempool = calloc(mempool_size, sizeof(ZZ)); +#else + ZZ mempool[SE_DEGREE_N + NTT_TESTS_ROOTS_MEM]; +#endif + + ZZ *vec = mempool; + +#ifdef SE_NTT_OTF + ZZ *ntt_roots = 0; +#else + ZZ *ntt_roots = &(mempool[vec_size]); +#endif + + Parms parms; + parms.nprimes = 1; + ckks_setup(n, 1, NULL, &parms); + +#ifdef SE_BENCH_NTT_ROOTS + const char *bench_name = "ntt (timing roots load/gen)"; +#elif defined(SE_BENCH_NTT_COMP) + const char *bench_name = "ntt (timing computation)"; +#else + const char *bench_name = "ntt (timing roots + computation)"; +#endif + print_bench_banner(bench_name, &parms); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + t_curr = 0; + random_zzq_poly(vec, n, parms.curr_modulus); + +#ifdef SE_NTT_FAST + se_secure_zero_memset(ntt_roots, n * sizeof(ZZ) * 2); +#elif defined(SE_NTT_REG) || defined(SE_NTT_ONE_SHOT) + se_secure_zero_memset(ntt_roots, n * sizeof(ZZ)); +#endif + +#if defined(SE_BENCH_NTT_ROOTS) || defined(SE_BENCH_NTT_FULL) + printf("curr runtime (us) = %0.2f\n", t_curr); + reset_start_timer(&timer); +#endif + + // -- Load or generate the roots + ntt_roots_initialize(&parms, ntt_roots); + +#ifdef SE_BENCH_NTT_ROOTS + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#elif defined(SE_BENCH_NTT_COMP) + reset_start_timer(&timer); +#endif + + // -- Ntt computation + ntt_inpl(&parms, ntt_roots, vec); + +#if defined(SE_BENCH_NTT_COMP) || defined(SE_BENCH_NTT_FULL) + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#endif + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_full("ntt(vec)", vec, n); + } + + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); + print_bench_banner(bench_name, &parms); + +#ifdef SE_USE_MALLOC + free(mempool); +#endif + delete_parameters(&parms); +} +#endif + +#ifdef SE_USE_MALLOC +#ifdef NTT_TESTS_ROOTS_MEM +#undef NTT_TESTS_ROOTS_MEM +#endif +#endif diff --git a/device/bench/bench_sample.c b/device/bench/bench_sample.c new file mode 100644 index 0000000..a5e6ba0 --- /dev/null +++ b/device/bench/bench_sample.c @@ -0,0 +1,227 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_sample.c +*/ + +#include "defines.h" + +#ifdef SE_ENABLE_TIMERS + +#include "bench_common.h" +#include "sample.h" +#include "timer.h" +#include "util_print.h" + +void bench_sample_poly_cbd(void) +{ +#ifdef SE_USE_MALLOC + size_t n = 4096; + int8_t *vec = calloc(n, sizeof(int8_t)); +#else + size_t n = SE_DEGREE_N; + int8_t vec[SE_DEGREE_N]; +#endif + + int8_t *poly = &(vec[0]); + const char *bench_name = "sample poly cbd"; + print_bench_banner(bench_name, 0); + + SE_PRNG prng; + prng_randomize_reset(&prng, NULL); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + reset_start_timer(&timer); + + sample_poly_cbd_generic_prng_16(n, &prng, poly); // now stores [e1] + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_int8_full("cbd poly", poly, n); + } + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); +#ifdef SE_USE_MALLOC + free(vec); +#endif +} + +void bench_sample_ternary_small(void) +{ + size_t n = 4096; + Parms parms; + parms.small_u = 1; + set_parms_ckks(n, 1, &parms); + +#ifdef SE_USE_MALLOC + ZZ *vec = malloc(n / 4); +#else + ZZ vec[SE_DEGREE_N / 4]; +#endif + ZZ *poly = &(vec[0]); + const char *bench_name = "sample poly ternary (small)"; + print_bench_banner(bench_name, &parms); + SE_PRNG prng; + prng_randomize_reset(&prng, NULL); + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + reset_start_timer(&timer); + + sample_small_poly_ternary_prng_96(n, &prng, poly); + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_ternary_full("ternary (small) poly", poly, n, 1); + } + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); +#ifdef SE_USE_MALLOC + free(vec); + delete_parameters(&parms); +#endif +} + +void bench_sample_uniform(void) +{ + size_t n = 4096; + Parms parms; + set_parms_ckks(n, 1, &parms); +#ifdef SE_USE_MALLOC + ZZ *vec = calloc(n, sizeof(ZZ)); +#else + ZZ vec[SE_DEGREE_N]; +#endif + + ZZ *poly = &(vec[0]); + const char *bench_name = "sample poly uniform"; + print_bench_banner(bench_name, &parms); + + SE_PRNG prng; + prng_randomize_reset(&prng, NULL); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + reset_start_timer(&timer); + + sample_poly_uniform(&parms, &prng, poly); + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_full("uniform poly", poly, n); + } + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); +#ifdef SE_USE_MALLOC + free(vec); + delete_parameters(&parms); +#endif +} + +void bench_prng_randomize_seed(void) +{ + const char *bench_name = "prng randomize seed"; + print_bench_banner(bench_name, 0); + + SE_PRNG prng; + Timer timer; + + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + reset_start_timer(&timer); + + prng_randomize_reset(&prng, NULL); + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_uint8_full("random seed", prng.seed, SE_PRNG_SEED_BYTE_COUNT); + } + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); +} + +void bench_prng_fill_buffer(void) +{ + size_t n = 4096; + const char *bench_name = "prng fill buffer"; + print_bench_banner(bench_name, 0); + +#ifdef SE_USE_MALLOC + ZZ *vec = calloc(n, sizeof(ZZ)); +#else + ZZ vec[SE_DEGREE_N]; +#endif + ZZ *poly = &(vec[0]); + + SE_PRNG prng; + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + prng_randomize_reset(&prng, NULL); + reset_start_timer(&timer); + + prng_fill_buffer(n * sizeof(ZZ), &prng, (void *)poly); + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_full("random buffer", vec, n); + } + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); +#ifdef SE_USE_MALLOC + free(vec); +#endif +} + +void bench_prng_randomize_seed_fill_buffer(void) +{ + size_t n = 4096; + + const char *bench_name = "prng randomize + fill buffer"; + print_bench_banner(bench_name, 0); + +#ifdef SE_USE_MALLOC + ZZ *vec = calloc(n, sizeof(ZZ)); +#else + ZZ vec[SE_DEGREE_N]; +#endif + ZZ *poly = &(vec[0]); + + SE_PRNG prng; + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + reset_start_timer(&timer); + + prng_randomize_reset(&prng, NULL); + prng_fill_buffer(n * sizeof(ZZ), &prng, (void *)poly); + + stop_timer(&timer); + t_curr = read_timer(timer, MICRO_SEC); + if (b_itr) set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + print_poly_full("random buffer", vec, n); + } + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); +#ifdef SE_USE_MALLOC + free(vec); +#endif +} +#endif diff --git a/device/bench/bench_sym.c b/device/bench/bench_sym.c new file mode 100644 index 0000000..1dc3382 --- /dev/null +++ b/device/bench/bench_sym.c @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file bench_sym.c +*/ + +#include "defines.h" +#ifdef SE_ENABLE_TIMERS +#include + +#include "bench_common.h" +#include "ckks_common.h" +#include "ckks_sym.h" +#include "parameters.h" +#include "timer.h" + +// -- Configuration +// #define SE_BENCH_ENCODE +// #define SE_BENCH_SAMPLE +// #define SE_BENCH_ENCRYPT +#define SE_BENCH_FULL + +// -- Sanity check +#if !defined(SE_BENCH_ENCODE) && !defined(SE_BENCH_SAMPLE) && !defined(SE_BENCH_ENCRYPT) && \ + !defined(SE_BENCH_FULL) +#define SE_BENCH_ENCODE +#endif + +void bench_sym(void) +{ + // ========================= + // Configuration + // ========================= +#ifdef SE_USE_MALLOC + size_t n = 4096; + size_t nprimes = 3; +#else + size_t n = SE_DEGREE_N; + size_t nprimes = SE_NPRIMES; +#endif + Parms parms; + parms.scale = (n > 1024) ? pow(2, 25) : pow(2, 20); + // ========================= + parms.is_asymmetric = 0; + parms.small_s = 1; + parms.sample_s = 0; + if (!parms.sample_s) se_assert(parms.small_s); // Make sure we didn't set this accidentally + +#ifdef SE_USE_MALLOC + print_ckks_mempool_size(n, 1); + ZZ *mempool = ckks_mempool_setup_sym(n); +#else + print_ckks_mempool_size(); + ZZ mempool_local[MEMPOOL_SIZE]; + ZZ *mempool = &(mempool_local[0]); +#endif + + // Get pointers + SE_PTRS se_ptrs_local; + ckks_set_ptrs_sym(n, mempool, &se_ptrs_local); + double complex *conj_vals = se_ptrs_local.conj_vals; + int64_t *conj_vals_int = se_ptrs_local.conj_vals_int_ptr; + double complex *ifft_roots = se_ptrs_local.ifft_roots; + ZZ *c0 = se_ptrs_local.c0_ptr; + ZZ *c1 = se_ptrs_local.c1_ptr; + uint16_t *index_map = se_ptrs_local.index_map_ptr; + ZZ *ntt_roots = se_ptrs_local.ntt_roots_ptr; + ZZ *ntt_pte = se_ptrs_local.ntt_pte_ptr; + ZZ *s = se_ptrs_local.ternary; + flpt *v = se_ptrs_local.values; + size_t vlen = n / 2; + + SE_PRNG prng; + SE_PRNG shareable_prng; + + // -- Set up parameters and index_map if applicable + ckks_setup(n, nprimes, index_map, &parms); + + // -- If s is allocated space ahead of time, can load ahead of time too + // -- If we are testing and sample s is set, this will also sample s + ckks_setup_s(&parms, NULL, &prng, s); + + const char *bench_name = "Symmetric_Encryption"; + print_bench_banner(bench_name, &parms); + + Timer timer; + size_t COUNT = 10; + float t_total = 0, t_min = 0, t_max = 0, t_curr = 0; + for (size_t b_itr = 0; b_itr < COUNT + 1; b_itr++) + { + t_curr = 0; + se_assert(parms.nprimes >= 1); + gen_flpt_quarter_poly(v, -10, vlen); + // print_poly_flpt("v ", v, vlen); + + // -- Begin encode-encrypt sequence +#if defined(SE_BENCH_ENCODE) || defined(SE_BENCH_FULL) + reset_start_timer(&timer); +#endif + // -- First, encode base + ckks_encode_base(&parms, v, vlen, index_map, ifft_roots, conj_vals); + // print_poly_int64_full("conj_vals_int ", conj_vals_int_ptr, n); + +#ifdef SE_BENCH_ENCODE + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#elif defined(SE_BENCH_SAMPLE) + reset_start_timer(&timer); +#endif + // -- First prime, so need to sample error e + // -- While sampling error e, add it in place to the base message + ckks_sym_init(&parms, NULL, NULL, &shareable_prng, &prng, conj_vals_int); + +#if defined(SE_BENCH_SAMPLE) || defined(SE_BENCH_FULL) + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#endif + + for (size_t i = 0; i < parms.nprimes; i++) + { +#if defined(SE_BENCH_ENCRYPT) || defined(SE_BENCH_FULL) + reset_start_timer(&timer); +#endif + // -- Per prime Encode + Encrypt + ckks_encode_encrypt_sym(&parms, conj_vals_int, 0, &shareable_prng, s, ntt_pte, + ntt_roots, c0, c1, 0, 0); + +#if defined(SE_BENCH_ENCRYPT) || defined(SE_BENCH_FULL) + stop_timer(&timer); + t_curr += read_timer(timer, MICRO_SEC); +#endif + + if (b_itr && ((i + 1) == parms.nprimes)) + set_print_time_vals(bench_name, t_curr, b_itr, &t_total, &t_min, &t_max); + + // -- We must execute these printf calls to ensure the compiler does not + // optimize away generation of c0 and c1 polynomials. (Note that in the + // ifft truly on-the-fly case, c1 is overwritten by c0 and their + // outputs at this point will therefore be the same. We do not have to + // be concerned about c1 not being evaluated in this case though since + // c1 == a, which is necessary for the generation of c0.) + print_poly_full("c0 ", c0, n); + print_poly_full("c1 ", c1, n); + if ((i + 1) < parms.nprimes) ckks_next_prime_sym(&parms, s); + } + } + + print_time_vals(bench_name, t_curr, COUNT, &t_total, &t_min, &t_max); + print_bench_banner(bench_name, &parms); + +#ifdef SE_USE_MALLOC + free(mempool); + delete_parameters(&parms); +#endif +} +#endif diff --git a/device/bench/main.c b/device/bench/main.c new file mode 100644 index 0000000..90cec7a --- /dev/null +++ b/device/bench/main.c @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file main.c +*/ + +#include "defines.h" +#include "util_print.h" + +#ifdef SE_ENABLE_TIMERS + +// -- Benchmarks +extern void bench_index_map(void); +extern void bench_ifft(void); +extern void bench_ntt(void); +extern void bench_prng_randomize_seed(void); +extern void bench_prng_fill_buffer(void); +extern void bench_prng_randomize_seed_fill_buffer(void); +extern void bench_sample_uniform(void); +extern void bench_sample_ternary_small(void); +extern void bench_sample_poly_cbd(void); +extern void bench_sym(void); +extern void bench_asym(void); + +int main(void) +{ + #ifdef SE_NRF5_UART_PRINTF_ENABLED + se_setup_uart(); + #endif + se_randomness_init(); // required for nrf. does nothing if not on nrf + + // -- Uncomment benchmark to run + bench_index_map(); + // bench_ifft(); + // bench_ntt(); + // bench_prng_randomize_seed(); + // bench_prng_fill_buffer(); + // bench_prng_randomize_seed_fill_buffer(); + // bench_sample_uniform(); + // bench_sample_ternary_small(); + // bench_sample_poly_cbd(); + // bench_sym(); + #ifdef SE_DEFINE_PK_DATA + bench_asym(); + #endif + printf("...done with all benchmarks!\n"); +} +#else +int main(void) +{ +} +#endif diff --git a/device/lib/CMakeLists.txt b/device/lib/CMakeLists.txt new file mode 100644 index 0000000..772b2cf --- /dev/null +++ b/device/lib/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +set(SE_LIB_SOURCE_FILES ${SE_LIB_SOURCE_FILES} + ${CMAKE_CURRENT_LIST_DIR}/ckks_common.c + ${CMAKE_CURRENT_LIST_DIR}/ckks_sym.c + ${CMAKE_CURRENT_LIST_DIR}/ckks_asym.c + ${CMAKE_CURRENT_LIST_DIR}/fft.c + ${CMAKE_CURRENT_LIST_DIR}/fileops.c + ${CMAKE_CURRENT_LIST_DIR}/modulus.c + ${CMAKE_CURRENT_LIST_DIR}/network.c + ${CMAKE_CURRENT_LIST_DIR}/parameters.c + ${CMAKE_CURRENT_LIST_DIR}/polymodmult.c + ${CMAKE_CURRENT_LIST_DIR}/rng.c + ${CMAKE_CURRENT_LIST_DIR}/sample.c + ${CMAKE_CURRENT_LIST_DIR}/timer.c + ${CMAKE_CURRENT_LIST_DIR}/uint_arith.c + ${CMAKE_CURRENT_LIST_DIR}/ntt.c + ${CMAKE_CURRENT_LIST_DIR}/intt.c + ${CMAKE_CURRENT_LIST_DIR}/seal_embedded.c +) + +add_subdirectory(shake256) +set(SE_LIB_SOURCE_FILES ${SE_LIB_SOURCE_FILES} PARENT_SCOPE) diff --git a/device/lib/ckks_asym.c b/device/lib/ckks_asym.c new file mode 100644 index 0000000..6d23fc3 --- /dev/null +++ b/device/lib/ckks_asym.c @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_asym.c +*/ + +#include "ckks_asym.h" + +#include +#include +#include +#include // memset + +#include "ckks_common.h" +#include "ckks_sym.h" +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "modulo.h" +#include "ntt.h" +#include "parameters.h" +#include "polymodarith.h" +#include "polymodmult.h" +#include "sample.h" +#include "uintmodarith.h" +#include "util_print.h" + +#ifdef SE_USE_MALLOC +size_t ckks_get_mempool_size_asym(size_t degree) +{ + se_assert(degree >= 16); + if (degree == SE_DEGREE_N) return MEMPOOL_SIZE_Asym; + size_t n = degree; + size_t mempool_size = 4 * n; // base + + #ifdef SE_IFFT_OTF + mempool_size += n + n / 4 + n / 16; + #if defined(SE_NTT_ONE_SHOT) || defined(SE_NTT_REG) + mempool_size += n; + #elif defined(SE_NTT_FAST) + mempool_size += 2 * n; + #endif + #else + mempool_size += 4 * n; + #endif + + #if defined(SE_INDEX_MAP_PERSIST) || defined(SE_INDEX_MAP_LOAD_PERSIST) + mempool_size += n / 2; + #endif + + #ifdef SE_MEMPOOL_ALLOC_VALUES + mempool_size += n / 2; + #endif + + se_assert(mempool_size); + return mempool_size; +} + +ZZ *ckks_mempool_setup_asym(size_t degree) +{ + size_t mempool_size = ckks_get_mempool_size_asym(degree); + ZZ *mempool = calloc(mempool_size, sizeof(ZZ)); + if (!mempool) + { + printf("Error! Allocation failed. Exiting...\n"); + exit(1); + } + se_assert(mempool_size && mempool); + return mempool; +} +#endif + +void ckks_set_ptrs_asym(size_t degree, ZZ *mempool, SE_PTRS *se_ptrs) +{ + se_assert(mempool && se_ptrs); + size_t n = degree; + + // -- First, set everything to the set size or 0 + se_ptrs->conj_vals = (double complex *)mempool; + se_ptrs->conj_vals_int_ptr = (int64_t *)mempool; + se_ptrs->c1_ptr = &(mempool[2 * n]); + se_ptrs->c0_ptr = &(mempool[3 * n]); + + // -- In asymmetric mode, ntt_pte_ptr == ntt_u2pte_ptr + se_ptrs->ternary = &(mempool[5 * n + n / 4]); // default: SE_INDEX_MAP_OTF + se_ptrs->ifft_roots = 0; // default: SE_IFFT_OTF + se_ptrs->index_map_ptr = 0; // default: SE_INDEX_MAP_OTF + se_ptrs->ntt_roots_ptr = 0; // default: SE_NTT_OTF + se_ptrs->values = 0; + + // -- Sizes + size_t ifft_roots_size = 0; + size_t ntt_roots_size = 0; + size_t index_map_persist_size = 0; + + // -- Set ifft_roots based on IFFT type +#ifndef SE_IFFT_OTF + ifft_roots_size = 4 * n; + se_ptrs->ifft_roots = (double complex *)&(mempool[4 * n]); +#endif + + // -- Set ntt_roots based on NTT type +#if defined(SE_NTT_ONE_SHOT) || defined(SE_NTT_REG) + ntt_roots_size = n; + se_ptrs->ntt_roots_ptr = &(mempool[4 * n]); +#elif defined(SE_NTT_FAST) + ntt_roots_size = 2 * n; + se_ptrs->ntt_roots_ptr = &(mempool[4 * n]); +#endif + + size_t total_block2_size = ifft_roots_size ? ifft_roots_size : (ntt_roots_size + n); + + // -- Set pi inverse based on index map type +#if defined(SE_INDEX_MAP_LOAD) + se_ptrs->index_map_ptr = (uint16_t *)&(mempool[4 * n]); +#elif defined(SE_INDEX_MAP_PERSIST) || defined(SE_INDEX_MAP_LOAD_PERSIST) + // -- If ifft, this will be + the ifft_roots size + // else, this will be + the ntt size, + ntt(e1) size + se_ptrs->index_map_ptr = (uint16_t *)&(mempool[4 * n + total_block2_size]); + index_map_persist_size = n / 2; +#endif + + se_ptrs->ntt_pte_ptr = &(mempool[4 * n + ntt_roots_size]); +#ifdef SE_IFFT_OTF + se_ptrs->e1_ptr = + (int8_t *)&(mempool[4 * n + ntt_roots_size + n + index_map_persist_size]); + se_ptrs->ternary = + &(mempool[4 * n + ntt_roots_size + n + index_map_persist_size + n / 4]); +#else + se_ptrs->e1_ptr = (int8_t *)&(mempool[4 * n + ntt_roots_size + n]); + se_ptrs->ternary = &(mempool[4 * n + ntt_roots_size + n + n / 4]); +#endif + +#ifdef SE_MEMPOOL_ALLOC_VALUES + #ifdef SE_IFFT_OTF + se_ptrs->values = (flpt *)&( + mempool[4 * n + total_block2_size + index_map_persist_size + n / 4 + n / 16]); + #else + se_ptrs->values = + (flpt *)&(mempool[4 * n + total_block2_size + index_map_persist_size]); + #endif +#endif + + size_t address_size = 4; + se_assert(((ZZ *)se_ptrs->conj_vals) == ((ZZ *)se_ptrs->conj_vals_int_ptr)); + se_assert(se_ptrs->c1_ptr == + (ZZ *)se_ptrs->conj_vals_int_ptr + 2 * n * sizeof(ZZ) / address_size); + se_assert(se_ptrs->c0_ptr == se_ptrs->c1_ptr + n * sizeof(ZZ) / address_size); + + // -- Debugging: print all adresses +#ifdef SE_USE_MALLOC + se_print_addresses(mempool, se_ptrs, n, 0); + se_print_relative_positions(mempool, se_ptrs, n, 0); +#else + se_print_addresses(mempool, se_ptrs); + se_print_relative_positions(mempool, se_ptrs); +#endif +} + +void gen_pk(const Parms *parms, ZZ *s_small, ZZ *ntt_roots, uint8_t *seed, + SE_PRNG *shareable_prng, ZZ *s_save, int8_t *ep_small, ZZ *ntt_ep, ZZ *pk_c0, + ZZ *pk_c1) +{ + se_assert(parms && shareable_prng); + prng_randomize_reset(shareable_prng, seed); // Safe to share this prng + + // -- pk_c0 := - [a . ntt(exp(s))] + ntt(ep) or := - [a * s] + ep + // pk_c1 := a + // -- If pk_c0 and pk_c1 point to the same location, pk0 will overwrite pk1. + // However, we need to return pk1. Therefore, must save it in extra buffer. + ckks_encode_encrypt_sym(parms, 0, ep_small, shareable_prng, s_small, ntt_ep, + ntt_roots, pk_c0, pk_c1, s_save, 0); +} + +void ckks_asym_init(const Parms *parms, uint8_t *seed, SE_PRNG *prng, + int64_t *conj_vals_int, ZZ *u, int8_t *e1) +{ + se_assert(parms && prng); + size_t n = parms->coeff_count; + + // -- The prng should be reset & re-randomized once per encode-encrypt sequence. + // -- (This is not strictly necessary, but it is more consistent.) + // -- This call both resets the prng counter to 0 and the sets seed to a random value. + // -- Note that this prng's seed value should not be shared. + // printf("About to randomize reset\n"); + prng_randomize_reset(prng, seed); + + // -- Sample ternary polynomial u + // printf("About to sample small poly ternary\n"); + if (parms->small_u) { sample_small_poly_ternary_prng_96(n, prng, u); } + else + { + sample_poly_ternary(parms, prng, u); + } + // print_poly_ternary("u", u, n, parms->small_u); + // printf("Back from sample small poly ternary\n"); + + // -- Sample error polynomials e0 and e1 +#ifdef SE_DEBUG_NO_ERRORS + memset(e1, 0, n * sizeof(int8_t)); +#else + sample_add_poly_cbd_generic_inpl_prng_16(conj_vals_int, n, + prng); // now stores [pt+e0] + sample_poly_cbd_generic_prng_16(n, prng, e1); // now stores [e1] +#endif +} + +void ckks_encode_encrypt_asym(const Parms *parms, const int64_t *conj_vals_int, + const ZZ *u, const int8_t *e1, ZZ *ntt_roots, + ZZ *ntt_u_e1_pte, ZZ *ntt_u_save, ZZ *ntt_e1_save, + ZZ *pk_c0, ZZ *pk_c1) +{ + se_assert(parms); +#ifdef SE_DISABLE_TESTING_CAPABILITY + SE_UNUSED(ntt_u_save); + SE_UNUSED(ntt_e1_save); +#endif + + // =============================================================================== + // Generate ciphertext: (c[1], c[0]) = ([pk1*u + e1]_Rq, [pk0*u + (m + e0)]_Rq) + // =============================================================================== + size_t n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + // ------------------------- + // Load pk1, pk0 + // ------------------------- + if (parms->pk_from_file) + { + load_pki(1, parms, pk_c1); + load_pki(0, parms, pk_c0); + } + + // ------------------------- + // [pk1*u]_Rq, [pk0*u]_Rq + // ------------------------- + // -- If u is in small form, expand so we can calculate ntt(u) + // print_poly_ternary_full("u", u, n, 1); + if (parms->small_u) expand_poly_ternary(u, parms, ntt_u_e1_pte); + // print_poly_full("u reduced", ntt_u_e1_pte, n); + + // -- Initialize ntt roots + ntt_roots_initialize(parms, ntt_roots); + + // -- Calculate ntt(u). If requested, save ntt(u) for testing later + ntt_inpl(parms, ntt_roots, ntt_u_e1_pte); + // print_poly("ntt(u) (inside)", ntt_u_e1_pte, n); + +#ifndef SE_DISABLE_TESTING_CAPABILITY + // if (ntt_u_save) printf("ntt u save is not null\n"); + // else printf("ntt u save is null!!!!\n"); + if (ntt_u_save) memcpy(ntt_u_save, ntt_u_e1_pte, n * sizeof(ZZ)); + // if (ntt_u_save) print_poly("ntt(u) (inside, ntt_u_save)", ntt_u_save, n); +#endif + + // -- Calculate [ntt(pk1) . ntt(u)]_Rq. Store result in pk_c1 + poly_mult_mod_ntt_form_inpl(pk_c1, ntt_u_e1_pte, n, mod); + // print_poly("pk1*u (ntt)", pk_c1, n); + + // -- Calculate [ntt(pk0) . ntt(u)]_Rq. Store result in pk_c0 + poly_mult_mod_ntt_form_inpl(pk_c0, ntt_u_e1_pte, n, mod); + // print_poly("pk0*u (ntt)", pk_c0, n); + + // ------------------------- + // [pk1*u + e1]_Rq + // ------------------------- + reduce_set_e_small(parms, e1, ntt_u_e1_pte); + // print_poly("e1 reduced", ntt_u_e1_pte, n); + ntt_inpl(parms, ntt_roots, ntt_u_e1_pte); + +#ifndef SE_DISABLE_TESTING_CAPABILITY + if (ntt_e1_save) memcpy(ntt_e1_save, ntt_u_e1_pte, n * sizeof(ntt_u_e1_pte[0])); +#endif + + // print_poly("ntt(e1)", ntt_u_e1_pte, n); + poly_add_mod_inpl(pk_c1, ntt_u_e1_pte, n, mod); + // print_poly("c1 = pk1*u + e1 (ntt)", pk_c1, n); + + // ----------------------------- + // [pk0*u + m + e0]_Rq + // ----------------------------- + // -- We no longer need ntt_u, but we do need ntt((m + e0) = conj_vals_in) + // print_poly_int64("conj_vals_int ", conj_vals_int, n); + reduce_set_pte(parms, conj_vals_int, ntt_u_e1_pte); + // print_poly("m+e0 reduced", ntt_u_e1_pte, n); + ntt_inpl(parms, ntt_roots, ntt_u_e1_pte); + // print_poly("ntt(m+e0)", ntt_u_e1_pte, n); + poly_add_mod_inpl(pk_c0, ntt_u_e1_pte, n, mod); + // print_poly("c0 = pk0*u + m + e0 (ntt)", pk_c0, n); +} + +bool ckks_next_prime_asym(Parms *parms, ZZ *u) +{ + se_assert(parms && parms->is_asymmetric); + se_assert(u || parms->small_u); + + // -- Update curr_modulus_idx to next index + if (!next_modulus(parms)) return 0; + + // -- If 'u' is not in small form, we convert it to use the next prime + if (!parms->small_u) convert_poly_ternary_inpl(u, parms); + return 1; +} diff --git a/device/lib/ckks_asym.h b/device/lib/ckks_asym.h new file mode 100644 index 0000000..9079ac6 --- /dev/null +++ b/device/lib/ckks_asym.h @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_asym.h +*/ + +#pragma once + +#include +#include + +#include "ckks_common.h" +#include "defines.h" +#include "modulo.h" +#include "parameters.h" +#include "rng.h" + +#ifdef SE_USE_MALLOC +/** +Returns the required size of the memory pool in units of sizeof(ZZ). + +@param[in] degree Desired polynomial ring degree +@returns Required size of the memory pool units of sizeof(ZZ) +*/ +size_t ckks_get_mempool_size_asym(size_t degree); + +/** +Sets up the memory pool for CKKS asymmetric encryption. + +Note: This function calls calloc. + +@param[in] degree Desired polynomial ring degree +@returns A handle to the memory pool +*/ +ZZ *ckks_mempool_setup_asym(size_t degree); +#endif + +/** +Sets addresses of objects according to parameter settings for asymmetric CKKS encryption. +Should only need to be called once during initial memory allocation. + +@param[in] degree Polynomial ring degree +@param[in] mempool Handle to memory pool +@param[out] se_ptrs Pointers object. Pointers will be updated to mempool locations +*/ +void ckks_set_ptrs_asym(size_t degree, ZZ *mempool, SE_PTRS *se_ptrs); + +/** +Generates a CKKS public key from a CKKS secret key. Mainly useful for testing. + +Size req: If SE_NTT_OTF is not defined, 'ntt_roots' must contain space for roots according +to NTT type chosen. If seed != NULL, seed must be SE_PRNG_SEED_BYTE_COUNT long. + +@param[in] parms Parameters set by ckks_setup +@param[in] s_small Secret key in small form +@param ntt_roots [Optional]. Scratch space to load ntt roots. Ignored if +SE_NTT_OTF is chosen. +@param[in] seed [Optional.] Seed to seed 'prng' with. +@param[in,out] shareable_prng A shareable prng instance. Will reset and update counter. +@param[out] s_save Secret key modulo the current modulus (in ntt form). Useful +for testing. +@param[out] ep_small Secret key error polynomial ep in small form +@param[out] ntt_ep Expanded ep modulus the current modulus (in ntt form) +@param[out] pk_c0 First component of public key for current modulus +@param[out] pk_c1 Second component of public key for current modulus +*/ +void gen_pk(const Parms *parms, ZZ *s_small, ZZ *ntt_roots, uint8_t *seed, + SE_PRNG *shareable_prng, ZZ *s_save, int8_t *ep_small, ZZ *ntt_ep, ZZ *pk_c0, + ZZ *pk_c1); + +/** +Initializes values for a single full asymmetric CKKS encryption. Samples the errors (w/o +respect to any prime), as well as the ternary polynomial 'u'. Should be called once per +encode-encrypt sequence (just after ckks_encode_base). + +Note: This function modifies (i.e. resets and re-randomizes) the prng instance. + +Size req: If seed != NULL, seed must be SE_PRNG_SEED_BYTE_COUNT long. + +@param[in] parms Parameters set by ckks_setup +@param[in] seed [Optional.] Seed to seed 'prng' with. +@param[in,out] prng PRNG instance needed to generate error and ternary +polynomials. +@param[in,out] conj_vals_int As pointed to by conj_vals_int_ptr (n int64 values). In: + ckks plaintext; Out: plaintext + e0 (in non-reduced form) +@param[out] u Ternary polynomial +@param[out] e1 Error e1 (in non-reduced form) +*/ +void ckks_asym_init(const Parms *parms, uint8_t *seed, SE_PRNG *prng, + int64_t *conj_vals_int, ZZ *u, int8_t *e1); + +/** +Encodes and asymmetrically encrypts a vector of values using CKKS, for the current modulus +prime. Optionally returns some additional values useful for testing, if +SE_DISABLE_TESTING_CAPABILITY is not defined. + +Size req: 'ntt_roots' should have space for NTT roots according to NTT option chosen. +'ntt_u_e1_pte', 'pk_c0', and 'pk_c1' should have space for n ZZ elements. If testing, +'ntt_u_save' and 'ntt_e1_save' should have space for n ZZ elements. + +@param[in] parms Parameters set by ckks_setup +@param[in] conj_vals_int As set by ckks_asym_init +@param[in] u As set by ckks_asym_init +@param[in] e1 As set by ckks_asym_init +@param ntt_roots [Optional]. Scratch space to load ntt roots. Ignored if +SE_NTT_OTF is chosen. +@param[out] ntt_u_e1_pte Scratch space. Out: m + e0 in reduced and ntt form (useful for +testing). +@param[out] ntt_u_save [Optional, ignored if NULL]. Expanded and ntt form of u (useful +for testing) +@param[out] ntt_e1_save [Optional, ignored if NULL]. Reduced and ntt form of e1 (useful +for testing) +@param[out] pk_c0 First component of ciphertext +@param[out] pk_c1 Second component of ciphertext +*/ +void ckks_encode_encrypt_asym(const Parms *parms, const int64_t *conj_vals_int, + const ZZ *u, const int8_t *e1, ZZ *ntt_roots, + ZZ *ntt_u_e1_pte, ZZ *ntt_u_save, ZZ *ntt_e1_save, + ZZ *pk_c0, ZZ *pk_c1); + +/** +Updates parameters to next prime in modulus switching chain for asymmetric CKKS +encryption. Also reduces ternary polynomial to next prime modulus if used in expanded form +(compressed form u will be reduced later). + +@param[in,out] parms Parameters set by ckks_setup. curr_modulus_idx will be advanced by 1 +@param[in,out] u [Optional]. Ternary polynomial for asymmetric encryption. Can be +null if 'u' is stored in compressed form. +@returns 1 on success, 0 on failure (reached end of modulus chain) +*/ +bool ckks_next_prime_asym(Parms *parms, ZZ *u); diff --git a/device/lib/ckks_common.c b/device/lib/ckks_common.c new file mode 100644 index 0000000..704f3e3 --- /dev/null +++ b/device/lib/ckks_common.c @@ -0,0 +1,374 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_common.c +*/ + +#include "ckks_common.h" + +#include +#include +#include +#include +#include + +#include "ckks_asym.h" +#include "ckks_sym.h" +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "modulo.h" +#include "ntt.h" +#include "parameters.h" +#include "polymodarith.h" +#include "polymodmult.h" +#include "sample.h" +#include "uintmodarith.h" +#include "util_print.h" + +static const double MAX_INT_64_DOUBLE = (double)(0x7FFFFFFFFFFFFFFFULL); + +void ckks_calc_index_map(const Parms *parms, uint16_t *index_map) +{ + // -- Note: If n > 16384, would not be able to use uint16_t + se_assert(parms); + size_t n = parms->coeff_count; + se_assert(n <= 16384); + + uint64_t m = (uint64_t)n * 2; // m = 2n + size_t slot_count = n / 2; // slot_count = n/2 + size_t logn = parms->logn; // number of bits to represent n + + // -- 3 generates a mult. group mod 2^n w/ order n/2 (Note that 5 would work as well) + // uint64_t gen = 5; + uint64_t gen = 3; + uint64_t pos = 1; + + for (size_t i = 0; i < n / 2; i++) + { + // -- We want index1 + index2 to equal n-1 + size_t index1 = ((size_t)pos - 1) / 2; + size_t index2 = n - index1 - 1; + + // -- Merge index mapping step w/ bitrev step req. for later application of + // ifft/fft + index_map[i] = (uint16_t)bitrev(index1, logn); + index_map[i + slot_count] = (uint16_t)bitrev(index2, logn); + + // -- Next root + pos *= gen; + + // -- Since m is a power of 2, m-1 sets all bits less significant than the '1' + // bit in the value m. A bit-wise 'and' with (m-1) is therefore essentially + // a reduction moduluo m. Ex: m = 4 = 0100, m-1 = 3 = 0011 --> if pos = 21 + // = 10101: 21 % 4 = 1 = 10101 & 0011 + pos &= (m - 1); + } + // print_poly_uint16("index map", index_map, n); +} + +void ckks_setup(size_t degree, size_t nprimes, uint16_t *index_map, Parms *parms) +{ + set_parms_ckks(degree, nprimes, parms); +#ifdef SE_INDEX_MAP_PERSIST + ckks_calc_index_map(parms, index_map); +#elif defined(SE_INDEX_MAP_LOAD_PERSIST) + load_index_map(parms, index_map); +#endif +} + +void ckks_setup_custom(size_t degree, size_t nprimes, const ZZ *modulus_vals, const ZZ *ratios, + uint16_t *index_map, Parms *parms) +{ + if (!modulus_vals || !ratios) + { + ckks_setup(degree, nprimes, index_map, parms); + return; + } + ckks_setup_custom(degree, nprimes, modulus_vals, ratios, index_map, parms); +#ifdef SE_INDEX_MAP_PERSIST + ckks_calc_index_map(parms, index_map); +#elif defined(SE_INDEX_MAP_LOAD_PERSIST) + load_index_map(parms, index_map); +#endif +} + +void ckks_reset_primes(Parms *parms) +{ + reset_primes(parms); +} + +bool ckks_encode_base(const Parms *parms, const flpt *values, size_t values_len, + uint16_t *index_map, double complex *ifft_roots, double complex *conj_vals) +{ + se_assert(parms); + size_t n = parms->coeff_count; + size_t logn = parms->logn; + double scale = parms->scale; + +#ifdef SE_INDEX_MAP_LOAD + se_assert(index_map); + load_index_map(parms, index_map); +#endif + // if (index_map) print_poly_uint16("index map", index_map, n); + +#ifdef SE_INDEX_MAP_OTF + SE_UNUSED(index_map); + // uint64_t gen = 5; + uint64_t gen = 3; + uint64_t pos = 1; + uint64_t m = (uint64_t)n * 2; // m = 2n + + for (size_t i = 0; i < values_len; i++, pos = ((pos * gen) & (m - 1))) + { + size_t index1 = ((size_t)pos - 1) / 2; + size_t index2 = n - index1 - 1; + uint16_t index1_rev = (uint16_t)bitrev(index1, logn); + uint16_t index2_rev = (uint16_t)bitrev(index2, logn); +#else + size_t slot_count = n / 2; + for (size_t i = 0; i < values_len; i++) + { + se_assert(index_map); + uint16_t index1_rev = index_map[i]; + uint16_t index2_rev = index_map[i + slot_count]; +#endif + se_assert(index1_rev < n); + se_assert(index2_rev < n); + double complex val = (double complex)_complex(values[i], 0); + conj_vals[index1_rev] = val; + conj_vals[index2_rev] = val; + // -- Note: conj_vals[index2_rev] should be set to conj(val), but since we + // assume values[i] is non-complex, val == conj(val) + } + +#ifdef SE_VERBOSE_TESTING + // print_poly_uint16("index_map", index_map, n); + print_poly_double_complex("conj_vals inside", conj_vals, n); +#endif + +#ifdef SE_IFFT_LOAD_FULL + se_assert(ifft_roots); + load_ifft_roots(n, ifft_roots); +#endif + + // -- Note: ifft_roots argument will be ignored if SE_IFFT_OTF is defined + ifft_inpl(conj_vals, n, logn, ifft_roots); + +#ifdef SE_VERBOSE_TESTING + print_poly_double_complex("ifft(conj_vals) ", conj_vals, n); +#endif + +#ifdef SE_VERBOSE_TESTING + // -- Don't combine ifft step of dividing by n with ckks step of scaling by "scale" + double n_inv = 1.0 / (double)n; + se_assert(n_inv >= 0); + + for (size_t i = 0; i < n; i++) conj_vals[i] *= n_inv; + print_poly_double_complex("conj_vals", conj_vals, n); + + n_inv = scale; +#else + // -- Combine ifft step of dividing by n with ckks step of scaling by "scale" + double n_inv = scale / (double)n; +#endif + + // -- We no longer need the imaginary part of conj_vals + int64_t *conj_vals_int = (int64_t *)conj_vals; + for (size_t i = 0; i < n; i++) + { + // print_poly_double_complex("conj_vals", conj_vals, n); + + double coeff = round(se_creal(conj_vals[i]) * n_inv); + + // -- Check to make sure value can fit in an int64_t + if (fabs(coeff) > MAX_INT_64_DOUBLE) + { + printf("Error! Value at index %zu is possibly too large.\n", i); + printf("se_creal(conj_vals[i]): %0.6f\n", se_creal(conj_vals[i])); + printf("ninv: %0.6f\n", n_inv); + printf("coeff: %0.6f\n", coeff); + printf("fabs(coeff): %0.6f\n", fabs(coeff)); + printf("MAX_INT_64_DOUBLE: %0.6f\n", MAX_INT_64_DOUBLE); + return false; + } + + conj_vals_int[i] = (int64_t)(coeff); + // conj_vals_int[i] = (int64_t)round((se_creal(conj_vals[i])) * n_inv); + // print_poly_int64("conj_vals_int", conj_vals_int, n); + } + +#ifdef SE_VERBOSE_TESTING + print_poly_int64("conj_vals_int", conj_vals_int, n); +#endif + return true; +} + +/** +Core functionality for following two reduce_ functions. + +@param[in] conj_vals_int Value to be reduced +@param[in] mod Modulus to reduce value by +@returns Reduced value +*/ +ZZ reduce_pte_core(int64_t conj_vals_int, const Modulus *mod) +{ + uint64_t coeff_abs = (uint64_t)(llabs(conj_vals_int)); + ZZ mask = (ZZ)(conj_vals_int < 0); + + ZZ *coeff_abs_vec = (ZZ *)&coeff_abs; + ZZ coeff_crt = barrett_reduce_64input_32modulus(coeff_abs_vec, mod); + + // -- This is the same as the following, but in constant-time + // ZZ val = (conj_vals_int < 0) ? (mod->value - coeff_crt) : coeff_crt; + ZZ val = ((mod->value - coeff_crt) & (-mask)) + (coeff_crt & (mask - 1)); + + return val; +} + +void reduce_set_pte(const Parms *parms, const int64_t *conj_vals_int, ZZ *out) +{ + PolySizeType n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + for (size_t i = 0; i < n; i++) { out[i] = reduce_pte_core(conj_vals_int[i], mod); } +} + +void reduce_add_pte(const Parms *parms, const int64_t *conj_vals_int, ZZ *out) +{ + PolySizeType n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + for (size_t i = 0; i < n; i++) + { + ZZ val = reduce_pte_core(conj_vals_int[i], mod); + add_mod_inpl(&(out[i]), val, mod); + } +} + +void reduce_set_e_small(const Parms *parms, const int8_t *e, ZZ *out) +{ + PolySizeType n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + for (size_t i = 0; i < n; i++) { out[i] = ((-(ZZ)(e[i] < 0)) & mod->value) + (ZZ)e[i]; } +} + +void reduce_add_e_small(const Parms *parms, const int8_t *e, ZZ *out) +{ + PolySizeType n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + for (size_t i = 0; i < n; i++) + { + add_mod_inpl(&(out[i]), ((-(ZZ)(e[i] < 0)) & mod->value) + (ZZ)e[i], mod); + } +} + +#ifdef SE_USE_MALLOC +void se_print_relative_positions(const ZZ *st, const SE_PTRS *se_ptrs, size_t n, bool sym) +{ +#else +void se_print_relative_positions(const ZZ *st, const SE_PTRS *se_ptrs) +{ +#ifdef SE_ENCRYPT_TYPE_SYMMETRIC + bool sym = 1; +#else + bool sym = 0; +#endif + size_t n = SE_DEGREE_N; +#endif + printf("\n\tPrinting relative positions (negative value == does not exist)...\n"); + printf("\t conj_vals: %0.4f\n", ((ZZ *)se_ptrs->conj_vals - st) / (double)n); + printf("\tconj_vals_int: %0.4f\n", ((ZZ *)se_ptrs->conj_vals_int_ptr - st) / (double)n); + printf("\t c1: %0.4f\n", ((ZZ *)se_ptrs->c1_ptr - st) / (double)n); + printf("\t c0: %0.4f\n", ((ZZ *)se_ptrs->c0_ptr - st) / (double)n); + printf("\t ntt_pte: %0.4f\n", ((ZZ *)se_ptrs->ntt_pte_ptr - st) / (double)n); + printf("\t ifft_roots: %0.4f\n", ((ZZ *)se_ptrs->ifft_roots - st) / (double)n); + printf("\t ntt_roots: %0.4f\n", ((ZZ *)se_ptrs->ntt_roots_ptr - st) / (double)n); + printf("\t index_map: %0.4f\n", ((ZZ *)se_ptrs->index_map_ptr - st) / (double)n); + if (!sym) { printf("\t e1: %0.4f\n", ((ZZ *)se_ptrs->e1_ptr - st) / (double)n); } + printf("\t ternary: %0.4f\n", ((ZZ *)se_ptrs->ternary - st) / (double)n); + printf("\t values: %0.4f\n", ((ZZ *)se_ptrs->values - st) / (double)n); + printf("\n"); +} + +#ifdef SE_USE_MALLOC +void se_print_addresses(const ZZ *mempool, const SE_PTRS *se_ptrs, size_t n, bool sym) +{ + size_t mempool_size = sym ? ckks_get_mempool_size_sym(n) : ckks_get_mempool_size_asym(n); +#else +void se_print_addresses(const ZZ *mempool, const SE_PTRS *se_ptrs) +{ + size_t mempool_size = MEMPOOL_SIZE; +#ifdef SE_ENCRYPT_TYPE_SYMMETRIC + bool sym = 1; +#else + bool sym = 0; +#endif +#endif + printf("\n\tPrinting addresses (nil == does not exist)...\n"); + printf("mempool begin address: %p\n", mempool); + printf("mempool end address: %p\n", &(mempool[mempool_size - 1])); + printf("\t conj_vals: %p\n", se_ptrs->conj_vals); + printf("\tconj_vals_int: %p\n", se_ptrs->conj_vals_int_ptr); + printf("\t c1: %p\n", se_ptrs->c1_ptr); + printf("\t c0: %p\n", se_ptrs->c0_ptr); + printf("\t ntt_pte: %p\n", se_ptrs->ntt_pte_ptr); + printf("\t ifft_roots: %p\n", se_ptrs->ifft_roots); + printf("\t ntt_roots: %p\n", se_ptrs->ntt_roots_ptr); + printf("\t index_map: %p\n", se_ptrs->index_map_ptr); + if (!sym) printf("\t e1: %p\n", se_ptrs->e1_ptr); + printf("\t ternary: %p\n", se_ptrs->ternary); + printf("\t values: %p\n", se_ptrs->values); + printf("\n"); +} + +#ifdef SE_USE_MALLOC +void print_ckks_mempool_size(size_t n, bool sym) +{ + se_assert(n >= 16); + size_t mempool_size = sym ? ckks_get_mempool_size_sym(n) : ckks_get_mempool_size_asym(n); + se_assert(mempool_size); +#else +void print_ckks_mempool_size(void) +{ + size_t mempool_size = MEMPOOL_SIZE; + size_t n = SE_DEGREE_N; +#endif + + size_t n_size_B = n * sizeof(ZZ); + size_t n_size_KB = n * sizeof(ZZ) / 1024; + +#ifdef SE_MEMPOOL_ALLOC_VALUES + bool alloc_values = 1; +#else + bool alloc_values = 0; +#endif + + const char *print_str1 = "\nTotal memory requirement (incl. values buffer) :"; + const char *print_str2 = "\nTotal memory requirement (without values buffer):"; + const char *print_str_curr = alloc_values ? print_str1 : print_str2; + + for (size_t i = 0; i < 1 + (size_t)alloc_values; i++) + { + size_t mempool_size_B = mempool_size * sizeof(ZZ); + size_t mempool_size_KB = mempool_size_B / 1024; + + if (mempool_size_KB) + printf("%s %zu KB\n", print_str_curr, mempool_size_KB); + else + printf("%s %zu bytes\n", print_str_curr, mempool_size_B); + + printf("\t( i.e. [(degree = %zu) * (sizeof(ZZ) = %zu bytes) = ", n, sizeof(ZZ)); + + if (n_size_KB) + printf("%zu KB] * %0.4f )\n\n", n_size_KB, mempool_size / (double)n); + else + printf("%zu bytes] * %0.4f )\n\n", n_size_B, mempool_size / (double)n); + + mempool_size -= n / 2; + print_str_curr = print_str2; + } +} diff --git a/device/lib/ckks_common.h b/device/lib/ckks_common.h new file mode 100644 index 0000000..864bd63 --- /dev/null +++ b/device/lib/ckks_common.h @@ -0,0 +1,282 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_common.h +*/ + +#pragma once + +#include +#include + +#include "defines.h" +#include "modulo.h" +#include "parameters.h" +#include "rng.h" + +/** +Object that stores pointers to various objects for CKKS encode/encryption. +For the following, n is the polynomial ring degree. + +@param conj_vals Storage for output of encode +@param ifft_roots Roots for inverse fft (n double complex values) +@param values Floating point values to encode/encrypt +@param ternary Ternary polynomial ('s' for symmetric, 'u' for asymmetric). + May be in small or expanded form, depending on parameters. +@param conj_vals_int_ptr +@param c0_ptr 1st component of a ciphertext for a particular prime +@param c1_ptr 2nd component of a ciphertext for a particular prime +@param index_map_ptr Index map values +@param ntt_roots_ptr Storage for NTT roots +@param ntt_pte_ptr Used for adding the plaintext to the error. If asymmetric, this +is also used for ntt(u) and ntt(e1). +@param e1_ptr Second error polynomial (unused in symmetric case) +*/ +typedef struct SE_PTRS +{ + double complex *conj_vals; + double complex *ifft_roots; // Roots for inverse fft (n double complex values) + flpt *values; // Floating point values to encode/encrypt (up to n/2 type-ZZ values) + ZZ *ternary; // Ternary polynomial ('s' for symmetric, 'u' for asymmetric). + + // -- The following will point to sections of conj_vals and ifft_roots above + + int64_t *conj_vals_int_ptr; + ZZ *c0_ptr; // 1st component of a ciphertext for a particular prime + ZZ *c1_ptr; // 2nd component of a ciphertext for a particular prime + uint16_t *index_map_ptr; // Index map values + ZZ *ntt_roots_ptr; // Storage for NTT roots + ZZ *ntt_pte_ptr; // Used for adding the plaintext to the error. + int8_t *e1_ptr; // Second error polynomial (unused in symmetric case) +} SE_PTRS; + +/** +Computes the values for the index map. This corresponds to the "pi" inverse projection +symbol in the CKKS paper, merged with the bit-reversal required for the ifft/fft. + +Size req: 'index_map' must constain space for n uint16_t elements. + +@param[in] parms Parameters set by ckks_setup +@param[out] index_map Index map values +*/ +void ckks_calc_index_map(const Parms *parms, uint16_t *index_map); + +/** +Sets the parameters according to the request polynomial ring degree. Also sets the index +map if required (based on index map option defined). This should be called once during +memory allocation to setup the parameters. + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: If 'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t +elements. + +@param[in] degree Desired polynomial ring degree. +@param[in] nprimes Desired number of primes +@param[out] index_map [Optional]. Pointer to index map values buffer +@param[out] parms Parameters instance +*/ +void ckks_setup(size_t degree, size_t nprimes, uint16_t *index_map, Parms *parms); + +/** +Sets the parameters according to request custom parameters. Also sets the index map if +required (based on index map option defined). This should be called once during memory +allocation to setup the parameters. If either 'modulus_vals' or 'ratios' is NULL, uses +regular (non-custom) ckks_setup instead. + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: If 'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t +elements. + +@param[in] degree Desired polynomial ring degree. +@param[in] nprimes Desired number of primes +@param[in] modulus_vals An array of nprimes type-ZZ modulus values. +@param[in] ratios An array of const_ratio values for each custom modulus value +(high word, followed by low word). +@param[out] index_map [Optional]. Pointer to index map values buffer +@param[out] parms Parameters instance +*/ +void ckks_setup_custom(size_t degree, size_t nprimes, const ZZ *modulus_vals, + const ZZ *ratios, uint16_t *index_map, Parms *parms); + +/** +Resets the encryption parameters. Should be called once per encode-encrypt sequence to set +curr_modulus_idx back to the start of the modulus chain. Does not need to be called the +very first time after ckks_setup_parms is called, however. + +@param[in,out] parms Parameters set by ckks_setup +*/ +void ckks_reset_primes(Parms *parms); + +/** +CKKS encoding base (w/o respect to a particular modulus). Should be called once per +encode-encrypt sequence. Encoding can fail for certain inputs, so returns a value +indicating success or failure. + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: 'values' can contain at most n/2 slots (i.e. n/2 ZZ values), where n is the +polynomial ring degree. 'conj_vals' must contain space for n double complex values. If +'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t elements. + +@param[in] parms Parameters set by ckks_setup +@param[in] values Initial message array with (up to) n/2 slots +@param[in] values_len Number of elements in values array. Must be <= n/2. +@param[in] index_map [Optional]. If passed in, can avoid 1 flash read +@param ifft_roots Scratch space to load ifft roots +@param[out] conj_vals conj_vals_int in first n ZZ values +@returns True on success, False on failure +*/ +bool ckks_encode_base(const Parms *parms, const flpt *values, size_t values_len, + uint16_t *index_map, double complex *ifft_roots, + double complex *conj_vals); + +/** +Reduces all values in conj_vals_int modulo the current modulus and stores result in out. + +Size req: out must contain space for n ZZ values + +@param[in] parms Parameters set by ckks_setup +@param[in] conj_vals_int Array of values to be reduced, as output by ckks_encode_base +@param[out] out Result array of reduced values +*/ +void reduce_set_pte(const Parms *parms, const int64_t *conj_vals_int, ZZ *out); + +/** +Reduces all values in conj_vals_int modulo the current modulus and addres result to out. + +Size req: out must contain space for n ZZ values + +@param[in] parms Parameters set by ckks_setup +@param[in] conj_vals_int Array of values to be reduced +@param[out] out Updated array +*/ +void reduce_add_pte(const Parms *parms, const int64_t *conj_vals_int, ZZ *out); + +/** +Converts an error polynomial in small form to an error polynomial modulo the current +modulus. + +Size req: out must contain space for n ZZ values + +@param[in] parms Parameters set by ckks_setup +@param[in] e Error polynomial in small form +@param[out] out Result error polynomial modulo the current modulus +*/ +void reduce_set_e_small(const Parms *parms, const int8_t *e, ZZ *out); + +/** +Converts an error polynomial in small form to an error polynomial modulo the current +modulus and adds it to the values stored in the 'out' array. + +Size req: out must contain space for n ZZ values + +@param[in] parms Parameters set by ckks_setup +@param[in] e Error polynomial in small form +@param[out] out Result updated polynomial +*/ +void reduce_add_e_small(const Parms *parms, const int8_t *e, ZZ *out); + +#ifdef SE_USE_MALLOC +/** +Prints the relative positions of various objects. + +@param[in] st Starting address of memory pool +@param[in] se_ptrs SE_PTRS object contains pointers to objects +@param[in] n Polynomial ring degree +@param[in] sym Set to 1 if in symmetric mode +*/ +void se_print_relative_positions(const ZZ *st, const SE_PTRS *se_ptrs, size_t n, + bool sym); + +/** +Prints the addresses of various objects. + +@param[in] mempool Memory pool handle +@param[in] se_ptrs SE_PTRS object contains pointers to objects +@param[in] n Polynomial ring degree +@param[in] sym Set to 1 if in symmetric mode +*/ +void se_print_addresses(const ZZ *mempool, const SE_PTRS *se_ptrs, size_t n, bool sym); + +/** +Prints a banner for the size of the memory pool + +@param[in] n Polynomial ring degree +@param[in] sym Set to 1 if in symmetric mode +*/ +void print_ckks_mempool_size(size_t n, bool sym); +#else +/** +Prints the relative positions of various objects. + +@param[in] st Starting address of memory pool +@param[in] se_ptrs SE_PTRS object contains pointers to objects +*/ +void se_print_relative_positions(const ZZ *st, const SE_PTRS *se_ptrs); + +/** +Prints the addresses of various objects. + +@param[in] mempool Memory pool handle +@param[in] se_ptrs SE_PTRS object contains pointers to objects +*/ +void se_print_addresses(const ZZ *mempool, const SE_PTRS *se_ptrs); + +/** +Prints a banner for the size of the memory pool +*/ +void print_ckks_mempool_size(void); +#endif + +// -- Calculate mempool size for no-malloc case +#ifdef SE_IFFT_OTF + #if defined(SE_NTT_ONE_SHOT) || defined(SE_NTT_REG) + #define MEMPOOL_SIZE_BASE 5 * SE_DEGREE_N + #elif defined(SE_NTT_FAST) + #define MEMPOOL_SIZE_BASE 7 * SE_DEGREE_N + #else + #define MEMPOOL_SIZE_BASE 4 * SE_DEGREE_N + #endif +#else + #define MEMPOOL_SIZE_BASE 8 * SE_DEGREE_N +#endif + +#if defined(SE_INDEX_MAP_PERSIST) || defined(SE_INDEX_MAP_LOAD_PERSIST) + #define SE_INDEX_MAP_PERSIST_SIZE SE_DEGREE_N / 2 +#else + #define SE_INDEX_MAP_PERSIST_SIZE 0 +#endif + +#ifdef SE_SK_PERSISTENT + #define SK_PERSIST_SIZE SE_DEGREE_N / 16 +#else + #define SK_PERSIST_SIZE 0 +#endif + +#ifdef SE_MEMPOOL_ALLOC_VALUES + #define VALUES_ALLOC_SIZE SE_DEGREE_N / 2 +#else + #define VALUES_ALLOC_SIZE 0 +#endif + +#define MEMPOOL_SIZE_sym \ + MEMPOOL_SIZE_BASE + SE_INDEX_MAP_PERSIST_SIZE + SK_PERSIST_SIZE + VALUES_ALLOC_SIZE + +#ifdef SE_IFFT_OTF + #define MEMPOOL_SIZE_BASE_Asym \ + MEMPOOL_SIZE_BASE + SE_DEGREE_N + SE_DEGREE_N / 4 + SE_DEGREE_N / 16 +#else + #define MEMPOOL_SIZE_BASE_Asym MEMPOOL_SIZE_BASE +#endif + +#define MEMPOOL_SIZE_Asym \ + MEMPOOL_SIZE_BASE_Asym + SE_INDEX_MAP_PERSIST_SIZE + VALUES_ALLOC_SIZE + +#ifdef SE_ENCRYPT_TYPE_SYMMETRIC + #define MEMPOOL_SIZE MEMPOOL_SIZE_sym +#else + #define MEMPOOL_SIZE MEMPOOL_SIZE_Asym +#endif diff --git a/device/lib/ckks_sym.c b/device/lib/ckks_sym.c new file mode 100644 index 0000000..c3c1267 --- /dev/null +++ b/device/lib/ckks_sym.c @@ -0,0 +1,314 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_sym.c +*/ + +#include "ckks_sym.h" + +#include +#include +#include +#include // memset + +#include "ckks_common.h" +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "modulo.h" +#include "ntt.h" +#include "parameters.h" +#include "polymodarith.h" +#include "polymodmult.h" +#include "sample.h" +#include "uintmodarith.h" +#include "util_print.h" + +#ifdef SE_USE_MALLOC +size_t ckks_get_mempool_size_sym(size_t degree) +{ + se_assert(degree >= 16); + if (degree == SE_DEGREE_N) return MEMPOOL_SIZE_sym; + size_t n = degree; + size_t mempool_size = 4 * n; // minimum + +#ifdef SE_IFFT_OTF +#if defined(SE_NTT_ONE_SHOT) || defined(SE_NTT_REG) + mempool_size += n; +#elif defined(SE_NTT_FAST) + mempool_size += 2 * n; +#endif +#else + mempool_size += 4 * n; +#endif + +#if defined(SE_INDEX_MAP_PERSIST) || defined(SE_INDEX_MAP_LOAD_PERSIST) + mempool_size += n / 2; +#endif + +#ifdef SE_SK_PERSISTENT + mempool_size += n / 16; +#endif + +#ifdef SE_MEMPOOL_ALLOC_VALUES + mempool_size += n / 2; +#endif + + se_assert(mempool_size); + return mempool_size; +} + +ZZ *ckks_mempool_setup_sym(size_t degree) +{ + size_t mempool_size = ckks_get_mempool_size_sym(degree); + ZZ *mempool = calloc(mempool_size, sizeof(ZZ)); + if (!mempool) + { + printf("Error! Allocation failed. Exiting...\n"); + exit(1); + } + se_assert(mempool_size && mempool); + return mempool; +} +#endif + +// Note: first elements of ntt_roots will also contain partial pt +// This is a convenience function to return the correct addresses +// This can be called once during initial memory allocation and never needs to +// be called again And also load in secret key if necessary + +void ckks_set_ptrs_sym(size_t degree, ZZ *mempool, SE_PTRS *se_ptrs) +{ + se_assert(mempool && se_ptrs); + size_t n = degree; + + // -- First, set everything to set size or 0 + se_ptrs->conj_vals = (double complex *)mempool; + se_ptrs->conj_vals_int_ptr = (int64_t *)mempool; + se_ptrs->c1_ptr = &(mempool[2 * n]); + se_ptrs->c0_ptr = &(mempool[3 * n]); + se_ptrs->ntt_pte_ptr = &(mempool[2 * n]); + + se_ptrs->ternary = &(mempool[3 * n]); // default: SE_SK_NOT_PERSISTENT + se_ptrs->ifft_roots = 0; // default: SE_IFFT_OTF + se_ptrs->index_map_ptr = 0; // default: SE_INDEX_MAP_OTF + se_ptrs->ntt_roots_ptr = 0; // default: SE_NTT_OTF + se_ptrs->values = 0; + + // -- Sizes + size_t ifft_roots_size = 0; + size_t ntt_roots_size = 0; + size_t index_map_persist_size = 0; + size_t s_persist_size = 0; + + // -- Set ifft_roots based on IFFT type +#ifndef SE_IFFT_OTF + ifft_roots_size = 4 * n; + se_ptrs->ifft_roots = (double complex *)&(mempool[4 * n]); + se_ptrs->ntt_pte_ptr = &(mempool[6 * n]); +#endif + + // -- Set ntt_roots based on NTT type +#if defined(SE_NTT_ONE_SHOT) || defined(SE_NTT_REG) + ntt_roots_size = n; + se_ptrs->ntt_roots_ptr = &(mempool[4 * n]); +#elif defined(SE_NTT_FAST) + ntt_roots_size = 2 * n; + se_ptrs->ntt_roots_ptr = &(mempool[4 * n]); +#endif + + size_t total_block2_size = ifft_roots_size ? ifft_roots_size : ntt_roots_size; + + // -- Set pi inverse based on index map type +#if defined(SE_INDEX_MAP_LOAD) + se_ptrs->index_map_ptr = (uint16_t *)&(mempool[4 * n]); +#elif defined(SE_INDEX_MAP_PERSIST) || defined(SE_INDEX_MAP_LOAD_PERSIST) + // -- If ifft, this will be + the ifft_roots size + // else, this will be + the ntt size + se_ptrs->index_map_ptr = (uint16_t *)&(mempool[4 * n + total_block2_size]); + index_map_persist_size = n / 2; +#endif + +#ifdef SE_SK_PERSISTENT + s_persist_size = n / 16; + se_ptrs->ternary = &(mempool[4 * n + total_block2_size + index_map_persist_size]); +#elif !defined(SE_IFFT_OTF) && defined(SE_SK_PERSISTENT_ACROSS_PRIMES) + se_ptrs->ternary = &(mempool[7 * n]); +#endif + +#ifdef SE_MEMPOOL_ALLOC_VALUES + se_ptrs->values = + (flpt *)&(mempool[4 * n + total_block2_size + index_map_persist_size + s_persist_size]); +#endif + + size_t address_size = 4; + se_assert(((ZZ *)se_ptrs->conj_vals) == ((ZZ *)se_ptrs->conj_vals_int_ptr)); + se_assert(se_ptrs->c1_ptr == + (ZZ *)se_ptrs->conj_vals_int_ptr + 2 * n * sizeof(ZZ) / address_size); + se_assert(se_ptrs->c1_ptr + n * sizeof(ZZ) / address_size == se_ptrs->c0_ptr); + + // -- Debugging: print all adresses +#ifdef SE_USE_MALLOC + se_print_addresses(mempool, se_ptrs, n, 1); + se_print_relative_positions(mempool, se_ptrs, n, 1); +#else + se_print_addresses(mempool, se_ptrs); + se_print_relative_positions(mempool, se_ptrs); +#endif +} + +void ckks_setup_s(const Parms *parms, uint8_t *seed, SE_PRNG *prng, ZZ *s) +{ + // -- Keep s in small form until a later point, so we can store in + // separate memory in compressed form + if (parms->sample_s) + { + se_assert(prng); + prng_randomize_reset(prng, seed); + sample_small_poly_ternary_prng_96(parms->coeff_count, prng, s); + } + else + { + SE_UNUSED(prng); + load_sk(parms, s); + } +} + +void ckks_sym_init(const Parms *parms, uint8_t *share_seed, uint8_t *seed, SE_PRNG *shareable_prng, + SE_PRNG *prng, int64_t *conj_vals_int) +{ + // -- Each prng must be reset & re-randomized once per encode-encrypt sequence. + // -- 'prng_randomize_reset' will set the prng seed to a random value and the prng + // counter to 0 + // -- (If seeds are !NULL, seeds will be used to seed prng instead of a random value.) + // -- The seed associated with the prng used to sample 'a' can be shared + // -- NOTE: The re-randomization is not strictly necessary if counter has not wrapped + // around + // and we share both the seed and starting counter value with the server + // for the shareable part. + prng_randomize_reset(shareable_prng, share_seed); // Used for 'a' + prng_randomize_reset(prng, seed); // Used for error + + // -- Sample ep and add it to the signed pt. + // -- This prng's seed value should not be shared. + sample_add_poly_cbd_generic_inpl_prng_16(conj_vals_int, parms->coeff_count, prng); +} + +void ckks_encode_encrypt_sym(const Parms *parms, const int64_t *conj_vals_int, + const int8_t *ep_small, SE_PRNG *shareable_prng, ZZ *s_small, + ZZ *ntt_pte, ZZ *ntt_roots, ZZ *c0_s, ZZ *c1, ZZ *s_save, ZZ *c1_save) +{ + se_assert(parms); +#ifdef SE_DISABLE_TESTING_CAPABILITY + SE_UNUSED(ep_small); + SE_UNUSED(s_save); + SE_UNUSED(c1_save); +#endif + + // ============================================================== + // Generate ciphertext: (c[1], c[0]) = (a, [-a*s + m + e]_Rq) + // ============================================================== + PolySizeType n = parms->coeff_count; + const Modulus *mod = parms->curr_modulus; + + // ---------------------- + // c1 = a <--- U + // ---------------------- + sample_poly_uniform(parms, shareable_prng, c1); + // print_poly("rlwe a(c1)", c1, n); + +#ifndef SE_DISABLE_TESTING_CAPABILITY + se_assert(conj_vals_int || ep_small); + // -- At this point, it is safe to send c1 away. This will allow us to re-use c1's + // memory. + // -- However, we may be debugging, in which case we need to store c1 somewhere for + // debugging later. + // -- Note: This provides very little memory savings overall, so isn't necessary to + // use. + if (c1_save) memcpy(c1_save, c1, n * sizeof(ZZ)); +#endif + + // ---------------------------- + // c0 = [-a*s + m + e]_Rq + // ---------------------------- + // -- Load s (if not already loaded) + // -- For now, we require s to be in small form. + se_assert(s_small); +#ifdef SE_SK_NOT_PERSISTENT + se_assert(!parms->sample_s); + load_sk(parms, s_small); +#elif defined(SE_SK_PERSISTENT_ACROSS_PRIMES) + // -- Note that if we are here, ifft type is not otf, which means that + // SE_REVERSE_CT_GEN_ENABLED + // cannot be defined. Therefore, we only have to check that the current + // modulus is 0 to know that we are in the first prime of the modulus chain + if (parms->curr_modulus_idx == 0) + { + se_assert(!parms->sample_s); + load_sk(parms, s_small); + } +#endif + // print_poly_small("s (small)", s_small, parms->coeff_count); + + // -- Expand and store s in c0 + // print_poly_small("s (small)", s_small, parms->coeff_count); + expand_poly_ternary(s_small, parms, c0_s); + // print_poly_full("s", c0_s, parms->coeff_count); + // print_poly_ternary("s", c0_s, parms->coeff_count, false); + + // -- Calculate [a*s]_Rq = [c1*s]_Rq. This will free up c1 space too. + // First calculate ntt(s) and store in c0_s. Note that this will load + // the ntt roots into ntt_roots memory as well (used later for + // calculating ntt(pte)) + + // -- Note: Calling ntt_roots_initialize will do nothing if SE_NTT_OTF is defined + ntt_roots_initialize(parms, ntt_roots); + ntt_inpl(parms, ntt_roots, c0_s); +#ifndef SE_DISABLE_TESTING_CAPABILITY + // -- Save ntt(reduced(s)) for later decryption + // print_poly_ternary("s (ntt)", c0_s, parms->coeff_count, false); + if (s_save) memcpy(s_save, c0_s, n * sizeof(c0_s[0])); + // print_poly_ternary("s_save (ntt)", s_save, parms->coeff_count, false); +#endif + poly_mult_mod_ntt_form_inpl(c0_s, c1, n, mod); + // print_poly("rlwe a*s ", c0_s, n); + + // -- Negate [a*s]_Rq to get [-a*s]_Rq + poly_neg_mod_inpl(c0_s, n, mod); + // print_poly("rlwe -a*s ", c0_s, n); + + // -- Calculate reduce(m + e) == reduce(conj_vals_int) ---> store in ntt_pte +#ifndef SE_DISABLE_TESTING_CAPABILITY + if (ep_small) + reduce_set_e_small(parms, ep_small, ntt_pte); + else +#endif + reduce_set_pte(parms, conj_vals_int, ntt_pte); + // print_poly("red(pte)", ntt_pte, parms->coeff_count); + + // -- Calculate ntt(m + e) = ntt(reduce(conj_vals_int)) = ntt(ntt_pte) + // and store result in ntt_pte. Note: ntt roots (if required) should already be + // loaded from step 3. + ntt_inpl(parms, ntt_roots, ntt_pte); + // print_poly("ntt(m + e)", ntt_pte, n); + + // -- Debugging + // intt_roots_initialize(parms, ntt_roots); + // intt(parms, ntt_roots, ntt_pte); + // print_poly_full("intt(ntt(pte))", ntt_pte, parms->coeff_count); + + poly_add_mod_inpl(c0_s, ntt_pte, n, mod); + // print_poly("a*s + m + e (ntt form)", c0_s, n); +} + +bool ckks_next_prime_sym(Parms *parms, ZZ *s) +{ + se_assert(parms && !parms->is_asymmetric); + + if (!parms->small_s) convert_poly_ternary_inpl(s, parms); + + // -- Update curr_modulus_idx to next index + bool ret = next_modulus(parms); + return ret; +} diff --git a/device/lib/ckks_sym.h b/device/lib/ckks_sym.h new file mode 100644 index 0000000..ffce561 --- /dev/null +++ b/device/lib/ckks_sym.h @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_sym.h + +Symmetric CKKS encryption. +*/ + +#pragma once + +#include + +#include "ckks_common.h" +#include "defines.h" +#include "parameters.h" +#include "rng.h" + +#ifdef SE_USE_MALLOC +/** +Returns the required size of the memory pool in units of sizeof(ZZ). + +@param[in] degree Desired polynomial ring degree +@returns Required size of the memory pool units of sizeof(ZZ) +*/ +size_t ckks_get_mempool_size_sym(size_t degree); + +/** +Sets up the memory pool for CKKS symmetric encryption. + +Note: This function calls calloc. + +@param[in] degree Desired polynomial ring degree +@returns A handle to the memory pool +*/ +ZZ *ckks_mempool_setup_sym(size_t degree); +#endif + +/** +Sets addresses of objects according to parameter settings for symmetric CKKS encryption. +Should only need to be called once during initial memory allocation. + +@param[in] degree Polynomial ring degree +@param[in] mempool Handle to memory pool +@param[out] se_ptrs Pointers object. Pointers will be updated to mempool locations. +*/ +void ckks_set_ptrs_sym(size_t degree, ZZ *mempool, SE_PTRS *se_ptrs); + +/** +Sets up a CKKS secret key. For symmetric encryption, this should either be +called once at the start during memory allocation (if storing s persistantly in +working memory) or once per encode-encrypt sequence (between base and remaining +steps, if not storing s persistantly in working memory). + +If parms.sample_s == 1, will internally generate the secret key (from the uniform ternary +distribution). This is mainly useful for testing. If parms.sample_s == 0, will read in +secret key from file. + +Size req: If seed is !NULL, seed must be SE_PRNG_SEED_BYTE_COUNT long. + +@param[in] parms Parameters set by ckks_setup +@param[in] seed [Optional.] Seed to seed 'prng' with, if prng is used. +@param[in,out] prng [Optional] PRNG instance needed to generate randomness for secret +key polynomial. Should not be shared. +@param[out] s Secret key polynomial. Must have space for n coefficients. If +'small' s is used, this must be 2 bits per coefficient. Otherwise, this must be sizeof(ZZ) +per coefficient. +*/ +void ckks_setup_s(const Parms *parms, uint8_t *seed, SE_PRNG *prng, ZZ *s); + +/** +Initializes values for a single full symmetric CKKS encryption. Samples the error (w/o +respect to any prime). Should be called once per encode-encrypt sequence (just after +ckks_encode_base). + +Note: This function modifies (i.e. resets and re-randomizes) the prng instance. + +Size req: If seeds are !NULL, seeds must be SE_PRNG_SEED_BYTE_COUNT long. + +@param[in] parms Parameters set by ckks_setup +@param[in] share_seed_in [Optional.] Seed to seed 'shareable_prng' with. +@param[in] seed_in [Optional.] Seed to seed 'prng' with. +@param[in,out] shareable_prng PRNG instance needed to generate first component of +ciphertexts. Is safe to share. +@param[in,out] prng PRNG instance needed to generate error polynomial. Should +not be shared +@param[in,out] conj_vals_int As pointed to by conj_vals_int_ptr (n int64 values). + In: ckks pt; Out: pt + error (non-reduced) +*/ +void ckks_sym_init(const Parms *parms, uint8_t *share_seed_in, uint8_t *seed_in, + SE_PRNG *shareable_prng, SE_PRNG *prng, int64_t *conj_vals_int); + +/** +Encodes and symmetrically encrypts a vector of values using CKKS for the current modulus +prime. + +Internally converts various objects to NTT form. If debugging encryption-only (assuming a +message of 0), or if calling to generate a public key, can set conj_vals_int to zero. In +this case, must set ep_small to the compessed form of the error. + +@param[in] parms Parameters set by ckks_setup +@param[in] conj_vals_int [Optional]. See description. +@param[in] ep_small [Optional]. See description. For debugging only. +@param[in,out] shareable_prng PRNG instance needed to generate first component of +ciphertexts. Is safe to share. +@param ntt_pte Scratch space. Will be used to store plaintext plus error +in NTT form. +@param ntt_roots Scratch space. May be used to load NTT roots. +@param[out] c0_s 1st component of the ciphertext. Stores n coefficients of +size ZZ. +@param[out] c1 2nd component of the ciphertext. Stores n coefficients of +size ZZ. +@param[out] s_save [Optional]. Useful for testing. +@param[out] c1_save [Optional]. Useful for testing. +*/ +void ckks_encode_encrypt_sym(const Parms *parms, const int64_t *conj_vals_int, + const int8_t *ep_small, SE_PRNG *shareable_prng, ZZ *s_small, + ZZ *ntt_pte, ZZ *ntt_roots, ZZ *c0_s, ZZ *c1, ZZ *s_save, ZZ *c1_save); + +/** +Updates parameters to next prime in modulus switching chain for symmetric CKKS encryption. +Also converts secret key polynomial to next prime modulus if used in expanded form +(compressed form s will be reduced later). + +@param[in,out] parms Parameters set by ckks_setup. curr_modulus_idx will be advanced by 1 +@param[in,out] s [Optional.] Secret key to convert to next modulus prime +@returns 1 on success, 0 on failure (reached end of modulus chain) +*/ +bool ckks_next_prime_sym(Parms *parms, ZZ *s); diff --git a/device/lib/defines.h b/device/lib/defines.h new file mode 100644 index 0000000..4f55694 --- /dev/null +++ b/device/lib/defines.h @@ -0,0 +1,541 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file defines.h +*/ + +#pragma once + +#include +#include // PRIu32, PRIu64 // not even sure this is needed... +#include // uint64_t, UINT64_MAX +#include +#include // size_t +#include // memset + +#include "user_defines.h" + +// clang-format off +// ============================================================== +// Configurations pertaining to testing +// ============================================================== +/** +Inverse NTT type. Options 1 - 3 not guaranteed to always work. + +0 = compute "on-the-fly" +1 = compute "one-shot" +2 = load +3 = load fast +*/ +#define SE_INTT_TYPE 0 + +// ============================================================== +// Configurations pertaining to benchmarking +// ============================================================== + +/** +Include timer code for benchmarking. +Uncomment to use. +*/ +#define SE_ENABLE_TIMERS + +// ============================================================== +// Configurations pertaining to debugging +// ============================================================== + +// --- If defined, "print_poly" functions will only print PRINT_LEN_SMALL elements +// of the requested polynomial, (unless a "full" version of "print_poly" is called) +#define SE_PRINT_SMALL +#define PRINT_LEN_SMALL 8 + +// #define SE_DEBUG_WITH_ZEROS +// #define SE_DEBUG_NO_ERRORS +// #define SE_VERBOSE_TESTING + +// ============================================================== +// Advanced configurations +// (Unlikely to need modification) +// ============================================================== + +/** +Number of bytes to store the seed for the prng. + +For compressed public keys and/or compressed ciphertexts +(in symmetric mode), must match prng byte count used in SEAL. +*/ +#define SE_PRNG_SEED_BYTE_COUNT 64 + +/** +Data path to use if SE_DATA_PATH is not set in CMAKE +*/ +#define SE_DATA_PATH_ "adapter_output_data" + +/** +Data path length to use if SE_DATA_PATH is not set in CMAKE. +Must be >= length of the SE_DATA_PATH_ define above (in bytes). + +(Most compilers are ok with this calling strlen strlen, but +some are not.) +*/ +#define SE_DATA_PATH_LEN_ strlen(SE_DATA_PATH_) + +// ============================================================== +// +// +// DO NOT MODIFY BELOW THIS LINE +// +// +// ============================================================== + +// -------------------------------------------------------------- +// Configurations dervied from user_defines.h and above. +// Do not modify. +// -------------------------------------------------------------- + + + +// -- Longest file type adapter output data to be loaded to device storage +// is named: intt_fast_roots__.dat +// -- max prime bitlen = 32 --> max # chars for prime = 10 +// -- max degree = 32768 --> max # chars for prime = 5 +// -- total max char count = 21 + 10 + 5 = 36 --> Round to 40 +#define MAX_DATA_FILE_SIZE 40 + +#if !defined(SE_DATA_PATH) || !defined(SE_DATA_PATH_LEN) + #undef SE_DATA_PATH + #define SE_DATA_PATH SE_DATA_PATH_ + #define MAX_FPATH_SIZE SE_DATA_PATH_LEN_ + 1 + MAX_DATA_FILE_SIZE +#else + #define MAX_FPATH_SIZE SE_DATA_PATH_LEN + 1 + MAX_DATA_FILE_SIZE +#endif + +// ----- Assert type +# if (SE_ASSERT_TYPE == 0) + // -- Do nothing +#elif (SE_ASSERT_TYPE == 1) + #define SE_ASSERT_STANDARD +#elif (SE_ASSERT_TYPE == 2) + #define SE_ASSERT_CUSTOM +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- Randomness generation type +# if (SE_RAND_TYPE == 0) + // -- Do nothing +#elif (SE_RAND_TYPE == 1) + #define SE_RAND_GETRANDOM +#elif (SE_RAND_TYPE == 2) + #define SE_RAND_NRF5 +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- Inverse FFT type +# if (SE_IFFT_TYPE == 0) + #define SE_IFFT_OTF +#elif (SE_IFFT_TYPE == 1) + #define SE_IFFT_LOAD_FULL +// #elif (SE_IFFT_TYPE == 2) +// #define SE_IFFT_ONE_SHOT // Not yet supported +// #elif (SE_IFFT_TYPE == 3) +// #define SE_IFFT_LOAD_BASE // Not yet supported +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- NTT type +# if (SE_NTT_TYPE == 0) + #define SE_NTT_OTF +#elif (SE_NTT_TYPE == 1) + #define SE_NTT_ONE_SHOT +#elif (SE_NTT_TYPE == 2) + #define SE_NTT_REG +#elif (SE_NTT_TYPE == 3) + #define SE_NTT_FAST +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- Index map type +# if (SE_INDEX_MAP_TYPE == 0) + #define SE_INDEX_MAP_OTF +#elif (SE_INDEX_MAP_TYPE == 1) + #define SE_INDEX_MAP_PERSIST +#elif (SE_INDEX_MAP_TYPE == 2) + #define SE_INDEX_MAP_LOAD +#elif (SE_INDEX_MAP_TYPE == 3) + #define SE_INDEX_MAP_LOAD_PERSIST +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- Secret key type +# if (SE_SK_TYPE == 0) + #define SE_SK_NOT_PERSISTENT +#elif (SE_SK_TYPE == 1) + #define SE_SK_PERSISTENT_ACROSS_PRIMES +#elif (SE_SK_TYPE == 2) + #define SE_SK_PERSISTENT +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- Data load type +# if (SE_DATA_LOAD_TYPE == 0) + // -- Do nothing +#elif (SE_DATA_LOAD_TYPE == 1) + #define SE_DATA_FROM_CODE_COPY +#elif (SE_DATA_LOAD_TYPE == 2) + #define SE_DATA_FROM_CODE_DIRECT +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +/** +FFT type. For now, we only support "on-the-fly" for the FFT type. + +0 = compute "on-the-fly" +1 = load (not yet supported) +2 = compute "one-shot" (not yet supported) +3 = load from base roots (not yet supported) +*/ +#define SE_FFT_TYPE 0 + +// ----- FFT type (for testing only) +// ----- Note: FFT type must not require more memory than IFFT type +# if (SE_FFT_TYPE == 0) + #define SE_FFT_OTF +#elif (SE_FFT_TYPE == 1) + #define SE_FFT_LOAD_FULL // Not yet supported +#elif (SE_FFT_TYPE == 2) + #define SE_FFT_ONE_SHOT // Not yet supported +#elif (SE_FFT_TYPE == 3) + #define SE_FFT_LOAD_BASE // Not yet supported +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// ----- Inverse NTT type (for testing only) +// ----- Note: INTT type must not require more memory than NTT type +# if (SE_INTT_TYPE == 0) + #define SE_INTT_OTF +#elif (SE_INTT_TYPE == 1) + #define SE_INTT_ONE_SHOT +#elif (SE_INTT_TYPE == 2) + #define SE_INTT_REG +#elif (SE_INTT_TYPE == 3) + #define SE_INTT_FAST +#else + #ifndef SE_CONFIG_ERROR + #define SE_CONFIG_ERROR + #endif +#endif + +// -------------------------------------------------------------- +// Non-configuration. Do not modify. +// -------------------------------------------------------------- + +// -- No asserts in release mode +#ifdef SE_UNDEF_ASSERT_FORCE +#ifdef SE_ASSERT_STANDARD +#undef SE_ASSERT_STANDARD +#endif +#ifdef SE_ASSERT_CUSTOM +#undef SE_ASSERT_CUSTOM +#endif +#endif + +#define SE_UNUSED(x) \ + do \ + { \ + (void)(x); \ + } while (0) +#ifdef SE_ASSERT_STANDARD +#include +#ifdef NDEBUG +#undef NDEBUG +#endif +#define se_assert(x) assert(x) +#define se_assert_msg(x, y) \ + do \ + { \ + if (!(x)) \ + { \ + printf(y); \ + printf("\n"); \ + assert(x); \ + } \ + } while (0) +#elif defined(SE_ASSERT_CUSTOM) +#define se_assert(x) \ + do \ + { \ + if (!(x)) \ + { \ + printf("Error: A SEAL-Embedded assert failed. Exiting..."); \ + while (1) \ + ; \ + } \ + } while (0) +#define se_assert_msg(x, y) \ + do \ + { \ + if (!(x)) \ + { \ + printf(y); \ + printf("\n"); \ + while (1) \ + ; \ + } \ + } while (0) +#else +#ifndef NDEBUG +#define NDEBUG +#endif +#define se_assert(x) \ + do \ + { \ + SE_UNUSED(x); \ + } while (0) +#define se_assert_msg(x, y) \ + do \ + { \ + SE_UNUSED(x); \ + SE_UNUSED(y); \ + } while (0) +#endif + +#ifndef __has_builtin // Optional of course +#define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif + +#if __has_builtin(__builtin_complex) +#define _complex(x, y) __builtin_complex(x, y) +#else +// #define _complex(x, y) CMPLX(x, y) +#define _complex(x, y) x + y *I +#endif + +#ifdef SE_USE_PREDEF_COMPLEX_FUNCS + #define se_conj(x) conj(x) + #define se_creal(x) creal(x) + #define se_cimag(x) cimag(x) +#else +static inline double complex se_conj(double complex val) +{ + double *val_double = (double *)(&val); + return _complex(val_double[0], -val_double[1]); +} +static inline double se_creal(double complex val) +{ + double *val_double = (double *)(&val); + return val_double[0]; +} +static inline double se_cimag(double complex val) +{ + double *val_double = (double *)(&val); + return val_double[1]; +} +#endif + +typedef size_t PolySizeType; +#ifdef SE_PRIMESIZE_64 +typedef uint64_t ZZ; +typedef int64_t ZZsign; // signed type +typedef double flpt; +#define PRIuZZ PRIu64 +#define PRIiZZ PRIi64 +#else +typedef uint32_t ZZ; +typedef int32_t ZZsign; // signed ZZ type +// typedef double flpt; +typedef float flpt; +#define PRIuZZ PRIu32 +#define PRIiZZ PRIi32 +#endif + +/** +Utility function to clear an array + +Size req: 'v' must contain at least n ZZ values + +@param[in] v Array to clear +@param[in] n Number of elements of v to set to 0 +*/ +static inline void clear(ZZ *v, PolySizeType n) +{ + memset(v, 0, n * sizeof(ZZ)); +} + +/** +Secure utility function to clear an array. Prevents compiler optimizing out memset(). + +Size req: 'v' must contain at least n ZZ values + +@param[in] v Array to clear +@param[in] n Number of elements of v to set to 0 +*/ +static inline void se_secure_zero_memset(void *v, size_t n) +{ + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; + memset_v(v, 0, n); +} + + +// -------------------------------------------------------------- +// Sanity Checks. Do not modify. +// -------------------------------------------------------------- +#ifdef SE_ON_NRF5 + #undef SE_RAND_GETRANDOM + // #if !defined(SE_DATA_FROM_CODE_COPY) && !defined(SE_DATA_FROM_CODE_DIRECT) + #ifndef SE_DATA_FROM_CODE_COPY + #define SE_DATA_FROM_CODE_COPY + #endif +#else + #undef SE_RAND_NRF5 + #undef SE_NRF5_UART_PRINTF_ENABLED +#endif + +#ifdef SE_ON_SPHERE_M4 + #undef SE_RAND_GETRANDOM + #undef SE_USE_MALLOC + // #if !defined(SE_DATA_FROM_CODE_COPY) && !defined(SE_DATA_FROM_CODE_DIRECT) + #ifndef SE_DATA_FROM_CODE_COPY + #define SE_DATA_FROM_CODE_COPY + #endif +#endif + +// -- Only need to check one case since only 2 cases are possible +#ifdef SE_ASSERT_STANDARD + #undef SE_ASSERT_CUSTOM +#endif + +// -- Only need to check one case since only 2 cases are possible +#ifdef SE_RAND_GETRANDOM + #undef SE_RAND_NRF5 +#endif + +// -- Only need to check one case since only 2 cases are possible +#ifdef SE_DATA_FROM_CODE_COPY + #undef SE_DATA_FROM_CODE_DIRECT +#endif + +#ifdef SE_INDEX_MAP_OTF + #undef SE_INDEX_MAP_LOAD + #undef SE_INDEX_MAP_PERSIST + #undef SE_INDEX_MAP_LOAD_PERSIST +#elif defined(SE_INDEX_MAP_LOAD) + #undef SE_INDEX_MAP_PERSIST + #undef SE_INDEX_MAP_LOAD_PERSIST +#elif defined(SE_INDEX_MAP_PERSIST) + #undef SE_INDEX_MAP_LOAD_PERSIST +#elif defined(SE_INDEX_MAP_LOAD_PERSIST) + // -- Do nothing +#else + #define SE_INDEX_MAP_OTF +#endif + +// -- IFFT defines checking. (The order of these is important) +#ifdef SE_IFFT_OTF + #undef SE_IFFT_ONE_SHOT + #undef SE_IFFT_LOAD_FULL + #define SE_FFT_OTF +#elif defined(SE_IFFT_ONE_SHOT) + #undef SE_IFFT_LOAD_FULL + #undef SE_FFT_LOAD_FULL +#elif defined(SE_IFFT_LOAD_FULL) + // -- Do nothing +#else + #define SE_IFFT_OTF + #define SE_FFT_OTF +#endif + +// -- FFT defines checking. (The order of these is important) +#ifdef SE_FFT_OTF + #undef SE_FFT_ONE_SHOT + #undef SE_FFT_LOAD_FULL +#elif defined(SE_FFT_ONE_SHOT) + #undef SE_FFT_LOAD_FULL +#elif defined(SE_FFT_LOAD_FULL) + // -- Do nothing +#else + #define SE_FFT_OTF +#endif + +// -- NTT defines checking. (The order of these is important) +#ifdef SE_NTT_OTF + #undef SE_NTT_ONE_SHOT + #undef SE_NTT_REG + #undef SE_NTT_FAST + #define SE_INTT_OTF +#elif defined(SE_NTT_ONE_SHOT) + #undef SE_NTT_REG + #undef SE_NTT_FAST + #undef SE_INTT_FAST +#elif defined(SE_NTT_REG) + #undef SE_NTT_FAST + #undef SE_INTT_FAST +#elif defined(SE_NTT_FAST) + // -- Do nothing +#else + #define SE_NTT_OTF + #define SE_INTT_OTF +#endif + +// -- INTT defines checking. (The order of these is important) +#ifdef SE_INTT_OTF + #undef SE_INTT_ONE_SHOT + #undef SE_INTT_REG + #undef SE_INTT_FAST +#elif defined(SE_INTT_ONE_SHOT) + #undef SE_INTT_REG + #undef SE_INTT_FAST +#elif defined(SE_INTT_REG) + #undef SE_INTT_FAST +#elif defined(SE_INTT_FAST) + // -- Do nothing +#else + #define SE_INTT_OTF +#endif + +// -- This must be after the IFFT sanity checks +#ifdef SE_REVERSE_CT_GEN_ENABLED + #if !(defined(SE_IFFT_OTF) && defined(SE_FFT_OTF)) + #undef SE_REVERSE_CT_GEN_ENABLED + #endif +#endif + +// -- This must be after IFFT sanity checks +#ifdef SE_SK_PERSISTENT + #undef SE_SK_PERSISTENT_ACROSS_PRIMES + #undef SE_SK_NOT_PERSISTENT +#elif defined(SE_SK_PERSISTENT_ACROSS_PRIMES) + #ifdef SE_IFFT_OTF + #undef SE_SK_PERSISTENT_ACROSS_PRIMES + #undef SE_SK_NOT_PERSISTENT + #define SE_SK_PERSISTENT + #endif +#elif defined(SE_SK_NOT_PERSISTENT) + // -- Do nothing +#else + #define SE_SK_NOT_PERSISTENT +#endif +// clang-format on diff --git a/device/lib/fft.c b/device/lib/fft.c new file mode 100644 index 0000000..d525473 --- /dev/null +++ b/device/lib/fft.c @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fft.c + +Note: All roots are mod 2n, where n is the number of elements (e.g. polynomial degree) in +the transformed vector. See the paper for more details. +*/ + +#include "fft.h" + +#include "defines.h" +#include "util_print.h" + +#ifndef M_PI + #define M_PI 3.14159265358979323846 +#endif + +/** +Helper function to calculate the angle of a particular root + +@param[in] k Index of root +@param[in] m Degree of roots (i.e. 2n) +@returns Angle of the root +*/ +double calc_angle(size_t k, size_t m) +{ + return 2 * M_PI * (double)k / (double)(m); +} + +/** +Helper function to calculates an FFT root + +@param[in] k Index of root to calculate +@param[in] m Degree of roots (i.e., 2n, where n is the transform size) +@returns The FFT root for index k +*/ +double complex calc_root_otf(size_t k, size_t m) +{ + // -- Note: See calc_root_from_base above for more info + k &= m - 1; + double angle = calc_angle(k, m); + return (double complex)_complex(cos(angle), sin(angle)); +} + +void calc_fft_roots(size_t n, size_t logn, double complex *roots) +{ + se_assert(n >= 4); + se_assert(roots); + + PolySizeType m = n << 1; + for (size_t i = 0; i < n; i++) { roots[i] = calc_root_otf(bitrev(i, logn), m); } +} + +void calc_ifft_roots(size_t n, size_t logn, double complex *ifft_roots) +{ + se_assert(n >= 4); + se_assert(ifft_roots); + + PolySizeType m = n << 1; + for (size_t i = 0; i < n; i++) + { + ifft_roots[i] = se_conj(calc_root_otf(bitrev(i - 1, logn), m)); + // ifft_roots[i] = se_conj(calc_root_otf(bitrev(i - 1, logn) + 1, m)); + } +} + +void ifft_inpl(double complex *vec, size_t n, size_t logn, const double complex *roots) +{ +#if defined(SE_IFFT_LOAD_FULL) || defined(SE_IFFT_ONE_SHOT) + se_assert(roots); + size_t root_idx = 1; +#elif defined(SE_IFFT_OTF) + SE_UNUSED(roots); + size_t m = n << 1; // Degree of roots +#else + printf("IFFT option not found!\n"); + exit(0); +#endif + + /* + Example: n = 8, logn = 3 : + + A0 _______ ............. + A1 _______X_________ \ + \ X + A2 _______ ..........X../ + A3 _______X_________/ + etc... + A4 _______ ............. + A5 _______X_________ \ + \ X + A6 _______ ..........X../ + A7 _______X_________/ + Round 0 Round 1 + + Note the following: + - 'X' represents a butterfly cross + - A 'group' is a collection of overlapping butterflies + - Butterfly 'size' is the difference between butterfly indices + - Round 0: 4 groups of size 1 butterflies + - Round 1: 2 groups of size 2 bufferflies + - Round 2: 1 group of size 4 butterflies + + Round 0: tt = 1, h = 4 + j = 0, k = [0-1), butterfly pair: (0, 1) + j = 1, k = [2-3), butterfly pair: (2, 3) + j = 2, k = [4-5), butterfly pair: (4, 5) + j = 3, k = [6-7), butterfly pair: (6, 7) + Round 1: tt = 2, h = 2 + j = 0, k = [0-2), butterfly pairs: (0, 2), (1, 3) + j = 1, k = [4-6), butterfly pairs: (4, 6), (5, 7) + Round 2: tt = 4, h = 1 + j = 0, k = [0-4), butterfly pairs: (0, 4), (1, 5), (2, 6), (3, 7) + */ + size_t tt = 1; // size of butterflies + size_t h = n / 2; // number of groups + for (size_t i = 0; i < logn; i++, tt *= 2, h /= 2) // rounds + { + for (size_t j = 0, kstart = 0; j < h; j++, kstart += 2 * tt) // groups + { +#if defined(SE_IFFT_LOAD_FULL) || defined(SE_IFFT_ONE_SHOT) + // -- The roots are assumed to be stored in a bit-reversed order + // in this case so that memory accesses are consecutive. + double complex s = roots[root_idx++]; +#elif defined(SE_IFFT_OTF) + + double complex s = se_conj(calc_root_otf(bitrev(h + j, logn), m)); // pairs +#else + printf("Error! IFFT option not found!\n"); + exit(1); +#endif + for (size_t k = kstart; k < (kstart + tt); k++) + { + // -- Use doubles to preserve precision + double complex u = vec[k]; + double complex v = vec[k + tt]; + vec[k] = u + v; + vec[k + tt] = (u - v) * s; + } + } + } +} + +void fft_inpl(double complex *vec, size_t n, size_t logn, const double complex *roots) +{ + size_t m = n << 1; // Degree of roots +#ifdef SE_FFT_OTF + SE_UNUSED(roots); +#else + se_assert(roots); +#endif + + /* + Example: n = 8, logn = 3 : + (see ckks_ifft above for illustration of ifft, ie inverse of fft) + + Note the following: + - A 'group' is a collection of overlapping butterflies + - Butterfly 'size' is the difference between butterfly indices + - Round 0: 1 group of size 4 butterflies + - Round 1: 2 groups of size 2 bufferflies + - Round 2: 4 groups of size 1 butterflies + + Round 0: tt = 4, h = 1 + j = 0, k = [0-4), butterfly pairs: (0, 4), (1, 5), (2, 6), (3, 7) + Round 1: tt = 2, h = 2 + j = 0, k = [0-2), butterfly pairs: (0, 2), (1, 3) + j = 1, k = [4-6), butterfly pairs: (4, 6), (5, 7) + Round 2: tt = 4, h = 1 + j = 0, k = [0-1), butterfly pair: (0, 1) + j = 1, k = [2-3), butterfly pair: (2, 3) + j = 2, k = [4-5), butterfly pair: (4, 5) + j = 3, k = [6-7), butterfly pair: (6, 7) + */ + + // -- This code is pretty much the same as the ifft code above, + // except that 'h' and 'tt' are basically switched, fft roots are + // used instead of inverse roots, and the butterfly multiplication + // happens before the addition/subtraction instead of after + size_t h = 1; + size_t tt = n / 2; +#if defined(SE_FFT_LOAD_FULL) || defined(SE_FFT_ONE_SHOT) + size_t root_idx = 1; +#endif + for (int i = 0; i < logn; i++, h *= 2, tt /= 2) // rounds + { + for (size_t j = 0, kstart = 0; j < h; j++, kstart += 2 * tt) // groups + { + double complex s; +#if defined(SE_FFT_LOAD_FULL) || defined(SE_FFT_ONE_SHOT) + // -- The roots are assumed to be stored in a bit-reversed order + // in this case so that memory accesses are consecutive. + s = roots[root_idx++]; +#elif defined(SE_FFT_OTF) + s = calc_root_otf(bitrev(h + j, logn), m); +#else + printf("Error! FFT option not found!\n"); + exit(1); +#endif + for (size_t k = kstart; k < (kstart + tt); k++) // pairs + { + // -- Use doubles to preserve precision + double complex u = vec[k]; + double complex v = vec[k + tt] * s; + vec[k] = u + v; + vec[k + tt] = u - v; + } + } + } +} diff --git a/device/lib/fft.h b/device/lib/fft.h new file mode 100644 index 0000000..c296652 --- /dev/null +++ b/device/lib/fft.h @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fft.h + +Forward and Inverse Fast Fourier Transform. + +Note: Currently, the fft is only useful for testing, since only the ifft is used in the +main algorithm. Leave both here for future use. +*/ + +#pragma once + +#include + +#include "defines.h" + +/* +static inline size_t bitrev(size_t input, size_t numbits) +{ + size_t res = 0; + for (size_t i = 0; i < numbits; i++) + { + // -- Get least significant bit of input and place in result. + // -- Shift result to the left towards msb position. + size_t lsb = input & 1; + res <<= 1; + res |= lsb; + input = input >> 1; + } + return res; +} +*/ + +/** +Bit reversal algorithm for the FFT/IFFT. +Ex: bitrev(6, 3): 6 = 0b110 -> (bit reverse) -> 0b011 = 3 + +Requires: numbits <= 16 + +(This does essentially the same as the commented out code above) + +@param[in] input Value to bit reverse +@param[in] numbits Number of bits required to represent input. Must be <= 16. +@returns 'input' in bit reversed order +*/ +static inline size_t bitrev(size_t input, size_t numbits) +{ + size_t t = (((input & 0xaaaa) >> 1) | ((input & 0x5555) << 1)); + t = (((t & 0xcccc) >> 2) | ((t & 0x3333) << 2)); + t = (((t & 0xf0f0) >> 4) | ((t & 0x0f0f) << 4)); + t = (((t & 0xff00) >> 8) | ((t & 0x00ff) << 8)); + return numbits == 0 ? 0 : t >> (16 - (size_t)(numbits)); +} + +// =========================== +// Roots +// =========================== + +/** +Generates the roots for the FFT from scratch (in bit-reversed order) + +Space req: 'roots' must have storage for n double complex values. + +@param[in] n FFT transform size (number of roots to generate) +@param[in] logn Minimum number of bits required to represent n (i.e. log2(n)) +@param[out] roots FFT roots (in bit-reversed order) +*/ +void calc_fft_roots(size_t n, size_t logn, double complex *roots); + +/** +Generates the roots for the IFFT from scratch (in bit-reversed order) + +Space req: 'ifft_roots' must have storage for n double complex values. + +@param[in] n IFFT transform size (number of roots to generate) +@param[in] logn Minimum number of bits required to represent n (i.e. log2(n)) +@param[out] ifft_roots IFFT roots (in bit-reversed order) +*/ +void calc_ifft_roots(size_t n, size_t logn, double complex *ifft_roots); + +// =========================== +// FFT/IFFT +// =========================== +/** +In-place Inverse Fast-Fourier Transform using the Harvey butterfly. +'roots' is ignored (and may be null) if SE_IFFT_OTF is chosen. + +Note: This function does not divide the final result by n. This steps must be performed +outside of this function. + +@param[in,out] vec Input/Output vector of n double complex values +@param[in] n IFFT transform size (i.e. polynomial degree) +@param[in] logn Minimum number of bits required to represent n (i.e. log2(n)) +@param[in] roots [Optional]. As set by calc_ifft_roots or load_ifft_roots +*/ +void ifft_inpl(double complex *vec, size_t n, size_t logn, const double complex *roots); + +/** +In-place forward Fast-Fourier Transform using the Harvey butterfly. +'roots' is ignored (and may be null) if SE_FFT_OTF is chosen. + +@param[in,out] vec Input/Output vector of n double complex values. +@param[in] n FFT transform size (i.e. polynomial degree) +@param[in] logn Minimum number of bits required to represent n (i.e. log2(n)) +@param[in] roots [Optional]. As set by calc_fft_roots or load_fft_roots +*/ +void fft_inpl(double complex *vec, size_t n, size_t logn, const double complex *roots); diff --git a/device/lib/fileops.c b/device/lib/fileops.c new file mode 100644 index 0000000..7ca004c --- /dev/null +++ b/device/lib/fileops.c @@ -0,0 +1,378 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fileops.c +*/ + +#include "fileops.h" + +#include // errno +#include +#include // memcpy + +#include "defines.h" +#include "parameters.h" +#include "util_print.h" + +#if defined(SE_DATA_FROM_CODE_COPY) || defined(SE_DATA_FROM_CODE_DIRECT) + #ifdef SE_DEFINE_SK_DATA + #include "str_sk.h" + #endif + #ifdef SE_DEFINE_PK_DATA + #include "str_pk_addr_array.h" + #endif + #ifdef SE_IFFT_LOAD_FULL + #include "str_ifft_roots.h" + #endif + #ifdef SE_FFT_LOAD_FULL + #include "str_fft_roots.h" + #endif + #if defined(SE_NTT_REG) || defined(SE_NTT_FAST) + #include "str_ntt_roots_addr_array.h" + #endif + #if defined(SE_INTT_REG) || defined(SE_INTT_FAST) + #include "str_intt_roots_addr_array.h" + #endif + #if defined(SE_INDEX_MAP_LOAD) || defined(SE_INDEX_MAP_LOAD_PERSIST) + #include "str_index_map.h" + #endif +#elif defined(SE_ON_SPHERE_A7) + #include + #include + #include // required for file open, close... +#else + #include + #include // required for file open, close... +#endif + +#if !(defined(SE_DATA_FROM_CODE_COPY) || defined(SE_DATA_FROM_CODE_DIRECT)) + +/** +Helper function to check return value of certain file access functions. + +@param[in] ret Return value to check +@param[in] bytes_expected Expected number of bytes to read from a file. Set to -1 to + skip checking if this value matches the value of 'ret'. +@param[in] fpath Path to file to close on detected error +*/ +void check_ret(ssize_t ret, ssize_t bytes_expected, const char *fpath) +{ + se_assert(fpath); + if (ret < 0) + { + printf("Error: problem with "); + if (bytes_expected == -1) + { + // printf("opening or closing mutable or non-mutable file\n"); + printf("opening or closing file\n"); + } + else + { + // printf("reading from mutable or non-mutable storage\n"); + printf("reading from file\n"); + } + + printf("errno value: %d\n", errno); + printf("errno message: %s\n", strerror(errno)); + printf("file path: %s\n", fpath); + exit(1); + } + if (bytes_expected != -1) + { + if ((size_t)ret != bytes_expected) + { + printf("bytes read : %zd bytes\n", ret); + printf("bytes expected : %zu bytes\n", bytes_expected); + } + se_assert(ret == bytes_expected); + } +} + +/** +Reads bytes stored at the specified location. On the Azure A7, this call called the +"image". + +Correctness: File located at 'fpath' must contain at least 'bytes_expected' values. +Space req: 'vec' must have space for 'bytes_expected' bytes. + +@param[in] fpath Path to file storing data to load +@param[in] bytes_expected Expected number of bytes to read from the file. Must be > 0. +@param[out] vec Buffer to store bytes read from the file +*/ +void read_from_image(const char *fpath, size_t bytes_expected, void *vec) +{ + se_assert(fpath); + se_assert(vec); + se_assert(bytes_expected); + #ifdef SE_ON_SPHERE_A7 + int imageFile = Storage_OpenFileInImagePackage(fpath); + #else + int imageFile = open(fpath, 0); + #endif + check_ret(imageFile, -1, fpath); + + ssize_t ret = 0; + + // -- Debugging + // ZZ val; + // ret = read(imageFile, &val, sizeof(ZZ)); + // check_ret(ret, sizeof(ZZ), fpath); + // print_zz("val", val); + + // -- Debugging + // char byte; + // ret = read(imageFile, &byte, 1); + // check_ret(ret, 1, fpath); + // print_zz("byte", (ZZ)byte); + + ret = read(imageFile, vec, bytes_expected); + // FILE *file = fdopen(imageFile, "r"); + // ret = fread(vec, 1, bytes_expected, file); + // check_ret(ret, bytes_expected, fpath); + + ret = close(imageFile); + // ret = fclose(file); + check_ret(ret, -1, fpath); +} +#endif + +void load_sk(const Parms *parms, ZZ *s) +{ + se_assert(parms && s); + size_t n = parms->coeff_count; + + // -- Image will always be in small form (2 bits per coeff) + size_t bytes_expected = n / 4; +#if defined(SE_DATA_FROM_CODE_COPY) || defined(SE_DATA_FROM_CODE_DIRECT) + #ifndef SE_DEFINE_SK_DATA + printf("Error! Sk data must be defined\n"); + while (1) + ; + #elif defined(SE_DATA_FROM_CODE_COPY) + // uint8_t *sk_bytes = (uint8_t*)s; + // for(size_t i = 0; i < bytes_expected; i++) sk_bytes[i] = secret_key[i]; + memcpy(s, &(secret_key[0]), bytes_expected); + return; + #else + SE_UNUSED(parms); + SE_UNUSED(n); + SE_UNUSED(bytes_expected); + s = (ZZ *)(&(secret_key[0])); + return; + #endif +#else + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/sk_%zu.dat", SE_DATA_PATH, n); + // printf("Retrieving secret key from file located at: %s\n", fpath); + read_from_image(fpath, bytes_expected, s); +#endif +} + +void load_pki(size_t i, const Parms *parms, ZZ *pki) +{ + se_assert(i == 0 || i == 1); + se_assert(parms && pki); + + size_t n = parms->coeff_count; + size_t midx = parms->curr_modulus_idx; +#if defined(SE_DATA_FROM_CODE_COPY) || defined(SE_DATA_FROM_CODE_DIRECT) + #ifndef SE_DEFINE_PK_DATA + SE_UNUSED(n); + SE_UNUSED(midx); + printf("Error! Pk data must be defined\n"); + while (1) + ; + #elif defined(SE_DATA_FROM_CODE_COPY) + // for(size_t k = 0; k < n; k++) pki[j] = pk_addr[k]; + memcpy(pki, pk_prime_addr[midx][i], n * sizeof(ZZ)); + #elif defined(SE_DATA_FROM_CODE_DIRECT) + pki = pk_prime_addr[midx][i]; + #endif +#else + SE_UNUSED(midx); + char fpath[MAX_FPATH_SIZE]; + ZZ q = parms->curr_modulus->value; + #ifdef SE_NTT_NONE + snprintf(fpath, MAX_FPATH_SIZE, "%s/pk%zu_%zu_%" PRIuZZ ".dat", SE_DATA_PATH, i, n, + q); + #else + snprintf(fpath, MAX_FPATH_SIZE, "%s/pk%zu_ntt_%zu_%" PRIuZZ ".dat", SE_DATA_PATH, i, + n, q); + #endif + + read_from_image(fpath, n * sizeof(ZZ), pki); +#endif +} + +#if defined(SE_INDEX_MAP_LOAD) || defined(SE_INDEX_MAP_LOAD_PERSIST) +void load_index_map(const Parms *parms, uint16_t *index_map) +{ + size_t n = parms->coeff_count; + #ifdef SE_DATA_FROM_CODE_COPY + memcpy(index_map, &(index_map_store[0]), n * sizeof(uint16_t)); + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(parms); + SE_UNUSED(n); + index_map = (uint16_t *)&(index_map_store[0]); + #else + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/index_map_%zu.dat", SE_DATA_PATH, n); + read_from_image(fpath, n * sizeof(uint16_t), index_map); + #endif +} +#endif + +#ifdef SE_IFFT_LOAD_FULL +void load_ifft_roots(size_t n, double complex *ifft_roots) +{ + se_assert(ifft_roots); + #ifdef SE_DATA_FROM_CODE_COPY + double *ifft_roots_double = (double *)(ifft_roots); + // -- This could be written more simply, but want to make clear + // that we are storing only 1 double at a time + for (size_t i = 0; i < 2 * n; i += 2) + { + ifft_roots_double[i] = (double)ifft_roots_save[i]; + ifft_roots_double[i + 1] = (double)ifft_roots_save[i + 1]; + } + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(n); + ifft_roots = (double complex *)&(ifft_roots_save[0]); + #else + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/ifft_roots_%zu.dat", SE_DATA_PATH, n); + // printf("Retrieving ifft_roots from file located at: %s\n", fpath); + read_from_image(fpath, n * sizeof(double complex), ifft_roots); + #endif +} +#endif + +#ifdef SE_FFT_LOAD_FULL +void load_fft_roots(size_t n, double complex *fft_roots) +{ + se_assert(fft_roots); + #ifdef SE_DATA_FROM_CODE_COPY + double *fft_roots_double = (double *)(fft_roots); + // -- This could be written more simply, but want to make clear + // that we are storing only 1 double at a time + for (size_t i = 0; i < 2 * n; i += 2) + { + fft_roots_double[i] = (double)fft_roots_store[i]; + fft_roots_double[i + 1] = (double)fft_roots_store[i + 1]; + } + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(n); + fft_roots = (double complex *)&(fft_roots_store[0]); + #else + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/fft_roots_%zu.dat", SE_DATA_PATH, n); + // printf("Retrieving fft_roots from file located at: %s\n", fpath); + read_from_image(fpath, n * sizeof(double complex), fft_roots); + #endif +} +#endif + +#ifdef SE_NTT_REG +void load_ntt_roots(const Parms *parms, ZZ *ntt_roots) +{ + se_assert(parms && ntt_roots); + size_t n = parms->coeff_count; + size_t midx = parms->curr_modulus_idx; + #ifdef SE_DATA_FROM_CODE_COPY + // for(size_t i = 0; i < n; i++) + // { ntt_roots[i] = ntt_roots_addr[midx][i]; } + memcpy(ntt_roots, ntt_roots_addr[midx], n * sizeof(ZZ)); + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(n); + ntt_roots = ntt_roots_addr[midx]; + #else + SE_UNUSED(midx); + ZZ q = parms->curr_modulus->value; + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/ntt_roots_%zu_%" PRIuZZ ".dat", SE_DATA_PATH, n, + q); + // printf("Retrieving ntt roots from file located at: %s\n", fpath); + read_from_image(fpath, n * sizeof(ZZ), ntt_roots); + #endif +} +#endif + +#ifdef SE_INTT_REG +void load_intt_roots(const Parms *parms, ZZ *intt_roots) +{ + se_assert(parms && intt_roots); + size_t n = parms->coeff_count; + size_t midx = parms->curr_modulus_idx; + #ifdef SE_DATA_FROM_CODE_COPY + // for(size_t i = 0; i < n; i++) + // { intt_roots[i] = intt_roots_addr[midx][i]; } + memcpy(intt_roots, intt_roots_addr[midx], n * sizeof(ZZ)); + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(n); + intt_roots = intt_roots_addr[midx]; + #else + SE_UNUSED(midx); + ZZ q = parms->curr_modulus->value; + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/intt_roots_%zu_%" PRIuZZ ".dat", SE_DATA_PATH, n, + q); + // printf("Retrieving inverse ntt roots from file located at: %s\n", fpath); + read_from_image(fpath, n * sizeof(ZZ), intt_roots); + #endif +} +#endif + +#ifdef SE_NTT_FAST +void load_ntt_fast_roots(const Parms *parms, MUMO *ntt_fast_roots) +{ + se_assert(parms && ntt_fast_roots); + size_t n = parms->coeff_count; + size_t midx = parms->curr_modulus_idx; + #ifdef SE_DATA_FROM_CODE_COPY + for (size_t i = 0; i < n; i++) + { + ntt_fast_roots[i].operand = ntt_roots_addr[midx][2 * i]; + ntt_fast_roots[i].quotient = ntt_roots_addr[midx][2 * i + 1]; + } + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(n); + ntt_fast_roots = ntt_roots_addr[midx]; + #else + SE_UNUSED(midx); + ZZ q = parms->curr_modulus->value; + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/ntt_fast_roots_%zu_%" PRIuZZ ".dat", SE_DATA_PATH, + n, q); + // printf("Retrieving fast roots from file located at: %s\n", fpath); + read_from_image(fpath, n * sizeof(MUMO), ntt_fast_roots); + #endif +} +#endif + +#ifdef SE_INTT_FAST +void load_intt_fast_roots(const Parms *parms, MUMO *intt_fast_roots) +{ + se_assert(parms && intt_fast_roots); + size_t n = parms->coeff_count; + size_t midx = parms->curr_modulus_idx; + #ifdef SE_DATA_FROM_CODE_COPY + for (size_t i = 0; i < n; i++) + { + intt_fast_roots[i].operand = intt_roots_addr[midx][2 * i]; + intt_fast_roots[i].quotient = intt_roots_addr[midx][2 * i + 1]; + } + #elif defined(SE_DATA_FROM_CODE_DIRECT) + SE_UNUSED(n); + intt_fast_roots = intt_roots_addr[midx]; + #else + SE_UNUSED(midx); + ZZ q = parms->curr_modulus->value; + char fpath[MAX_FPATH_SIZE]; + snprintf(fpath, MAX_FPATH_SIZE, "%s/intt_fast_roots_%zu_%" PRIuZZ ".dat", + SE_DATA_PATH, n, q); + // printf("Retrieving fast inverse ntt roots from file located at: %s\n", fpath); + read_from_image(fpath, n * sizeof(MUMO), intt_fast_roots); + #endif +} +#endif diff --git a/device/lib/fileops.h b/device/lib/fileops.h new file mode 100644 index 0000000..2ad7cda --- /dev/null +++ b/device/lib/fileops.h @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fileops.h + +Load values from storage. +*/ + +#pragma once + +#include "defines.h" +#include "parameters.h" +#include "uintmodarith.h" + +#if !defined(SE_DATA_FROM_CODE_COPY) || !defined(SE_DATA_FROM_CODE_DIRECT) +void read_from_image(const char *fpath, size_t bytes_expected, void *vec); +#endif + +/** +Loads the secret key from storage, where the secret key is assumed to be stored in small +(compressed) form. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the secret key +should be hard-coded in "kri_data/str_secret_key.h" in an array object called +"secret_key_store". This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, the secret key is assumed to be stored in +binary form in a file called "sk_.dat", where is the value of the +polynomial degree. This file can also be generated using the SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 's' should contain space for 2n +bits. + +@param[in] parms Parameters set by ckks_setup +@param[out] s Secret key (in small form) +*/ +void load_sk(const Parms *parms, ZZ *s); + +/** +Loads (one component of) the public key from storage. + +A full public key consists of two components per modulus prime in the modulus +switching chain. For example, if there are 3 primes in the modulus switching +chain, the public key would consists of 3 per-prime public keys PK0, PK1, PK2, +where each PKi conists of two polynomials {pk0, pk1}, for a total of 6 +polynomials, and therefore 6 components. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the public key +should be hard-coded in "kri_data/str_pk_addr_array.h", with the starting addresses +of each component stored in an array called 'pk_prime_addr' (contained in the same +file). This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, each public key component is assumed to be +stored in a separate file and in binary form. The file should be called +"pk__.dat", where is 0 or 1, is the value of the polynomial degree, +and is the value of the modulus prime for the particular public key component, +if the public key is in non-ntt form. If the public key is in NTT form, the file +should actually be called "pk_ntt__.dat". Both of these files can also be +generated using the SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'pki' should contain space +for n ZZ elements. + +@param[in] i Requested polynomial component of the public key for + the current modulus prime +@param[in] parms Parameters set by ckks_setup +@param[out] pki Public key component +*/ +void load_pki(size_t i, const Parms *parms, ZZ *pki); + +#if defined(SE_INDEX_MAP_LOAD) || defined(SE_INDEX_MAP_LOAD_PERSIST) +/** +Loads the values of the index map from storage. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the index map +should be hard-coded in "kri_data/str_index_map.h" in an array object called +"index_map_store". This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, the index map is assumed to be stored in +binary form in a file called "index_map_.dat", where is the value of the +polynomial degree. This file can also be generated using the SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'index_map' should contain space +for n uint16_t values. + +@param[in] parms Parameters set by ckks_setup +@param[out] index_map Buffer containing index map values +*/ +void load_index_map(const Parms *parms, uint16_t *index_map); +#endif + +#ifdef SE_IFFT_LOAD_FULL +/** +Loads the values of the IFFT roots from storage. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the ifft roots +should be hard-coded in "kri_data/str_ifft_roots.h" in an array object called +"ifft_roots_store". This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, the ifft_roots are assumed to be stored in +binary form in a file called "ifft_roots_.dat", where is the value of the +polynomial degree. This file can also be generated using the SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'ifft_roots' must contain space for +n double complex values. + +@param[in] n Number of roots to load (i.e. polynomial degree) +@param[out] ifft_roots IFFT roots +*/ +void load_ifft_roots(size_t n, double complex *ifft_roots); +#endif + +#ifdef SE_FFT_LOAD_FULL +/** +Loads the values of the FFT roots from storage. This is mainly useful for debugging. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the fft roots +should be hard-coded in "kri_data/str_fft_roots.h" in an array object called +"fft_roots_store". This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, the fft_roots are assumed to be stored in +binary form in a file called "fft_roots_.dat", where is the value of the +polynomial degree. This file can also be generated using the SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'fft_roots' must contain space for +n double complex values. + +@param[in] n Number of roots to load (i.e. polynomial degree) +@param[out] fft_roots FFT roots +*/ +void load_fft_roots(size_t n, double complex *fft_roots); +#endif + +#ifdef SE_NTT_REG +/** +Loads the values of the NTT roots (in regular form) for the current modulus prime from +storage. + +A full set of NTT roots consists of as many components are there are modulus primes +in the modulus switching chain. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the full set of NTT +roots should be hard-coded in "kri_data/str_ntt_roots_addr_array.h", with the starting +addresses of each component of NTT roots stored in an array called 'ntt_roots_addr' +(contained in the same file). This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, each NTT component is assumed to be stored in a +separate file and in binary form. The file should be called "ntt_roots__.dat", +where is the value of the polynomial degree, and is the value of the modulus prime +for the particular NTT component. Both of these files can be generated using the +SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'ntt_roots' must contain space for +n ZZ values. + +@param[in] n Number of roots to load (i.e. polynomial degree) +@param[out] ntt_roots NTT roots +*/ +void load_ntt_roots(const Parms *parms, ZZ *ntt_roots); +#endif + +#ifdef SE_INTT_REG +/** +Loads the values of the INTT roots (in regular form) for the current modulus prime from +storage. + +A full set of INTT roots consists of as many components are there are modulus primes +in the modulus switching chain. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the full set of INTT +roots should be hard-coded in "kri_data/str_intt_roots_addr_array.h", with the starting +addresses of each component of INTT roots stored in an array called 'intt_roots_addr' +(contained in the same file). This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, each INTT component is assumed to be stored in a +separate file and in binary form. The file should be called "intt_roots__.dat", +where is the value of the polynomial degree, and is the value of the modulus prime +for the particular INTT component. Both of these files can be generated using the +SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'intt_roots' must contain space for +n ZZ values. + +@param[in] n Number of roots to load (i.e. polynomial degree) +@param[out] intt_roots INTT roots +*/ +void load_intt_roots(const Parms *parms, ZZ *intt_roots); +#endif + +#ifdef SE_NTT_FAST +/** +Loads the values of the "fast" (a.k.a. "lazy") NTT roots for the current modulus prime +from storage. + +A full set of "fast" NTT roots consists of as many components are there are modulus primes +in the modulus switching chain. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the full set of NTT +roots should be hard-coded in "kri_data/str_ntt_roots_addr_array.h", with the starting +addresses of each component of NTT roots stored in an array called 'ntt_fast_roots_addr' +(contained in the same file). This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, each NTT component is assumed to be stored in a +separate file and in binary form. The file should be called "ntt_fast_roots__.dat", +where is the value of the polynomial degree, and is the value of the modulus prime +for the particular NTT component. Both of these files can be generated using the +SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'fast_ntt_roots' must contain space +for 2n ZZ values. + +@param[in] n Number of roots to load (i.e. polynomial degree) +@param[out] ntt_roots_fast "Fast" NTT roots +*/ +void load_ntt_fast_roots(const Parms *parms, MUMO *ntt_fast_roots); +#endif + +#ifdef SE_INTT_FAST +/** +Loads the values of the "fast" (a.k.a. "lazy") INTT roots for the current modulus prime +from storage. + +A full set of "fast" INTT roots consists of as many components are there are modulus +primes in the modulus switching chain. + +If SE_DATA_FROM_CODE_COPY or SE_DATA_FROM_CODE_DIRECT are defined, the full set of INTT +roots should be hard-coded in "kri_data/str_intt_roots_addr_array.h", with the starting +addresses of each component of INTT roots stored in an array called 'intt_fast_roots_addr' +(contained in the same file). This file can be generated using the SEAL-Embedded adapter. + +Otherwise, if this function is called, each INTT component is assumed to be stored in a +separate file and in binary form. The file should be called "intt_fast_roots__.dat", +where is the value of the polynomial degree, and is the value of the modulus prime +for the particular INTT component. Both of these files can be generated using the +SEAL-Embedded adapter. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'intt_fast_roots' must contain +space for 2n ZZ values. + +@param[in] n Number of roots to load (i.e. polynomial degree) +@param[out] intt_fast_roots "Fast" INTT roots +*/ +void load_intt_fast_roots(const Parms *parms, MUMO *intt_fast_roots); +#endif diff --git a/device/lib/intt.c b/device/lib/intt.c new file mode 100644 index 0000000..765497a --- /dev/null +++ b/device/lib/intt.c @@ -0,0 +1,378 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file intt.c +*/ + +#include "intt.h" + +#include + +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "parameters.h" +#include "polymodarith.h" +#include "uintmodarith.h" +#include "util_print.h" + +#if defined(SE_INTT_OTF) || defined(SE_INTT_ONE_SHOT) + +/** +Helper function to return root for certain modulus prime values if SE_INTT_OTF or +SE_INTT_ONE_SHOT is used. Implemented as a table lookup. + +@param[in] n Transform size (i.e. polynomial ring degree) +@param[in] q Modulus value +*/ +ZZ get_intt_root(size_t n, ZZ q) +{ + // TODO: Add more primes + ZZ inv_root; + switch (n) + { + case 16: + switch (q) + { + case 1071415297: inv_root = 615162833; break; + case 1071513601: inv_root = 208824698; break; + case 1072496641: inv_root = 750395333; break; + default: { + printf("Error! Need first power of root for intt, n = 16\n"); + print_zz("Modulus value", q); + exit(1); + } + } + break; + case 4096: + switch (q) + { + case 1071415297: inv_root = 710091420; break; + case 1071513601: inv_root = 711691669; break; + case 1072496641: inv_root = 85699917; break; + default: { + printf("Error! Need first power of root for intt, n = 4K\n"); + print_zz("Modulus value", q); + exit(1); + } + } + break; + default: { + printf("Error! Need first power of root for intt\n"); + print_zz("Modulus value", q); + exit(1); + } + } + return inv_root; +} +#endif + +void intt_roots_initialize(const Parms *parms, ZZ *intt_roots) +{ +#ifdef SE_INTT_OTF + SE_UNUSED(parms); + SE_UNUSED(intt_roots); + return; +#endif + + se_assert(parms && parms->curr_modulus && intt_roots); + +#ifdef SE_INTT_ONE_SHOT + size_t n = parms->coeff_count; + size_t logn = parms->logn; + Modulus *mod = parms->curr_modulus; + + ZZ inv_root = get_intt_root(n, mod->value); + ZZ power = inv_root; + intt_roots[0] = 1; // Not necessary but set anyway + for (size_t i = 1; i < n; i++) + { + intt_roots[bitrev(i - 1, logn) + 1] = power; + power = mul_mod(power, inv_root, mod); + } +#elif defined(SE_INTT_FAST) + load_intt_fast_roots(parms, (MUMO *)intt_roots); +#elif defined(SE_INTT_REG) + load_intt_roots(parms, intt_roots); +#else + se_assert(0); +#endif +} + +#ifdef SE_INTT_FAST +/** +Performs a "fast" (a.k.a. "lazy") negacyclic in-place inverse NTT using the Harvey +butterfly, using "fast" INTT roots. Only used if "SE_INTT_FAST" is defined. See +SEAL_Embedded paper for a more detailed description. "Lazy"-ness refers to fact that we +here we lazily opt to reduce values only at the very end. + +@param[in] parms Parameters set by ckks_setup +@param[in] intt_fast_roots INTT roots set by intt_roots_initialize +@param[in] inv_n Inverse of n mod q, where q is the value of the current +modulus prime +@param[in] inv_n_w +@param[in,out] vec Input/output polynomial of n ZZ elements +*/ +void intt_lazy_inpl(const Parms *parms, const MUMO *intt_fast_roots, const MUMO *inv_n, + const MUMO *inv_n_w, ZZ *vec) +{ + se_assert(parms && ntt_lazy_roots && vec); + size_t n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + ZZ two_q = mod->value << 1; + + size_t tt = 1; // size of butterflies + size_t h = n / 2; // number of groups + size_t root_idx = 1; // We technically don't need to store the 0th one... + + // print_poly_full("vec", vec, n); + for (size_t i = 0; i < (parms->logn - 1); i++, tt *= 2, h /= 2) // rounds + { + for (size_t j = 0, kstart = 0; j < h; j++, kstart += 2 * tt) // groups + { + const MUMO *s = &(intt_fast_roots[root_idx++]); + + for (size_t k = kstart; k < (kstart + tt); k++) // pairs + { + ZZ u = vec[k]; + ZZ v = vec[k + tt]; + + ZZ val1 = u + v; + ZZ val2 = u + two_q - v; + + vec[k] = val1 - (two_q & (ZZ)(-(ZZsign)(val1 >= two_q))); + vec[k + tt] = mul_mod_mumo_lazy(val2, s, mod); + } + } + // print_poly_full("vec", vec, n); + } + + // print_poly_full("vec", vec, n); + for (size_t j = 0; j < n / 2; j++) + { + ZZ u = vec[j]; + ZZ v = vec[j + n / 2]; + + ZZ val1 = u + v; + ZZ val2 = u + two_q - v; + + ZZ tval1 = val1 - (two_q & (ZZ)(-(ZZsign)(val1 >= two_q))); + + vec[j] = mul_mod_mumo_lazy(tval1, inv_n, mod); + vec[j + n / 2] = mul_mod_mumo_lazy(val2, inv_n_w, mod); + } + // print_poly_full("vec", vec, n); +} +#else +/** +Performs a negacyclic in-place inverse NTT using the Harvey butterfly. Only used if +SE_INTT_FAST is not defined. + +If SE_INTT_REG or SE_INTT_ONE_SHOT is defined, will use regular INTT computation. +Else, (SE_INTT_OTF is defined), will use truly "on-the-fly" INTT computation. In this last +case, 'intt_roots' may be null (and will be ignored). + +@param[in] parms Parameters set by ckks_setup +@param[in] intt_roots INTT roots set by intt_roots_initialize. Ignored if SE_INTT_OTF +is defined. +@param[in] inv_n Inverse of n mod q, where q is the value of the current modulus +prime +@param[in] inv_n_w +@param[in,out] vec Input/output polynomial of n ZZ elements +*/ +void intt_non_lazy_inpl(const Parms *parms, const ZZ *intt_roots, ZZ inv_n, ZZ inv_n_w, + ZZ *vec) +{ + // -- See ntt.c for a more detailed explaination of algorithm + + se_assert(parms && parms->curr_modulus && vec); + + size_t n = parms->coeff_count; + size_t logn = parms->logn; + Modulus *mod = parms->curr_modulus; + + size_t tt = 1; // size of butterflies + size_t h = n / 2; // number of groups + + #ifdef SE_INTT_OTF + SE_UNUSED(intt_roots); + ZZ root = get_intt_root(n, mod->value); + #elif defined(SE_INTT_ONE_SHOT) || defined(SE_INTT_REG) + size_t root_idx = 1; // We technically don't need to store the 0th one... + #endif + + for (size_t i = 0; i < (logn - 1); i++, tt *= 2, h /= 2) // rounds + { + for (size_t j = 0, kstart = 0; j < h; j++, kstart += 2 * tt) // groups + { + #ifdef SE_INTT_OTF + // ZZ power = bitrev(h + j, logn); + // ZZ s = exponentiate_uint_mod(root, power, mod); + ZZ power = h + j; + ZZ s = exponentiate_uint_mod_bitrev(root, power, logn, mod); + #elif defined(SE_INTT_ONE_SHOT) || defined(SE_INTT_REG) + se_assert(intt_roots); + ZZ s = intt_roots[root_idx++]; + #endif + for (size_t k = kstart; k < (kstart + tt); k++) // pairs + { + ZZ u = vec[k]; + ZZ v = vec[k + tt]; + vec[k] = add_mod(u, v, mod); + vec[k + tt] = mul_mod(sub_mod(u, v, mod), s, mod); + } + } + // print_poly_full("vec", vec, n); + } + // print_poly_full("vec", vec, n); + + // -- Finally, need to multiply by n^{-1} + for (size_t i = 0; i < n / 2; i++) + { + ///* + ZZ u = vec[i]; + ZZ v = vec[i + n / 2]; + vec[i] = mul_mod(add_mod(u, v, mod), inv_n, mod); + vec[i + n / 2] = mul_mod(sub_mod(u, v, mod), inv_n_w, mod); + //*/ + // mul_mod_inpl(&(vec[i]), inv_n, mod); + // mul_mod_inpl(&(vec[i + n / 2]), inv_n, mod); + } +} +#endif + +void intt_inpl(const Parms *parms, const ZZ *intt_roots, ZZ *vec) +{ + se_assert(parms && parms->curr_modulus); + size_t n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + ZZ inv_n, inv_n_w; + + // -- Note: inv_n equals n^(-1) mod qi + // and inv_n_w equals (n * w)^(-1) mod qi + // where w = first (non-inverse) ntt root + switch (mod->value) + { + case 1071415297: + switch (n) + { + // w = 595045951 + case 16: + inv_n = 1004451841; + inv_n_w = 967261469; + break; + case 1024: inv_n = 1070368993; inv_n_w = 601043701; + case 2048: inv_n = 1070892145; inv_n_w = 836229499; + case 4096: + inv_n = 1071153721; + inv_n_w = 953822398; + break; + default: printf("Error with intt inv_n\n"); exit(1); + } + break; + + case 1071513601: + switch (n) + { + // w = 677515442 + case 16: + inv_n = 1004544001; + inv_n_w = 91594485; + break; + case 1024: + inv_n = 1070467201; + inv_n_w = 46399391; + break; + case 2048: + inv_n = 1070990401; + inv_n_w = 92798782; + break; + case 4096: + inv_n = 1071252001; + inv_n_w = 46399391; + break; + default: printf("Error with intt inv_n\n"); exit(1); + } + break; + + case 1072496641: + switch (n) + { + // w = 652017833 + case 16: + inv_n = 1005465601; + inv_n_w = 562528246; + break; + case 1024: + inv_n = 1071449281; + inv_n_w = 176367104; + break; + case 2048: + inv_n = 1071972961; + inv_n_w = 88183552; + break; + case 4096: + inv_n = 1072234801; + inv_n_w = 44091776; + break; + default: printf("Error with intt inv_n\n"); exit(1); + } + break; + default: printf("Error with intt inv_n\n"); exit(1); + } +#ifdef SE_INTT_FAST + se_assert(intt_roots); + MUMO inv_n_mumo, inv_n_w_mumo; + inv_n_mumo.operand = inv_n; + inv_n_w_mumo.operand = inv_n_w; + switch (n) + { + case 16: inv_n_mumo.quotient = 4026531840; break; + case 4096: inv_n_mumo.quotient = 4293918720; break; + default: printf("Error with intt inv_n\n"); exit(1); + } + switch (mod->value) + { + case 1071415297: + switch (n) + { + case 16: inv_n_w_mumo.quotient = 417519972; break; + case 4096: inv_n_w_mumo.quotient = 3823574310; break; + default: printf("Error with intt inv_n\n"); exit(1); + } + break; + case 1071513601: + switch (n) + { + case 16: inv_n_w_mumo.quotient = 3927827469; break; + case 4096: inv_n_w_mumo.quotient = 185983515; break; + default: printf("Error with intt inv_n\n"); exit(1); + } + break; + case 1072496641: + switch (n) + { + case 16: inv_n_w_mumo.quotient = 2252725395; break; + case 4096: inv_n_w_mumo.quotient = 176571868; break; + default: printf("Error with intt inv_n\n"); exit(1); + } + break; + default: printf("Error with intt inv_n_w\n"); exit(1); + } + + intt_lazy_inpl(parms, (MUMO *)intt_roots, &inv_n_mumo, &inv_n_w_mumo, vec); + + // -- Final adjustments: compute a[j] = a[j] * n^{-1} mod q. + // -- We incorporated mult by n inverse in the butterfly. Only need to reduce here. + ZZ q = mod->value; + for (size_t i = 0; i < n; i++) + { + if (vec[i] >= q) vec[i] -= q; + } +#else + // -- Note: intt_roots will be ignored if SE_INTT_OTF is defined + intt_non_lazy_inpl(parms, intt_roots, inv_n, inv_n_w, vec); +#endif +} diff --git a/device/lib/intt.h b/device/lib/intt.h new file mode 100644 index 0000000..184c1e6 --- /dev/null +++ b/device/lib/intt.h @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file intt.h + +Inverse Number Theoretic Transform. +*/ + +#pragma once + +#include "defines.h" +#include "parameters.h" + +/* +For all "Harvey" butterfly INTTs: +The input is a polynomial of degree n in R_q, where n is assumed to be a power +of 2 and q is a prime such that q = 1 (mod 2n). The output is a vector A such +that the following hold: A[j] = a(psi**(2*bit_reverse(j) + 1)), 0 <= j < n. For +more details, see the SEAL-Embedded paper and Michael Naehrig and Patrick Longa's paper. +*/ + +/** +Initializes INTT roots. + +If SE_INTT_ONE_SHOT is defined, will calculate INTT roots one by one. +Else if SE_INTT_FAST is defined, will load "fast"/"lazy" roots from file. +Else (if SE_INTT_REG is defined), will load regular roots from file. + +Space req: If SE_DATA_FROM_CODE_DIRECT is not defined, 'intt_roots' should +have space for n ZZ elements if SE_INTT_ONE_SHOT or SE_INTT_REG is defined, +or 2n ZZ elements (i.e. n MUMO elements) if SE_INTT_FAST is defined. + +@param[in] parms Parameters set by ckks_setup +@param[out] intt_roots INTT roots. Will be null if SE_INTT_OTF is defined. +*/ +void intt_roots_initialize(const Parms *parms, ZZ *intt_roots); + +/** +Performs a negacyclic inverse NTT using the Harvey butterfly. + +@param[in] parms Parameters set by ckks_setup +@param[in] intt_roots INTT roots set by ntt_roots_initialize. Ignored if SE_NTT_OTF +is defined. +@param[in, out] vec Input/output polynomial of n ZZ elements +*/ +void intt_inpl(const Parms *parms, const ZZ *intt_roots, ZZ *vec); diff --git a/device/lib/modulo.h b/device/lib/modulo.h new file mode 100644 index 0000000..408c8f9 --- /dev/null +++ b/device/lib/modulo.h @@ -0,0 +1,188 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file modulo.h +*/ + +#pragma once + +#include "defines.h" +#include "modulus.h" // Modulus +#include "uintops.h" // multiply_uint64 + +/** +Constant-time shift of input from [0, 2q) to [0, q) + +@param[in] input Input in [0, 2q) +@param[in] q Modulus value +@returns Result in [0, q) +*/ +static inline ZZ shift_result(ZZ input, ZZ q) +{ + // -- If input is in [0, 2q) instead of [0, q), is_2q = 1 (case 1) + // Otherwise, is_2q = 0 (case 2) + ZZsign is_2q = (ZZsign)(input >= q); + + // -- If case 1, mask = all 1s; if case 2, mask = all 0s; + ZZ mask = (ZZ)(-is_2q); + + // -- Use mask to subtract q if necessary + return (ZZ)(input) - (q & mask); +} + +/** +Reduces input using base 2^32 Barrett reduction + +Req: modulus must be at most 31 bits + +@param[in] input 32-bit input +@param[in] modulus Modulus object with 31-bit value +@returns Result of input mod q in a uint32_t +*/ +static inline uint32_t barrett_reduce_32input_32modulus(uint32_t input, + const Modulus *modulus) +{ + // -- (x = input) + // -- (q = modulus->value) + // -- We want to calculate [x]q (i.e. x mod q). + // we will use barrett reduction to calculate r + // where r = [x]q or [x]2q + // the formula for this is: + // r = [ [x]b - [floor(x*u/t) * q]b ]b + // where u = floor(t/q), b = 2^64, t = 2^64 + + uint32_t tmp; + // -- We have modulus->const_ratio = floor(2^128/q). + // We can get floor(2^64/q) from floor(2^128/q) by taking the upper 64-bits. + // So, x*u = x * floor(2^64/q) + // = x * floor((2^128/2^64) * 1/q) + // = x * upper_64_bits(floor(2^128/q)) + // = x * modulus->const_ratio[1] + // Note that we only need the upper 64 bits of temp (see below) + tmp = mul_uint32_high(input, modulus->const_ratio[1]); + + // -- Barrett subtraction. We use the same technique as above once more: + // floor(x*u/t) * q = floor(tmp/2^64) * q + // = upper_64_bits(tmp) * q + // Since we actually want [floor(x*u/t) * q]b, we can just take lower bits + + // -- These are the same because q is bounded by 2^31 - 1 + tmp = input - tmp * modulus->value; + // tmp = input - mul_uint32_low(tmp, modulus->value); + + // -- Make sure result is in [0, q) instead of [0, 2q) + return shift_result(tmp, modulus->value); +} + +/** +Reduces input using constant-time base 2^32 Barrett reduction + +@param[in] input 64-bit input +@param[in] modulus Modulus object with 32-bit value +@returns 32-bit result of input mod q +*/ +static inline uint32_t barrett_reduce_64input_32modulus(const uint32_t *input, + const Modulus *modulus) +{ + const uint32_t *const_ratio = modulus->const_ratio; + + // The following code is essentially multiplying input (128-bit) + // with modulus->const_ratio (63-bit--> ~64 bits), but is optimized + // to only calculate the highest word of the 192-bit result since + // it is equivalent to the reduced result. + // (hw = high_word, lw = low_word) + + // Round 1 + uint32_t right_hw = mul_uint32_high(input[0], const_ratio[0]); + + uint32_t middle_temp[2]; + mul_uint32_wide(input[0], const_ratio[1], middle_temp); + uint32_t middle_lw; + uint32_t middle_lw_carry = add_uint32(right_hw, middle_temp[0], &middle_lw); + uint32_t middle_hw = middle_temp[1] + middle_lw_carry; + + // Round 2 + uint32_t middle2_temp[2]; + mul_uint32_wide(input[1], const_ratio[0], middle2_temp); + uint32_t middle2_lw; + uint32_t middle2_lw_carry = add_uint32(middle_lw, middle2_temp[0], &middle2_lw); + uint32_t middle2_hw = middle2_temp[1] + middle2_lw_carry; // We don't need the carry + + uint32_t tmp = input[1] * const_ratio[1] + middle_hw + middle2_hw; + + // Barrett subtraction + tmp = input[0] - tmp * modulus->value; + return shift_result(tmp, modulus->value); +} + +/** +Reduces a 2B-bit input using constant-time base 2^B Barrett reduction for a B-bit modulus +(B = 32 if ZZ = uint32). + +@param[in] input (2*B)-bit input to reduce +@param[in] modulus Modulus object with B-bit value q +@returns B-bit result of input mod q +*/ +static inline ZZ barrett_reduce_wide(const ZZ *input, const Modulus *modulus) +{ + return barrett_reduce_64input_32modulus(input, modulus); +} + +/** +Reduces a B-bit input using constant-time base 2^B Barrett reduction for a B-bit modulus +(B = 32 if ZZ = uint32). + +@param[in] input B-bit input to reduce +@param[in] modulus Modulus object with B-bit value q +@returns B-bit result of input mod q +*/ +static inline ZZ barrett_reduce(const ZZ input, const Modulus *modulus) +{ + return barrett_reduce_32input_32modulus(input, modulus); +} + +/** +Optimized constant-time modulo 3 reduction for an 8-bit unsigned integer input + +@param[in] r 8-bit unsigned integer input +@returns 'r' mod 3 +*/ +static inline uint8_t mod3_uint8input(uint8_t r) +{ + // r = (r >> 4) + (r & 0xf); // r' = r mod 3, since 2^4=1 + // r = (r >> 2) + (r & 0x3); // r'= r mod 3, since 2^2=1 + // r = (r >> 2) + (r & 0x3); // r'= r mod 3, since 2^2=1, reducing r to [0, 3] + // int8_t t = r - 3; // 0,1,2 --> 0xF?, 3 --> 0x00 + // int8_t c = t >> 7; // 0xF? --> 0x01, 0x00 --> 0x00 + // return (c & r) ^ ((~c) & t); + r = (uint8_t)((r >> 4) + (r & 0xf)); // r' = r mod 3, since 2^4=1 + r = (uint8_t)((r >> 2) + (r & 0x3)); // r'= r mod 3, since 2^2=1 + r = (uint8_t)((r >> 2) + + (r & 0x3)); // r'= r mod 3, since 2^2=1, reducing r to [0, 3] + int8_t t = (int8_t)(r - 3); // 0,1,2 --> 0xF?, 3 --> 0x00 + int8_t c = t >> 7; // 0xF? --> 0x01, 0x00 --> 0x00 + return (uint8_t)((c & r) ^ ((~c) & t)); +} + +/** +Constant-time modulo 3 reduction + +@param[in] input Input to be reduced +@returns 'input' mod 3 +*/ +static inline uint8_t mod3_zzinput(uint32_t input) +{ + uint32_t r; + r = (input >> 16) + (input & 0xffff); // r' = r mod 3, since 2^16=1 + r = (r >> 8) + (r & 0xff); // r' = r mod 3, since 2^8=1 + r = (r >> 4) + (r & 0xf); // r' = r mod 3, since 2^4=1 + r = (r >> 2) + (r & 0x3); // r'= r mod 3, since 2^2=1 + r = (r >> 2) + (r & 0x3); // r'= r mod 3, since 2^2=1, reducing r to [0, 3] + // int8_t t = (uint8_t)r - 3; // 0,1,2 --> 0xF?, 3 --> 0x00 + // int8_t c = t >> 7; // 0xF? --> 0x01, 0x00 --> 0x00 + // return (c & (uint8_t)r) ^ ((~c) & t); + int8_t t = (int8_t)((uint8_t)r - 3); // 0,1,2 --> 0xF?, 3 --> 0x00 + int8_t c = t >> 7; // 0xF? --> 0x01, 0x00 --> 0x00 + return (uint8_t)((c & (uint8_t)r) ^ ((~c) & t)); +} diff --git a/device/lib/modulus.c b/device/lib/modulus.c new file mode 100644 index 0000000..7257a1f --- /dev/null +++ b/device/lib/modulus.c @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file modulus.c +*/ + +#include "modulus.h" + +#include // uint64_t, UINT64_MAX +#include // memcpy + +#include "defines.h" +#include "util_print.h" + +void set_modulus_custom(const ZZ q, ZZ hw, ZZ lw, Modulus *mod) +{ + mod->value = q; + mod->const_ratio[1] = hw; + mod->const_ratio[0] = lw; +} + +bool set_modulus(const uint32_t q, Modulus *mod) +{ + switch (q) + { + // -- 30-bit ckks primes + case 1073479681: set_modulus_custom(q, 0x4, 0x004003f0, mod); return 1; + case 1073184769: set_modulus_custom(q, 0x4, 0x00881202, mod); return 1; + case 1073053697: set_modulus_custom(q, 0x4, 0x00a81b84, mod); return 1; + case 1072857089: set_modulus_custom(q, 0x4, 0x00d82d89, mod); return 1; + case 1072496641: set_modulus_custom(q, 0x4, 0x01305a4a, mod); return 1; + case 1071513601: set_modulus_custom(q, 0x4, 0x02212189, mod); return 1; + case 1071415297: set_modulus_custom(q, 0x4, 0x02393baf, mod); return 1; + + // -- Other values for testing + case 2: set_modulus_custom(q, 1UL << 31, 0, mod); return 1; + case 3: set_modulus_custom(q, 0x55555555, 0x55555555, mod); return 1; + case 10: set_modulus_custom(q, 0x19999999, 0x99999999, mod); return 1; + case 0xFFFF: set_modulus_custom(q, 0x10001, 0x00010001, mod); return 1; + case 0x10000: set_modulus_custom(q, 0x10000, 0x00000000, mod); return 1; + case 1559578058: set_modulus_custom(q, 0x2, 0xc1017e44, mod); return 1; + case 2147483647: + set_modulus_custom(q, 0x2, 0x4, mod); // Max Q + return 1; + + default: + printf("Modulus const ratio values not found for "); + print_zz("Modulus value", q); + printf("Please try set_modulus_custom instead."); + return 0; + } + return 0; +} diff --git a/device/lib/modulus.h b/device/lib/modulus.h new file mode 100644 index 0000000..db79faa --- /dev/null +++ b/device/lib/modulus.h @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file modulus.h +*/ + +#pragma once + +#include +#include // uint64_t + +#include "defines.h" + +/** +Struct to store a modulus. 'const_ratio' can be precomputed and used later for faster +modular reduction in some cases. + +@param value Value of the modulus (aka 'q') +@param const_ratio floor(2^128/q) +*/ +typedef struct Modulus +{ + ZZ value; // Value of the modulus (aka 'q') + + // -- Note: SEAL const_ratio is size 3 to store the remainder, + // but we don't need the remainder so we can use a size 2 array + + ZZ const_ratio[2]; // floor(2^128/q) +} Modulus; + +/** +Sets up the modulus object for a particular modulus value. Useful for setting up a modulus +if const_ratio for modulus value has not been pre-computed by set_modulus' table. + +@param[in] q Modulus value +@param[in] hw High word of const_ratio for 'q' +@param[in] lw Low word of const_ratio for 'q' +@param[out] mod Modulus object to set +*/ +void set_modulus_custom(const ZZ q, ZZ hw, ZZ lw, Modulus *mod); + +/** +Sets up the modulus object for a particular modulus value. Implements const_ratio set as a +table lookup. If table does not contain const_ratio for the requested modulus value, +returns a failure. In this case, set_modulus_custom should be used instead. + +@param[in] q Modulus value +@param[out] mod Modulus object to set +@returns 1 on success, 0 on failure +*/ +bool set_modulus(const ZZ q, Modulus *mod); diff --git a/device/lib/network.c b/device/lib/network.c new file mode 100644 index 0000000..1c6c18f --- /dev/null +++ b/device/lib/network.c @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file network.c +*/ + +#include "defines.h" + +#ifdef SE_ON_SPHERE_A7 + #include + #include + #include + #include + #include + #include // strerror + + #include "network.h" + #include "util_print.h" + +bool is_network_connected(void) +{ + Networking_InterfaceConnectionStatus status; + static const char networkInterface[] = "wlan0"; + + int err = Networking_GetInterfaceConnectionStatus(networkInterface, &status); + if (err) + { + if (errno != EAGAIN) + { + printf("ERROR: Networking_GetInterfaceConnectionStatus: %d (%s)\n", errno, + strerror(errno)); + return false; + } + printf("Error: Networking stack is not ready.\n"); + return false; + } + + // -- Use a mask to get bits of the status related to internet connection + uint32_t connected = + status & Networking_InterfaceConnectionStatus_ConnectedToInternet; + if (!connected) + { + printf("Error: No internet connectivity.\n"); + return false; + } + + return true; +} + +bool is_curl_error(void *ret, const char *name) +{ + if (!ret || (*(CURLcode *)(ret) != CURLE_OK)) + { + // -- Error occured. Print debugging info + if (name) printf("Failed: %s. ", name); + if (ret) + { + CURLcode c = *((CURLcode *)ret); + printf("Error: %s\n", curl_easy_strerror(c)); + } + return true; + } + return false; +} + +void send_over_network(ZZ *data, size_t num_data_bytes) +{ + CURL *curl = NULL; + CURLcode ret = CURLE_OK; + + // -- First, check that we are connected to the internet + if (!is_network_connected()) return; + + ret = curl_global_init(CURL_GLOBAL_ALL); + if (is_curl_error(&ret, "init")) return; + + curl = curl_easy_init(); + if (is_curl_error(curl, "init")) { goto cleanup; } + + // -- Specify URL to download. Important: any change + // in the domain name must be reflected in the + // AllowedConnections capability in app_manifest.json. + ret = curl_easy_setopt(curl, CURLOPT_URL, url); + if (is_curl_error(&ret, "url")) { goto cleanup; } + + ret = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if (is_curl_error(&ret, "verbose")) { goto cleanup; } + + // -- Let cURL follow any HTTP 3xx redirects. Important: + // any redirection to different domain names requires + // that domain name to be added to app_manifest.json. + ret = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + if (is_curl_error(&ret, "follow")) { goto cleanup; } + + ret = curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + if (is_curl_error(&ret, "user agent")) { goto cleanup; } + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: vector"); + + printf("Sending the following data over the network: \n"); + print_poly("data", data, num_data_bytes / sizeof(ZZ)); + + ret = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + if (is_curl_error(&ret, "postfields")) { goto cleanup2; } + + ret = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, num_data_bytes); + if (is_curl_error(&ret, "postfieldsize")) { goto cleanup2; } + + ret = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + if (is_curl_error(&ret, "headers")) { goto cleanup2; } + + ret = curl_easy_perform(curl); + if (is_curl_error(&ret, "post")) { goto cleanup2; } + +cleanup2: + curl_slist_free_all(curl); + +cleanup: + if (curl) curl_easy_cleanup(curl); + curl_global_cleanup(); +} + +#endif diff --git a/device/lib/network.h b/device/lib/network.h new file mode 100644 index 0000000..7969e3d --- /dev/null +++ b/device/lib/network.h @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file network.h +*/ + +#pragma once + +#include "defines.h" + +#ifdef SE_ON_SPHERE_A7 + #include + +// static const char *url = "http://neverssl.com"; +// static const char *url = "http://httpstat.us"; +static const char *url = "http://192.168.86.30:1234/"; // Macbook + +/** +Checks that the interface is connected to the internet. + +@returns True if connected, false otherwise +*/ +bool is_network_connected(void); + +/** +Checks if there was an error with curl. Prints a message upon error detection (does not +exit upon error detection). + +@param[in] ret Curl-function returned value to check +@param[in] name A name or message to print upon error detection +@returns True if there was an error detected, false otherwise +*/ +bool is_curl_error(void *ret, const char *name); + +/** +Sends 'num_data_bytes' of bytes from location pointed to by 'data' over the network +connection using cURL. Prints a message upon error detection (does not exit upon error +detection). + +@param[in] data Pointer to location of data to send over the network +@param[in] num_data_bytes Number of bytes of 'data' to send +*/ +void send_over_network(ZZ *data, size_t num_data_bytes); +#endif diff --git a/device/lib/ntt.c b/device/lib/ntt.c new file mode 100644 index 0000000..07f93c8 --- /dev/null +++ b/device/lib/ntt.c @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ntt.c +*/ + +#include "ntt.h" + +#include + +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "parameters.h" +#include "polymodarith.h" +#include "uintmodarith.h" +#include "util_print.h" + +#if defined(SE_NTT_OTF) || defined(SE_NTT_ONE_SHOT) + +/** +Helper function to return root for certain modulus prime values if SE_NTT_OTF or +SE_NTT_ONE_SHOT is used. Implemented as a table lookup. + +@param[in] n Transform size (i.e. polynomial ring degree) +@param[in] q Modulus value +*/ +ZZ get_ntt_root(size_t n, ZZ q) +{ + // TODO: Add more primes + ZZ root; + switch (n) + { + case 16: + switch (q) + { + case 1071415297: root = 161442378; break; + case 1071513601: root = 113726644; break; + case 1072496641: root = 15809412; break; + default: { + printf("Error! Need first power of root, n = 16\n"); + print_zz("Modulus value", q); + exit(1); + } + } + break; + case 4096: + switch (q) + { + case 1071415297: root = 12202; break; + case 1071513601: root = 143907; break; + case 1072496641: root = 27507; break; + default: { + printf("Error! Need first power of root for ntt, n = 4K\n"); + print_zz("Modulus value", q); + exit(1); + } + } + break; + default: { + printf("Error! Need first power of root for ntt\n"); + print_zz("Modulus value", q); + exit(1); + } + } + return root; +} +#endif + +void ntt_roots_initialize(const Parms *parms, ZZ *ntt_roots) +{ +#ifdef SE_REVERSE_CT_GEN_ENABLED + SE_UNUSED(parms); + SE_UNUSED(ntt_roots); + if (parms->skip_ntt_load) return; +#endif + +#ifdef SE_NTT_OTF + SE_UNUSED(parms); + SE_UNUSED(ntt_roots); + return; +#endif + + se_assert(parms && parms->curr_modulus && ntt_roots); + +#ifdef SE_NTT_ONE_SHOT + size_t n = parms->coeff_count; + size_t logn = parms->logn; + Modulus *mod = parms->curr_modulus; + + ZZ root = get_ntt_root(n, mod->value); + ZZ power = root; + ntt_roots[0] = 1; // Not necessary but set anyway + for (size_t i = 1; i < n; i++) + { + ntt_roots[bitrev(i, logn)] = power; + power = mul_mod(power, root, mod); + } +#elif defined(SE_NTT_FAST) + load_ntt_fast_roots(parms, (MUMO *)ntt_roots); +#elif defined(SE_NTT_REG) + load_ntt_roots(parms, ntt_roots); +#else + se_assert(0); +#endif +} + +#ifdef SE_NTT_FAST +/** +Performs a "fast" (a.k.a. "lazy") negacyclic in-place NTT using the Harvey butterfly. Only +used if "SE_NTT_FAST" is defined. See SEAL_Embedded paper for a more detailed description. +"Lazy"-ness refers to fact that we here we lazily opt to reduce values only at the very +end. + +@param[in] parms Parameters set by ckks_setup +@param[in] ntt_fast_roots NTT roots set by ntt_roots_initialize +@param[in,out] vec Input/output polynomial of n ZZ elements +*/ +void ntt_lazy_inpl(const Parms *parms, const MUMO *ntt_fast_roots, ZZ *vec) +{ + se_assert(parms && ntt_fast_roots && vec); + size_t n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + ZZ two_q = mod->value << 1; + + // -- Return the NTT in scrambled order + size_t h = 1; + size_t tt = n / 2; + // size_t root_idx = 1; + + for (int i = 0; i < parms->logn; i++, h *= 2, tt /= 2) // Rounds + { + // print_poly_full("s in ntt", vec, n); + for (size_t j = 0, kstart = 0; j < h; j++, kstart += 2 * tt) // Groups + { + const MUMO *s = &(ntt_fast_roots[h + j]); + // const MUMO *s = &(ntt_fast_roots[bitrev(h + j, parms->logn)]); + // const MUMO *s = &(ntt_fast_roots[root_idx++]); + + // -- The Harvey butterfly. Assume val1, val2 in [0, 2p) + // -- Return vec[k], vec[k+tt] in [0, 4p) + for (size_t k = kstart; k < (kstart + tt); k++) // Pairs + { + ZZ val1 = vec[k]; + ZZ val2 = vec[k + tt]; + + ZZ u = val1 - (two_q & (ZZ)(-(ZZsign)(val1 >= two_q))); + ZZ v = mul_mod_mumo_lazy(val2, s, mod); + + // -- We know these will not generate carries/overflows + vec[k] = u + v; + vec[k + tt] = u + two_q - v; + } + } + } +} +#else + +/** +Performs a negacyclic in-place NTT using the Harvey butterfly. Only used if SE_NTT_FAST is +not defined. + +If SE_NTT_REG or SE_NTT_ONE_SHOT is defined, will use regular NTT computation. +Else, (SE_NTT_OTF is defined), will use truly "on-the-fly" NTT computation. In this last +case, 'ntt_roots' may be null (and will be ignored). + +@param[in] parms Parameters set by ckks_setup +@param[in] ntt_roots NTT roots set by ntt_roots_initialize. Ignored if SE_NTT_OTF is +defined. +@param[in,out] vec Input/output polynomial of n ZZ elements +*/ +void ntt_non_lazy_inpl(const Parms *parms, const ZZ *ntt_roots, ZZ *vec) +{ + se_assert(parms && parms->curr_modulus && vec); + + size_t n = parms->coeff_count; + size_t logn = parms->logn; + Modulus *mod = parms->curr_modulus; + + // -- Return the NTT in scrambled order + size_t h = 1; + size_t tt = n / 2; + + #ifdef SE_NTT_OTF + SE_UNUSED(ntt_roots); + ZZ root = get_ntt_root(n, mod->value); + #endif + + for (int i = 0; i < logn; i++, h *= 2, tt /= 2) // rounds + { + for (size_t j = 0, kstart = 0; j < h; j++, kstart += 2 * tt) // groups + { + #ifdef SE_NTT_OTF + // printf("h+j: %zu\n", h+j); + // ZZ power = bitrev(h+j, logn); + // ZZ s = exponentiate_uint_mod(root, power, mod); + ZZ power = h + j; + ZZ s = exponentiate_uint_mod_bitrev(root, power, logn, mod); + #else + se_assert(ntt_roots); + ZZ s = ntt_roots[h + j]; + #endif + // -- The Harvey butterfly. Assume val1, val2 in [0, 2p) + // -- Return vec[k], vec[k+tt] in [0, 4p) + for (size_t k = kstart; k < (kstart + tt); k++) // pairs + { + ZZ u = vec[k]; + ZZ v = mul_mod(vec[k + tt], s, mod); + vec[k] = add_mod(u, v, mod); // vec[k] = u + v; + vec[k + tt] = sub_mod(u, v, mod); // vec[k+tt] = u - v; + } + } + } +} +#endif + +void ntt_inpl(const Parms *parms, const ZZ *ntt_roots, ZZ *vec) +{ + se_assert(parms && parms->curr_modulus && vec); +#ifdef SE_NTT_FAST + se_assert(ntt_roots); + ntt_lazy_inpl(parms, (MUMO *)ntt_roots, vec); + // print_poly_full("vec", vec, parms->coeff_count); + + // -- Finally, we might need to reduce coefficients modulo q, but we know each + // coefficient is in the range [0, 4q). Since word size is controlled, this + // should be fast. + ZZ q = parms->curr_modulus->value; + ZZ two_q = q << 1; + for (size_t i = 0; i < parms->coeff_count; i++) + { + if (vec[i] >= two_q) vec[i] -= two_q; + if (vec[i] >= q) vec[i] -= q; + } +#else + ntt_non_lazy_inpl(parms, ntt_roots, vec); +#endif +} diff --git a/device/lib/ntt.h b/device/lib/ntt.h new file mode 100644 index 0000000..a527a94 --- /dev/null +++ b/device/lib/ntt.h @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ntt.h + +Number Theoretic Transform. +*/ + +#pragma once + +#include "defines.h" +#include "fileops.h" +#include "parameters.h" +#include "polymodarith.h" +#include "uintmodarith.h" + +/* +For all "Harvey" butterfly NTTs: +The input is a polynomial of degree n in R_q, where n is assumed to be a power +of 2 and q is a prime such that q = 1 (mod 2n). The output is a vector A such +that the following hold: A[j] = a(psi**(2*bit_reverse(j) + 1)), 0 <= j < n. For +more details, see the SEAL-Embedded paper and Michael Naehrig and Patrick Longa's paper. +*/ + +/** +Initializes NTT roots. + +If SE_NTT_FAST is defined, will load "fast"(a.k.a. "lazy") roots from file. +Else, if SE_NTT_REG is defined, will load regular roots from file. +Else, if SE_NTT_ONE_SHOT is defined, will calculate NTT roots one by one. +Else, (SE_NTT_OTF), will do nothing ('ntt_roots' can be null and will be ignored). + +Space req: 'ntt_roots' should have space for 2n ZZ elements if SE_NTT_FAST is defined or n +ZZ elements if SE_NTT_ONE_SHOT or SE_NTT_REG is defined. + +@param[in] parms Parameters set by ckks_setup +@param[out] ntt_roots NTT roots. Will be null if SE_NTT_OTF is defined. +*/ +void ntt_roots_initialize(const Parms *parms, ZZ *ntt_roots); + +/** +Negacyclic in-place NTT using the Harvey butterfly. + +If SE_NTT_FAST is defined, will use "fast"(a.k.a. "lazy") NTT computation. +Else, if SE_NTT_REG or SE_NTT_ONE_SHOT is defined, will use regular NTT computation. +Else, (SE_NTT_OTF is defined), will use truly "on-the-fly" NTT computation. In this last +case, 'ntt_roots' may be null (and will be ignored). + +@param[in] parms Parameters set by ckks_setup +@param[in] ntt_roots NTT roots set by ntt_roots_initialize. Ignored if SE_NTT_OTF is +defined. +@param[in,out] vec Input/output polynomial of n ZZ elements +*/ +void ntt_inpl(const Parms *parms, const ZZ *ntt_roots, ZZ *vec); + +/** +Polynomial multiplication for inputs already in NTT form. 'res' and 'a' may share the same +starting address (see: poly_mult_mod_ntt_form_inpl) + +@param[in] a Input polynomial 1, in NTT form, with n ZZ coefficients +@param[in] b Input polynomial 2, in NTT form, with n ZZ coefficients +@param[in] n Number of coefficients in a, b, and c +@param[in] mod Modulus +@param[out] res Result polynomial, in NTT form, with n ZZ coefficients +*/ +static inline void poly_mult_mod_ntt_form(const ZZ *a, const ZZ *b, size_t n, + const Modulus *mod, ZZ *res) +{ + // -- Inputs in NTT form can be multiplied component-wise + poly_pointwise_mul_mod(a, b, n, mod, res); +} + +/** +In-place polynomial multiplication for inputs already in NTT form. + +@param[in,out] a In: Input polynomial 1; Out: Result polynomial +@param[in] b Input polynomial 2, in NTT form, with n ZZ coefficients. +@param[in] n Number of coefficients in a and b +@param[in] mod Modulus +*/ +static inline void poly_mult_mod_ntt_form_inpl(ZZ *a, const ZZ *b, size_t n, + const Modulus *mod) +{ + // -- Values in NTT form can be multiplied component-wise + poly_pointwise_mul_mod_inpl(a, b, n, mod); +} diff --git a/device/lib/parameters.c b/device/lib/parameters.c new file mode 100644 index 0000000..c8da905 --- /dev/null +++ b/device/lib/parameters.c @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file parameters.c +*/ + +#include "parameters.h" + +#include // log2 + +#include "defines.h" +#include "util_print.h" + +/** +Helper function to check if a value is a power of 2. + +@param[in] val Value to verify if is a power of 2 +@returns True if val is a power of 2, false otherwise +*/ +static bool is_power_of_2(size_t val) +{ + return val && (!(val & (val - 1))); +} + +void delete_parameters(Parms *parms) +{ +#ifdef SE_USE_MALLOC + se_assert(parms); + free(parms->moduli); + parms->moduli = NULL; + parms = NULL; +#else + SE_UNUSED(parms); +#endif +} + +void reset_primes(Parms *parms) +{ + se_assert(parms); +#ifdef SE_REVERSE_CT_GEN_ENABLED + parms->curr_param_direction = 0; + parms->skip_ntt_load = 0; +#endif + parms->curr_modulus_idx = 0; + parms->curr_modulus = &(parms->moduli[0]); +} + +bool next_modulus(Parms *parms) +{ + se_assert(parms); + bool ret_val = 1; // success +#ifdef SE_REVERSE_CT_GEN_ENABLED + if (parms->curr_param_direction == 0) // forwards + { + if ((parms->curr_modulus_idx + 1) >= parms->nprimes) + { + parms->curr_param_direction = 1; + parms->skip_ntt_load = 1; + return 0; + } + parms->curr_modulus_idx++; + } + else + { + if (parms->curr_modulus_idx == 0) + { + parms->curr_param_direction = 0; + parms->skip_ntt_load = 1; + return 0; + } + parms->curr_modulus_idx--; + } + parms->skip_ntt_load = 0; +#else + if ((parms->curr_modulus_idx + 1) >= parms->nprimes) + { + parms->curr_modulus_idx = 0; + ret_val = 0; + } + else + parms->curr_modulus_idx++; +#endif + parms->curr_modulus = &(parms->moduli[parms->curr_modulus_idx]); + return ret_val; +} + +/** +Helper function to set the Parameters instance. + +@param[in] degree Polynomial ring degree +@param[in] nprimes Number of prime moduli +@param[out] parms Parameters instance to set +*/ +static void set_params_base(size_t degree, size_t nprimes, Parms *parms) +{ + se_assert(degree >= 2 && degree <= 16384); + se_assert(is_power_of_2(degree)); + se_assert(parms); + se_assert(nprimes >= 1); + + parms->coeff_count = degree; + parms->logn = (size_t)log2(degree); + parms->nprimes = nprimes; +#ifdef SE_USE_MALLOC + se_assert(parms && parms->nprimes); + parms->moduli = calloc(parms->nprimes, sizeof(Modulus)); + se_assert(parms->moduli); +#endif + + // -- Set curr_modulus to first prime + parms->curr_modulus_idx = 0; + parms->curr_modulus = &(parms->moduli[0]); + se_assert(parms->curr_modulus); +#ifdef SE_REVERSE_CT_GEN_ENABLED + parms->curr_param_direction = 0; // 0 = forward, 1 = reverse + parms->skip_ntt_load = 0; +#endif +} + +void set_parms_ckks(size_t degree, size_t nprimes, Parms *parms) +{ + se_assert(parms); + set_params_base(degree, nprimes, parms); + se_assert(nprimes < 8); + + // -- These precomputed values of q satisfy q = 1 (mod m), for m = 2*n and + // n = polynomial degree = a power of 2. + // -- We know that any prime q that satisfies: + // q = 1 (mod m) (m = 2^k, k = a positive integer) + // also satisfies: + // q = 1 (mod m') (m' = 2^(k-i), i = a positive integer < k) + // and we can use the same modulus q for all powers of 2 < m. Note that the + // reverse is not true. That is, a prime q that satisfies q = 1 (mod m') + // does not necessarily satisfy q = 1 (mod m). + switch (parms->nprimes) + { +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 7) + case 7: set_modulus(1073479681, &(parms->moduli[6])); +#endif +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 6) + case 6: set_modulus(1073184769, &(parms->moduli[5])); +#endif +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 5) + case 5: set_modulus(1073053697, &(parms->moduli[4])); +#endif +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 4) + case 4: set_modulus(1072857089, &(parms->moduli[3])); +#endif +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 3) + case 3: set_modulus(1072496641, &(parms->moduli[2])); +#endif +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 2) + case 2: set_modulus(1071513601, &(parms->moduli[1])); +#endif +#if (defined(SE_USE_MALLOC) || SE_NPRIMES >= 1) + case 1: set_modulus(1071415297, &(parms->moduli[0])); +#endif + } +} + +void set_custom_parms_ckks(size_t degree, size_t nprimes, const ZZ *modulus_vals, const ZZ *ratios, + Parms *parms) +{ + if (!modulus_vals || !ratios) + { + set_parms_ckks(degree, nprimes, parms); + return; + } + + set_params_base(degree, nprimes, parms); + for (size_t i = 0; i < nprimes; i++) + { + se_assert(modulus_vals[i]); // Should never be 0 + set_modulus_custom(modulus_vals[i], ratios[i], ratios[i + 1], &(parms->moduli[i])); + } +} diff --git a/device/lib/parameters.h b/device/lib/parameters.h new file mode 100644 index 0000000..87101c5 --- /dev/null +++ b/device/lib/parameters.h @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file parameters.h + +Functions for creating and modifying a Parameters instance +*/ + +#pragma once + +#include +#include +#include // uint64_t +#include // size_t + +#include "defines.h" +#include "modulo.h" // Modulus + +/** +Storage for encryption parameters. + +@param coeff_count Number of coefficients in polyomial = n = poly_modulus_degree +@param logn log2(n) (number of bits to represent n) +@param moduli Array of moduli that represents the modulus switching chain +@param curr_modulus Pointer to current modulus in switching chain +@param curr_modulus_idx Index of current modulus in 'moduli' vector +@param nprimes Number of 'Modulus' objects in 'moduli' array +@param scale CKKS scale value +@param is_asymmetric Set to 1 if using public key encryption +@param pk_from_file Set to 1 to use a public key from a file +@param sample_s Set to 1 to sample the secret key +@param small_s Set to 1 to store the secret key in small form while processing + Note: SEAL-Embedded currently only works if this is 1 +@param small_u Set to 1 to store the 'u' vector in small form while processing + Note: SEAL-Embedded currently only works if this is 1 +@param curr_param_direction Set to 1 to operate over primes in reverse order. Only +available if SE_REVERSE_CT_GEN_ENABLED is enabled. +@param skip_ntt_load Set to 1 to skip a load of the NTT roots (if applicable, +based on NTT option chosen.) Only available if SE_REVERSE_CT_GEN_ENABLED is enabled. +*/ +typedef struct +{ + size_t coeff_count; // Number of coefficients in polyomial = n = poly_modulus_degree + size_t logn; // log2(n) (number of bits to represent n) +#ifdef SE_USE_MALLOC + Modulus *moduli; // Array of moduli that represents the modulus switching chain +#else + Modulus moduli[SE_NPRIMES]; // Array of moduli that represents the modulus switching + // chain +#endif + Modulus *curr_modulus; // Pointer to current modulus in switching chain + size_t curr_modulus_idx; // Index of current modulus in 'moduli' vector + + size_t nprimes; // Number of 'Modulus' objects in 'moduli' array + double scale; // CKKS scale value + bool is_asymmetric; // Set to 1 if using public key encryption + bool pk_from_file; // Set to 1 to use a public key from a file + bool sample_s; // Set to 1 to sample the secret key + bool small_s; // Set to 1 to store the secret key in small form while processing + bool small_u; // Set to 1 to store the 'u' vector in small form while processing +#ifdef SE_REVERSE_CT_GEN_ENABLED + bool curr_param_direction; // Set to 1 to operate over primes in reverse order + bool skip_ntt_load; // Set to 1 to skip a load of the NTT roots +#endif +} Parms; + +/** +Utility function to get the log2 of a value. + +@param[in] val Value +@returns log2(val) +*/ +static inline size_t get_log2(size_t val) +{ + switch (val) + { + case 2048: return 11; + case 4096: return 12; + case 8192: return 13; + case 16384: return 14; + default: return (size_t)log2(val); + } +} + +/** +Releases the memory allocated for modulus chain is SE_USE_MALLOC is defined. +Does nothing otherwise. + +@param[in,out] parms Parameters instance +*/ +void delete_parameters(Parms *parms); + +/** +Resets parameters (sets curr_modulus_idx back to the start of modulus chain) + +@param[in,out] parms Parameters instance +*/ +void reset_primes(Parms *parms); + +/** +Updates parms to next modulus in modulus chain. + +@param[in,out] parms Parameters instance +@returns 1 on success, 0 on fail (reached end of chain) +*/ +bool next_modulus(Parms *parms); + +/** +Sets up SEAL-Embedded parameters object with default (30-bit) moduli for requested degree +for CKKS. + +Req: degree must be a power of 2 (between 2 and 16384, inclusive) +Note: Moduli should be the same as the 30-bit default moduli chosen by the SEAL-Embedded +adapter (not including the special prime). + +@param[in] degree Polynomial ring degree +@param[in] nprimes Number of prime moduli +@param[out] parms Parameters instance +*/ +void set_parms_ckks(size_t degree, size_t nprimes, Parms *parms); + +/** +Sets up SEAL-Embedded parameters object according to requested custom parameters for CKKS. +If either 'modulus_vals' or 'ratios' is NULL, uses default values for 30-bit primes. + +@param[in] degree Polynomial ring degree +@param[in] nprimes Number of prime moduli +@param[in] modulus_vals An array of nprimes type-ZZ modulus values. +@param[in] ratios An array of const_ratio values for each custom modulus value +(high word, followed by low word). +@param[out] parms Parameters instance +*/ +void set_custom_parms_ckks(size_t degree, size_t nprimes, const ZZ *modulus_vals, const ZZ *ratios, + Parms *parms); diff --git a/device/lib/polymodarith.h b/device/lib/polymodarith.h new file mode 100644 index 0000000..85da672 --- /dev/null +++ b/device/lib/polymodarith.h @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file polymodarith.h +*/ + +#pragma once + +#include "defines.h" +#include "uintmodarith.h" + +/** +Modular polynomial addition. 'p1' and 'res' may share the same starting address for +in-place computation. + +Space req: 'res' must have space for n ZZ values. + +@param[in] p1 Input polynomial 1 +@param[in] p2 Input polynomial 2 +@param[in] n Number of elements (ZZ coefficients) in p1 and p2 +@param[in] mod Modulus +@param[out] res Result polynomial +*/ +static inline void poly_add_mod(const ZZ *p1, const ZZ *p2, PolySizeType n, + const Modulus *mod, ZZ *res) +{ + for (PolySizeType i = 0; i < n; i++) { res[i] = add_mod(p1[i], p2[i], mod); } +} + +/** +In-place modular polynomial addition. + +@param[in] p1 In: Input polynomial 1; Out: Result polynomial +@param[in] p2 Input polynomial 2 +@param[in] n Number of elements (ZZ coefficients) in p1 and p2 +@param[in] mod Modulus +*/ +static inline void poly_add_mod_inpl(ZZ *p1, const ZZ *p2, PolySizeType n, + const Modulus *mod) +{ + for (PolySizeType i = 0; i < n; i++) { add_mod_inpl(&(p1[i]), p2[i], mod); } +} + +/** +Modular polynomial negation. 'p1' and 'res' may share the same starting address for +in-place computation. + +Space req: 'res' must have space for n ZZ values. + +@param[in] p1 Input polynomial +@param[in] n Number of elements (ZZ coefficients) in p1 +@param[in] mod Modulus +@param[out] res Result polynomial +*/ +static inline void poly_neg_mod(const ZZ *p1, PolySizeType n, const Modulus *mod, ZZ *res) +{ + for (PolySizeType i = 0; i < n; i++) { res[i] = neg_mod(p1[i], mod); } +} + +/** +In-place modular polynomial negation. + +@param[in] p1 In: Input polynomial; Out: Result polynomial +@param[in] n Number of elements (ZZ coefficients) in p1 +@param[in] mod Modulus +*/ +static inline void poly_neg_mod_inpl(ZZ *p1, PolySizeType n, const Modulus *mod) +{ + for (PolySizeType i = 0; i < n; i++) { neg_mod_inpl(&(p1[i]), mod); } +} + +/** +Pointwise modular polynomial negation. 'p1' and 'res' may share the same starting address +for in-place computation (see: poly_pointwise_mul_mod_inpl). + +Space req: 'res' must have space for n ZZ values. + +@param[in] p1 Input polynomial +@param[in] n Number of elements (ZZ coefficients) in p1 +@param[in] mod Modulus +@param[out] res Result polynomial. +*/ +static inline void poly_pointwise_mul_mod(const ZZ *p1, const ZZ *p2, PolySizeType n, + const Modulus *mod, ZZ *res) +{ + for (PolySizeType i = 0; i < n; i++) { res[i] = mul_mod(p1[i], p2[i], mod); } +} + +/** +In-place pointwise modular polynomial negation. + +@param[in] p1 In: Input polynomial 1; Out: Result polynomial +@param[in] p2 Input polynomial 2 +@param[in] n Number of elements (ZZ coefficients) in p1 +@param[in] mod Modulus +*/ +static inline void poly_pointwise_mul_mod_inpl(ZZ *p1, const ZZ *p2, PolySizeType n, + const Modulus *mod) +{ + poly_pointwise_mul_mod(p1, p2, n, mod, p1); +} diff --git a/device/lib/polymodmult.c b/device/lib/polymodmult.c new file mode 100644 index 0000000..87c1a12 --- /dev/null +++ b/device/lib/polymodmult.c @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file polymodmult.c + +Polynomial modular multiplication. +*/ + +#include "polymodmult.h" + +#include + +#include "defines.h" +#include "parameters.h" +#include "sample.h" +#include "string.h" // memset +#include "uintmodarith.h" +#include "util_print.h" + +/** +Helper funciton to multiply two ring polynomials using schoolbook multiplication, without +the final polynomial reduction. The result polynomial will be returned in the first n ZZ +elements pointed to by 'res'. + +Note: This function is *not* constant-time, and is mainly useful for testing. + +Space req: 'res' must contain space for 2n ZZ elements, where each input polynomial +consists of n ZZ elements (each). + +@param[in] a Input polynomial 1 +@param[in] b Input polynomial 2 +@param[in] n Number of coefficients to multiply +@param[in] mod Modulus +@param[out] res Sized-(2*sizeof(ZZ)) result of [a . b]_mod +*/ +void poly_mult_mod_sb_not_reduced(const ZZ *a, const ZZ *b, PolySizeType n, + const Modulus *mod, ZZ *res) +{ + memset(res, 0, 2 * n * sizeof(ZZ)); // This is necessary + + for (PolySizeType i = 0; i < n; i++) + { + for (PolySizeType j = 0; j < n; j++) + { + // -- Does the following: res[i + j] += a[i] * b[j] (mod p) + mul_add_mod_inpl(&(res[i + j]), a[i], b[j], mod); + } + } +} + +/** +Helper function to perform the final polynomial reduction. +Initial address of 'a' and initial address of 'res' may be the same (see: +poly_reduce_inpl). + +Space req: 'res' must contain space for n ZZ elements (for 'a' with 2n coefficients) + +@param[in] a Input polynomial +@param[in] n Degree to reduce by +@param[in] mod Modulus +@param[out] res Result polynomial +*/ +void poly_reduce(const ZZ *a, PolySizeType n, const Modulus *mod, ZZ *res) +{ + for (PolySizeType i = 0; i < n; i++) + { + // -- Does the following: + // res[i] = (a[i] - a[i + n]) % (ZZ)(mod->value); + res[i] = sub_mod((a[i]), a[i + n], mod); + } +} + +/** +Helper function to perform the final polynomial reduction in place. + +@param[in] a In: Input polynomial (of 2n ZZ values); + Out: Result polynomial (in first n ZZ values). +@param[in] n Degree to reduce by +@param[in] mod Modulus +*/ +void poly_reduce_inpl(ZZ *a, PolySizeType n, const Modulus *mod) +{ + for (PolySizeType i = 0; i < n; i++) + { + // -- Does the following: a[i] = (a[i] - a[i + n]) % (ZZ)(mod->value); + sub_mod_inpl(&(a[i]), a[i + n], mod); + } +} + +void poly_mult_mod_sb(const ZZ *a, const ZZ *b, PolySizeType n, const Modulus *mod, + ZZ *res) +{ + // -- Multiplication (not reduced) + poly_mult_mod_sb_not_reduced(a, b, n, mod, res); + // print_poly("not-reduced (expected)", res, 2*n); + // print_poly_ring("not-reduced", res, 2*n); + // print_poly_"not-reduced (expected)", res, 2*n); + + // -- Ring reduction + poly_reduce_inpl(res, n, mod); + // print_poly("reduced (expected) ", res, n); +} diff --git a/device/lib/polymodmult.h b/device/lib/polymodmult.h new file mode 100644 index 0000000..d33f873 --- /dev/null +++ b/device/lib/polymodmult.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file polymodmult.h +*/ + +#pragma once + +#include + +#include "defines.h" +#include "inttypes.h" +#include "modulo.h" + +/** +Multiplies two ring polynomials using schoolbook multiplication. The result polynomial +will be returned in the first n ZZ elements pointed to by 'res'. + +Note: This function is *not* constant-time, and is mainly useful for testing. + +Space req: 'res' must constain space for 2n ZZ elements, where each input polynomial +consists of n ZZ elements (each). + +@param[in] a Input polynomial 1 +@param[in] b Input polynomial 2 +@param[in] n Number of coefficients to multiply +@param[in] mod Modulus +@param[out] res Result of [a . b]_mod (in the first n ZZ elements) +*/ +void poly_mult_mod_sb(const ZZ *a, const ZZ *b, PolySizeType n, const Modulus *mod, + ZZ *res); diff --git a/device/lib/rng.c b/device/lib/rng.c new file mode 100644 index 0000000..30d760e --- /dev/null +++ b/device/lib/rng.c @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file rng.c + +Pseudo-random number generator. +*/ + +#include "rng.h" + +#include "defines.h" + +// -- Need these to avoid duplicate symbols error + +extern inline void prng_randomize_reset(SE_PRNG *prng, uint8_t *seed_in); +extern inline void prng_fill_buffer(size_t byte_count, SE_PRNG *prng, void *buffer); +extern inline void prng_clear(SE_PRNG *prng); diff --git a/device/lib/rng.h b/device/lib/rng.h new file mode 100644 index 0000000..6e72081 --- /dev/null +++ b/device/lib/rng.h @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file rng.h + +Pseudo-random number generator. +*/ + +#pragma once + +#include +#include +#include // memset + +#include "defines.h" +#include "inttypes.h" +#include "shake256/fips202.h" + +#ifdef SE_RAND_GETRANDOM + #include // getrandom +#elif defined(SE_RAND_NRF5) + #include "nrf_crypto.h" +#endif + +typedef struct SE_PRNG +{ + uint8_t seed[SE_PRNG_SEED_BYTE_COUNT]; + uint64_t counter; +} SE_PRNG; + +/** +Randomizes the seed of a PRNG object and resets its internal counter. + +Size req: If seed_in != NULL, seed_in must be SE_PRNG_SEED_BYTE_COUNT long. + +@param[in,out] prng PRNG instance to modify +@param[in] seed_in [Optional]. Seed to seed the prng with. +*/ +inline void prng_randomize_reset(SE_PRNG *prng, uint8_t *seed_in) +{ + se_assert(prng); + prng->counter = 0; + + if (seed_in) + { + memcpy(&(prng->seed[0]), seed_in, SE_PRNG_SEED_BYTE_COUNT); + return; + } + +#ifdef SE_RAND_GETRANDOM + ssize_t ret = getrandom((void *)(&(prng->seed[0])), SE_PRNG_SEED_BYTE_COUNT, 0); + se_assert(ret == SE_PRNG_SEED_BYTE_COUNT); +#elif defined(SE_RAND_NRF5) + ret_code_t ret_val = + nrf_crypto_rng_vector_generate(prng->seed, SE_PRNG_SEED_BYTE_COUNT); + if (ret_val != NRF_SUCCESS) + { + printf("Error: Something went wrong with nrf_crypto_rng_vector_generate()\n"); + while (1) + ; + } + se_assert(ret_val == NRF_SUCCESS); +#else + // -- This should only be used for debugging!! + memset(&(prng->seed[0]), 0, SE_PRNG_SEED_BYTE_COUNT); + prng->seed[0] = 1; +#endif +} + +/** +Fills a buffer with random bytes expanded from a SE_PRNG's seed (and counter). +A call to this function updates the prng object's internal counter. + +@param[in] byte_count Number of random bytes to generate +@param[in,out] prng PRNG instance +@param[out] buffer Buffer to store the random bytes +*/ +inline void prng_fill_buffer(size_t byte_count, SE_PRNG *prng, void *buffer) +{ + uint8_t seed_ext[SE_PRNG_SEED_BYTE_COUNT + 8]; + memcpy(&(seed_ext[0]), &(prng->seed[0]), SE_PRNG_SEED_BYTE_COUNT); + memcpy(&(seed_ext[SE_PRNG_SEED_BYTE_COUNT]), &(prng->counter), 8); + shake256(buffer, byte_count, &(seed_ext[0]), SE_PRNG_SEED_BYTE_COUNT + 8); + prng->counter++; + if (prng->counter == 0) // overflow! + { + printf("PRNG counter overflowed."); + printf("Re-randomizing seed and resetting counter to 0.\n"); + prng_randomize_reset(prng, NULL); + } +} + +/** +Clears the values (both seed and counter) of a prng instance to 0. +Clears the bit that ties prng to a custom seed (so next prng_randomize_reset *will* +generate a random seed) + +@param[in,out] prng PRNG instance to clear +*/ +inline void prng_clear(SE_PRNG *prng) +{ + memset(&(prng->seed[0]), 0, SE_PRNG_SEED_BYTE_COUNT); + prng->counter = 0; +} diff --git a/device/lib/sample.c b/device/lib/sample.c new file mode 100644 index 0000000..c305fa4 --- /dev/null +++ b/device/lib/sample.c @@ -0,0 +1,357 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file sample.c +*/ + +#include "sample.h" + +#include "defines.h" +#include "parameters.h" +// #include "util_print.h" // TODO: Including this breaks things + +#ifdef SE_RAND_NRF5 +#include "nrf_crypto.h" +#endif + +//#define DEBUG_EASYMOD + +// ---------------------------- Randomness ------------------------------ + +#ifdef SE_RAND_NRF5 +void se_randomness_init() +{ + // -- Initialize crypto library. + ret_code_t ret_val = nrf_crypto_init(); + if (ret_val != NRF_SUCCESS) + { + printf("Error: Something went wrong with nrf_crypto_init\n"); + while (1) + ; + } + se_assert(ret_val == NRF_SUCCESS); +} +#endif + +// ----------------------------- Uniform --------------------------------- + +void sample_poly_uniform(const Parms *parms, SE_PRNG *prng, ZZ *poly) +{ + PolySizeType n = parms->coeff_count; + const Modulus *q = parms->curr_modulus; + + // -- We sample numbers up to 2^32-1 + ZZ max_random = (ZZ)0xFFFFFFFFUL; + ZZ max_multiple = max_random - barrett_reduce_32input_32modulus(max_random, q) - 1; + + prng_fill_buffer(n * sizeof(ZZ), prng, (void *)poly); + + for (size_t i = 0; i < n; i++) + { + // -- Rejection sampling + ZZ rand_val = poly[i]; + while (rand_val >= max_multiple) { prng_fill_buffer(sizeof(ZZ), prng, (void *)&rand_val); } + poly[i] = barrett_reduce_32input_32modulus(rand_val, q); + } +} + +// ---------------------------- Ternary --------------------------------- + +void set_small_poly_idx(size_t idx, uint8_t val_in, ZZ *poly) +{ + se_assert(val_in <= 2); + + // -- This isn't really necessary if inputs are correct + val_in &= 0x3; + + // -- Every byte can hold 4 samples + uint8_t *poly_small = (uint8_t *)(&(poly[0])); + size_t byte_num = idx / 4; + size_t byte_pos = idx % 4; // 0, 1, 2, or 3 + + uint8_t byte_in = poly_small[byte_num]; + uint8_t get_mask = (uint8_t)(~(0x3 << (6 - byte_pos * 2))); + uint8_t old_val = byte_in & get_mask; + uint8_t new_val = (uint8_t)(old_val | (val_in << (6 - byte_pos * 2))); + + // -- Debugging + // printf("idx: %zu, val_in: %"PRIu8"\n", idx, val_in); + // printf("byte_num: %zu, byte_pos: %zu\n", byte_num, byte_pos); + + poly_small[byte_num] = new_val; + + // -- Debugging + // uint8_t byte_out = poly_small[byte_num]; + // printf("b_in: 0x%x, b_out: 0x%x\n\n", byte_in, byte_out); +} + +uint8_t get_small_poly_idx(const ZZ *poly, size_t idx) +{ + const uint8_t *poly_small = (uint8_t *)(&(poly[0])); + size_t byte_num = idx / 4; + size_t byte_pos = idx % 4; // 0, 1, 2, or 3 + uint8_t byte_val = poly_small[byte_num]; + return (byte_val >> (6 - byte_pos * 2)) & 0x3; +} + +ZZ get_small_poly_idx_expanded(const ZZ *poly, size_t idx, ZZ q) +{ + uint8_t val = get_small_poly_idx(poly, idx); + // print_zz("val", val); // Debugging + se_assert(val <= 2); +#ifdef DEBUG_EASYMOD + // -- If debugging, want an easy mapping: 0 -> 0, 1 -> 1, 2 -> q-1 + return (val < 2) ? val : (q - 1); +#else + // -- Mapping: 0 -> q-1, 1 -> 0, 2 -> 1 (see explanation below) + // return (!val) ? (q-1) : (ZZ)(val - 1); + return val + ((-(ZZ)(val == 0)) & q) - 1; +#endif +} + +void expand_poly_ternary(const ZZ *src, const Parms *parms, ZZ *dest) +{ + PolySizeType n = parms->coeff_count; + ZZ q = parms->curr_modulus->value; + + // -- Debugging + // print_poly("src", src, n); + + // -- Fill in back first so we don't overwrite any values + // -- Need to be careful with overflow and backwards loops + for (size_t i = n; i > 0; i--) + { + ZZ val = get_small_poly_idx_expanded(src, i - 1, q); + dest[i - 1] = val; + } +} + +void expand_poly_ternary_inpl(ZZ *poly, const Parms *parms) +{ + expand_poly_ternary(poly, parms, poly); +} + +void convert_poly_ternary(const ZZ *src, const Parms *parms, ZZ *dest) +{ + PolySizeType n = parms->coeff_count; + ZZ q_m1 = parms->curr_modulus->value - 1; + + if (src != dest) memcpy(dest, src, n * sizeof(ZZ)); + for (size_t i = 0; i < n; i++) + { + if (src[i] > 1) dest[i] = q_m1; + } +} + +void convert_poly_ternary_inpl(ZZ *poly, const Parms *parms) +{ + convert_poly_ternary(poly, parms, poly); +} + +void sample_poly_ternary(const Parms *parms, SE_PRNG *prng, ZZ *poly) +{ + PolySizeType n = parms->coeff_count; + ZZ q = parms->curr_modulus->value; + + // -- Follow same format as sample_poly_uniform, but with modulus of 3 + // Uniform over [-1, 1] ---> Uniform over [0, 2] + // We know that max_random = 0xFFFF_FFFFULL = 0 (mod 3) + // So, max_multiple = max_random - (max_random % 3) - 1 + // = max_random - 1 + // = 0xFFFF_FFFEULL + ZZ max_multiple = (ZZ)0xFFFFFFFEUL; + + prng_fill_buffer(n * sizeof(ZZ), prng, (void *)poly); + + for (size_t i = 0; i < n; i++) + { + // -- Rejection sampling + ZZ rand_val = poly[i]; + while (rand_val >= max_multiple) { prng_fill_buffer(sizeof(ZZ), prng, (void *)&rand_val); } +#ifdef DEBUG_EASYMOD + // -- If debugging, want an easy mapping: 0 -> 0, 1 -> 1, 2 -> q-1 + // We also don't need constant-time + ZZ rand_ternary = rand_val % 3; + poly[i] = (rand_ternary > 1) ? q - 1 : rand_ternary; +#else + // -- If not debugging, we want a constant time version of mod 3. + // This leads to a mapping of: 0 -> q-1, 1 -> 0, 2 -> 1 + // This mapping matches the mapping in SEAL + uint8_t rand_ternary = mod3_zzinput(rand_val); + poly[i] = rand_ternary + ((-(ZZ)(rand_val == 0)) & q) - 1; +#endif + } +} + +/* +void sample_small_poly_ternary_prng_k(size_t k, PolySizeType n, SE_PRNG *prng, ZZ *poly) +{ + se_assert(k <= n); + se_assert(prng && poly); + uint8_t max_multiple = (uint8_t)(0xFE); + for (size_t j = 0; j < n; j += k) + { + uint8_t buffer[k]; + prng_fill_buffer(k, prng, &(buffer[0])); + + size_t i_stop = ((j + k - 1) < n) ? k : (n - j); + + for (size_t i = 0; i < i_stop; i++) + { + uint8_t rand_val = buffer[i]; + while (rand_val >= max_multiple) + { + prng_fill_buffer(1, prng, (void *)&rand_val); + } +#ifdef DEBUG_EASYMOD + uint8_t rand_ternary = rand_val % 3; +#else + uint8_t rand_ternary = mod3_uint8input(rand_val); +#endif + set_small_poly_idx(i + j, rand_ternary, poly); + } + } +} +*/ + +void sample_small_poly_ternary_prng_96(PolySizeType n, SE_PRNG *prng, ZZ *poly) +{ + se_assert(96 <= n); + se_assert(prng && poly); + uint8_t max_multiple = (uint8_t)(0xFE); + for (size_t j = 0; j < n; j += 96) + { + uint8_t buffer[96]; + prng_fill_buffer(96, prng, &(buffer[0])); + + size_t i_stop = ((j + 95) < n) ? 96 : (n - j); + + for (size_t i = 0; i < i_stop; i++) + { + uint8_t rand_val = buffer[i]; + while (rand_val >= max_multiple) { prng_fill_buffer(1, prng, (void *)&rand_val); } +#ifdef DEBUG_EASYMOD + uint8_t rand_ternary = rand_val % 3; +#else + uint8_t rand_ternary = mod3_uint8input(rand_val); +#endif + set_small_poly_idx(i + j, rand_ternary, poly); + } + } +} + +// --------------------------- Centered Binomial ----------------------------- +// -- A binomial distribution with parameter k has standard deviation \sqrt{k/2}. +// Each sample requires 2k random bits computed as \sum_{i=0}^{k-1}{a_i - b_i} +// where a_i and b_i are two random bits. Alternatively, it can be represented +// as the difference between the Hamming weights of two k-bit uniform random +// values. The HE security standard (homomorphicencryption.org) suggests a +// standard deviation value of >= 3.2 This translates to k >= 21. We choose k=21, +// resulting in a standard deviation value of 3.24. +// +// When this is used, noise should be interpreted modulo the modulus. +// That is, when noise is negative, must convert it to modulus - |noise| instead. +// However, we never actually need to reduce the error polynomial in this library. + +/** +Helper function to return the hamming weight of a byte value + +@param[in] value A byte-sized value +@returns Hamming weight of value +*/ +static inline int8_t hamming_weight(uint8_t value) +{ + int t = (int)value; + t -= (t >> 1) & 0x55; + t = (t & 0x33) + ((t >> 2) & 0x33); + return (int8_t)(t + (t >> 4)) & 0x0F; +} + +/** +Helper function to get the cbd sample (from a cbd w/ stddev 3.24) from 6 random bytes +(non-modulo reduced) + +@param[in] x Six random bytes +@returns A (non-modulo-reduced) cbd sample +*/ +static inline int8_t get_cbd_val(uint8_t x[6]) +{ + x[2] &= 0x1F; + x[5] &= 0x1F; + return (int8_t)(hamming_weight(x[0]) + hamming_weight(x[1]) + hamming_weight(x[2]) - + hamming_weight(x[3]) - hamming_weight(x[4]) - hamming_weight(x[5])); +} + +void sample_poly_cbd_generic(PolySizeType n, SE_PRNG *prng, int8_t *poly) +{ + // -- Every 42 bits (6 bytes) generates a sample + uint8_t buffer[6]; + for (size_t i = 0; i < n; i++) + { + prng_fill_buffer(6, prng, (void *)buffer); + poly[i] = get_cbd_val(buffer); + } +} + +/* +void sample_poly_cbd_generic_prng_k(size_t k, PolySizeType n, SE_PRNG *prng, int8_t *poly) +{ + for (size_t j = 0; j < n; j += k) + { + // -- Every 42 bits (6 bytes) generates a sample + uint8_t buffer[6 * k]; // Generate k samples at once + prng_fill_buffer(6 * k, prng, (void *)buffer); + + for (size_t i = 0; i < k; i++) { poly[i + j] = get_cbd_val(buffer + 6 * i); } + } +} +*/ + +void sample_poly_cbd_generic_prng_16(PolySizeType n, SE_PRNG *prng, int8_t *poly) +{ + for (size_t j = 0; j < n; j += 16) + { + // -- Every 42 bits (6 bytes) generates a sample + uint8_t buffer[96]; // Generate 16 samples at once (6 * 16 = 96 bytes) + prng_fill_buffer(96, prng, (void *)buffer); + + for (size_t i = 0; i < 16; i++) { poly[i + j] = get_cbd_val(buffer + 6 * i); } + } +} + +void sample_add_poly_cbd_generic_inpl(int64_t *poly, PolySizeType n, SE_PRNG *prng) +{ + // -- Every 42 bits (6 bytes) generates a sample + uint8_t buffer[6]; + for (size_t i = 0; i < n; i++) + { + prng_fill_buffer(6, prng, (void *)buffer); + poly[i] += get_cbd_val(buffer); + } +} + +/* +void sample_add_poly_cbd_generic_inpl_prng_k(int64_t *poly, size_t k, PolySizeType n, + SE_PRNG *prng) +{ + for (size_t j = 0; j < n; j += k) + { + // -- Every 42 bits (6 bytes) generates a sample + uint8_t buffer[6 * k]; // Generate k samples at once + prng_fill_buffer(6 * k, prng, (void *)buffer); + for (size_t i = 0; i < k; i++) { poly[i + j] += get_cbd_val(buffer + 6 * i); } + } +} +*/ + +void sample_add_poly_cbd_generic_inpl_prng_16(int64_t *poly, PolySizeType n, SE_PRNG *prng) +{ + for (size_t j = 0; j < n; j += 16) + { + // -- Every 42 bits (6 bytes) generates a sample + uint8_t buffer[96]; // Generate 16 samples at once (6 * 16 = 96 bytes) + prng_fill_buffer(96, prng, (void *)buffer); + for (size_t i = 0; i < 16; i++) { poly[i + j] += get_cbd_val(buffer + 6 * i); } + } +} diff --git a/device/lib/sample.h b/device/lib/sample.h new file mode 100644 index 0000000..c07c30d --- /dev/null +++ b/device/lib/sample.h @@ -0,0 +1,356 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file sample.h + +Functions to sample a value or polynomial from a uniform, uniform ternary or centered +binomial distribution. +*/ + +#pragma once + +#include // uint64_t + +#include "defines.h" +#include "parameters.h" +#include "rng.h" +#include "stdio.h" + +#ifdef SE_RAND_GETRANDOM +#include // getrandom +#elif defined(SE_RAND_NRF5) +#include "nrf_crypto.h" +#include "nrf_crypto_rng.h" +#endif + +// ---------------------------------------------- +// Randomness +// ---------------------------------------------- +#ifdef SE_ON_NRF5 +/** Enables randomness on the NRF5. Must be called once on the NRF5. */ +void se_randomness_init(void); +#else +/** Empty stub. Does nothing. */ +#define se_randomness_init() {}; +#endif + +/** +Samples sizeof(ZZ) bytes from the udev device using getrandom(). + +@returns A random value of type ZZ +*/ +static inline ZZ random_zz(void) +{ + ZZ result; +#ifdef SE_RAND_GETRANDOM + ssize_t ret = getrandom((void *)&result, sizeof(result), 0); + se_assert(ret == sizeof(result)); +#elif defined(SE_RAND_NRF5) + ret_code_t ret_val = nrf_crypto_rng_vector_generate((uint8_t *)&result, sizeof(ZZ)); + // printf("val: %d\n", result); + // if (result < 1) printf("something possibly went wrong...\n"); + if (ret_val != NRF_SUCCESS) + { + printf("Error: Something went wrong with nrf_crypto_rng_vector_generate()\n"); + while (1) + ; + } + se_assert(ret_val == NRF_SUCCESS); +#else + // -- NOTE: THIS IS A CONST (NON RANDOM) NUMBER! + // Should only be used for debug + result = 0x12345678; +#endif + return result; +} + +/** +Samples 1 byte from the udev device using getrandom(). + +@returns A random uint8 +*/ +static inline uint8_t random_uint8(void) +{ + uint8_t result; +#ifdef SE_RAND_GETRANDOM + ssize_t ret = getrandom((void *)&result, sizeof(result), 0); + se_assert(ret == sizeof(result)); +#elif defined(SE_RAND_NRF5) + ret_code_t ret_val = nrf_crypto_rng_vector_generate(&result, 1); + if (ret_val != NRF_SUCCESS) + { + printf("Error: Something went wrong with nrf_crypto_rng_vector_generate()\n"); + while (1) + ; + } + se_assert(ret_val == NRF_SUCCESS); +#else + // -- NOTE: THIS IS A CONST (NON RANDOM) NUMBER! + // Should only be used for debug + result = 0x7; +#endif + return result; +} + +/** +Samples 8 bytes from the udev device using getrandom(). + +@returns A random double +*/ +static inline double random_double(void) +{ + double result; +#ifdef SE_RAND_GETRANDOM + ssize_t ret = getrandom((void *)&result, sizeof(result), 0); + se_assert(ret == sizeof(result)); +#elif defined(SE_RAND_NRF5) + ret_code_t ret_val = nrf_crypto_rng_vector_generate((uint8_t *)&result, sizeof(double)); + if (ret_val != NRF_SUCCESS) + { + printf("Error: Something went wrong with nrf_crypto_rng_vector_generate()\n"); + while (1) + ; + } + se_assert(ret_val == NRF_SUCCESS); +#else + // -- NOTE: THIS IS A CONST (NON RANDOM) NUMBER! + // Should only be used for debug + result = 1234; +#endif + return result; +} + +// ---------------------------------------------------- +// Uniform +// ---------------------------------------------------- +/** +Samples a polynomial with coefficients from the uniform distribution over [0, q). +Used to sample the second element of a ciphertext for symmetric encryption. +Internally samples from the udev device using getrandom(). Uses rejection sampling. + +Space req: 'poly' must have space for n ZZ elements. + +@param[in] parms Parameters set by ckks_setup +@param[in,out] prng A prng instance to generate the randomness +@param[out] poly The sampled polynomial. +*/ +void sample_poly_uniform(const Parms *parms, SE_PRNG *prng, ZZ *poly); + +// ---------------------------------------------------- +// Ternary +// ---------------------------------------------------- +/** +Sets the value of the (idx)-th coefficient of 'poly' directly in small (compressed) form. + +Note: val_in should be either 0, 1, or 2 (which represent -1, 0, and 1, in some order) + +@param[in] idx Requested coefficient to set +@param[in] val_in Value to write to (idx)-th coefficient of 'poly' +@param[out] poly A ternary polynomial in small (compressed) form +*/ +void set_small_poly_idx(size_t idx, uint8_t val_in, ZZ *poly); + +/** +Returns the value of the (idx)-th coefficient of 'poly', leaving 'poly' and result in +small form. + +@param[in] poly A ternary polynomial in small (compressed) form +@param[in] idx Requested coefficient to read +@returns Value of requested coefficient of 'poly' in small form +*/ +uint8_t get_small_poly_idx(const ZZ *poly, size_t idx); + +/** +Returns the value of the (idx)-th coefficient of 'poly', leaving 'poly' in small form. + +@param[in] poly A ternary polynomial in small (compressed) form +@param[in] idx Requested coefficient to read +@param[in] q Modulus value +@returns Value of requested coefficient of 'poly' in expanded form +*/ +ZZ get_small_poly_idx_expanded(const ZZ *poly, size_t idx, ZZ q); + +/** +Expands a small (compressed) ternary polynomial w.r.t. the current modulus and places the +result in `dest'. 'dest' and 'src' memory may share the same starting address, since +expansion occurs backwards (see: 'in place' version below). + +Note: This function does not keep track of 'small_u' or 'small_s' flag. These flags must +be tracked/updated outside of this function if necessary. Space req: 'dest' must have +space for n ZZ elements. + +@param[in] src Source polynomial in small form +@param[in] parms Parameters instance +@param[out] dest Destination polynomial in expanded form. +*/ +void expand_poly_ternary(const ZZ *src, const Parms *parms, ZZ *dest); + +/** +Expands a small (compressed) ternary polynomial in place w.r.t. the current modulus. + +Note: This function does not keep track of 'small_u' or 'small_s' flag. These flags must +be tracked/updated outside of this function if necessary. Space req: 'poly' must have +space for n ZZ elements. + +@param[in,out] poly In: Source polynomial in small form; Out: result polynomial in +expanded form +@param[in] parms Parameters instance +*/ +void expand_poly_ternary_inpl(ZZ *poly, const Parms *parms); + +/** +Reduces an (expanded) ternary polynomial w.r.t. the current modulus and places the result +in `dest'. 'dest' and 'src' memory may share the same starting address (see: 'in place' +version below). + +Space req: 'dest' must have space for n ZZ elements. + +@param[in] src Source polynomial (reduced modulo previous modulus) +@param[in] parms Parameters instance +@param[out] dest Destination polynomial (reduced modulo current modulus) +*/ +void convert_poly_ternary(const ZZ *src, const Parms *parms, ZZ *dest); + +/** +In-place reduces an (expanded) ternary polynomial w.r.t. the current modulus. + +Space req: 'poly' must have space for n ZZ elements. + +@param[in,out] poly In: Polynomial to convert; Out: converted polynomial +@param[in] parms Parameters instance +*/ +void convert_poly_ternary_inpl(ZZ *poly, const Parms *parms); + +/** +Samples an (expanded) polynomial from the uniform ternary distribution over {-q-1, 0, 1}, +where q is the value of the current modulus. This function is mainly useful for testing, +since most of the time we would want to sample the polynomial in compressed form (see: +sample_small_poly_ternary). PRNG instance is used to generate randomness for 1 coefficient +at a time. + +Space req: 'poly' must have space for n ZZ values + +@param[in] parms Parameters instance +@param[in,out] prng PRNG instance (counter will be updated) +@param[out] poly Output sampled polynomial +*/ +void sample_poly_ternary(const Parms *parms, SE_PRNG *prng, ZZ *poly); + +/** +Samples a small (compressed) polynomial from the uniform ternary distribution over {-q-1, +0, 1}, where q is the value of the current modulus. Note that this does *not* assume the +input buffer is in small form. It assumes that the input buffer has space for n uint8_t +values. PRNG instance is used to generate randomness for 1 coefficient at a time. + +Space req: 'poly' must have space for n uin8_t values + +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (counter will be updated) +@param[out] poly Result sampled polynomial +*/ +void sample_small_poly_ternary(PolySizeType n, SE_PRNG *prng, ZZ *poly); + +/** +Samples a small (compressed) polynomial from the uniform ternary distribution over {-q-1, +0, 1}, where q is the value of the current modulus, while leaving the polynomial in +compressed form. PRNG instance is used to generate randomness for k coefficients at a time +(prior to any required rejection re-sampling, which occurs 1 coefficient at a time). + +Space req: 'poly' must have space for n uin8_t values + +@param[in] k Number of coefficients to generate randomness for at once +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (counter will be updated) +@param[out] poly Result sampled polynomial +*/ +// void sample_small_poly_ternary_prng_k(size_t k, PolySizeType n, SE_PRNG *prng, ZZ *poly); + +/** +Samples a small (compressed) polynomial from the uniform ternary distribution over {-q-1, +0, 1}, where q is the value of the current modulus, while leaving the polynomial in +compressed form. PRNG instance is used to generate randomness for 96 coefficients at a +time (prior to any required rejection re-sampling, which occurs 1 coefficient at a time). + +Space req: 'poly' must have space for n uin8_t values + +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (counter will be updated) +@param[out] poly Result sampled polynomial +*/ +void sample_small_poly_ternary_prng_96(PolySizeType n, SE_PRNG *prng, ZZ *poly); + +// ------------------------------------------------------ +// Centered Binomial +// ------------------------------------------------------ + +/** +Samples coefficients of a polynomial from a centered binomial distribution +(non-modulo-reduced). PRNG instance is used to sample 1 coefficient at a time. + +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (will update counter) +@param[out] poly Result sampled polynomial +*/ +void sample_poly_cbd_generic(PolySizeType n, SE_PRNG *prng, int8_t *poly); + +/** +Samples coefficients of a polynomial from a centered binomial distribution +(non-modulo-reduced). PRNG instance is used to generate randomness for k coefficients at a +time. + +@param[in] k Number of coefficients to generate randomness for at once +@param[in] n Total number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (will update counter) +@param[out] poly Result sampled polynomial +*/ +// void sample_poly_cbd_generic_prng_k(size_t k, PolySizeType n, SE_PRNG *prng, int8_t *poly); + +/** +Samples coefficients of a polynomial from a centered binomial distribution +(non-modulo-reduced). PRNG instance is used to generate randomness for 16 coefficients at +a time. + +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (will update counter) +@param[out] poly Result sampled polynomial +*/ +void sample_poly_cbd_generic_prng_16(PolySizeType n, SE_PRNG *prng, int8_t *poly); + +/** +Samples coefficients of a polynomial from a centered binomial distribution +(non-modulo-reduced) and adds them in-place to the polynomial 'poly'. PRNG instance is +used to generate randomness for 1 coefficient at a time. + +@param[in, out] poly In: Initial polynomial; Out: Initial polynomial + cbd error +polynomial +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (will update counter) +*/ +void sample_add_poly_cbd_generic_inpl(int64_t *poly, PolySizeType n, SE_PRNG *prng); + +/** +Samples coefficients of a polynomial from a centered binomial distribution +(non-modulo-reduced) and adds them in-place to the polynomial 'poly'. PRNG instance is +used to generate randomness for k coefficients at a time. + +@param[in, out] poly In: Initial polynomial; Out: Initial polynomial + cbd error +polynomial +@param[in] k Number of coefficients to generate randomness for at once +@param[in] n Total number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (will update counter) +*/ +// void sample_add_poly_cbd_generic_inpl_prng_k(int64_t *poly, size_t k, PolySizeType n, SE_PRNG +// *prng); + +/** +Samples coefficients of a polynomial from a centered binomial distribution +(non-modulo-reduced) and adds them in-place to the polynomial 'poly'. PRNG instance is +used to generate randomness for 16 coefficients at a time. + +@param[in, out] poly In: Initial polynomial; Out: Initial polynomial + cbd error +polynomial +@param[in] n Number of coefficients to sample (e.g. degree of 'poly') +@param[in,out] prng PRNG instance (will update counter) +*/ +void sample_add_poly_cbd_generic_inpl_prng_16(int64_t *poly, PolySizeType n, SE_PRNG *prng); diff --git a/device/lib/sdk_config.h b/device/lib/sdk_config.h new file mode 100644 index 0000000..e796212 --- /dev/null +++ b/device/lib/sdk_config.h @@ -0,0 +1,5009 @@ +/* +Origin: https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download +*/ + +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * 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, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, 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 Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be + * reverse engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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. + * + */ + +#ifndef SDK_CONFIG_H +#define SDK_CONFIG_H +// <<< Use Configuration Wizard in Context Menu >>>\n +#ifdef USE_APP_CONFIG +#include "app_config.h" +#endif +// nRF_Crypto + +//========================================================== +// NRF_CRYPTO_ENABLED - nrf_crypto - Cryptography library. +//========================================================== +#ifndef NRF_CRYPTO_ENABLED +#define NRF_CRYPTO_ENABLED 1 +#endif +// NRF_CRYPTO_ALLOCATOR - Memory allocator + +// Choose memory allocator used by nrf_crypto. Default is alloca if possible +// or nrf_malloc otherwise. If 'User macros' are selected, the user has to +// create 'nrf_crypto_allocator.h' file that contains NRF_CRYPTO_ALLOC, +// NRF_CRYPTO_FREE, and NRF_CRYPTO_ALLOC_ON_STACK. <0=> Default <1=> User macros +// <2=> On stack (alloca) +// <3=> C dynamic memory (malloc) +// <4=> SDK Memory Manager (nrf_malloc) + +#ifndef NRF_CRYPTO_ALLOCATOR +#define NRF_CRYPTO_ALLOCATOR 0 +#endif + +// NRF_CRYPTO_BACKEND_CC310_BL_ENABLED - Enable the ARM Cryptocell CC310 +// reduced backend. + +// The CC310 hardware-accelerated cryptography backend with reduced +// functionality and footprint (only available on nRF52840). +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_BL_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP224R1_ENABLED - Enable the secp224r1 +// elliptic curve support using CC310_BL. + +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP224R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP224R1_ENABLED 0 +#endif + +// NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP256R1_ENABLED - Enable the secp256r1 +// elliptic curve support using CC310_BL. + +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP256R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_BL_HASH_SHA256_ENABLED - CC310_BL SHA-256 hash +// functionality. + +// CC310_BL backend implementation for hardware-accelerated SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_HASH_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_BL_HASH_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED - +// nrf_cc310_bl buffers to RAM before running hash operation + +// Enabling this makes hashing of addresses in FLASH range possible. Size of +// buffer allocated for hashing is set by +// NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE + +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED 0 +#endif + +// NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE - nrf_cc310_bl +// hash outputs digests in little endian Makes the nrf_cc310_bl hash +// functions output digests in little endian format. Only for use in nRF SDK +// DFU! + +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE +#define NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE 4096 +#endif + +// NRF_CRYPTO_BACKEND_CC310_BL_INTERRUPTS_ENABLED - Enable Interrupts while +// support using CC310 bl. + +// Select a library version compatible with the configuration. When +// interrupts are disable, a version named _noint must be used + +#ifndef NRF_CRYPTO_BACKEND_CC310_BL_INTERRUPTS_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_BL_INTERRUPTS_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_CC310_ENABLED - Enable the ARM Cryptocell CC310 +// backend. + +// The CC310 hardware-accelerated cryptography backend (only available on +// nRF52840). +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_CC310_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ENABLED 1 +#endif +// NRF_CRYPTO_BACKEND_CC310_AES_CBC_ENABLED - Enable the AES CBC mode using +// CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_CBC_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_CBC_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_AES_CTR_ENABLED - Enable the AES CTR mode using +// CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_CTR_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_CTR_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_AES_ECB_ENABLED - Enable the AES ECB mode using +// CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_ECB_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_ECB_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_AES_CBC_MAC_ENABLED - Enable the AES CBC_MAC +// mode using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_CBC_MAC_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_CBC_MAC_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_AES_CMAC_ENABLED - Enable the AES CMAC mode +// using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_CMAC_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_CMAC_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_AES_CCM_ENABLED - Enable the AES CCM mode using +// CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_CCM_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_CCM_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_AES_CCM_STAR_ENABLED - Enable the AES CCM* mode +// using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_AES_CCM_STAR_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_AES_CCM_STAR_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_CHACHA_POLY_ENABLED - Enable the CHACHA-POLY +// mode using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_CHACHA_POLY_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_CHACHA_POLY_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R1_ENABLED - Enable the secp160r1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R2_ENABLED - Enable the secp160r2 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R2_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R2_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP192R1_ENABLED - Enable the secp192r1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP192R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP192R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP224R1_ENABLED - Enable the secp224r1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP224R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP224R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP256R1_ENABLED - Enable the secp256r1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP256R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP384R1_ENABLED - Enable the secp384r1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP384R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP384R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP521R1_ENABLED - Enable the secp521r1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP521R1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP521R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP160K1_ENABLED - Enable the secp160k1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP160K1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP160K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP192K1_ENABLED - Enable the secp192k1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP192K1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP192K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP224K1_ENABLED - Enable the secp224k1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP224K1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP224K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_SECP256K1_ENABLED - Enable the secp256k1 +// elliptic curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP256K1_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_SECP256K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_CURVE25519_ENABLED - Enable the Curve25519 +// curve support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_CURVE25519_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_CURVE25519_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_ECC_ED25519_ENABLED - Enable the Ed25519 curve +// support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_ECC_ED25519_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_ECC_ED25519_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_HASH_SHA256_ENABLED - CC310 SHA-256 hash +// functionality. + +// CC310 backend implementation for hardware-accelerated SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_CC310_HASH_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_HASH_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_HASH_SHA512_ENABLED - CC310 SHA-512 hash +// functionality + +// CC310 backend implementation for SHA-512 (in software). + +#ifndef NRF_CRYPTO_BACKEND_CC310_HASH_SHA512_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_HASH_SHA512_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_HMAC_SHA256_ENABLED - CC310 HMAC using SHA-256 + +// CC310 backend implementation for HMAC using hardware-accelerated SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_CC310_HMAC_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_HMAC_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_HMAC_SHA512_ENABLED - CC310 HMAC using SHA-512 + +// CC310 backend implementation for HMAC using SHA-512 (in software). + +#ifndef NRF_CRYPTO_BACKEND_CC310_HMAC_SHA512_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_HMAC_SHA512_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_RNG_ENABLED - Enable RNG support using CC310. + +#ifndef NRF_CRYPTO_BACKEND_CC310_RNG_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_RNG_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_CC310_INTERRUPTS_ENABLED - Enable Interrupts while +// support using CC310. + +// Select a library version compatible with the configuration. When +// interrupts are disable, a version named _noint must be used + +#ifndef NRF_CRYPTO_BACKEND_CC310_INTERRUPTS_ENABLED +#define NRF_CRYPTO_BACKEND_CC310_INTERRUPTS_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_CIFRA_ENABLED - Enable the Cifra backend. +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_CIFRA_ENABLED +#define NRF_CRYPTO_BACKEND_CIFRA_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_CIFRA_AES_EAX_ENABLED - Enable the AES EAX mode using +// Cifra. + +#ifndef NRF_CRYPTO_BACKEND_CIFRA_AES_EAX_ENABLED +#define NRF_CRYPTO_BACKEND_CIFRA_AES_EAX_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_MBEDTLS_ENABLED - Enable the mbed TLS backend. +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_ENABLED - Enable the AES CBC mode +// mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_CTR_ENABLED - Enable the AES CTR mode +// using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CTR_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CTR_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_CFB_ENABLED - Enable the AES CFB mode +// using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CFB_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CFB_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_ECB_ENABLED - Enable the AES ECB mode +// using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_ECB_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_ECB_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_MAC_ENABLED - Enable the AES CBC MAC +// mode using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_MAC_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_MAC_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_CMAC_ENABLED - Enable the AES CMAC mode +// using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CMAC_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CMAC_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_CCM_ENABLED - Enable the AES CCM mode +// using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CCM_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CCM_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_AES_GCM_ENABLED - Enable the AES GCM mode +// using mbed TLS. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_GCM_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_AES_GCM_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192R1_ENABLED - Enable secp192r1 +// (NIST 192-bit) curve + +// Enable this setting if you need secp192r1 (NIST 192-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224R1_ENABLED - Enable secp224r1 +// (NIST 224-bit) curve + +// Enable this setting if you need secp224r1 (NIST 224-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256R1_ENABLED - Enable secp256r1 +// (NIST 256-bit) curve + +// Enable this setting if you need secp256r1 (NIST 256-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP384R1_ENABLED - Enable secp384r1 +// (NIST 384-bit) curve + +// Enable this setting if you need secp384r1 (NIST 384-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP384R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP384R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP521R1_ENABLED - Enable secp521r1 +// (NIST 521-bit) curve + +// Enable this setting if you need secp521r1 (NIST 521-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP521R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP521R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192K1_ENABLED - Enable secp192k1 +// (Koblitz 192-bit) curve + +// Enable this setting if you need secp192k1 (Koblitz 192-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192K1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224K1_ENABLED - Enable secp224k1 +// (Koblitz 224-bit) curve + +// Enable this setting if you need secp224k1 (Koblitz 224-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224K1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256K1_ENABLED - Enable secp256k1 +// (Koblitz 256-bit) curve + +// Enable this setting if you need secp256k1 (Koblitz 256-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256K1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256K1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP256R1_ENABLED - Enable bp256r1 +// (Brainpool 256-bit) curve + +// Enable this setting if you need bp256r1 (Brainpool 256-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP256R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP384R1_ENABLED - Enable bp384r1 +// (Brainpool 384-bit) curve + +// Enable this setting if you need bp384r1 (Brainpool 384-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP384R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP384R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP512R1_ENABLED - Enable bp512r1 +// (Brainpool 512-bit) curve + +// Enable this setting if you need bp512r1 (Brainpool 512-bit) support using +// MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP512R1_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP512R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_ECC_CURVE25519_ENABLED - Enable Curve25519 +// curve + +// Enable this setting if you need Curve25519 support using MBEDTLS + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_CURVE25519_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_CURVE25519_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA256_ENABLED - Enable mbed TLS SHA-256 +// hash functionality. + +// mbed TLS backend implementation for SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA512_ENABLED - Enable mbed TLS SHA-512 +// hash functionality. + +// mbed TLS backend implementation for SHA-512. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA512_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA512_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA256_ENABLED - Enable mbed TLS HMAC +// using SHA-256. + +// mbed TLS backend implementation for HMAC using SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA512_ENABLED - Enable mbed TLS HMAC +// using SHA-512. + +// mbed TLS backend implementation for HMAC using SHA-512. + +#ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA512_ENABLED +#define NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA512_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED - Enable the micro-ecc backend. +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED +#define NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP192R1_ENABLED - Enable secp192r1 +// (NIST 192-bit) curve + +// Enable this setting if you need secp192r1 (NIST 192-bit) support using +// micro-ecc + +#ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP192R1_ENABLED +#define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP192R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP224R1_ENABLED - Enable secp224r1 +// (NIST 224-bit) curve + +// Enable this setting if you need secp224r1 (NIST 224-bit) support using +// micro-ecc + +#ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP224R1_ENABLED +#define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP224R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256R1_ENABLED - Enable secp256r1 +// (NIST 256-bit) curve + +// Enable this setting if you need secp256r1 (NIST 256-bit) support using +// micro-ecc + +#ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256K1_ENABLED - Enable secp256k1 +// (Koblitz 256-bit) curve + +// Enable this setting if you need secp256k1 (Koblitz 256-bit) support using +// micro-ecc + +#ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256K1_ENABLED +#define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256K1_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED - Enable the nRF HW RNG backend. + +// The nRF HW backend provide access to RNG peripheral in nRF5x devices. +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED +#define NRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_NRF_HW_RNG_MBEDTLS_CTR_DRBG_ENABLED - Enable mbed TLS +// CTR-DRBG algorithm. + +// Enable mbed TLS CTR-DRBG standardized by NIST (NIST SP 800-90A Rev. 1). +// The nRF HW RNG is used as an entropy source for seeding. + +#ifndef NRF_CRYPTO_BACKEND_NRF_HW_RNG_MBEDTLS_CTR_DRBG_ENABLED +#define NRF_CRYPTO_BACKEND_NRF_HW_RNG_MBEDTLS_CTR_DRBG_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_NRF_SW_ENABLED - Enable the legacy nRFx sw for crypto. + +// The nRF SW cryptography backend (only used in bootloader context). +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_NRF_SW_ENABLED +#define NRF_CRYPTO_BACKEND_NRF_SW_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_NRF_SW_HASH_SHA256_ENABLED - nRF SW hash backend +// support for SHA-256 + +// The nRF SW backend provide access to nRF SDK legacy hash implementation +// of SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_NRF_SW_HASH_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_NRF_SW_HASH_SHA256_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_OBERON_ENABLED - Enable the Oberon backend + +// The Oberon backend +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_OBERON_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_OBERON_CHACHA_POLY_ENABLED - Enable the CHACHA-POLY +// mode using Oberon. + +#ifndef NRF_CRYPTO_BACKEND_OBERON_CHACHA_POLY_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_CHACHA_POLY_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_ECC_SECP256R1_ENABLED - Enable secp256r1 curve + +// Enable this setting if you need secp256r1 curve support using Oberon +// library + +#ifndef NRF_CRYPTO_BACKEND_OBERON_ECC_SECP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_ECC_SECP256R1_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_ECC_CURVE25519_ENABLED - Enable Curve25519 +// ECDH + +// Enable this setting if you need Curve25519 ECDH support using Oberon +// library + +#ifndef NRF_CRYPTO_BACKEND_OBERON_ECC_CURVE25519_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_ECC_CURVE25519_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_ECC_ED25519_ENABLED - Enable Ed25519 signature +// scheme + +// Enable this setting if you need Ed25519 support using Oberon library + +#ifndef NRF_CRYPTO_BACKEND_OBERON_ECC_ED25519_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_ECC_ED25519_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_HASH_SHA256_ENABLED - Oberon SHA-256 hash +// functionality + +// Oberon backend implementation for SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_OBERON_HASH_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_HASH_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_HASH_SHA512_ENABLED - Oberon SHA-512 hash +// functionality + +// Oberon backend implementation for SHA-512. + +#ifndef NRF_CRYPTO_BACKEND_OBERON_HASH_SHA512_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_HASH_SHA512_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA256_ENABLED - Oberon HMAC using +// SHA-256 + +// Oberon backend implementation for HMAC using SHA-256. + +#ifndef NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA256_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA256_ENABLED 1 +#endif + +// NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA512_ENABLED - Oberon HMAC using +// SHA-512 + +// Oberon backend implementation for HMAC using SHA-512. + +#ifndef NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA512_ENABLED +#define NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA512_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_BACKEND_OPTIGA_ENABLED - Enable the nrf_crypto Optiga Trust X +// backend. + +// Enables the nrf_crypto backend for Optiga Trust X devices. +//========================================================== +#ifndef NRF_CRYPTO_BACKEND_OPTIGA_ENABLED +#define NRF_CRYPTO_BACKEND_OPTIGA_ENABLED 0 +#endif +// NRF_CRYPTO_BACKEND_OPTIGA_RNG_ENABLED - Optiga backend support for RNG + +// The Optiga backend provide external chip RNG. + +#ifndef NRF_CRYPTO_BACKEND_OPTIGA_RNG_ENABLED +#define NRF_CRYPTO_BACKEND_OPTIGA_RNG_ENABLED 0 +#endif + +// NRF_CRYPTO_BACKEND_OPTIGA_ECC_SECP256R1_ENABLED - Optiga backend support +// for ECC secp256r1 + +// The Optiga backend provide external chip ECC using secp256r1. + +#ifndef NRF_CRYPTO_BACKEND_OPTIGA_ECC_SECP256R1_ENABLED +#define NRF_CRYPTO_BACKEND_OPTIGA_ECC_SECP256R1_ENABLED 1 +#endif + +// + +// NRF_CRYPTO_CURVE25519_BIG_ENDIAN_ENABLED - Big-endian byte order in raw +// Curve25519 data + +// Enable big-endian byte order in Curve25519 API, if set to 1. Use +// little-endian, if set to 0. + +#ifndef NRF_CRYPTO_CURVE25519_BIG_ENDIAN_ENABLED +#define NRF_CRYPTO_CURVE25519_BIG_ENDIAN_ENABLED 0 +#endif + +// + +// nrf_crypto_rng - RNG Configuration + +//========================================================== +// NRF_CRYPTO_RNG_STATIC_MEMORY_BUFFERS_ENABLED - Use static memory buffers +// for context and temporary init buffer. + +// Always recommended when using the nRF HW RNG as the context and temporary +// buffers are small. Consider disabling if using the CC310 RNG in a RAM +// constrained application. In this case, memory must be provided to +// nrf_crypto_rng_init, or it can be allocated internally provided that +// NRF_CRYPTO_ALLOCATOR does not allocate memory on the stack. + +#ifndef NRF_CRYPTO_RNG_STATIC_MEMORY_BUFFERS_ENABLED +#define NRF_CRYPTO_RNG_STATIC_MEMORY_BUFFERS_ENABLED 1 +#endif + +// NRF_CRYPTO_RNG_AUTO_INIT_ENABLED - Initialize the RNG module +// automatically when nrf_crypto is initialized. + +// Automatic initialization is only supported with static or internally +// allocated context and temporary memory. + +#ifndef NRF_CRYPTO_RNG_AUTO_INIT_ENABLED +#define NRF_CRYPTO_RNG_AUTO_INIT_ENABLED 1 +#endif + +// +//========================================================== + +// +//========================================================== + +// nRF_Drivers + +//========================================================== +// NRFX_TIMER_ENABLED - nrfx_timer - TIMER periperal driver +//========================================================== +#ifndef NRFX_TIMER_ENABLED +#define NRFX_TIMER_ENABLED 1 +#endif +// NRFX_TIMER0_ENABLED - Enable TIMER0 instance + +#ifndef NRFX_TIMER0_ENABLED +#define NRFX_TIMER0_ENABLED 0 +#endif + +// NRFX_TIMER1_ENABLED - Enable TIMER1 instance + +#ifndef NRFX_TIMER1_ENABLED +#define NRFX_TIMER1_ENABLED 0 +#endif + +// NRFX_TIMER2_ENABLED - Enable TIMER2 instance + +#ifndef NRFX_TIMER2_ENABLED +#define NRFX_TIMER2_ENABLED 0 +#endif + +// NRFX_TIMER3_ENABLED - Enable TIMER3 instance + +#ifndef NRFX_TIMER3_ENABLED +#define NRFX_TIMER3_ENABLED 0 +#endif + +// NRFX_TIMER4_ENABLED - Enable TIMER4 instance + +#ifndef NRFX_TIMER4_ENABLED +#define NRFX_TIMER4_ENABLED 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode + +// <0=> 16 MHz +// <1=> 8 MHz +// <2=> 4 MHz +// <3=> 2 MHz +// <4=> 1 MHz +// <5=> 500 kHz +// <6=> 250 kHz +// <7=> 125 kHz +// <8=> 62.5 kHz +// <9=> 31.25 kHz + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY +#define NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_MODE - Timer mode or operation + +// <0=> Timer +// <1=> Counter + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_MODE +#define NRFX_TIMER_DEFAULT_CONFIG_MODE 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH - Timer counter bit width + +// <0=> 16 bit +// <1=> 8 bit +// <2=> 24 bit +// <3=> 32 bit + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH +#define NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY +#define NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_TIMER_CONFIG_LOG_ENABLED +#define NRFX_TIMER_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_TIMER_CONFIG_LOG_LEVEL +#define NRFX_TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_TIMER_CONFIG_INFO_COLOR +#define NRFX_TIMER_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_TIMER_CONFIG_DEBUG_COLOR +#define NRFX_TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// TIMER_ENABLED - nrf_drv_timer - TIMER periperal driver - legacy layer +//========================================================== +#ifndef TIMER_ENABLED +#define TIMER_ENABLED 1 +#endif +// TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode + +// <0=> 16 MHz +// <1=> 8 MHz +// <2=> 4 MHz +// <3=> 2 MHz +// <4=> 1 MHz +// <5=> 500 kHz +// <6=> 250 kHz +// <7=> 125 kHz +// <8=> 62.5 kHz +// <9=> 31.25 kHz + +#ifndef TIMER_DEFAULT_CONFIG_FREQUENCY +#define TIMER_DEFAULT_CONFIG_FREQUENCY 0 +#endif + +// TIMER_DEFAULT_CONFIG_MODE - Timer mode or operation + +// <0=> Timer +// <1=> Counter + +#ifndef TIMER_DEFAULT_CONFIG_MODE +#define TIMER_DEFAULT_CONFIG_MODE 0 +#endif + +// TIMER_DEFAULT_CONFIG_BIT_WIDTH - Timer counter bit width + +// <0=> 16 bit +// <1=> 8 bit +// <2=> 24 bit +// <3=> 32 bit + +#ifndef TIMER_DEFAULT_CONFIG_BIT_WIDTH +#define TIMER_DEFAULT_CONFIG_BIT_WIDTH 3 +#endif + +// TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef TIMER_DEFAULT_CONFIG_IRQ_PRIORITY +#define TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// TIMER0_ENABLED - Enable TIMER0 instance + +#ifndef TIMER0_ENABLED +#define TIMER0_ENABLED 1 +#endif + +// TIMER1_ENABLED - Enable TIMER1 instance + +#ifndef TIMER1_ENABLED +#define TIMER1_ENABLED 0 +#endif + +// TIMER2_ENABLED - Enable TIMER2 instance + +#ifndef TIMER2_ENABLED +#define TIMER2_ENABLED 0 +#endif + +// TIMER3_ENABLED - Enable TIMER3 instance + +#ifndef TIMER3_ENABLED +#define TIMER3_ENABLED 0 +#endif + +// TIMER4_ENABLED - Enable TIMER4 instance + +#ifndef TIMER4_ENABLED +#define TIMER4_ENABLED 0 +#endif + +//========================================================== +// NRFX_PRS_ENABLED - nrfx_prs - Peripheral Resource Sharing module +//========================================================== +#ifndef NRFX_PRS_ENABLED +#define NRFX_PRS_ENABLED 1 +#endif +// NRFX_PRS_BOX_0_ENABLED - Enables box 0 in the module. + +#ifndef NRFX_PRS_BOX_0_ENABLED +#define NRFX_PRS_BOX_0_ENABLED 0 +#endif + +// NRFX_PRS_BOX_1_ENABLED - Enables box 1 in the module. + +#ifndef NRFX_PRS_BOX_1_ENABLED +#define NRFX_PRS_BOX_1_ENABLED 0 +#endif + +// NRFX_PRS_BOX_2_ENABLED - Enables box 2 in the module. + +#ifndef NRFX_PRS_BOX_2_ENABLED +#define NRFX_PRS_BOX_2_ENABLED 0 +#endif + +// NRFX_PRS_BOX_3_ENABLED - Enables box 3 in the module. + +#ifndef NRFX_PRS_BOX_3_ENABLED +#define NRFX_PRS_BOX_3_ENABLED 0 +#endif + +// NRFX_PRS_BOX_4_ENABLED - Enables box 4 in the module. + +#ifndef NRFX_PRS_BOX_4_ENABLED +#define NRFX_PRS_BOX_4_ENABLED 1 +#endif + +// NRFX_PRS_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_PRS_CONFIG_LOG_ENABLED +#define NRFX_PRS_CONFIG_LOG_ENABLED 1 +#endif +// NRFX_PRS_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_PRS_CONFIG_LOG_LEVEL +#define NRFX_PRS_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_PRS_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_PRS_CONFIG_INFO_COLOR +#define NRFX_PRS_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_PRS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_PRS_CONFIG_DEBUG_COLOR +#define NRFX_PRS_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_RNG_ENABLED - nrfx_rng - RNG peripheral driver +//========================================================== +#ifndef NRFX_RNG_ENABLED +#define NRFX_RNG_ENABLED 1 +#endif +// NRFX_RNG_CONFIG_ERROR_CORRECTION - Error correction + +#ifndef NRFX_RNG_CONFIG_ERROR_CORRECTION +#define NRFX_RNG_CONFIG_ERROR_CORRECTION 1 +#endif + +// NRFX_RNG_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_RNG_CONFIG_IRQ_PRIORITY +#define NRFX_RNG_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_RNG_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_RNG_CONFIG_LOG_ENABLED +#define NRFX_RNG_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_RNG_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_RNG_CONFIG_LOG_LEVEL +#define NRFX_RNG_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_RNG_CONFIG_INFO_COLOR +#define NRFX_RNG_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_RNG_CONFIG_DEBUG_COLOR +#define NRFX_RNG_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_UARTE_ENABLED - nrfx_uarte - UARTE peripheral driver +//========================================================== +#ifndef NRFX_UARTE_ENABLED +#define NRFX_UARTE_ENABLED 1 +#endif +// NRFX_UARTE0_ENABLED - Enable UARTE0 instance +#ifndef NRFX_UARTE0_ENABLED +#define NRFX_UARTE0_ENABLED 0 +#endif + +// NRFX_UARTE1_ENABLED - Enable UARTE1 instance +#ifndef NRFX_UARTE1_ENABLED +#define NRFX_UARTE1_ENABLED 0 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_HWFC +#define NRFX_UARTE_DEFAULT_CONFIG_HWFC 0 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_PARITY +#define NRFX_UARTE_DEFAULT_CONFIG_PARITY 0 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <8388608=> 31250 baud +// <10289152=> 38400 baud +// <15007744=> 56000 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30801920=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE +#define NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE 30801920 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY +#define NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_UARTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_UARTE_CONFIG_LOG_ENABLED +#define NRFX_UARTE_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_UARTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_UARTE_CONFIG_LOG_LEVEL +#define NRFX_UARTE_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_UARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UARTE_CONFIG_INFO_COLOR +#define NRFX_UARTE_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_UARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UARTE_CONFIG_DEBUG_COLOR +#define NRFX_UARTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_UART_ENABLED - nrfx_uart - UART peripheral driver +//========================================================== +#ifndef NRFX_UART_ENABLED +#define NRFX_UART_ENABLED 1 +#endif +// NRFX_UART0_ENABLED - Enable UART0 instance +#ifndef NRFX_UART0_ENABLED +#define NRFX_UART0_ENABLED 0 +#endif + +// NRFX_UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef NRFX_UART_DEFAULT_CONFIG_HWFC +#define NRFX_UART_DEFAULT_CONFIG_HWFC 0 +#endif + +// NRFX_UART_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef NRFX_UART_DEFAULT_CONFIG_PARITY +#define NRFX_UART_DEFAULT_CONFIG_PARITY 0 +#endif + +// NRFX_UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3866624=> 14400 baud +// <5152768=> 19200 baud +// <7729152=> 28800 baud +// <8388608=> 31250 baud +// <10309632=> 38400 baud +// <15007744=> 56000 baud +// <15462400=> 57600 baud +// <20615168=> 76800 baud +// <30924800=> 115200 baud +// <61845504=> 230400 baud +// <67108864=> 250000 baud +// <123695104=> 460800 baud +// <247386112=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef NRFX_UART_DEFAULT_CONFIG_BAUDRATE +#define NRFX_UART_DEFAULT_CONFIG_BAUDRATE 30924800 +#endif + +// NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY +#define NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_UART_CONFIG_LOG_ENABLED +#define NRFX_UART_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_UART_CONFIG_LOG_LEVEL +#define NRFX_UART_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UART_CONFIG_INFO_COLOR +#define NRFX_UART_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UART_CONFIG_DEBUG_COLOR +#define NRFX_UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// RNG_ENABLED - nrf_drv_rng - RNG peripheral driver - legacy layer +//========================================================== +#ifndef RNG_ENABLED +#define RNG_ENABLED 1 +#endif +// RNG_CONFIG_ERROR_CORRECTION - Error correction + +#ifndef RNG_CONFIG_ERROR_CORRECTION +#define RNG_CONFIG_ERROR_CORRECTION 1 +#endif + +// RNG_CONFIG_POOL_SIZE - Pool size +#ifndef RNG_CONFIG_POOL_SIZE +#define RNG_CONFIG_POOL_SIZE 64 +#endif + +// RNG_CONFIG_IRQ_PRIORITY - Interrupt priority + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef RNG_CONFIG_IRQ_PRIORITY +#define RNG_CONFIG_IRQ_PRIORITY 6 +#endif + +// + +// UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver - legacy layer +//========================================================== +#ifndef UART_ENABLED +#define UART_ENABLED 1 +#endif +// UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef UART_DEFAULT_CONFIG_HWFC +#define UART_DEFAULT_CONFIG_HWFC 0 +#endif + +// UART_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef UART_DEFAULT_CONFIG_PARITY +#define UART_DEFAULT_CONFIG_PARITY 0 +#endif + +// UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <10289152=> 38400 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30801920=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef UART_DEFAULT_CONFIG_BAUDRATE +#define UART_DEFAULT_CONFIG_BAUDRATE 30801920 +#endif + +// UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef UART_DEFAULT_CONFIG_IRQ_PRIORITY +#define UART_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// UART_EASY_DMA_SUPPORT - Driver supporting EasyDMA + +#ifndef UART_EASY_DMA_SUPPORT +#define UART_EASY_DMA_SUPPORT 1 +#endif + +// UART_LEGACY_SUPPORT - Driver supporting Legacy mode + +#ifndef UART_LEGACY_SUPPORT +#define UART_LEGACY_SUPPORT 1 +#endif + +// UART0_ENABLED - Enable UART0 instance +//========================================================== +#ifndef UART0_ENABLED +#define UART0_ENABLED 1 +#endif +// UART0_CONFIG_USE_EASY_DMA - Default setting for using EasyDMA + +#ifndef UART0_CONFIG_USE_EASY_DMA +#define UART0_CONFIG_USE_EASY_DMA 1 +#endif + +// + +// UART1_ENABLED - Enable UART1 instance +//========================================================== +#ifndef UART1_ENABLED +#define UART1_ENABLED 0 +#endif +// + +// + +// +//========================================================== + +// nRF_Libraries + +//========================================================== +// APP_FIFO_ENABLED - app_fifo - Software FIFO implementation + +#ifndef APP_FIFO_ENABLED +#define APP_FIFO_ENABLED 1 +#endif + +// APP_UART_ENABLED - app_uart - UART driver +//========================================================== +#ifndef APP_UART_ENABLED +#define APP_UART_ENABLED 1 +#endif +// APP_UART_DRIVER_INSTANCE - UART instance used + +// <0=> 0 + +#ifndef APP_UART_DRIVER_INSTANCE +#define APP_UART_DRIVER_INSTANCE 0 +#endif + +// + +//========================================================== +// MEM_MANAGER_ENABLED - mem_manager - Dynamic memory allocator +//========================================================== +#ifndef MEM_MANAGER_ENABLED +#define MEM_MANAGER_ENABLED 1 +#endif +// MEMORY_MANAGER_SMALL_BLOCK_COUNT - Size of each memory blocks identified +// as 'small' block. <0-255> + +#ifndef MEMORY_MANAGER_SMALL_BLOCK_COUNT +#define MEMORY_MANAGER_SMALL_BLOCK_COUNT 1 +#endif + +// MEMORY_MANAGER_SMALL_BLOCK_SIZE - Size of each memory blocks identified +// as 'small' block. Size of each memory blocks identified as 'small' +// block. Memory block are recommended to be word-sized. + +#ifndef MEMORY_MANAGER_SMALL_BLOCK_SIZE +#define MEMORY_MANAGER_SMALL_BLOCK_SIZE 32 +#endif + +// MEMORY_MANAGER_MEDIUM_BLOCK_COUNT - Size of each memory blocks identified +// as 'medium' block. <0-255> + +#ifndef MEMORY_MANAGER_MEDIUM_BLOCK_COUNT +#define MEMORY_MANAGER_MEDIUM_BLOCK_COUNT 0 +#endif + +// MEMORY_MANAGER_MEDIUM_BLOCK_SIZE - Size of each memory blocks identified +// as 'medium' block. Size of each memory blocks identified as 'medium' +// block. Memory block are recommended to be word-sized. + +#ifndef MEMORY_MANAGER_MEDIUM_BLOCK_SIZE +#define MEMORY_MANAGER_MEDIUM_BLOCK_SIZE 256 +#endif + +// MEMORY_MANAGER_LARGE_BLOCK_COUNT - Size of each memory blocks identified +// as 'large' block. <0-255> + +#ifndef MEMORY_MANAGER_LARGE_BLOCK_COUNT +#define MEMORY_MANAGER_LARGE_BLOCK_COUNT 0 +#endif + +// MEMORY_MANAGER_LARGE_BLOCK_SIZE - Size of each memory blocks identified +// as 'large' block. Size of each memory blocks identified as 'large' +// block. Memory block are recommended to be word-sized. + +#ifndef MEMORY_MANAGER_LARGE_BLOCK_SIZE +#define MEMORY_MANAGER_LARGE_BLOCK_SIZE 256 +#endif + +// MEMORY_MANAGER_XLARGE_BLOCK_COUNT - Size of each memory blocks identified +// as 'extra large' block. <0-255> + +#ifndef MEMORY_MANAGER_XLARGE_BLOCK_COUNT +#define MEMORY_MANAGER_XLARGE_BLOCK_COUNT 0 +#endif + +// MEMORY_MANAGER_XLARGE_BLOCK_SIZE - Size of each memory blocks identified +// as 'extra large' block. Size of each memory blocks identified as 'extra +// large' block. Memory block are recommended to be word-sized. + +#ifndef MEMORY_MANAGER_XLARGE_BLOCK_SIZE +#define MEMORY_MANAGER_XLARGE_BLOCK_SIZE 1320 +#endif + +// MEMORY_MANAGER_XXLARGE_BLOCK_COUNT - Size of each memory blocks +// identified as 'extra extra large' block. <0-255> + +#ifndef MEMORY_MANAGER_XXLARGE_BLOCK_COUNT +#define MEMORY_MANAGER_XXLARGE_BLOCK_COUNT 0 +#endif + +// MEMORY_MANAGER_XXLARGE_BLOCK_SIZE - Size of each memory blocks +// identified as 'extra extra large' block. Size of each memory blocks +// identified as 'extra extra large' block. Memory block are recommended to be +// word-sized. + +#ifndef MEMORY_MANAGER_XXLARGE_BLOCK_SIZE +#define MEMORY_MANAGER_XXLARGE_BLOCK_SIZE 3444 +#endif + +// MEMORY_MANAGER_XSMALL_BLOCK_COUNT - Size of each memory blocks identified +// as 'extra small' block. <0-255> + +#ifndef MEMORY_MANAGER_XSMALL_BLOCK_COUNT +#define MEMORY_MANAGER_XSMALL_BLOCK_COUNT 0 +#endif + +// MEMORY_MANAGER_XSMALL_BLOCK_SIZE - Size of each memory blocks identified +// as 'extra small' block. Size of each memory blocks identified as 'extra +// large' block. Memory block are recommended to be word-sized. + +#ifndef MEMORY_MANAGER_XSMALL_BLOCK_SIZE +#define MEMORY_MANAGER_XSMALL_BLOCK_SIZE 64 +#endif + +// MEMORY_MANAGER_XXSMALL_BLOCK_COUNT - Size of each memory blocks +// identified as 'extra extra small' block. <0-255> + +#ifndef MEMORY_MANAGER_XXSMALL_BLOCK_COUNT +#define MEMORY_MANAGER_XXSMALL_BLOCK_COUNT 0 +#endif + +// MEMORY_MANAGER_XXSMALL_BLOCK_SIZE - Size of each memory blocks +// identified as 'extra extra small' block. Size of each memory blocks +// identified as 'extra extra small' block. Memory block are recommended to be +// word-sized. + +#ifndef MEMORY_MANAGER_XXSMALL_BLOCK_SIZE +#define MEMORY_MANAGER_XXSMALL_BLOCK_SIZE 32 +#endif + +// MEM_MANAGER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef MEM_MANAGER_CONFIG_LOG_ENABLED +#define MEM_MANAGER_CONFIG_LOG_ENABLED 0 +#endif +// MEM_MANAGER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef MEM_MANAGER_CONFIG_LOG_LEVEL +#define MEM_MANAGER_CONFIG_LOG_LEVEL 3 +#endif + +// MEM_MANAGER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef MEM_MANAGER_CONFIG_INFO_COLOR +#define MEM_MANAGER_CONFIG_INFO_COLOR 0 +#endif + +// MEM_MANAGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef MEM_MANAGER_CONFIG_DEBUG_COLOR +#define MEM_MANAGER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// MEM_MANAGER_DISABLE_API_PARAM_CHECK - Disable API parameter checks in +// the module. + +#ifndef MEM_MANAGER_DISABLE_API_PARAM_CHECK +#define MEM_MANAGER_DISABLE_API_PARAM_CHECK 0 +#endif + +// + +// NRF_BALLOC_ENABLED - nrf_balloc - Block allocator module +//========================================================== +#ifndef NRF_BALLOC_ENABLED +#define NRF_BALLOC_ENABLED 1 +#endif +// NRF_BALLOC_CONFIG_DEBUG_ENABLED - Enables debug mode in the module. +//========================================================== +#ifndef NRF_BALLOC_CONFIG_DEBUG_ENABLED +#define NRF_BALLOC_CONFIG_DEBUG_ENABLED 0 +#endif +// NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS - Number of words used as head guard. +// <0-255> + +#ifndef NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS +#define NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS 1 +#endif + +// NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS - Number of words used as tail guard. +// <0-255> + +#ifndef NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS +#define NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS 1 +#endif + +// NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED - Enables basic checks in this +// module. + +#ifndef NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED +#define NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED 0 +#endif + +// NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED - Enables double memory free +// check in this module. + +#ifndef NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED +#define NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED 0 +#endif + +// NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED - Enables free memory +// corruption check in this module. + +#ifndef NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED +#define NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED 0 +#endif + +// NRF_BALLOC_CLI_CMDS - Enable CLI commands specific to the module + +#ifndef NRF_BALLOC_CLI_CMDS +#define NRF_BALLOC_CLI_CMDS 0 +#endif + +// + +// + +// NRF_MEMOBJ_ENABLED - nrf_memobj - Linked memory allocator module + +#ifndef NRF_MEMOBJ_ENABLED +#define NRF_MEMOBJ_ENABLED 1 +#endif + +// NRF_QUEUE_ENABLED - nrf_queue - Queue module +//========================================================== +#ifndef NRF_QUEUE_ENABLED +#define NRF_QUEUE_ENABLED 1 +#endif +// NRF_QUEUE_CLI_CMDS - Enable CLI commands specific to the module + +#ifndef NRF_QUEUE_CLI_CMDS +#define NRF_QUEUE_CLI_CMDS 0 +#endif + +// + +// NRF_STRERROR_ENABLED - nrf_strerror - Library for converting error code +// to string. + +#ifndef NRF_STRERROR_ENABLED +#define NRF_STRERROR_ENABLED 1 +#endif + +// RETARGET_ENABLED - retarget - Retargeting stdio functions + +#ifndef RETARGET_ENABLED +#define RETARGET_ENABLED 1 +#endif + +// nrf_fprintf - fprintf function. + +//========================================================== +// NRF_FPRINTF_ENABLED - Enable/disable fprintf module. + +#ifndef NRF_FPRINTF_ENABLED +#define NRF_FPRINTF_ENABLED 1 +#endif + +// NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED - For each printed LF, +// function will add CR. + +#ifndef NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED +#define NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED 0 +#endif + +// NRF_FPRINTF_DOUBLE_ENABLED - Enable IEEE-754 double precision +// formatting. + +#ifndef NRF_FPRINTF_DOUBLE_ENABLED +#define NRF_FPRINTF_DOUBLE_ENABLED 0 +#endif + +// +//========================================================== + +// +//========================================================== + +// nRF_Log + +//========================================================== +// NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend +//========================================================== +#ifndef NRF_LOG_BACKEND_RTT_ENABLED +#define NRF_LOG_BACKEND_RTT_ENABLED 0 +#endif +// NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE - Size of buffer for partially +// processed strings. Size of the buffer is a trade-off between RAM usage +// and processing. if buffer is smaller then strings will often be +// fragmented. It is recommended to use size which will fit typical log and +// only the longer one will be fragmented. + +#ifndef NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE +#define NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE 64 +#endif + +// NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS - Period before retrying writing to +// RTT +#ifndef NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS +#define NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS 1 +#endif + +// NRF_LOG_BACKEND_RTT_TX_RETRY_CNT - Writing to RTT retries. +// If RTT fails to accept any new data after retries +// module assumes that host is not active and on next +// request it will perform only one write attempt. +// On successful writing, module assumes that host is active +// and scheme with retry is applied again. + +#ifndef NRF_LOG_BACKEND_RTT_TX_RETRY_CNT +#define NRF_LOG_BACKEND_RTT_TX_RETRY_CNT 3 +#endif + +// + +// NRF_LOG_BACKEND_UART_ENABLED - nrf_log_backend_uart - Log UART backend +//========================================================== +#ifndef NRF_LOG_BACKEND_UART_ENABLED +#define NRF_LOG_BACKEND_UART_ENABLED 0 +#endif +// NRF_LOG_BACKEND_UART_TX_PIN - UART TX pin +#ifndef NRF_LOG_BACKEND_UART_TX_PIN +#define NRF_LOG_BACKEND_UART_TX_PIN 6 +#endif + +// NRF_LOG_BACKEND_UART_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <10289152=> 38400 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30801920=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef NRF_LOG_BACKEND_UART_BAUDRATE +#define NRF_LOG_BACKEND_UART_BAUDRATE 30801920 +#endif + +// NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE - Size of buffer for partially +// processed strings. Size of the buffer is a trade-off between RAM usage +// and processing. if buffer is smaller then strings will often be +// fragmented. It is recommended to use size which will fit typical log and +// only the longer one will be fragmented. + +#ifndef NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE +#define NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE 64 +#endif + +// + +// NRF_LOG_ENABLED - nrf_log - Logger +//========================================================== +#ifndef NRF_LOG_ENABLED +#define NRF_LOG_ENABLED 0 +#endif +// Log message pool - Configuration of log message pool + +//========================================================== +// NRF_LOG_MSGPOOL_ELEMENT_SIZE - Size of a single element in the pool of +// memory objects. If a small value is set, then performance of logs +// processing is degraded because data is fragmented. Bigger value impacts +// RAM memory utilization. The size is set to fit a message with +// a timestamp and up to 2 arguments in a single memory object. + +#ifndef NRF_LOG_MSGPOOL_ELEMENT_SIZE +#define NRF_LOG_MSGPOOL_ELEMENT_SIZE 20 +#endif + +// NRF_LOG_MSGPOOL_ELEMENT_COUNT - Number of elements in the pool of memory +// objects If a small value is set, then it may lead to a deadlock in +// certain cases if backend has high latency and holds multiple messages for +// long time. Bigger value impacts RAM memory usage. + +#ifndef NRF_LOG_MSGPOOL_ELEMENT_COUNT +#define NRF_LOG_MSGPOOL_ELEMENT_COUNT 8 +#endif + +// +//========================================================== + +// NRF_LOG_ALLOW_OVERFLOW - Configures behavior when circular buffer is +// full. + +// If set then oldest logs are overwritten. Otherwise a +// marker is injected informing about overflow. + +#ifndef NRF_LOG_ALLOW_OVERFLOW +#define NRF_LOG_ALLOW_OVERFLOW 1 +#endif + +// NRF_LOG_BUFSIZE - Size of the buffer for storing logs (in bytes). + +// Must be power of 2 and multiple of 4. +// If NRF_LOG_DEFERRED = 0 then buffer size can be reduced to minimum. +// <128=> 128 +// <256=> 256 +// <512=> 512 +// <1024=> 1024 +// <2048=> 2048 +// <4096=> 4096 +// <8192=> 8192 +// <16384=> 16384 + +#ifndef NRF_LOG_BUFSIZE +#define NRF_LOG_BUFSIZE 1024 +#endif + +// NRF_LOG_CLI_CMDS - Enable CLI commands for the module. + +#ifndef NRF_LOG_CLI_CMDS +#define NRF_LOG_CLI_CMDS 0 +#endif + +// NRF_LOG_DEFAULT_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_LOG_DEFAULT_LEVEL +#define NRF_LOG_DEFAULT_LEVEL 4 +#endif + +// NRF_LOG_DEFERRED - Enable deffered logger. + +// Log data is buffered and can be processed in idle. + +#ifndef NRF_LOG_DEFERRED +#define NRF_LOG_DEFERRED 0 +#endif + +// NRF_LOG_FILTERS_ENABLED - Enable dynamic filtering of logs. + +#ifndef NRF_LOG_FILTERS_ENABLED +#define NRF_LOG_FILTERS_ENABLED 0 +#endif + +// NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED - Enable use of critical +// region for non deffered mode when flushing logs. + +// When enabled NRF_LOG_FLUSH is called from critical section when non +// deffered mode is used. Log output will never be corrupted as access to +// the log backend is exclusive but system will spend significant amount of +// time in critical section + +#ifndef NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED +#define NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED 0 +#endif + +// NRF_LOG_STR_PUSH_BUFFER_SIZE - Size of the buffer dedicated for strings +// stored using @ref NRF_LOG_PUSH. + +// <16=> 16 +// <32=> 32 +// <64=> 64 +// <128=> 128 +// <256=> 256 +// <512=> 512 +// <1024=> 1024 + +#ifndef NRF_LOG_STR_PUSH_BUFFER_SIZE +#define NRF_LOG_STR_PUSH_BUFFER_SIZE 128 +#endif + +// NRF_LOG_STR_PUSH_BUFFER_SIZE - Size of the buffer dedicated for strings +// stored using @ref NRF_LOG_PUSH. + +// <16=> 16 +// <32=> 32 +// <64=> 64 +// <128=> 128 +// <256=> 256 +// <512=> 512 +// <1024=> 1024 + +#ifndef NRF_LOG_STR_PUSH_BUFFER_SIZE +#define NRF_LOG_STR_PUSH_BUFFER_SIZE 128 +#endif + +// NRF_LOG_USES_COLORS - If enabled then ANSI escape code for colors is +// prefixed to every string +//========================================================== +#ifndef NRF_LOG_USES_COLORS +#define NRF_LOG_USES_COLORS 0 +#endif +// NRF_LOG_COLOR_DEFAULT - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_COLOR_DEFAULT +#define NRF_LOG_COLOR_DEFAULT 0 +#endif + +// NRF_LOG_ERROR_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_ERROR_COLOR +#define NRF_LOG_ERROR_COLOR 2 +#endif + +// NRF_LOG_WARNING_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_WARNING_COLOR +#define NRF_LOG_WARNING_COLOR 4 +#endif + +// + +// NRF_LOG_USES_TIMESTAMP - Enable timestamping + +// Function for getting the timestamp is provided by the user +//========================================================== +#ifndef NRF_LOG_USES_TIMESTAMP +#define NRF_LOG_USES_TIMESTAMP 0 +#endif +// NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY - Default frequency of the timestamp +// (in Hz) or 0 to use app_timer frequency. +#ifndef NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY +#define NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY 0 +#endif + +// + +// nrf_log module configuration + +//========================================================== +// nrf_log in nRF_Core + +//========================================================== +// NRF_MPU_LIB_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_MPU_LIB_CONFIG_LOG_ENABLED +#define NRF_MPU_LIB_CONFIG_LOG_ENABLED 0 +#endif +// NRF_MPU_LIB_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_MPU_LIB_CONFIG_LOG_LEVEL +#define NRF_MPU_LIB_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_MPU_LIB_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MPU_LIB_CONFIG_INFO_COLOR +#define NRF_MPU_LIB_CONFIG_INFO_COLOR 0 +#endif + +// NRF_MPU_LIB_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MPU_LIB_CONFIG_DEBUG_COLOR +#define NRF_MPU_LIB_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_STACK_GUARD_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_STACK_GUARD_CONFIG_LOG_ENABLED +#define NRF_STACK_GUARD_CONFIG_LOG_ENABLED 0 +#endif +// NRF_STACK_GUARD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_STACK_GUARD_CONFIG_LOG_LEVEL +#define NRF_STACK_GUARD_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_STACK_GUARD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_STACK_GUARD_CONFIG_INFO_COLOR +#define NRF_STACK_GUARD_CONFIG_INFO_COLOR 0 +#endif + +// NRF_STACK_GUARD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_STACK_GUARD_CONFIG_DEBUG_COLOR +#define NRF_STACK_GUARD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TASK_MANAGER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TASK_MANAGER_CONFIG_LOG_ENABLED +#define TASK_MANAGER_CONFIG_LOG_ENABLED 0 +#endif +// TASK_MANAGER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TASK_MANAGER_CONFIG_LOG_LEVEL +#define TASK_MANAGER_CONFIG_LOG_LEVEL 3 +#endif + +// TASK_MANAGER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TASK_MANAGER_CONFIG_INFO_COLOR +#define TASK_MANAGER_CONFIG_INFO_COLOR 0 +#endif + +// TASK_MANAGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TASK_MANAGER_CONFIG_DEBUG_COLOR +#define TASK_MANAGER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// nrf_log in nRF_Drivers + +//========================================================== +// CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef CLOCK_CONFIG_LOG_ENABLED +#define CLOCK_CONFIG_LOG_ENABLED 0 +#endif +// CLOCK_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef CLOCK_CONFIG_LOG_LEVEL +#define CLOCK_CONFIG_LOG_LEVEL 3 +#endif + +// CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_INFO_COLOR +#define CLOCK_CONFIG_INFO_COLOR 0 +#endif + +// CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_DEBUG_COLOR +#define CLOCK_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// COMP_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef COMP_CONFIG_LOG_ENABLED +#define COMP_CONFIG_LOG_ENABLED 0 +#endif +// COMP_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef COMP_CONFIG_LOG_LEVEL +#define COMP_CONFIG_LOG_LEVEL 3 +#endif + +// COMP_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMP_CONFIG_INFO_COLOR +#define COMP_CONFIG_INFO_COLOR 0 +#endif + +// COMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMP_CONFIG_DEBUG_COLOR +#define COMP_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// GPIOTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef GPIOTE_CONFIG_LOG_ENABLED +#define GPIOTE_CONFIG_LOG_ENABLED 0 +#endif +// GPIOTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef GPIOTE_CONFIG_LOG_LEVEL +#define GPIOTE_CONFIG_LOG_LEVEL 3 +#endif + +// GPIOTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef GPIOTE_CONFIG_INFO_COLOR +#define GPIOTE_CONFIG_INFO_COLOR 0 +#endif + +// GPIOTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef GPIOTE_CONFIG_DEBUG_COLOR +#define GPIOTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// LPCOMP_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef LPCOMP_CONFIG_LOG_ENABLED +#define LPCOMP_CONFIG_LOG_ENABLED 0 +#endif +// LPCOMP_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef LPCOMP_CONFIG_LOG_LEVEL +#define LPCOMP_CONFIG_LOG_LEVEL 3 +#endif + +// LPCOMP_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef LPCOMP_CONFIG_INFO_COLOR +#define LPCOMP_CONFIG_INFO_COLOR 0 +#endif + +// LPCOMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef LPCOMP_CONFIG_DEBUG_COLOR +#define LPCOMP_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// MAX3421E_HOST_CONFIG_LOG_ENABLED - Enable logging in the module +//========================================================== +#ifndef MAX3421E_HOST_CONFIG_LOG_ENABLED +#define MAX3421E_HOST_CONFIG_LOG_ENABLED 0 +#endif +// MAX3421E_HOST_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef MAX3421E_HOST_CONFIG_LOG_LEVEL +#define MAX3421E_HOST_CONFIG_LOG_LEVEL 3 +#endif + +// MAX3421E_HOST_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef MAX3421E_HOST_CONFIG_INFO_COLOR +#define MAX3421E_HOST_CONFIG_INFO_COLOR 0 +#endif + +// MAX3421E_HOST_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef MAX3421E_HOST_CONFIG_DEBUG_COLOR +#define MAX3421E_HOST_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRFX_USBD_CONFIG_LOG_ENABLED - Enable logging in the module +//========================================================== +#ifndef NRFX_USBD_CONFIG_LOG_ENABLED +#define NRFX_USBD_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_USBD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_USBD_CONFIG_LOG_LEVEL +#define NRFX_USBD_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_USBD_CONFIG_INFO_COLOR +#define NRFX_USBD_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_USBD_CONFIG_DEBUG_COLOR +#define NRFX_USBD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PDM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PDM_CONFIG_LOG_ENABLED +#define PDM_CONFIG_LOG_ENABLED 0 +#endif +// PDM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PDM_CONFIG_LOG_LEVEL +#define PDM_CONFIG_LOG_LEVEL 3 +#endif + +// PDM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PDM_CONFIG_INFO_COLOR +#define PDM_CONFIG_INFO_COLOR 0 +#endif + +// PDM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PDM_CONFIG_DEBUG_COLOR +#define PDM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PPI_CONFIG_LOG_ENABLED +#define PPI_CONFIG_LOG_ENABLED 0 +#endif +// PPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PPI_CONFIG_LOG_LEVEL +#define PPI_CONFIG_LOG_LEVEL 3 +#endif + +// PPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PPI_CONFIG_INFO_COLOR +#define PPI_CONFIG_INFO_COLOR 0 +#endif + +// PPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PPI_CONFIG_DEBUG_COLOR +#define PPI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PWM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PWM_CONFIG_LOG_ENABLED +#define PWM_CONFIG_LOG_ENABLED 0 +#endif +// PWM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PWM_CONFIG_LOG_LEVEL +#define PWM_CONFIG_LOG_LEVEL 3 +#endif + +// PWM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PWM_CONFIG_INFO_COLOR +#define PWM_CONFIG_INFO_COLOR 0 +#endif + +// PWM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PWM_CONFIG_DEBUG_COLOR +#define PWM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// QDEC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef QDEC_CONFIG_LOG_ENABLED +#define QDEC_CONFIG_LOG_ENABLED 0 +#endif +// QDEC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef QDEC_CONFIG_LOG_LEVEL +#define QDEC_CONFIG_LOG_LEVEL 3 +#endif + +// QDEC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef QDEC_CONFIG_INFO_COLOR +#define QDEC_CONFIG_INFO_COLOR 0 +#endif + +// QDEC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef QDEC_CONFIG_DEBUG_COLOR +#define QDEC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// RNG_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RNG_CONFIG_LOG_ENABLED +#define RNG_CONFIG_LOG_ENABLED 0 +#endif +// RNG_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RNG_CONFIG_LOG_LEVEL +#define RNG_CONFIG_LOG_LEVEL 3 +#endif + +// RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_INFO_COLOR +#define RNG_CONFIG_INFO_COLOR 0 +#endif + +// RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_DEBUG_COLOR +#define RNG_CONFIG_DEBUG_COLOR 0 +#endif + +// RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED - Enables logging of random +// numbers. + +#ifndef RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED +#define RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED 0 +#endif + +// + +// RTC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RTC_CONFIG_LOG_ENABLED +#define RTC_CONFIG_LOG_ENABLED 0 +#endif +// RTC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RTC_CONFIG_LOG_LEVEL +#define RTC_CONFIG_LOG_LEVEL 3 +#endif + +// RTC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RTC_CONFIG_INFO_COLOR +#define RTC_CONFIG_INFO_COLOR 0 +#endif + +// RTC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RTC_CONFIG_DEBUG_COLOR +#define RTC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// SAADC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SAADC_CONFIG_LOG_ENABLED +#define SAADC_CONFIG_LOG_ENABLED 0 +#endif +// SAADC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SAADC_CONFIG_LOG_LEVEL +#define SAADC_CONFIG_LOG_LEVEL 3 +#endif + +// SAADC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SAADC_CONFIG_INFO_COLOR +#define SAADC_CONFIG_INFO_COLOR 0 +#endif + +// SAADC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SAADC_CONFIG_DEBUG_COLOR +#define SAADC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// SPIS_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SPIS_CONFIG_LOG_ENABLED +#define SPIS_CONFIG_LOG_ENABLED 0 +#endif +// SPIS_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SPIS_CONFIG_LOG_LEVEL +#define SPIS_CONFIG_LOG_LEVEL 3 +#endif + +// SPIS_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPIS_CONFIG_INFO_COLOR +#define SPIS_CONFIG_INFO_COLOR 0 +#endif + +// SPIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPIS_CONFIG_DEBUG_COLOR +#define SPIS_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// SPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SPI_CONFIG_LOG_ENABLED +#define SPI_CONFIG_LOG_ENABLED 0 +#endif +// SPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SPI_CONFIG_LOG_LEVEL +#define SPI_CONFIG_LOG_LEVEL 3 +#endif + +// SPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPI_CONFIG_INFO_COLOR +#define SPI_CONFIG_INFO_COLOR 0 +#endif + +// SPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPI_CONFIG_DEBUG_COLOR +#define SPI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TIMER_CONFIG_LOG_ENABLED +#define TIMER_CONFIG_LOG_ENABLED 0 +#endif +// TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TIMER_CONFIG_LOG_LEVEL +#define TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TIMER_CONFIG_INFO_COLOR +#define TIMER_CONFIG_INFO_COLOR 0 +#endif + +// TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TIMER_CONFIG_DEBUG_COLOR +#define TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TWIS_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TWIS_CONFIG_LOG_ENABLED +#define TWIS_CONFIG_LOG_ENABLED 0 +#endif +// TWIS_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TWIS_CONFIG_LOG_LEVEL +#define TWIS_CONFIG_LOG_LEVEL 3 +#endif + +// TWIS_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWIS_CONFIG_INFO_COLOR +#define TWIS_CONFIG_INFO_COLOR 0 +#endif + +// TWIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWIS_CONFIG_DEBUG_COLOR +#define TWIS_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TWI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TWI_CONFIG_LOG_ENABLED +#define TWI_CONFIG_LOG_ENABLED 0 +#endif +// TWI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TWI_CONFIG_LOG_LEVEL +#define TWI_CONFIG_LOG_LEVEL 3 +#endif + +// TWI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWI_CONFIG_INFO_COLOR +#define TWI_CONFIG_INFO_COLOR 0 +#endif + +// TWI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWI_CONFIG_DEBUG_COLOR +#define TWI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef UART_CONFIG_LOG_ENABLED +#define UART_CONFIG_LOG_ENABLED 0 +#endif +// UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef UART_CONFIG_LOG_LEVEL +#define UART_CONFIG_LOG_LEVEL 3 +#endif + +// UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef UART_CONFIG_INFO_COLOR +#define UART_CONFIG_INFO_COLOR 0 +#endif + +// UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef UART_CONFIG_DEBUG_COLOR +#define UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// USBD_CONFIG_LOG_ENABLED - Enable logging in the module +//========================================================== +#ifndef USBD_CONFIG_LOG_ENABLED +#define USBD_CONFIG_LOG_ENABLED 0 +#endif +// USBD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef USBD_CONFIG_LOG_LEVEL +#define USBD_CONFIG_LOG_LEVEL 3 +#endif + +// USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef USBD_CONFIG_INFO_COLOR +#define USBD_CONFIG_INFO_COLOR 0 +#endif + +// USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef USBD_CONFIG_DEBUG_COLOR +#define USBD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// WDT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef WDT_CONFIG_LOG_ENABLED +#define WDT_CONFIG_LOG_ENABLED 0 +#endif +// WDT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef WDT_CONFIG_LOG_LEVEL +#define WDT_CONFIG_LOG_LEVEL 3 +#endif + +// WDT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef WDT_CONFIG_INFO_COLOR +#define WDT_CONFIG_INFO_COLOR 0 +#endif + +// WDT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef WDT_CONFIG_DEBUG_COLOR +#define WDT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// nrf_log in nRF_Libraries + +//========================================================== +// APP_BUTTON_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_BUTTON_CONFIG_LOG_ENABLED +#define APP_BUTTON_CONFIG_LOG_ENABLED 0 +#endif +// APP_BUTTON_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_BUTTON_CONFIG_LOG_LEVEL +#define APP_BUTTON_CONFIG_LOG_LEVEL 3 +#endif + +// APP_BUTTON_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic +// filtering is enabled. + +// If module generates a lot of logs, initial log level can +// be decreased to prevent flooding. Severity level can be +// increased on instance basis. +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_BUTTON_CONFIG_INITIAL_LOG_LEVEL +#define APP_BUTTON_CONFIG_INITIAL_LOG_LEVEL 3 +#endif + +// APP_BUTTON_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_BUTTON_CONFIG_INFO_COLOR +#define APP_BUTTON_CONFIG_INFO_COLOR 0 +#endif + +// APP_BUTTON_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_BUTTON_CONFIG_DEBUG_COLOR +#define APP_BUTTON_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_TIMER_CONFIG_LOG_ENABLED +#define APP_TIMER_CONFIG_LOG_ENABLED 0 +#endif +// APP_TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_TIMER_CONFIG_LOG_LEVEL +#define APP_TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// APP_TIMER_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic +// filtering is enabled. + +// If module generates a lot of logs, initial log level can +// be decreased to prevent flooding. Severity level can be +// increased on instance basis. +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_TIMER_CONFIG_INITIAL_LOG_LEVEL +#define APP_TIMER_CONFIG_INITIAL_LOG_LEVEL 3 +#endif + +// APP_TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_TIMER_CONFIG_INFO_COLOR +#define APP_TIMER_CONFIG_INFO_COLOR 0 +#endif + +// APP_TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_TIMER_CONFIG_DEBUG_COLOR +#define APP_TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED +#define APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL +#define APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_CDC_ACM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CDC_ACM_CONFIG_INFO_COLOR +#define APP_USBD_CDC_ACM_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR +#define APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_CONFIG_LOG_ENABLED - Enable logging in the module. +//========================================================== +#ifndef APP_USBD_CONFIG_LOG_ENABLED +#define APP_USBD_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_CONFIG_LOG_LEVEL +#define APP_USBD_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CONFIG_INFO_COLOR +#define APP_USBD_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CONFIG_DEBUG_COLOR +#define APP_USBD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_DUMMY_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_DUMMY_CONFIG_LOG_ENABLED +#define APP_USBD_DUMMY_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_DUMMY_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_DUMMY_CONFIG_LOG_LEVEL +#define APP_USBD_DUMMY_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_DUMMY_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_DUMMY_CONFIG_INFO_COLOR +#define APP_USBD_DUMMY_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_DUMMY_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_DUMMY_CONFIG_DEBUG_COLOR +#define APP_USBD_DUMMY_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_MSC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_MSC_CONFIG_LOG_ENABLED +#define APP_USBD_MSC_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_MSC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_MSC_CONFIG_LOG_LEVEL +#define APP_USBD_MSC_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_MSC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_MSC_CONFIG_INFO_COLOR +#define APP_USBD_MSC_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_MSC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_MSC_CONFIG_DEBUG_COLOR +#define APP_USBD_MSC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED - Enables logging in the +// module. +//========================================================== +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_ATFIFO_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_ATFIFO_CONFIG_LOG_ENABLED +#define NRF_ATFIFO_CONFIG_LOG_ENABLED 0 +#endif +// NRF_ATFIFO_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_ATFIFO_CONFIG_LOG_LEVEL +#define NRF_ATFIFO_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if +// dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_ATFIFO_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_ATFIFO_CONFIG_INFO_COLOR +#define NRF_ATFIFO_CONFIG_INFO_COLOR 0 +#endif + +// NRF_ATFIFO_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_ATFIFO_CONFIG_DEBUG_COLOR +#define NRF_ATFIFO_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BALLOC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BALLOC_CONFIG_LOG_ENABLED +#define NRF_BALLOC_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BALLOC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BALLOC_CONFIG_LOG_LEVEL +#define NRF_BALLOC_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic +// filtering is enabled. + +// If module generates a lot of logs, initial log level can +// be decreased to prevent flooding. Severity level can be +// increased on instance basis. +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL +#define NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL 3 +#endif + +// NRF_BALLOC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BALLOC_CONFIG_INFO_COLOR +#define NRF_BALLOC_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BALLOC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BALLOC_CONFIG_DEBUG_COLOR +#define NRF_BALLOC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED +#define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL +#define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity +// level if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR +#define NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR +#define NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED +#define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL +#define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level +// if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR +#define NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR +#define NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED +#define NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL +#define NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level +// if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR +#define NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR +#define NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED +#define NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED 0 +#endif +// NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL +#define NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_CLI_BLE_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_BLE_UART_CONFIG_INFO_COLOR +#define NRF_CLI_BLE_UART_CONFIG_INFO_COLOR 0 +#endif + +// NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR +#define NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED +#define NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED 0 +#endif +// NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL +#define NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR +#define NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR 0 +#endif + +// NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR +#define NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_CLI_UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_CLI_UART_CONFIG_LOG_ENABLED +#define NRF_CLI_UART_CONFIG_LOG_ENABLED 0 +#endif +// NRF_CLI_UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_CLI_UART_CONFIG_LOG_LEVEL +#define NRF_CLI_UART_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_CLI_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_UART_CONFIG_INFO_COLOR +#define NRF_CLI_UART_CONFIG_INFO_COLOR 0 +#endif + +// NRF_CLI_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_UART_CONFIG_DEBUG_COLOR +#define NRF_CLI_UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_LIBUARTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_LIBUARTE_CONFIG_LOG_ENABLED +#define NRF_LIBUARTE_CONFIG_LOG_ENABLED 0 +#endif +// NRF_LIBUARTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_LIBUARTE_CONFIG_LOG_LEVEL +#define NRF_LIBUARTE_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_LIBUARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LIBUARTE_CONFIG_INFO_COLOR +#define NRF_LIBUARTE_CONFIG_INFO_COLOR 0 +#endif + +// NRF_LIBUARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LIBUARTE_CONFIG_DEBUG_COLOR +#define NRF_LIBUARTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_MEMOBJ_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_MEMOBJ_CONFIG_LOG_ENABLED +#define NRF_MEMOBJ_CONFIG_LOG_ENABLED 0 +#endif +// NRF_MEMOBJ_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_MEMOBJ_CONFIG_LOG_LEVEL +#define NRF_MEMOBJ_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_MEMOBJ_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MEMOBJ_CONFIG_INFO_COLOR +#define NRF_MEMOBJ_CONFIG_INFO_COLOR 0 +#endif + +// NRF_MEMOBJ_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MEMOBJ_CONFIG_DEBUG_COLOR +#define NRF_MEMOBJ_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_PWR_MGMT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_PWR_MGMT_CONFIG_LOG_ENABLED +#define NRF_PWR_MGMT_CONFIG_LOG_ENABLED 0 +#endif +// NRF_PWR_MGMT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_PWR_MGMT_CONFIG_LOG_LEVEL +#define NRF_PWR_MGMT_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_PWR_MGMT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_PWR_MGMT_CONFIG_INFO_COLOR +#define NRF_PWR_MGMT_CONFIG_INFO_COLOR 0 +#endif + +// NRF_PWR_MGMT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_PWR_MGMT_CONFIG_DEBUG_COLOR +#define NRF_PWR_MGMT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_QUEUE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_QUEUE_CONFIG_LOG_ENABLED +#define NRF_QUEUE_CONFIG_LOG_ENABLED 0 +#endif +// NRF_QUEUE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_QUEUE_CONFIG_LOG_LEVEL +#define NRF_QUEUE_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if +// dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_QUEUE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_QUEUE_CONFIG_INFO_COLOR +#define NRF_QUEUE_CONFIG_INFO_COLOR 0 +#endif + +// NRF_QUEUE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_QUEUE_CONFIG_DEBUG_COLOR +#define NRF_QUEUE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_ANT_LOG_ENABLED - Enable logging in SoftDevice handler (ANT) +// module. +//========================================================== +#ifndef NRF_SDH_ANT_LOG_ENABLED +#define NRF_SDH_ANT_LOG_ENABLED 0 +#endif +// NRF_SDH_ANT_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_ANT_LOG_LEVEL +#define NRF_SDH_ANT_LOG_LEVEL 3 +#endif + +// NRF_SDH_ANT_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_ANT_INFO_COLOR +#define NRF_SDH_ANT_INFO_COLOR 0 +#endif + +// NRF_SDH_ANT_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_ANT_DEBUG_COLOR +#define NRF_SDH_ANT_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_BLE_LOG_ENABLED - Enable logging in SoftDevice handler (BLE) +// module. +//========================================================== +#ifndef NRF_SDH_BLE_LOG_ENABLED +#define NRF_SDH_BLE_LOG_ENABLED 0 +#endif +// NRF_SDH_BLE_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_BLE_LOG_LEVEL +#define NRF_SDH_BLE_LOG_LEVEL 3 +#endif + +// NRF_SDH_BLE_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_BLE_INFO_COLOR +#define NRF_SDH_BLE_INFO_COLOR 0 +#endif + +// NRF_SDH_BLE_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_BLE_DEBUG_COLOR +#define NRF_SDH_BLE_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_LOG_ENABLED - Enable logging in SoftDevice handler module. +//========================================================== +#ifndef NRF_SDH_LOG_ENABLED +#define NRF_SDH_LOG_ENABLED 0 +#endif +// NRF_SDH_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_LOG_LEVEL +#define NRF_SDH_LOG_LEVEL 3 +#endif + +// NRF_SDH_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_INFO_COLOR +#define NRF_SDH_INFO_COLOR 0 +#endif + +// NRF_SDH_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_DEBUG_COLOR +#define NRF_SDH_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_SOC_LOG_ENABLED - Enable logging in SoftDevice handler (SoC) +// module. +//========================================================== +#ifndef NRF_SDH_SOC_LOG_ENABLED +#define NRF_SDH_SOC_LOG_ENABLED 0 +#endif +// NRF_SDH_SOC_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_SOC_LOG_LEVEL +#define NRF_SDH_SOC_LOG_LEVEL 3 +#endif + +// NRF_SDH_SOC_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_SOC_INFO_COLOR +#define NRF_SDH_SOC_INFO_COLOR 0 +#endif + +// NRF_SDH_SOC_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_SOC_DEBUG_COLOR +#define NRF_SDH_SOC_DEBUG_COLOR 0 +#endif + +// + +// NRF_SORTLIST_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_SORTLIST_CONFIG_LOG_ENABLED +#define NRF_SORTLIST_CONFIG_LOG_ENABLED 0 +#endif +// NRF_SORTLIST_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SORTLIST_CONFIG_LOG_LEVEL +#define NRF_SORTLIST_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_SORTLIST_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SORTLIST_CONFIG_INFO_COLOR +#define NRF_SORTLIST_CONFIG_INFO_COLOR 0 +#endif + +// NRF_SORTLIST_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SORTLIST_CONFIG_DEBUG_COLOR +#define NRF_SORTLIST_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_TWI_SENSOR_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_TWI_SENSOR_CONFIG_LOG_ENABLED +#define NRF_TWI_SENSOR_CONFIG_LOG_ENABLED 0 +#endif +// NRF_TWI_SENSOR_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_TWI_SENSOR_CONFIG_LOG_LEVEL +#define NRF_TWI_SENSOR_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_TWI_SENSOR_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_TWI_SENSOR_CONFIG_INFO_COLOR +#define NRF_TWI_SENSOR_CONFIG_INFO_COLOR 0 +#endif + +// NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR +#define NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR 0s +#endif + +// + +// PM_LOG_ENABLED - Enable logging in Peer Manager and its submodules. +//========================================================== +#ifndef PM_LOG_ENABLED +#define PM_LOG_ENABLED 1 +#endif +// PM_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PM_LOG_LEVEL +#define PM_LOG_LEVEL 3 +#endif + +// PM_LOG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PM_LOG_INFO_COLOR +#define PM_LOG_INFO_COLOR 0 +#endif + +// PM_LOG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PM_LOG_DEBUG_COLOR +#define PM_LOG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// nrf_log in nRF_Serialization + +//========================================================== +// SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED +#define SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED 0 +#endif +// SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL +#define SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL 3 +#endif + +// SER_HAL_TRANSPORT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SER_HAL_TRANSPORT_CONFIG_INFO_COLOR +#define SER_HAL_TRANSPORT_CONFIG_INFO_COLOR 0 +#endif + +// SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR +#define SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// +//========================================================== + +// + +// NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED - nrf_log_str_formatter - +// Log string formatter + +#ifndef NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED +#define NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED 1 +#endif + +// +//========================================================== + +// nRF_Segger_RTT + +//========================================================== +// segger_rtt - SEGGER RTT + +//========================================================== +// SEGGER_RTT_CONFIG_BUFFER_SIZE_UP - Size of upstream buffer. +// Note that either @ref NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE +// or this value is actually used. It depends on which one is bigger. + +#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_UP +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 2048 +#endif + +// SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS - Maximum number of upstream +// buffers. +#ifndef SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS +#define SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS 2 +#endif + +// SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN - Size of downstream buffer. +#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN 16 +#endif + +// SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS - Maximum number of downstream +// buffers. +#ifndef SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS +#define SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS 2 +#endif + +// SEGGER_RTT_CONFIG_DEFAULT_MODE - RTT behavior if the buffer is full. + +// The following modes are supported: +// - SKIP - Do not block, output nothing. +// - TRIM - Do not block, output as much as fits. +// - BLOCK - Wait until there is space in the buffer. +// <0=> SKIP +// <1=> TRIM +// <2=> BLOCK_IF_FIFO_FULL + +#ifndef SEGGER_RTT_CONFIG_DEFAULT_MODE +#define SEGGER_RTT_CONFIG_DEFAULT_MODE 0 +#endif + +// +//========================================================== + +// +//========================================================== + +// <<< end of configuration section >>> +#endif // SDK_CONFIG_H diff --git a/device/lib/seal_embedded.c b/device/lib/seal_embedded.c new file mode 100644 index 0000000..750cb29 --- /dev/null +++ b/device/lib/seal_embedded.c @@ -0,0 +1,233 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file seal_embedded.c +*/ + +#include "seal_embedded.h" + +#include "ckks_asym.h" +#include "ckks_common.h" +#include "ckks_sym.h" +#include "defines.h" +#include "fileops.h" +#include "parameters.h" +#include "util_print.h" + +static Parms se_encr_params_global; +static SE_PTRS se_ptrs_global; +static SE_PARMS se_parms_global; +static SE_PRNG se_shareable_prng_global; +static SE_PRNG se_prng_global; + +SE_PARMS *se_setup_custom(size_t degree, size_t nprimes, const ZZ *modulus_vals, const ZZ *ratios, + double scale, EncryptType encrypt_type) +{ + SE_PARMS *se_parms = &se_parms_global; + Parms *parms = &se_encr_params_global; + SE_PTRS *se_ptrs = &se_ptrs_global; + + se_assert(se_parms && parms && se_ptrs); + se_parms->parms = parms; + se_parms->se_ptrs = se_ptrs; + + size_t n = degree; + parms->scale = scale; + parms->is_asymmetric = (encrypt_type == SE_ASYM_ENCR); + parms->pk_from_file = 1; + parms->sample_s = 0; + parms->small_u = 1; + parms->small_s = 1; + se_parms->parms = parms; + +#ifdef SE_USE_MALLOC + ZZ *mempool; + if (encrypt_type == SE_ASYM_ENCR) + { + print_ckks_mempool_size(n, 0); + mempool = ckks_mempool_setup_asym(n); + } + else + { + print_ckks_mempool_size(n, 1); + mempool = ckks_mempool_setup_sym(n); + } + se_assert(mempool); +#else + print_ckks_mempool_size(); + printf("In se_setup_custom, no malloc\n"); + // ZZ *mempool = &(mempool_ptr_global[0]); + ZZ *mempool = *mempool_ptr_global; + printf("Address of mempool_ptr_global: %p\n", &mempool_ptr_global); + printf("Value of mempool_ptr_global (should be address of mempool_local): %p\n", + mempool_ptr_global); + printf("Value of mempool (should be equal to value of mempool_ptr_global): %p\n", mempool); +#endif + + if (encrypt_type == SE_ASYM_ENCR) { ckks_set_ptrs_asym(n, mempool, se_ptrs); } + else + { + ckks_set_ptrs_sym(n, mempool, se_ptrs); + } + + if (!modulus_vals || !ratios) { ckks_setup(n, nprimes, se_ptrs->index_map_ptr, parms); } + else + { + ckks_setup_custom(n, nprimes, modulus_vals, ratios, se_ptrs->index_map_ptr, parms); + } + + if (encrypt_type == SE_SYM_ENCR) { ckks_setup_s(parms, NULL, NULL, se_ptrs->ternary); } + + return se_parms; +} + +SE_PARMS *se_setup(size_t degree, size_t nprimes, double scale, EncryptType encrypt_type) +{ + return se_setup_custom(degree, nprimes, NULL, NULL, scale, encrypt_type); +} + +SE_PARMS *se_setup_default(EncryptType encrypt_type) +{ + double scale = pow(2, 25); + // double scale = pow(2, 35); + // double scale = pow(2, 40); + return se_setup(4096, 3, scale, encrypt_type); +} + +bool se_encrypt_seeded(uint8_t *shareable_seed, uint8_t *seed, SEND_FNCT_PTR network_send_function, + void *v, size_t vlen_bytes, bool print, SE_PARMS *se_parms) +{ + se_assert(se_parms); + se_assert(se_parms && se_parms->se_ptrs); + se_assert(se_parms->parms && se_parms->se_ptrs->values); + Parms *parms = se_parms->parms; + SE_PTRS *se_ptrs = se_parms->se_ptrs; + size_t n = parms->coeff_count; + + size_t copy_size_bytes = (n / 2) * sizeof(ZZ); + if (vlen_bytes < copy_size_bytes) copy_size_bytes = vlen_bytes; + memset(se_ptrs->values, 0, copy_size_bytes); + memcpy(se_ptrs->values, v, copy_size_bytes); + + ckks_reset_primes(parms); + + bool ret = ckks_encode_base(parms, se_ptrs->values, n / 2, se_ptrs->index_map_ptr, + se_ptrs->ifft_roots, se_ptrs->conj_vals); + se_assert(ret); + if (!ret) return ret; + // -- Debugging + // print_poly_int64("pt, reg", se_ptrs->conj_vals_int_ptr, n); + + // -- Uncomment this line and the similar line below to check against + // expected values with adapter + // print_poly_int64_full("pt, reg", se_ptrs->conj_vals_int_ptr, n); + + if (parms->is_asymmetric) + { + ckks_asym_init(parms, seed, &se_prng_global, se_ptrs->conj_vals_int_ptr, se_ptrs->ternary, + se_ptrs->e1_ptr); + load_pki(0, parms, se_ptrs->c0_ptr); + load_pki(1, parms, se_ptrs->c1_ptr); + } + else + { + ckks_sym_init(parms, shareable_seed, seed, &se_shareable_prng_global, &se_prng_global, + se_ptrs->conj_vals_int_ptr); + } + // -- Debugging + // print_poly_int64("pte, reg", se_ptrs->conj_vals_int_ptr, n); + + // -- Uncomment this line and the similar line above to check against + // expected values with adapter + // print_poly_int64_full("pte, reg", se_ptrs->conj_vals_int_ptr, n); + + for (size_t i = 0; i < parms->nprimes; i++) + { + if (parms->is_asymmetric) + { + ckks_encode_encrypt_asym(parms, se_ptrs->conj_vals_int_ptr, se_ptrs->ternary, + se_ptrs->e1_ptr, se_ptrs->ntt_roots_ptr, se_ptrs->ntt_pte_ptr, + NULL, NULL, se_ptrs->c0_ptr, se_ptrs->c1_ptr); + } + else + { + ckks_encode_encrypt_sym(parms, se_ptrs->conj_vals_int_ptr, NULL, + &se_shareable_prng_global, se_ptrs->ternary, + se_ptrs->ntt_pte_ptr, se_ptrs->ntt_roots_ptr, se_ptrs->c0_ptr, + se_ptrs->c1_ptr, NULL, NULL); + } + + if (print) + { + print_poly("c0: ", se_ptrs->c0_ptr, n); + print_poly("c1: ", se_ptrs->c1_ptr, n); + } + +#ifndef SE_DISABLE_TESTING_CAPABILITY +#ifndef SE_REVERSE_CT_GEN_ENABLED + // -- Sanity check + se_assert(se_parms->parms->curr_modulus_idx == i); +#endif + // -- Sanity checks + for (size_t i = 0; i < n; i++) + { + se_assert((se_ptrs->c0_ptr)[i] < se_parms->parms->curr_modulus->value); + se_assert((se_ptrs->c1_ptr)[i] < se_parms->parms->curr_modulus->value); + } +#endif + + if (network_send_function) + { + size_t nbytes_send, nbytes_recv; + + // TODO: Finish this feature, add to defines +#ifdef SE_ENABLE_SYM_SEED_CT + if (!parms->is_asymmetric) + { + nbytes_send = SE_PRNG_SEED_BYTE_COUNT; + nbytes_recv = + network_send_function(&(se_shareable_prng_global.seed[0]), nbytes_send); + se_assert(nbytes_recv == nbytes_send); + } + else +#endif + { + nbytes_send = n * sizeof(ZZ); + nbytes_recv = network_send_function(se_ptrs->c0_ptr, nbytes_send); + se_assert(nbytes_recv == nbytes_send); + } + + nbytes_send = n * sizeof(ZZ); + nbytes_recv = network_send_function(se_ptrs->c1_ptr, nbytes_send); + se_assert(nbytes_recv == nbytes_send); + } + + if ((i + 1) < parms->nprimes) + { + if (parms->is_asymmetric) + ckks_next_prime_asym(parms, se_ptrs->ternary); + else + ckks_next_prime_sym(parms, se_ptrs->ternary); + } + } + return true; +} + +bool se_encrypt(SEND_FNCT_PTR network_send_function, void *v, size_t vlen_bytes, bool print, + SE_PARMS *se_parms) +{ + return se_encrypt_seeded(NULL, NULL, network_send_function, v, vlen_bytes, print, se_parms); +} + +void se_cleanup(SE_PARMS *se_parms) +{ +#ifdef SE_USE_MALLOC + delete_parameters(se_parms->parms); + + // -- Free the memory pool. Note that conj_vals should always point to the start. + // -- TODO: Make this better + free(se_parms->se_ptrs->conj_vals); +#endif + se_parms->parms = 0; +} diff --git a/device/lib/seal_embedded.h b/device/lib/seal_embedded.h new file mode 100644 index 0000000..924725e --- /dev/null +++ b/device/lib/seal_embedded.h @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file seal_embedded.h +*/ + +#pragma once + +#include +#include +#include +#include + +#include "ckks_common.h" +#include "defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +Error code values range from 0 to -9999. +Application-specific errors should use values outside this range. +*/ +#define SE_SUCCESS 0 +#define SE_ERR_NO_MEMORY -12 +#define SE_ERR_INVALD_ARGUMENT -22 +#define SE_ERR_UNKNOWN -1000 +/** Minimum negative error code value used by SEAL-Embedded */ +#define SE_ERR_MINIMUM -9999 + +#ifndef SE_USE_MALLOC +static ZZ **mempool_ptr_global; +#endif + +/** +SEAL-Embedded parameters struct for API. + +@param parms Pointer to internal parameters struct +@param se_ptrs Pointer to SE_PTRS struct +*/ +typedef struct +{ + Parms *parms; + SE_PTRS *se_ptrs; +} SE_PARMS; + +typedef enum { SE_SYM_ENCR, SE_ASYM_ENCR } EncryptType; + +/** +Network send function pointer. +The first input parameter should represent a pointer to the data to send. +The second input parameter should represent the size of the data to send. +*/ +typedef size_t (*SEND_FNCT_PTR)(void *, size_t); + +/** +Randomness generator function pointer +The first input parameter should represent a pointer to the buffer to store the random +values. The second input parameter should represent the size of randomness to generate. +The third input parameters can specify any required flags. +*/ +typedef ssize_t (*RND_FNCT_PTR)(void *, size_t, unsigned int flags); + +/* +(Modified from SEAL:) A larger coeff_modulus implies a larger noise budget, hence more +encrypted computation capabilities. However, an upper bound for the total bit-length of +the coeff_modulus is determined by the degree, as follows: + +--------------------------------------------------------+ + | degree | max coeff_modulus | max nprimes | max nprimes | + | | bit-length | (30 bit) | (25 bit) | + +--------------------------------------------------------+ + | 1024 | 27 | 0 | 1 + | 2048 | 54 | 1 | 2 + | 4096 | 109 | 3 | 4 + | 8192 | 218 | 7 | 8 + | 16384 | 438 | 14 | 16 + | 32768 | 881 | 29 | 32 + +--------------------------------------------------------+ +*/ + +/** +SEAL-Embedded offers an easy to use API for easy development. A user should call the +se_setup function once at the start of library initialization and se_encrypt once for +every array of values to encode/encrypt. 'se_cleanup' exists for completeness to free +library memory, but should never need to be called. SEAL-Embedded offers three types of +setup functions for developers, providing different degrees of library configurability. +se_setup_custom allows for the most customization, while se_setup_default uses default +parameter settings. Note: Currently, SEAL-Embedded does not support reconfiguring +parameters to a different parameter set after initial setup. +*/ + +/** +Setups up SEAL-Embedded for a particular encryption type for a custom parameter set, +including a custom degree, number of modulus primes, modulus prime values, and scale. If +either modulus_vals or ratios is NULL, reverts to se_setup functionality. + +Note: This function calls calloc. + +@param[in] degree Polynomial ring degree +@param[in] nprimes Number of prime moduli +@param[in] modulus_vals An array of nprimes type-ZZ modulus values. +@param[in] ratios An array of const_ratio values for each custom modulus value +(high word, followed by low word). +@param[in] scale Scale +@param[in] enc_type Encryption type (symMETRIC or AsymMETRIC) +@returns A handle to the set SE_PARMS instance +*/ +SE_PARMS *se_setup_custom(size_t degree, size_t nprimes, const ZZ *modulus_vals, + const ZZ *ratios, double scale, EncryptType encrypt_type); + +/** +Setups up SEAL-Embedded for a particular encryption type for the requested parameter set. + +Note: This function calls calloc. + +@param[in] degree Polynomial ring degree +@param[in] nprimes Number of prime moduli +@param[in] scale Scale +@param[in] enc_type Encryption type (symMETRIC or AsymMETRIC) +@returns A handle to an SE_PARMS instance +*/ +SE_PARMS *se_setup(size_t degree, size_t nprimes, double scale, EncryptType encrypt_type); + +/** +Setups up SEAL-Embedded for a particular encryption type for a parameter set of degree = +4096, a coefficient modulus of 3 30-bit primes, and a scale of pow(2,25). + +Note: This function calls calloc. + +@param[in] enc_type Encryption type (SE_SYM_ENCR or SE_ASYM_ENCR) +@returns A handle to the set SE_PARMS instance +*/ +SE_PARMS *se_setup_default(EncryptType enc_type); + +bool se_encrypt_seeded(uint8_t *shareable_seed, uint8_t *seed, + SEND_FNCT_PTR network_send_function, void *v, size_t vlen_bytes, + bool print, SE_PARMS *se_parms); + +bool se_encrypt(SEND_FNCT_PTR network_send_function, void *v, size_t vlen_bytes, + bool print, SE_PARMS *se_parms); + +/** +Frees some library memory and resets parameters object. Should never need to be called by +the typical user. +@param[in] se_parms SE_PARMS instance to free +*/ +void se_cleanup(SE_PARMS *se_parms); + +#ifdef __cplusplus +} +#endif diff --git a/device/lib/shake256/CMakeLists.txt b/device/lib/shake256/CMakeLists.txt new file mode 100644 index 0000000..c0189f7 --- /dev/null +++ b/device/lib/shake256/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +set(SE_LIB_SOURCE_FILES ${SE_LIB_SOURCE_FILES} + ${CMAKE_CURRENT_LIST_DIR}/fips202.c +) + +if (NOT SE_BUILD_LOCAL) + set(SE_LIB_SOURCE_FILES ${SE_LIB_SOURCE_FILES} + ${CMAKE_CURRENT_LIST_DIR}/keccakf1600.asm + ) +else() + set(SE_LIB_SOURCE_FILES ${SE_LIB_SOURCE_FILES} + ${CMAKE_CURRENT_LIST_DIR}/keccakf1600.c + ) +endif() + +set(SE_LIB_SOURCE_FILES ${SE_LIB_SOURCE_FILES} PARENT_SCOPE) diff --git a/device/lib/shake256/cgmanifest.json b/device/lib/shake256/cgmanifest.json new file mode 100644 index 0000000..e35e88b --- /dev/null +++ b/device/lib/shake256/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "Registrations": [ + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/microsoft/SEAL", + "commitHash": "2d2a25c916047d2f356d45ad50c98f0a9695905a" + } + } + }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/mupq/pqm4", + "commitHash": "65f12c6c66f10492eac2b434e93fd1aee742f0e1" + } + } + } + { + "component": { + "type": "other", + "Other": { + "Name": "nRF5 SDK", + "Version": "17.1.0", + "DownloadUrl": "https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v12.x.x/doc/12.3.0/nRF5_Nordic_license.txt" + } + } + } + ] +} diff --git a/device/lib/shake256/fips202.c b/device/lib/shake256/fips202.c new file mode 100644 index 0000000..eacb1ef --- /dev/null +++ b/device/lib/shake256/fips202.c @@ -0,0 +1,130 @@ +/* +Origin: Microsoft SEAL library (https://github.com/microsoft/SEAL) commit +2d2a25c916047d2f356d45ad50c98f0a9695905a. +*/ + +/* +This file is a part of the Kyber library (https://github.com/pq-crystals/kyber) +commit 844057468e69527bd15b17fbe03f4b61f9a22065. The Kyber library is licensed +under CC0 Universal, version 1.0. You can find a copy of this license at +https://creativecommons.org/publicdomain/zero/1.0/legalcode +Minor modifications to the original file have been made and marked +as `Microsoft SEAL edit: ...`. +*/ + +/* Based on the public domain implementation in + * crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html + * by Ronny Van Keer + * and the public domain "TweetFips202" implementation + * from https://twitter.com/tweetfips202 + * by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include +#include +/* Microsoft SEAL edit: changed the header file path */ +#include "fips202.h" +#include "keccakf1600.h" + +/* Microsoft SEAL edit: moved the rate macros here from Kyber header fips202.h + */ +#define SHAKE256_RATE 136 + +// Context for non-incremental API +typedef struct +{ + uint64_t ctx[25]; +} shake256ctx; + +/************************************************* + * Name: keccak_absorb + * + * Description: Absorb step of Keccak; + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak + *state + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *m: pointer to input to be absorbed into s + * - size_t mlen: length of input in bytes + * - uint8_t p: domain-separation byte for different + *Keccak-derived functions + **************************************************/ +static void keccak_absorb(uint64_t *s, uint32_t r, const uint8_t *m, size_t mlen, + uint8_t p) +{ + size_t i; + uint8_t t[200]; + + while (mlen >= r) + { + KeccakF1600_StateXORBytes(s, m, 0, r); + KeccakF1600_StatePermute(s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) t[i] = 0; + for (i = 0; i < mlen; ++i) t[i] = m[i]; + t[i] = p; + t[r - 1] |= 128; + + KeccakF1600_StateXORBytes(s, t, 0, r); +} + +/************************************************* + * Name: keccak_squeezeblocks + * + * Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each. + * Modifies the state. Can be called multiple times to keep + *squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *h: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed (written to h) + * - uint64_t *s: pointer to in/output Keccak state + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + **************************************************/ +static void keccak_squeezeblocks(uint8_t *h, size_t nblocks, uint64_t *s, uint32_t r) +{ + while (nblocks > 0) + { + KeccakF1600_StatePermute(s); + KeccakF1600_StateExtractBytes(s, h, 0, r); + h += r; + nblocks--; + } +} + +/************************************************* + * Name: shake256 + * + * Description: SHAKE256 XOF with non-incremental API + * + * Arguments: - uint8_t *out: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *in: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256(uint8_t *output, size_t outlen, const uint8_t *input, size_t inlen) +{ + shake256ctx state; + uint8_t t[SHAKE256_RATE]; + size_t nblocks = outlen / SHAKE256_RATE; + size_t i; + + for (i = 0; i < 25; ++i) { state.ctx[i] = 0; } + + /* Absorb input */ + keccak_absorb((uint64_t *)state.ctx, SHAKE256_RATE, input, inlen, 0x1F); + + /* Squeeze output */ + keccak_squeezeblocks(output, nblocks, (uint64_t *)state.ctx, SHAKE256_RATE); + + output += nblocks * SHAKE256_RATE; + outlen -= nblocks * SHAKE256_RATE; + + if (outlen) + { + keccak_squeezeblocks(t, 1, (uint64_t *)state.ctx, SHAKE256_RATE); + for (i = 0; i < outlen; i++) output[i] = t[i]; + } +} diff --git a/device/lib/shake256/fips202.h b/device/lib/shake256/fips202.h new file mode 100644 index 0000000..ea53481 --- /dev/null +++ b/device/lib/shake256/fips202.h @@ -0,0 +1,14 @@ +/* +Origin: Microsoft SEAL library (https://github.com/microsoft/SEAL) commit +2d2a25c916047d2f356d45ad50c98f0a9695905a. +*/ + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +#pragma once + +#include +#include + +void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); diff --git a/device/lib/shake256/keccakf1600.asm b/device/lib/shake256/keccakf1600.asm new file mode 100644 index 0000000..a106a4e --- /dev/null +++ b/device/lib/shake256/keccakf1600.asm @@ -0,0 +1,739 @@ +@ +@ Origin: pqm4 library (https://github.com/mupq/pqm4) commit 65f12c6c66f10492eac2b434e93fd1aee742f0e1. +@ + +@ +@ Implementation by the Keccak, Keyak and Ketje Teams, namely, Guido Bertoni, +@ Joan Daemen, Michaël Peeters, Gilles Van Assche and Ronny Van Keer, hereby +@ denoted as "the implementer". +@ +@ For more information, feedback or questions, please refer to our websites: +@ http://keccak.noekeon.org/ +@ http://keyak.noekeon.org/ +@ http://ketje.noekeon.org/ +@ +@ To the extent possible under law, the implementer has waived all copyright +@ and related or neighboring rights to the source code in this file. +@ http://creativecommons.org/publicdomain/zero/1.0/ +@ + +@ WARNING: These functions work only on little endian CPU with@ ARMv7m architecture (ARM Cortex-M3, ...). + + + .thumb + .syntax unified +.text + + @ Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 +.macro toBitInterleaving x0,x1,s0,s1,t,over + + and \t,\x0,#0x55555555 + orr \t,\t,\t, LSR #1 + and \t,\t,#0x33333333 + orr \t,\t,\t, LSR #2 + and \t,\t,#0x0F0F0F0F + orr \t,\t,\t, LSR #4 + and \t,\t,#0x00FF00FF + bfi \t,\t,#8, #8 + .if \over != 0 + lsr \s0,\t, #8 + .else + eor \s0,\s0,\t, LSR #8 + .endif + + and \t,\x1,#0x55555555 + orr \t,\t,\t, LSR #1 + and \t,\t,#0x33333333 + orr \t,\t,\t, LSR #2 + and \t,\t,#0x0F0F0F0F + orr \t,\t,\t, LSR #4 + and \t,\t,#0x00FF00FF + orr \t,\t,\t, LSR #8 + eor \s0,\s0,\t, LSL #16 + + and \t,\x0,#0xAAAAAAAA + orr \t,\t,\t, LSL #1 + and \t,\t,#0xCCCCCCCC + orr \t,\t,\t, LSL #2 + and \t,\t,#0xF0F0F0F0 + orr \t,\t,\t, LSL #4 + and \t,\t,#0xFF00FF00 + orr \t,\t,\t, LSL #8 + .if \over != 0 + lsr \s1,\t, #16 + .else + eor \s1,\s1,\t, LSR #16 + .endif + + and \t,\x1,#0xAAAAAAAA + orr \t,\t,\t, LSL #1 + and \t,\t,#0xCCCCCCCC + orr \t,\t,\t, LSL #2 + and \t,\t,#0xF0F0F0F0 + orr \t,\t,\t, LSL #4 + and \t,\t,#0xFF00FF00 + orr \t,\t,\t, LSL #8 + bfc \t, #0, #16 + eors \s1,\s1,\t + .endm + + @ Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 +.macro fromBitInterleaving x0, x1, t + + movs \t, \x0 @ t = x0@ + bfi \x0, \x1, #16, #16 @ x0 = (x0 & 0x0000FFFF) | (x1 << 16)@ + bfc \x1, #0, #16 @ x1 = (t >> 16) | (x1 & 0xFFFF0000)@ + orr \x1, \x1, \t, LSR #16 + + eor \t, \x0, \x0, LSR #8 @ t = (x0 ^ (x0 >> 8)) & 0x0000FF00UL@ x0 = x0 ^ t ^ (t << 8)@ + and \t, #0x0000FF00 + eors \x0, \x0, \t + eor \x0, \x0, \t, LSL #8 + + eor \t, \x0, \x0, LSR #4 @ t = (x0 ^ (x0 >> 4)) & 0x00F000F0UL@ x0 = x0 ^ t ^ (t << 4)@ + and \t, #0x00F000F0 + eors \x0, \x0, \t + eor \x0, \x0, \t, LSL #4 + + eor \t, \x0, \x0, LSR #2 @ t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0CUL@ x0 = x0 ^ t ^ (t << 2)@ + and \t, #0x0C0C0C0C + eors \x0, \x0, \t + eor \x0, \x0, \t, LSL #2 + + eor \t, \x0, \x0, LSR #1 @ t = (x0 ^ (x0 >> 1)) & 0x22222222UL@ x0 = x0 ^ t ^ (t << 1)@ + and \t, #0x22222222 + eors \x0, \x0, \t + eor \x0, \x0, \t, LSL #1 + + eor \t, \x1, \x1, LSR #8 @ t = (x1 ^ (x1 >> 8)) & 0x0000FF00UL@ x1 = x1 ^ t ^ (t << 8)@ + and \t, #0x0000FF00 + eors \x1, \x1, \t + eor \x1, \x1, \t, LSL #8 + + eor \t, \x1, \x1, LSR #4 @ t = (x1 ^ (x1 >> 4)) & 0x00F000F0UL@ x1 = x1 ^ t ^ (t << 4)@ + and \t, #0x00F000F0 + eors \x1, \x1, \t + eor \x1, \x1, \t, LSL #4 + + eor \t, \x1, \x1, LSR #2 @ t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0CUL@ x1 = x1 ^ t ^ (t << 2)@ + and \t, #0x0C0C0C0C + eors \x1, \x1, \t + eor \x1, \x1, \t, LSL #2 + + eor \t, \x1, \x1, LSR #1 @ t = (x1 ^ (x1 >> 1)) & 0x22222222UL@ x1 = x1 ^ t ^ (t << 1)@ + and \t, #0x22222222 + eors \x1, \x1, \t + eor \x1, \x1, \t, LSL #1 + .endm + +@ --- offsets in state +.equ Aba0, 0*4 +.equ Aba1, 1*4 +.equ Abe0, 2*4 +.equ Abe1, 3*4 +.equ Abi0, 4*4 +.equ Abi1, 5*4 +.equ Abo0, 6*4 +.equ Abo1, 7*4 +.equ Abu0, 8*4 +.equ Abu1, 9*4 +.equ Aga0, 10*4 +.equ Aga1, 11*4 +.equ Age0, 12*4 +.equ Age1, 13*4 +.equ Agi0, 14*4 +.equ Agi1, 15*4 +.equ Ago0, 16*4 +.equ Ago1, 17*4 +.equ Agu0, 18*4 +.equ Agu1, 19*4 +.equ Aka0, 20*4 +.equ Aka1, 21*4 +.equ Ake0, 22*4 +.equ Ake1, 23*4 +.equ Aki0, 24*4 +.equ Aki1, 25*4 +.equ Ako0, 26*4 +.equ Ako1, 27*4 +.equ Aku0, 28*4 +.equ Aku1, 29*4 +.equ Ama0, 30*4 +.equ Ama1, 31*4 +.equ Ame0, 32*4 +.equ Ame1, 33*4 +.equ Ami0, 34*4 +.equ Ami1, 35*4 +.equ Amo0, 36*4 +.equ Amo1, 37*4 +.equ Amu0, 38*4 +.equ Amu1, 39*4 +.equ Asa0, 40*4 +.equ Asa1, 41*4 +.equ Ase0, 42*4 +.equ Ase1, 43*4 +.equ Asi0, 44*4 +.equ Asi1, 45*4 +.equ Aso0, 46*4 +.equ Aso1, 47*4 +.equ Asu0, 48*4 +.equ Asu1, 49*4 + +@ --- offsets on stack +.equ mDa0, 0*4 +.equ mDa1, 1*4 +.equ mDo0, 2*4 +.equ mDo1, 3*4 +.equ mDi0, 4*4 +.equ mRC , 5*4 +.equ mSize, 6*4 + + +.macro xor5 result,b,g,k,m,s + + ldr \result, [r0, #\b] + ldr r1, [r0, #\g] + eors \result, \result, r1 + ldr r1, [r0, #\k] + eors \result, \result, r1 + ldr r1, [r0, #\m] + eors \result, \result, r1 + ldr r1, [r0, #\s] + eors \result, \result, r1 + .endm + +.macro xorrol result, aa, bb + + eor \result, \aa, \bb, ROR #31 + .endm + +.macro xandnot resofs, aa, bb, cc + + bic r1, \cc, \bb + eors r1, r1, \aa + str r1, [r0, #\resofs] + .endm + +.macro KeccakThetaRhoPiChiIota aA1, aDax, aA2, aDex, rot2, aA3, aDix, rot3, aA4, aDox, rot4, aA5, aDux, rot5, offset, last + ldr r3, [r0, #\aA1] + ldr r4, [r0, #\aA2] + ldr r5, [r0, #\aA3] + ldr r6, [r0, #\aA4] + ldr r7, [r0, #\aA5] + eors r3, r3, \aDax + eors r5, r5, \aDix + eors r4, r4, \aDex + eors r6, r6, \aDox + eors r7, r7, \aDux + rors r4, #32-\rot2 + rors r5, #32-\rot3 + rors r6, #32-\rot4 + rors r7, #32-\rot5 + xandnot \aA2, r4, r5, r6 + xandnot \aA3, r5, r6, r7 + xandnot \aA4, r6, r7, r3 + xandnot \aA5, r7, r3, r4 + ldr r1, [sp, #mRC] + bics r5, r5, r4 + ldr r4, [r1, #\offset] + eors r3, r3, r5 + eors r3, r3, r4 + .if \last == 1 + ldr r4, [r1, #32]! + str r1, [sp, #mRC] + cmp r4, #0xFF + .endif + str r3, [r0, #\aA1] + .endm + +.macro KeccakThetaRhoPiChi aB1, aA1, aDax, rot1, aB2, aA2, aDex, rot2, aB3, aA3, aDix, rot3, aB4, aA4, aDox, rot4, aB5, aA5, aDux, rot5 + ldr \aB1, [r0, #\aA1] + ldr \aB2, [r0, #\aA2] + ldr \aB3, [r0, #\aA3] + ldr \aB4, [r0, #\aA4] + ldr \aB5, [r0, #\aA5] + eors \aB1, \aB1, \aDax + eors \aB3, \aB3, \aDix + eors \aB2, \aB2, \aDex + eors \aB4, \aB4, \aDox + eors \aB5, \aB5, \aDux + rors \aB1, #32-\rot1 + .if \rot2 > 0 + rors \aB2, #32-\rot2 + .endif + rors \aB3, #32-\rot3 + rors \aB4, #32-\rot4 + rors \aB5, #32-\rot5 + xandnot \aA1, r3, r4, r5 + xandnot \aA2, r4, r5, r6 + xandnot \aA3, r5, r6, r7 + xandnot \aA4, r6, r7, r3 + xandnot \aA5, r7, r3, r4 + .endm + +.macro KeccakRound0 + + xor5 r3, Abu0, Agu0, Aku0, Amu0, Asu0 + xor5 r7, Abe1, Age1, Ake1, Ame1, Ase1 + xorrol r6, r3, r7 + str r6, [sp, #mDa0] + xor5 r6, Abu1, Agu1, Aku1, Amu1, Asu1 + xor5 lr, Abe0, Age0, Ake0, Ame0, Ase0 + eors r8, r6, lr + str r8, [sp, #mDa1] + + xor5 r5, Abi0, Agi0, Aki0, Ami0, Asi0 + xorrol r9, r5, r6 + str r9, [sp, #mDo0] + xor5 r4, Abi1, Agi1, Aki1, Ami1, Asi1 + eors r3, r3, r4 + str r3, [sp, #mDo1] + + xor5 r3, Aba0, Aga0, Aka0, Ama0, Asa0 + xorrol r10, r3, r4 + xor5 r6, Aba1, Aga1, Aka1, Ama1, Asa1 + eors r11, r6, r5 + + xor5 r4, Abo1, Ago1, Ako1, Amo1, Aso1 + xorrol r5, lr, r4 + str r5, [sp, #mDi0] + xor5 r5, Abo0, Ago0, Ako0, Amo0, Aso0 + eors r2, r7, r5 + + xorrol r12, r5, r6 + eors lr, r4, r3 + + KeccakThetaRhoPiChi r5, Aka1, r8, 2, r6, Ame1, r11, 23, r7, Asi1, r2, 31, r3, Abo0, r9, 14, r4, Agu0, r12, 10 + KeccakThetaRhoPiChi r7, Asa1, r8, 9, r3, Abe0, r10, 0, r4, Agi1, r2, 3, r5, Ako0, r9, 12, r6, Amu1, lr, 4 + ldr r8, [sp, #mDa0] + KeccakThetaRhoPiChi r4, Aga0, r8, 18, r5, Ake0, r10, 5, r6, Ami1, r2, 8, r7, Aso0, r9, 28, r3, Abu1, lr, 14 + KeccakThetaRhoPiChi r6, Ama0, r8, 20, r7, Ase1, r11, 1, r3, Abi1, r2, 31, r4, Ago0, r9, 27, r5, Aku0, r12, 19 + ldr r9, [sp, #mDo1] + KeccakThetaRhoPiChiIota Aba0, r8, Age0, r10, 22, Aki1, r2, 22, Amo1, r9, 11, Asu0, r12, 7, 0, 0 + + ldr r2, [sp, #mDi0] + KeccakThetaRhoPiChi r5, Aka0, r8, 1, r6, Ame0, r10, 22, r7, Asi0, r2, 30, r3, Abo1, r9, 14, r4, Agu1, lr, 10 + KeccakThetaRhoPiChi r7, Asa0, r8, 9, r3, Abe1, r11, 1, r4, Agi0, r2, 3, r5, Ako1, r9, 13, r6, Amu0, r12, 4 + ldr r8, [sp, #mDa1] + KeccakThetaRhoPiChi r4, Aga1, r8, 18, r5, Ake1, r11, 5, r6, Ami0, r2, 7, r7, Aso1, r9, 28, r3, Abu0, r12, 13 + KeccakThetaRhoPiChi r6, Ama1, r8, 21, r7, Ase0, r10, 1, r3, Abi0, r2, 31, r4, Ago1, r9, 28, r5, Aku1, lr, 20 + ldr r9, [sp, #mDo0] + KeccakThetaRhoPiChiIota Aba1, r8, Age1, r11, 22, Aki0, r2, 21, Amo0, r9, 10, Asu1, lr, 7, 4, 0 + .endm + +.macro KeccakRound1 + + xor5 r3, Asu0, Agu0, Amu0, Abu1, Aku1 + xor5 r7, Age1, Ame0, Abe0, Ake1, Ase1 + xorrol r6, r3, r7 + str r6, [sp, #mDa0] + xor5 r6, Asu1, Agu1, Amu1, Abu0, Aku0 + xor5 lr, Age0, Ame1, Abe1, Ake0, Ase0 + eors r8, r6, lr + str r8, [sp, #mDa1] + + xor5 r5, Aki1, Asi1, Agi0, Ami1, Abi0 + xorrol r9, r5, r6 + str r9, [sp, #mDo0] + xor5 r4, Aki0, Asi0, Agi1, Ami0, Abi1 + eors r3, r3, r4 + str r3, [sp, #mDo1] + + xor5 r3, Aba0, Aka1, Asa0, Aga0, Ama1 + xorrol r10, r3, r4 + xor5 r6, Aba1, Aka0, Asa1, Aga1, Ama0 + eors r11, r6, r5 + + xor5 r4, Amo0, Abo1, Ako0, Aso1, Ago0 + xorrol r5, lr, r4 + str r5, [sp, #mDi0] + xor5 r5, Amo1, Abo0, Ako1, Aso0, Ago1 + eors r2, r7, r5 + + xorrol r12, r5, r6 + eors lr, r4, r3 + + KeccakThetaRhoPiChi r5, Asa1, r8, 2, r6, Ake1, r11, 23, r7, Abi1, r2, 31, r3, Amo1, r9, 14, r4, Agu0, r12, 10 + KeccakThetaRhoPiChi r7, Ama0, r8, 9, r3, Age0, r10, 0, r4, Asi0, r2, 3, r5, Ako1, r9, 12, r6, Abu0, lr, 4 + ldr r8, [sp, #mDa0] + KeccakThetaRhoPiChi r4, Aka1, r8, 18, r5, Abe1, r10, 5, r6, Ami0, r2, 8, r7, Ago1, r9, 28, r3, Asu1, lr, 14 + KeccakThetaRhoPiChi r6, Aga0, r8, 20, r7, Ase1, r11, 1, r3, Aki0, r2, 31, r4, Abo0, r9, 27, r5, Amu0, r12, 19 + ldr r9, [sp, #mDo1] + KeccakThetaRhoPiChiIota Aba0, r8, Ame1, r10, 22, Agi1, r2, 22, Aso1, r9, 11, Aku1, r12, 7, 8, 0 + + ldr r2, [sp, #mDi0] + KeccakThetaRhoPiChi r5, Asa0, r8, 1, r6, Ake0, r10, 22, r7, Abi0, r2, 30, r3, Amo0, r9, 14, r4, Agu1, lr, 10 + KeccakThetaRhoPiChi r7, Ama1, r8, 9, r3, Age1, r11, 1, r4, Asi1, r2, 3, r5, Ako0, r9, 13, r6, Abu1, r12, 4 + ldr r8, [sp, #mDa1] + KeccakThetaRhoPiChi r4, Aka0, r8, 18, r5, Abe0, r11, 5, r6, Ami1, r2, 7, r7, Ago0, r9, 28, r3, Asu0, r12, 13 + KeccakThetaRhoPiChi r6, Aga1, r8, 21, r7, Ase0, r10, 1, r3, Aki1, r2, 31, r4, Abo1, r9, 28, r5, Amu1, lr, 20 + ldr r9, [sp, #mDo0] + KeccakThetaRhoPiChiIota Aba1, r8, Ame0, r11, 22, Agi0, r2, 21, Aso0, r9, 10, Aku0, lr, 7, 12, 0 + .endm + +.macro KeccakRound2 + + xor5 r3, Aku1, Agu0, Abu1, Asu1, Amu1 + xor5 r7, Ame0, Ake0, Age0, Abe0, Ase1 + xorrol r6, r3, r7 + str r6, [sp, #mDa0] + xor5 r6, Aku0, Agu1, Abu0, Asu0, Amu0 + xor5 lr, Ame1, Ake1, Age1, Abe1, Ase0 + eors r8, r6, lr + str r8, [sp, #mDa1] + + xor5 r5, Agi1, Abi1, Asi1, Ami0, Aki1 + xorrol r9, r5, r6 + str r9, [sp, #mDo0] + xor5 r4, Agi0, Abi0, Asi0, Ami1, Aki0 + eors r3, r3, r4 + str r3, [sp, #mDo1] + + xor5 r3, Aba0, Asa1, Ama1, Aka1, Aga1 + xorrol r10, r3, r4 + xor5 r6, Aba1, Asa0, Ama0, Aka0, Aga0 + eors r11, r6, r5 + + xor5 r4, Aso0, Amo0, Ako1, Ago0, Abo0 + xorrol r5, lr, r4 + str r5, [sp, #mDi0] + xor5 r5, Aso1, Amo1, Ako0, Ago1, Abo1 + eors r2, r7, r5 + + xorrol r12, r5, r6 + eors lr, r4, r3 + + KeccakThetaRhoPiChi r5, Ama0, r8, 2, r6, Abe0, r11, 23, r7, Aki0, r2, 31, r3, Aso1, r9, 14, r4, Agu0, r12, 10 + KeccakThetaRhoPiChi r7, Aga0, r8, 9, r3, Ame1, r10, 0, r4, Abi0, r2, 3, r5, Ako0, r9, 12, r6, Asu0, lr, 4 + ldr r8, [sp, #mDa0] + KeccakThetaRhoPiChi r4, Asa1, r8, 18, r5, Age1, r10, 5, r6, Ami1, r2, 8, r7, Abo1, r9, 28, r3, Aku0, lr, 14 + KeccakThetaRhoPiChi r6, Aka1, r8, 20, r7, Ase1, r11, 1, r3, Agi0, r2, 31, r4, Amo1, r9, 27, r5, Abu1, r12, 19 + ldr r9, [sp, #mDo1] + KeccakThetaRhoPiChiIota Aba0, r8, Ake1, r10, 22, Asi0, r2, 22, Ago0, r9, 11, Amu1, r12, 7, 16, 0 + + ldr r2, [sp, #mDi0] + KeccakThetaRhoPiChi r5, Ama1, r8, 1, r6, Abe1, r10, 22, r7, Aki1, r2, 30, r3, Aso0, r9, 14, r4, Agu1, lr, 10 + KeccakThetaRhoPiChi r7, Aga1, r8, 9, r3, Ame0, r11, 1, r4, Abi1, r2, 3, r5, Ako1, r9, 13, r6, Asu1, r12, 4 + ldr r8, [sp, #mDa1] + KeccakThetaRhoPiChi r4, Asa0, r8, 18, r5, Age0, r11, 5, r6, Ami0, r2, 7, r7, Abo0, r9, 28, r3, Aku1, r12, 13 + KeccakThetaRhoPiChi r6, Aka0, r8, 21, r7, Ase0, r10, 1, r3, Agi1, r2, 31, r4, Amo0, r9, 28, r5, Abu0, lr, 20 + ldr r9, [sp, #mDo0] + KeccakThetaRhoPiChiIota Aba1, r8, Ake0, r11, 22, Asi1, r2, 21, Ago1, r9, 10, Amu0, lr, 7, 20, 0 + .endm + +.macro KeccakRound3 + + xor5 r3, Amu1, Agu0, Asu1, Aku0, Abu0 + xor5 r7, Ake0, Abe1, Ame1, Age0, Ase1 + xorrol r6, r3, r7 + str r6, [sp, #mDa0] + xor5 r6, Amu0, Agu1, Asu0, Aku1, Abu1 + xor5 lr, Ake1, Abe0, Ame0, Age1, Ase0 + eors r8, r6, lr + str r8, [sp, #mDa1] + + xor5 r5, Asi0, Aki0, Abi1, Ami1, Agi1 + xorrol r9, r5, r6 + str r9, [sp, #mDo0] + xor5 r4, Asi1, Aki1, Abi0, Ami0, Agi0 + eors r3, r3, r4 + str r3, [sp, #mDo1] + + xor5 r3, Aba0, Ama0, Aga1, Asa1, Aka0 + xorrol r10, r3, r4 + xor5 r6, Aba1, Ama1, Aga0, Asa0, Aka1 + eors r11, r6, r5 + + xor5 r4, Ago1, Aso0, Ako0, Abo0, Amo1 + xorrol r5, lr, r4 + str r5, [sp, #mDi0] + xor5 r5, Ago0, Aso1, Ako1, Abo1, Amo0 + eors r2, r7, r5 + + xorrol r12, r5, r6 + eors lr, r4, r3 + + KeccakThetaRhoPiChi r5, Aga0, r8, 2, r6, Age0, r11, 23, r7, Agi0, r2, 31, r3, Ago0, r9, 14, r4, Agu0, r12, 10 + KeccakThetaRhoPiChi r7, Aka1, r8, 9, r3, Ake1, r10, 0, r4, Aki1, r2, 3, r5, Ako1, r9, 12, r6, Aku1, lr, 4 + ldr r8, [sp, #mDa0] + KeccakThetaRhoPiChi r4, Ama0, r8, 18, r5, Ame0, r10, 5, r6, Ami0, r2, 8, r7, Amo0, r9, 28, r3, Amu0, lr, 14 + KeccakThetaRhoPiChi r6, Asa1, r8, 20, r7, Ase1, r11, 1, r3, Asi1, r2, 31, r4, Aso1, r9, 27, r5, Asu1, r12, 19 + ldr r9, [sp, #mDo1] + KeccakThetaRhoPiChiIota Aba0, r8, Abe0, r10, 22, Abi0, r2, 22, Abo0, r9, 11, Abu0, r12, 7, 24, 0 + + ldr r2, [sp, #mDi0] + KeccakThetaRhoPiChi r5, Aga1, r8, 1, r6, Age1, r10, 22, r7, Agi1, r2, 30, r3, Ago1, r9, 14, r4, Agu1, lr, 10 + KeccakThetaRhoPiChi r7, Aka0, r8, 9, r3, Ake0, r11, 1, r4, Aki0, r2, 3, r5, Ako0, r9, 13, r6, Aku0, r12, 4 + ldr r8, [sp, #mDa1] + KeccakThetaRhoPiChi r4, Ama1, r8, 18, r5, Ame1, r11, 5, r6, Ami1, r2, 7, r7, Amo1, r9, 28, r3, Amu1, r12, 13 + KeccakThetaRhoPiChi r6, Asa0, r8, 21, r7, Ase0, r10, 1, r3, Asi0, r2, 31, r4, Aso0, r9, 28, r5, Asu0, lr, 20 + ldr r9, [sp, #mDo0] + KeccakThetaRhoPiChiIota Aba1, r8, Abe1, r11, 22, Abi1, r2, 21, Abo1, r9, 10, Abu1, lr, 7, 28, 1 + .endm + + +@---------------------------------------------------------------------------- +@ +@ void KeccakF1600_Initialize( void ) +@ +.align 8 +.global KeccakF1600_Initialize +KeccakF1600_Initialize: + bx lr + + + +@---------------------------------------------------------------------------- +@ +@ void KeccakF1600_StateXORBytes(void *state, const unsigned char *data, unsigned int offset, unsigned int length) +@ +.align 8 +.global KeccakF1600_StateXORBytes +KeccakF1600_StateXORBytes: + cbz r3, KeccakF1600_StateXORBytes_Exit1 + push {r4 - r8, lr} @ then + bic r4, r2, #7 @ offset &= ~7 + adds r0, r0, r4 @ add whole lane offset to state pointer + ands r2, r2, #7 @ offset &= 7 (part not lane aligned) + beq KeccakF1600_StateXORBytes_CheckLanes @ .if offset != 0 + movs r4, r3 @ then, do remaining bytes in first lane + rsb r5, r2, #8 @ max size in lane = 8 - offset + cmp r4, r5 + ble KeccakF1600_StateXORBytes_BytesAlign + movs r4, r5 +KeccakF1600_StateXORBytes_BytesAlign: + sub r8, r3, r4 @ size left + movs r3, r4 + bl __KeccakF1600_StateXORBytesInLane + mov r3, r8 +KeccakF1600_StateXORBytes_CheckLanes: + lsrs r2, r3, #3 @ .if length >= 8 + beq KeccakF1600_StateXORBytes_Bytes + mov r8, r3 + bl __KeccakF1600_StateXORLanes + and r3, r8, #7 +KeccakF1600_StateXORBytes_Bytes: + cbz r3, KeccakF1600_StateXORBytes_Exit + movs r2, #0 + bl __KeccakF1600_StateXORBytesInLane +KeccakF1600_StateXORBytes_Exit: + pop {r4 - r8, pc} +KeccakF1600_StateXORBytes_Exit1: + bx lr + + +@---------------------------------------------------------------------------- +@ +@ __KeccakF1600_StateXORLanes +@ +@ Input: +@ r0 state pointer +@ r1 data pointer +@ r2 laneCount +@ +@ Output: +@ r0 state pointer next lane +@ r1 data pointer next byte to input +@ +@ Changed: r2-r7 +@ +.align 8 +__KeccakF1600_StateXORLanes: +__KeccakF1600_StateXORLanes_LoopAligned: + ldr r4, [r1], #4 + ldr r5, [r1], #4 + ldrd r6, r7, [r0] + toBitInterleaving r4, r5, r6, r7, r3, 0 + strd r6, r7, [r0], #8 + subs r2, r2, #1 + bne __KeccakF1600_StateXORLanes_LoopAligned + bx lr + + +@---------------------------------------------------------------------------- +@ +@ __KeccakF1600_StateXORBytesInLane +@ +@ Input: +@ r0 state pointer +@ r1 data pointer +@ r2 offset in lane +@ r3 length +@ +@ Output: +@ r0 state pointer next lane +@ r1 data pointer next byte to input +@ +@ Changed: r2-r7 +@ +.align 8 +__KeccakF1600_StateXORBytesInLane: + movs r4, #0 + movs r5, #0 + push { r4 - r5 } + add r2, r2, sp +__KeccakF1600_StateXORBytesInLane_Loop: + ldrb r5, [r1], #1 + strb r5, [r2], #1 + subs r3, r3, #1 + bne __KeccakF1600_StateXORBytesInLane_Loop + pop { r4 - r5 } + ldrd r6, r7, [r0] + toBitInterleaving r4, r5, r6, r7, r3, 0 + strd r6, r7, [r0], #8 + bx lr + + + + +@---------------------------------------------------------------------------- +@ +@ void KeccakF1600_StateExtractBytes(void *state, const unsigned char *data, unsigned int offset, unsigned int length) +@ +.align 8 +.global KeccakF1600_StateExtractBytes +KeccakF1600_StateExtractBytes: + cbz r3, KeccakF1600_StateExtractBytes_Exit1 @ .if length != 0 + push {r4 - r8, lr} @ then + bic r4, r2, #7 @ offset &= ~7 + adds r0, r0, r4 @ add whole lane offset to state pointer + ands r2, r2, #7 @ offset &= 7 (part not lane aligned) + beq KeccakF1600_StateExtractBytes_CheckLanes @ .if offset != 0 + movs r4, r3 @ then, do remaining bytes in first lane + rsb r5, r2, #8 @ max size in lane = 8 - offset + cmp r4, r5 + ble KeccakF1600_StateExtractBytes_BytesAlign + movs r4, r5 +KeccakF1600_StateExtractBytes_BytesAlign: + sub r8, r3, r4 @ size left + movs r3, r4 + bl __KeccakF1600_StateExtractBytesInLane + mov r3, r8 +KeccakF1600_StateExtractBytes_CheckLanes: + lsrs r2, r3, #3 @ .if length >= 8 + beq KeccakF1600_StateExtractBytes_Bytes + mov r8, r3 + bl __KeccakF1600_StateExtractLanes + and r3, r8, #7 +KeccakF1600_StateExtractBytes_Bytes: + cbz r3, KeccakF1600_StateExtractBytes_Exit + movs r2, #0 + bl __KeccakF1600_StateExtractBytesInLane +KeccakF1600_StateExtractBytes_Exit: + pop {r4 - r8, pc} +KeccakF1600_StateExtractBytes_Exit1: + bx lr + + +@---------------------------------------------------------------------------- +@ +@ __KeccakF1600_StateExtractLanes +@ +@ Input: +@ r0 state pointer +@ r1 data pointer +@ r2 laneCount +@ +@ Output: +@ r0 state pointer next lane +@ r1 data pointer next byte to input +@ +@ Changed: r2-r5 +@ +.align 8 +__KeccakF1600_StateExtractLanes: +__KeccakF1600_StateExtractLanes_LoopAligned: + ldrd r4, r5, [r0], #8 + fromBitInterleaving r4, r5, r3 + str r4, [r1], #4 + subs r2, r2, #1 + str r5, [r1], #4 + bne __KeccakF1600_StateExtractLanes_LoopAligned + bx lr + + +@---------------------------------------------------------------------------- +@ +@ __KeccakF1600_StateExtractBytesInLane +@ +@ Input: +@ r0 state pointer +@ r1 data pointer +@ r2 offset in lane +@ r3 length +@ +@ Output: +@ r0 state pointer next lane +@ r1 data pointer next byte to input +@ +@ Changed: r2-r6 +@ +.align 8 +__KeccakF1600_StateExtractBytesInLane: + ldrd r4, r5, [r0], #8 + fromBitInterleaving r4, r5, r6 + push {r4, r5} + add r2, sp, r2 +__KeccakF1600_StateExtractBytesInLane_Loop: + ldrb r4, [r2], #1 + subs r3, r3, #1 + strb r4, [r1], #1 + bne __KeccakF1600_StateExtractBytesInLane_Loop + add sp, #8 + bx lr + + + +.align 8 +KeccakF1600_StatePermute_RoundConstantsWithTerminator: + @ 0 1 + .long 0x00000001, 0x00000000 + .long 0x00000000, 0x00000089 + .long 0x00000000, 0x8000008b + .long 0x00000000, 0x80008080 + + .long 0x00000001, 0x0000008b + .long 0x00000001, 0x00008000 + .long 0x00000001, 0x80008088 + .long 0x00000001, 0x80000082 + + .long 0x00000000, 0x0000000b + .long 0x00000000, 0x0000000a + .long 0x00000001, 0x00008082 + .long 0x00000000, 0x00008003 + + .long 0x00000001, 0x0000808b + .long 0x00000001, 0x8000000b + .long 0x00000001, 0x8000008a + .long 0x00000001, 0x80000081 + + .long 0x00000000, 0x80000081 + .long 0x00000000, 0x80000008 + .long 0x00000000, 0x00000083 + .long 0x00000000, 0x80008003 + + .long 0x00000001, 0x80008088 + .long 0x00000000, 0x80000088 + .long 0x00000001, 0x00008000 + .long 0x00000000, 0x80008082 + + .long 0x000000FF @terminator + +@---------------------------------------------------------------------------- +@ +@ void KeccakF1600_StatePermute( void *state ) +@ +.align 8 +.global KeccakF1600_StatePermute +KeccakF1600_StatePermute: + adr r1, KeccakF1600_StatePermute_RoundConstantsWithTerminator + push { r4 - r12, lr } + sub sp, #mSize + str r1, [sp, #mRC] +KeccakF1600_StatePermute_RoundLoop: + KeccakRound0 + KeccakRound1 + KeccakRound2 + KeccakRound3 + bne KeccakF1600_StatePermute_RoundLoop + add sp, #mSize + pop { r4 - r12, pc } diff --git a/device/lib/shake256/keccakf1600.c b/device/lib/shake256/keccakf1600.c new file mode 100644 index 0000000..3ebfcf2 --- /dev/null +++ b/device/lib/shake256/keccakf1600.c @@ -0,0 +1,321 @@ +/* +Origin: pqm4 library (https://github.com/mupq/pqm4) commit +65f12c6c66f10492eac2b434e93fd1aee742f0e1. +*/ + +/* Based on the public domain implementation in + * crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html + * by Ronny Van Keer + * and the public domain "TweetFips202" implementation + * from https://twitter.com/tweetfips202 + * by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include "keccakf1600.h" + +#include +#include + +#define NROUNDS 24 +#define ROL(a, offset) ((a << offset) ^ (a >> (64 - offset))) + +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + (uint64_t)0x0000000000000001ULL, (uint64_t)0x0000000000008082ULL, + (uint64_t)0x800000000000808aULL, (uint64_t)0x8000000080008000ULL, + (uint64_t)0x000000000000808bULL, (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008081ULL, (uint64_t)0x8000000000008009ULL, + (uint64_t)0x000000000000008aULL, (uint64_t)0x0000000000000088ULL, + (uint64_t)0x0000000080008009ULL, (uint64_t)0x000000008000000aULL, + (uint64_t)0x000000008000808bULL, (uint64_t)0x800000000000008bULL, + (uint64_t)0x8000000000008089ULL, (uint64_t)0x8000000000008003ULL, + (uint64_t)0x8000000000008002ULL, (uint64_t)0x8000000000000080ULL, + (uint64_t)0x000000000000800aULL, (uint64_t)0x800000008000000aULL, + (uint64_t)0x8000000080008081ULL, (uint64_t)0x8000000000008080ULL, + (uint64_t)0x0000000080000001ULL, (uint64_t)0x8000000080008008ULL}; + +void KeccakF1600_StateExtractBytes(uint64_t *state, unsigned char *data, + unsigned int offset, unsigned int length) +{ + unsigned int i; + for (i = 0; i < length; i++) + { + data[i] = + (unsigned char)(state[(offset + i) >> 3] >> (8 * ((offset + i) & 0x07))); + } +} + +void KeccakF1600_StateXORBytes(uint64_t *state, const unsigned char *data, + unsigned int offset, unsigned int length) +{ + unsigned int i; + for (i = 0; i < length; i++) + { + state[(offset + i) >> 3] ^= (uint64_t)data[i] << (8 * ((offset + i) & 0x07)); + } +} + +void KeccakF1600_StatePermute(uint64_t *state) +{ + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + // copyFromState(A, state) + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < NROUNDS; round += 2) + { + // prepareTheta + BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase; + BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + // thetaRhoPiChiIotaPrepareTheta(round , A, E) + Da = BCu ^ ROL(BCe, 1); + De = BCa ^ ROL(BCi, 1); + Di = BCe ^ ROL(BCo, 1); + Do = BCi ^ ROL(BCu, 1); + Du = BCo ^ ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = ROL(Age, 44); + Aki ^= Di; + BCi = ROL(Aki, 43); + Amo ^= Do; + BCo = ROL(Amo, 21); + Asu ^= Du; + BCu = ROL(Asu, 14); + Eba = BCa ^ ((~BCe) & BCi); + Eba ^= (uint64_t)KeccakF_RoundConstants[round]; + Ebe = BCe ^ ((~BCi) & BCo); + Ebi = BCi ^ ((~BCo) & BCu); + Ebo = BCo ^ ((~BCu) & BCa); + Ebu = BCu ^ ((~BCa) & BCe); + + Abo ^= Do; + BCa = ROL(Abo, 28); + Agu ^= Du; + BCe = ROL(Agu, 20); + Aka ^= Da; + BCi = ROL(Aka, 3); + Ame ^= De; + BCo = ROL(Ame, 45); + Asi ^= Di; + BCu = ROL(Asi, 61); + Ega = BCa ^ ((~BCe) & BCi); + Ege = BCe ^ ((~BCi) & BCo); + Egi = BCi ^ ((~BCo) & BCu); + Ego = BCo ^ ((~BCu) & BCa); + Egu = BCu ^ ((~BCa) & BCe); + + Abe ^= De; + BCa = ROL(Abe, 1); + Agi ^= Di; + BCe = ROL(Agi, 6); + Ako ^= Do; + BCi = ROL(Ako, 25); + Amu ^= Du; + BCo = ROL(Amu, 8); + Asa ^= Da; + BCu = ROL(Asa, 18); + Eka = BCa ^ ((~BCe) & BCi); + Eke = BCe ^ ((~BCi) & BCo); + Eki = BCi ^ ((~BCo) & BCu); + Eko = BCo ^ ((~BCu) & BCa); + Eku = BCu ^ ((~BCa) & BCe); + + Abu ^= Du; + BCa = ROL(Abu, 27); + Aga ^= Da; + BCe = ROL(Aga, 36); + Ake ^= De; + BCi = ROL(Ake, 10); + Ami ^= Di; + BCo = ROL(Ami, 15); + Aso ^= Do; + BCu = ROL(Aso, 56); + Ema = BCa ^ ((~BCe) & BCi); + Eme = BCe ^ ((~BCi) & BCo); + Emi = BCi ^ ((~BCo) & BCu); + Emo = BCo ^ ((~BCu) & BCa); + Emu = BCu ^ ((~BCa) & BCe); + + Abi ^= Di; + BCa = ROL(Abi, 62); + Ago ^= Do; + BCe = ROL(Ago, 55); + Aku ^= Du; + BCi = ROL(Aku, 39); + Ama ^= Da; + BCo = ROL(Ama, 41); + Ase ^= De; + BCu = ROL(Ase, 2); + Esa = BCa ^ ((~BCe) & BCi); + Ese = BCe ^ ((~BCi) & BCo); + Esi = BCi ^ ((~BCo) & BCu); + Eso = BCo ^ ((~BCu) & BCa); + Esu = BCu ^ ((~BCa) & BCe); + + // prepareTheta + BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + // thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu ^ ROL(BCe, 1); + De = BCa ^ ROL(BCi, 1); + Di = BCe ^ ROL(BCo, 1); + Do = BCi ^ ROL(BCu, 1); + Du = BCo ^ ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = ROL(Ege, 44); + Eki ^= Di; + BCi = ROL(Eki, 43); + Emo ^= Do; + BCo = ROL(Emo, 21); + Esu ^= Du; + BCu = ROL(Esu, 14); + Aba = BCa ^ ((~BCe) & BCi); + Aba ^= (uint64_t)KeccakF_RoundConstants[round + 1]; + Abe = BCe ^ ((~BCi) & BCo); + Abi = BCi ^ ((~BCo) & BCu); + Abo = BCo ^ ((~BCu) & BCa); + Abu = BCu ^ ((~BCa) & BCe); + + Ebo ^= Do; + BCa = ROL(Ebo, 28); + Egu ^= Du; + BCe = ROL(Egu, 20); + Eka ^= Da; + BCi = ROL(Eka, 3); + Eme ^= De; + BCo = ROL(Eme, 45); + Esi ^= Di; + BCu = ROL(Esi, 61); + Aga = BCa ^ ((~BCe) & BCi); + Age = BCe ^ ((~BCi) & BCo); + Agi = BCi ^ ((~BCo) & BCu); + Ago = BCo ^ ((~BCu) & BCa); + Agu = BCu ^ ((~BCa) & BCe); + + Ebe ^= De; + BCa = ROL(Ebe, 1); + Egi ^= Di; + BCe = ROL(Egi, 6); + Eko ^= Do; + BCi = ROL(Eko, 25); + Emu ^= Du; + BCo = ROL(Emu, 8); + Esa ^= Da; + BCu = ROL(Esa, 18); + Aka = BCa ^ ((~BCe) & BCi); + Ake = BCe ^ ((~BCi) & BCo); + Aki = BCi ^ ((~BCo) & BCu); + Ako = BCo ^ ((~BCu) & BCa); + Aku = BCu ^ ((~BCa) & BCe); + + Ebu ^= Du; + BCa = ROL(Ebu, 27); + Ega ^= Da; + BCe = ROL(Ega, 36); + Eke ^= De; + BCi = ROL(Eke, 10); + Emi ^= Di; + BCo = ROL(Emi, 15); + Eso ^= Do; + BCu = ROL(Eso, 56); + Ama = BCa ^ ((~BCe) & BCi); + Ame = BCe ^ ((~BCi) & BCo); + Ami = BCi ^ ((~BCo) & BCu); + Amo = BCo ^ ((~BCu) & BCa); + Amu = BCu ^ ((~BCa) & BCe); + + Ebi ^= Di; + BCa = ROL(Ebi, 62); + Ego ^= Do; + BCe = ROL(Ego, 55); + Eku ^= Du; + BCi = ROL(Eku, 39); + Ema ^= Da; + BCo = ROL(Ema, 41); + Ese ^= De; + BCu = ROL(Ese, 2); + Asa = BCa ^ ((~BCe) & BCi); + Ase = BCe ^ ((~BCi) & BCo); + Asi = BCi ^ ((~BCo) & BCu); + Aso = BCo ^ ((~BCu) & BCa); + Asu = BCu ^ ((~BCa) & BCe); + } + + // copyToState(state, A) + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; + +#undef round +} diff --git a/device/lib/shake256/keccakf1600.h b/device/lib/shake256/keccakf1600.h new file mode 100644 index 0000000..a50ed2c --- /dev/null +++ b/device/lib/shake256/keccakf1600.h @@ -0,0 +1,17 @@ +/* +Origin: pqm4 library (https://github.com/mupq/pqm4) commit +65f12c6c66f10492eac2b434e93fd1aee742f0e1. +*/ + +#ifndef KECCAKF1600_H +#define KECCAKF1600_H + +#include + +void KeccakF1600_StateExtractBytes(uint64_t *state, unsigned char *data, + unsigned int offset, unsigned int length); +void KeccakF1600_StateXORBytes(uint64_t *state, const unsigned char *data, + unsigned int offset, unsigned int length); +void KeccakF1600_StatePermute(uint64_t *state); + +#endif diff --git a/device/lib/timer.c b/device/lib/timer.c new file mode 100644 index 0000000..73d555c --- /dev/null +++ b/device/lib/timer.c @@ -0,0 +1,398 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file timer.c +*/ + +#include "defines.h" + +#ifdef SE_ENABLE_TIMERS +#include "timer.h" + +// TODO: Account for counter overflow + +#ifdef SE_ON_SPHERE_M4 +// -- These values are from CodethinkLabs. We will use this even though +// we are using MediaTek drivers, since MediaTek does not seem to have +// something similar. See the following url: +// https://github.com/CodethinkLabs/mt3620-m4-drivers/blob/bef9d7187a621a79f68683049dc42c5b413a13a4/mt3620/gpt.h +#ifndef ROUND_DIVIDE +#define ROUND_DIVIDE(x, y) ((x + (y / 2)) / y) +#endif +#ifndef MT3620_BUS_CLOCK +#define MT3620_BUS_CLOCK 160000000.0f // Note: This is different from the gpt bus clock! +#endif +#ifndef MT3620_GPT_BUS_CLOCK +#define MT3620_GPT_BUS_CLOCK 26000000.0f +#endif +#ifndef MT3620_GPT_012_HIGH_SPEED +#define MT3620_GPT_012_HIGH_SPEED 32768.0f +#endif +#ifndef MT3620_GPT_012_LOW_SPEED +#define MT3620_GPT_012_LOW_SPEED \ + ROUND_DIVIDE(MT3620_GPT_012_HIGH_SPEED, 33) // (should be 32/33 kHz - according to datasheet) +#endif +#ifndef MT3620_GPT_3_SRC_CLK_HZ +#define MT3620_GPT_3_SRC_CLK_HZ MT3620_GPT_BUS_CLOCK +#endif +#ifndef MT3620_GPT_3_LOW_SPEED +#define MT3620_GPT_3_LOW_SPEED ROUND_DIVIDE(MT3620_GPT_BUS_CLOCK, 26) +#endif +#ifndef MT3620_GPT_3_HIGH_SPEED +#define MT3620_GPT_3_HIGH_SPEED MT3620_GPT_BUS_CLOCK +#endif +#ifndef MT3620_GPT_3_SPEED_RANGE +#define MT3620_GPT_3_SPEED_RANGE (MT3620_GPT_3_HIGH_SPEED - MT3620_GPT_3_LOW_SPEED) +#endif +#ifndef MT3620_GPT_4_HIGH_SPEED +#define MT3620_GPT_4_HIGH_SPEED MT3620_BUS_CLOCK +#endif +#ifndef MT3620_GPT_4_LOW_SPEED +#define MT3620_GPT_4_LOW_SPEED MT3620_BUS_CLOCK / 2 +#endif + +/** +Helper function to get the current value of the counter at global_gpt_timer_id + +@param[out] clc Value of the counter +*/ +static void get_counter(uint32_t *clc) +{ + *clc = mtk_os_hal_gpt_get_cur_count(global_gpt_timer_id); +} + +/** +Helper function to get the frequency of the timer at global_gpt_timer_id + +@param[out] freq Frequency value +*/ +static inline void get_freq(uint32_t *freq) +{ + size_t freq_temp; + switch (global_gpt_timer_id) + { + case OS_HAL_GPT0: + case OS_HAL_GPT1: + case OS_HAL_GPT2: + freq_temp = + global_gpt_high_speed ? MT3620_GPT_012_HIGH_SPEED : MT3620_GPT_012_LOW_SPEED; + break; + case OS_HAL_GPT3: + freq_temp = global_gpt_high_speed ? MT3620_GPT_3_HIGH_SPEED : MT3620_GPT_3_LOW_SPEED; + break; + case OS_HAL_GPT4: + freq_temp = global_gpt_high_speed ? MT3620_GPT_4_HIGH_SPEED : MT3620_GPT_4_LOW_SPEED; + break; + default: return; + } + se_assert(freq_temp < UINT32_MAX); + *freq = freq_temp; +} + +/** +Start the timer at global_gpt_timer_id using global_gpt_high_speed. + +@param[in,out] timer Timer instance +*/ +void start_timer(Timer *timer) +{ + static bool already_started = 0; + if (!already_started) + { + int err = mtk_os_hal_gpt_config(global_gpt_timer_id, global_gpt_high_speed, NULL); + // if (err) printf("Error (gpt config): %d\n", err); + se_assert(!err); + already_started = 1; + } + get_counter(&timer->start); + int err = mtk_os_hal_gpt_start(global_gpt_timer_id); + // if (err) printf("Error (gpt start): %d\n", err); + se_assert_msg(!err, "gpt start"); +} + +/** +Helper function to calculated the elapsed time. Should only be called after timer has been +stopped. + +@param[in] start Starting counter value +@param[in] stop Ending counter value +@returns Elapsed time (in nanoseconds) +*/ +static float calc_elapsed_time(uint32_t start, uint32_t stop) +{ + uint32_t freq; + get_freq(&freq); + static bool here_before = 0; + if (!here_before) printf("frequency (Hz): %u\n", freq); + here_before = 1; + return (float)((double)(stop - start) * (double)1e9 / (double)freq); +} + +/** +Stop the timer at global_gpt_timer_id. Should only be called after timer has been started. + +@param[in,out] timer Timer instance +*/ +void stop_timer(Timer *timer) +{ + get_counter(&(timer->stop)); + printf("called stop_timer. timer->start: %u, timer->stop: %u\n", timer->start, timer->stop); + timer->elapsed_time += calc_elapsed_time(timer->start, timer->stop); + + // -- This will clear the counter too + // int err = + mtk_os_hal_gpt_stop(global_gpt_timer_id); + // if (err) printf("Error (gpt stop): %d\n", err); + // se_assert(!err); +} + +#elif defined(SE_ON_NRF5) +#include "app_error.h" +#include "nrf.h" +#include "nrf_drv_timer.h" // TODO: Check if this is needed + +static nrfx_timer_t timer_instance_global = (nrfx_timer_t)NRFX_TIMER_INSTANCE(0); +static nrfx_timer_config_t timer_config_global = (nrfx_timer_config_t)NRFX_TIMER_DEFAULT_CONFIG; + +/** +Helper function to get the current value of the NRFX0 timer instance + +@param[in] timer Timer instance +@param[out] clc Value of the counter +*/ +static void get_counter(Timer *timer, uint32_t *clc) +{ + // volatile uint32_t time_result = + // nrfx_timer_capture(&(timer->timer_instance), NRF_TIMER_CC_CHANNEL0); + uint32_t time_result = nrfx_timer_capture(timer->timer_instance, NRF_TIMER_CC_CHANNEL0); + // printf("count: %u\n", time_result); + *clc = time_result; +} + +/** +NRF timer timeout function handler. Throws an error if called if debugging is enabled. + +@param[in] event_type +@param[in] p_context +*/ +void se_nrf5_timer_timeout_func(nrf_timer_event_t event_type, void *p_context) +{ + se_assert_msg(0, "Error: Timer expired when it probably should not have!\n"); +} + +/** +Starts the NRFX0 timer instance. + +@param[in,out] timer Timer instance +*/ +void start_timer(Timer *timer) +{ + static bool already_started = 0; + nrfx_err_t ret_val; + if (!already_started) + { + // nrfx_timer_event_handler_t event_handler = &se_nrf5_timer_timeout_func; + nrfx_timer_event_handler_t event_handler = se_nrf5_timer_timeout_func; + // -- Note that a different frequency value would require a change to + // calc_elapsed_time + timer->timer_instance = &timer_instance_global; // (nrfx_timer_t)NRFX_TIMER_INSTANCE(0); + timer->timer_config = + &timer_config_global; // (nrfx_timer_config_t)NRFX_TIMER_DEFAULT_CONFIG; + se_assert(timer->timer_config->frequency == + (nrf_timer_frequency_t)NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY); + se_assert(timer->timer_config->frequency == (nrf_timer_frequency_t)NRF_TIMER_FREQ_16MHz); + se_assert(timer->timer_config->mode == (nrf_timer_mode_t)NRFX_TIMER_DEFAULT_CONFIG_MODE); + se_assert(timer->timer_config->mode == (nrf_timer_mode_t)NRF_TIMER_MODE_TIMER); + se_assert(timer->timer_config->bit_width == + (nrf_timer_bit_width_t)NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH); + se_assert(timer->timer_config->bit_width == (nrf_timer_bit_width_t)NRF_TIMER_BIT_WIDTH_32); + se_assert(timer->timer_config->p_context == NULL); + // ret_val = nrfx_timer_init(&(timer->timer_instance), + // &(timer->timer_config), event_handler); + ret_val = nrfx_timer_init(timer->timer_instance, timer->timer_config, event_handler); + se_assert(ret_val == NRF_SUCCESS); + already_started = 1; + } + // nrfx_timer_clear(&(timer->timer_instance)); + nrfx_timer_clear(timer->timer_instance); + get_counter(timer, &timer->start); + se_assert(timer->start == 0); + // nrfx_timer_enable(&(timer->timer_instance)); + // se_assert(nrfx_timer_is_enabled(&(timer->timer_instance))); + nrfx_timer_enable(timer->timer_instance); + se_assert(nrfx_timer_is_enabled(timer->timer_instance)); +} + +/** +Stops the NRFX0 timer instance. Should only be called after timer has been started. + +@param[in,out] timer Timer instance +*/ +void stop_timer(Timer *timer) +{ + uint32_t time_result; + get_counter(timer, &time_result); + timer->stop = time_result; + // nrfx_timer_disable(&(timer->timer_instance)); + nrfx_timer_disable(timer->timer_instance); + printf("called stop_timer. timer->start: %u, timer->stop: %u\n", timer->start, timer->stop); + se_assert(timer->start < timer->stop); + + // -- Time in ns = [# of ticks = (stop-start)] * [(1/freq) = (seconds/tick)] * + // (10^9 ns/1sec) + // -- We know frequency = 16 MHz = 16 * 10^6 from start_timer + // -- So, return Time in ns = (stop-start) * (1/16) * 10^3 = (stop-start)*62.5 + // -- (Note that the resolution of this timer is only microseconds though) + timer->elapsed_time += (float)((double)(timer->stop - timer->start) * 62.5); +} + +#elif defined(SE_ON_SPHERE_A7) + +/** +Helper function to get the current value of the counter at CNTVCT. + +Note: Uses assembly. + +@param[out] clc Value of the counter +*/ +static void get_counter(uint64_t *clc) +{ + uint32_t *clc32 = (uint32_t *)clc; + __asm volatile( + "ISB\n\t" // memfence + "DSB\n\t" + "DMB\n\t" + "MRRC p15, 1, %0, %1, c14\n\t" // reads CNTVCT into R0 (low) and R1 + // (high) + "ISB\n\t" // memfence + "DSB\n\t" + "DMB\n\t" + : "=r"(clc32[0]), "=r"(clc32[1])); +} + +/** +Helper function to get the frequency of the counter + +@param[out] freq Frequency value +*/ +static void get_freq(uint32_t *freq) +{ + __asm volatile("MRC p15, 0, %0, c14, c0, 0\n\t" : "=r"(freq[0])); +} + +/** +Starts the timer + +@param[in,out] timer Timer instance +*/ +void start_timer(Timer *timer) +{ + get_counter(&(timer->start)); +} + +/** +Helper function to calculated the elapsed time. Should only be called after timer has been +stopped. + +@param[in] start Starting counter value +@param[in] stop Ending counter value +@returns Elapsed time (in nanoseconds) +*/ +static float calc_elapsed_time(uint64_t start, uint64_t stop) +{ + uint32_t freq; + get_freq(&freq); + return (float)((double)(stop - start) * (double)1e9 / (double)freq); +} + +/** +Stops the timer. Should only be called after timer has been started. + +@param[in,out] timer Timer instance +*/ +void stop_timer(Timer *timer) +{ + get_counter(&(timer->stop)); + timer->elapsed_time += calc_elapsed_time(timer->start, timer->stop); +} + +#else + +/** +Starts the timer (uses clock_gettime). + +@param[in,out] timer Timer instance +*/ +void start_timer(Timer *timer) +{ + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timer->start); +} + +/** +Helper function to calculated the elapsed time. Should only be called after timer has been +stopped. + +@param[in] start Starting counter value +@param[in] stop Ending counter value +@returns Elapsed time (in nanoseconds) +*/ +static float calc_elapsed_time(struct timespec start, struct timespec stop) +{ + return (float)(1e9 * (stop.tv_sec - start.tv_sec) + stop.tv_nsec - start.tv_nsec); +} + +/** +Stops the timer (uses clock_gettime). Should only be called after timer has been started. + +@param[in,out] timer Timer instance +*/ +void stop_timer(Timer *timer) +{ + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timer->stop); + timer->elapsed_time += calc_elapsed_time(timer->start, timer->stop); +} +#endif + +/** +Resets the timer instance. + +@param[in,out] timer Timer instance +*/ +void reset_timer(Timer *timer) +{ +#if defined(SE_ON_SPHERE_A7) || defined(SE_ON_SPHERE_M4) || defined(SE_ON_NRF5) + timer->start = 0; + timer->stop = 0; +#else + timer->start.tv_sec = 0; + timer->start.tv_nsec = 0; + timer->stop.tv_sec = 0; + timer->stop.tv_nsec = 0; +#endif + timer->elapsed_time = 0; +} + +/** +Resets the timer instance and starts the timer. + +@param[in,out] timer Timer instance +*/ +void reset_start_timer(Timer *timer) +{ + reset_timer(timer); + start_timer(timer); +} + +/** +Returns the elapsed time of the timer in the requested unit of time. + +@param[in] timer Timer instance +@param[in] unit Time unit +@returns The elapsed time of the timer in the requested unit +*/ +float read_timer(Timer timer, TimeUnit unit) +{ + return timer.elapsed_time / (float)(NANO_SEC / unit); +} +#endif diff --git a/device/lib/timer.h b/device/lib/timer.h new file mode 100644 index 0000000..c582aa9 --- /dev/null +++ b/device/lib/timer.h @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file timer.h + +Timer struct and timing functions. Mainly useful for testing. We leave it as part of the +library in case we want to time internal functions. +*/ + +#pragma once + +#include "defines.h" + +#ifdef SE_ENABLE_TIMERS + #include + + #ifdef SE_ON_SPHERE_M4 + #include "os_hal_gpt.h" +// -- We set these to the clock we want to use +// There are 5 GPT clocks: {0, 1, 3} --> interrupt based, {2, 4} --> free-run +// GPT2 ~= 32KHz or 1Hz, GPT4 = ~= bus clock speed or (1/2)*bus clockspeed +static const uint8_t global_gpt_timer_id = OS_HAL_GPT4; +static const bool global_gpt_high_speed = 1; + #elif defined(SE_ON_NRF5) + #include "nrf.h" + #include "nrf_drv_timer.h" // TODO: Check if this is needed + #include "sdk_config.h" + #elif !defined(SE_ON_SPHERE_A7) + #include + #endif + +/** +Struct for storing time points. + +@param start Starting time +@param stop End time +@param elapsed_time Elapsed time (start-stop) in nanoseconds +@param timer_instance Timer instance (Used only with SE_ON_NRF5). Equal to +NRFX_TIMER_INSTANCE(0) +@param timer_config Timer config (Used only with SE_ON_NRF5). Equal to +NRFX_TIMER_DEFAULT_CONFIG +*/ +typedef struct Timer +{ + #if defined(SE_ON_SPHERE_M4) || defined(SE_ON_NRF5) + uint32_t start; // Starting time + uint32_t stop; // End time + #elif defined(SE_ON_SPHERE_A7) + uint64_t start; // Starting time + uint64_t stop; // End time + #else + struct timespec start; // Starting time + struct timespec stop; // End time + #endif + float elapsed_time; // Elapsed time (start-stop) in nanoseconds + #ifdef SE_ON_NRF5 + nrfx_timer_t *timer_instance; // Timer instance (= NRFX_TIMER_INSTANCE(0)) + nrfx_timer_config_t *timer_config; // Timer config (= NRFX_TIMER_DEFAULT_CONFIG) + #endif +} Timer; + +typedef enum TimeUnit { + SEC = 1UL, + MILLI_SEC = 1000UL, + MICRO_SEC = 1000000UL, + NANO_SEC = 1000000000UL +} TimeUnit; + +void start_timer(Timer *timer); + +void stop_timer(Timer *timer); + +void reset_timer(Timer *timer); + +void reset_start_timer(Timer *timer); + +float read_timer(Timer timer, TimeUnit unit); + +#endif diff --git a/device/lib/uint_arith.c b/device/lib/uint_arith.c new file mode 100644 index 0000000..bec6aa0 --- /dev/null +++ b/device/lib/uint_arith.c @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file uint_arith.c +*/ + +#include "uint_arith.h" + +#include "defines.h" + +// -- Need these to avoid duplicate symbols error + +extern inline uint8_t add_uint32(uint32_t op1, uint32_t op2, uint32_t *res); +extern inline uint8_t add_uint64(uint64_t op1, uint64_t op2, uint64_t *res); + +extern inline void mul_uint32_wide(uint32_t op1, uint32_t op2, uint32_t *res); +extern inline uint32_t mul_uint32_high(uint32_t op1, uint32_t op2); +extern inline uint32_t mul_uint32_low(uint32_t op1, uint32_t op2); diff --git a/device/lib/uint_arith.h b/device/lib/uint_arith.h new file mode 100644 index 0000000..a5fda33 --- /dev/null +++ b/device/lib/uint_arith.h @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file uint_arith.h +*/ + +#pragma once + +#include + +#include "defines.h" + +/** +Adds two uint32_t values. + +@param[in] op1 32-bit Operand 1 +@param[in] op2 32-bit Operand 2 +@param[out] res Pointer to 64-bit result of op1 + op2 (w/o the carry) +@retuns The carry bit +*/ +static inline uint8_t add_uint32(uint32_t op1, uint32_t op2, uint32_t *res) +{ + *res = op1 + op2; + return (uint8_t)(*res < op1); +} + +/** +Adds two uint64_t values. + +@param[in] op1 64-bit Operand 1 +@param[in] op2 64-bit Operand 2 +@param[out] res Pointer to 128-bit result of op1 + op2 (w/o the carry) +@retuns The carry bit +*/ +static inline uint8_t add_uint64(uint64_t op1, uint64_t op2, uint64_t *res) +{ + *res = op1 + op2; + return (uint8_t)(*res < op1); +} + +/** +Multiplies two uint32_t values. + +@param[in] op1 32-bit Operand 1 +@param[in] op2 32-bit Operand 2 +@param[out] res Pointer to full 64-bit result of op1 * op2 +*/ +static inline void mul_uint32_wide(uint32_t op1, uint32_t op2, uint32_t *res) +{ +#ifdef SE_USE_ASM_ARITH + __asm("UMULL %0, %1, %2, %3" : "=r"(res[0]), "=r"(res[1]) : "r"(op1), "r"(op2)); +#else + uint64_t res_temp = (uint64_t)op1 * (uint64_t)op2; + res[0] = (uint32_t)(res_temp & 0xFFFFFFFF); + res[1] = (uint32_t)((res_temp >> 32) & 0xFFFFFFFF); +#endif +} + +/** +Multiplies two uint32_t values and returns the upper 32 bits of the 64-bit result. + +@param[in] op1 Operand 1 +@param[in] op2 Operand 2 +@returns Upper 32 bits of the 64-bit result of op1 * op2 +*/ +static inline uint32_t mul_uint32_high(uint32_t op1, uint32_t op2) +{ + uint32_t res[2]; + mul_uint32_wide(op1, op2, res); + return res[1]; +} + +/** +Multiplies two uint32_t values and returns the lower 32 bits of the 64-bit result. + +@param[in] op1 Operand 1 +@param[in] op2 Operand 2 +@returns Lower 32 bits of the 64-bit result of op1 * op2 +*/ +static inline uint32_t mul_uint32_low(uint32_t op1, uint32_t op2) +{ + return op1 * op2; +} diff --git a/device/lib/uintmodarith.h b/device/lib/uintmodarith.h new file mode 100644 index 0000000..813fe54 --- /dev/null +++ b/device/lib/uintmodarith.h @@ -0,0 +1,340 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file uintmodarith.h +*/ + +#pragma once + +#include // uint64_t + +#include "defines.h" +#include "modulo.h" // barrett_reduce_128, shift_result +#include "uint_arith.h" +#include "uintops.h" // mul_uint64 +#include "util_print.h" + +/** +Modular addition. Correctness: (op1 + op2) <= (2q - 1). + +@param[in] op1 Operand 1 +@param[in] op2 Operand 2 +@param[in] q Modulus. Must be < 2^32 (if ZZ == uint32) +@returns (op1 + op2) mod q +*/ +static inline ZZ add_mod(ZZ op1, ZZ op2, const Modulus *q) +{ + ZZ q_val = q->value; + + // -- We know that the sum can fit into a uint32: + // max(uint32_t) = 2^32 - 1 + // max(q) = 2^32 - 1 + // max(op1 + op2) = 2*max(q) - 1 + // = 2*(2^32 - 1) - 1 + // = 2^33 - 3 + // < max(uint32_t) + + ZZ sum; + add_uint_nocarry(op1, op2, &sum); // We don't need the carry + return shift_result(sum, q_val); +} + +/** +In-place modular addition. Correctness: (op1 + op2) <= (2q - 1). + +@param[in,out] op1 In: Operand 1; Out: (op1 + op2) mod q +@param[in] op2 Operand 2 +@param[in] q Modulus. Must be < 2^32 (if ZZ == uint32) +*/ +static inline void add_mod_inpl(ZZ *op1, ZZ op2, const Modulus *q) +{ + op1[0] = add_mod(op1[0], op2, q); +} + +/** +Modular negation. + +@param[in] op Operand. Must <= q. +@param[in] q Modulus +@returns (-op) mod q +*/ +static inline ZZ neg_mod(ZZ op, const Modulus *q) +{ + ZZsign non_zero = (ZZsign)(op != 0); + ZZ mask = (ZZ)(-non_zero); + + // -- If op == 0, return 0; + // Else, result is q - op (if op < q) + return (q->value - op) & mask; +} + +/** +In-place modular negation. + +@param[in,out] op In: Operand. Must be <= q; Out: (-op) mod q +@param[in] q Modulus +*/ +static inline void neg_mod_inpl(ZZ *op, const Modulus *q) +{ + op[0] = neg_mod(op[0], q); +} + +/** +Modular subtraction. + +@param[in] op1 Operand 1. Must be <= q. +@param[in] op2 Operand 2. Must be <= q. +@param[in] q Modulus +@returns (op1 - op2) mod q +*/ +static inline ZZ sub_mod(ZZ op1, ZZ op2, const Modulus *q) +{ + ZZ negated = neg_mod(op2, q); + return add_mod(op1, negated, q); +} + +/** +In-place modular subtraction. + +@param[in,out] op1 In: Operand 1. Must be <= q; Out: (op1 - op2) mod q +@param[in] op2 Operand 2. Must be <= q +@param[in] q Modulus +*/ +static inline void sub_mod_inpl(ZZ *op1, ZZ op2, const Modulus *q) +{ + add_mod_inpl(op1, neg_mod(op2, q), q); +} + +/** +Modular multiplication using Barrett reduction. + +@param[in] op1 Operand 1 +@param[in] op2 Operand 2 +@param[in] q Modulus +@returns (op1 * op2) mod q +*/ +static inline ZZ mul_mod(ZZ op1, ZZ op2, const Modulus *q) +{ + ZZ product[2]; + mul_uint_wide(op1, op2, product); + return barrett_reduce_wide(product, q); +} + +/** +In-place modular multiplication using Barrett reduction. + +@param[in,out] op1 In: Operand 1; Out: (op1 * op2) mod q +@param[in] op2 Operand 2 +@param[in] q Modulus +*/ +static inline void mul_mod_inpl(ZZ *op1, ZZ op2, const Modulus *q) +{ + op1[0] = mul_mod(op1[0], op2, q); +} + +/** +Modular multiplication of two values followed by a modular addion of a third value. + +@param[in] op1 Operand 1 +@param[in] op2 Operand 2 +@param[in] op3 Operand 3 +@param[in] q Modulus +@returns (op1 + (op2 * op3) mod q) mod q +*/ +static inline ZZ mul_add_mod(ZZ op1, ZZ op2, ZZ op3, const Modulus *q) +{ + return add_mod(op1, mul_mod(op2, op3, q), q); +} + +/** +In-place modular multiplication of two values followed by a modular addition of +a third value. +@param[in,out] op1 In: Operand 1; Out: (op1 + (op2 * op3) mod q) mod q +@param[in] op2 Operand 2 +@param[in] op3 Operand 3 +@param[in] q Modulus +*/ +static inline void mul_add_mod_inpl(ZZ *op1, ZZ op2, ZZ op3, const Modulus *q) +{ + add_mod_inpl(op1, mul_mod(op2, op3, q), q); +} + +/** +Exponentiates a ZZ-type value with respect to a modulus. Prior to exponentiation, +bit-reverses the value. May potentially be faster than calling bit-reverse first and +exponentiate_uint_mod after. + +@param[in] operand Value to exponentiate +@param[in] exponent Exponent to raise operand to +@param[in] logn +@param[in] mod Modulus +@returns [(operand)^(exponent)]_mod +*/ +static inline ZZ exponentiate_uint_mod_bitrev(ZZ operand, ZZ exponent, size_t logn, + const Modulus *mod) +{ + // -- Result is supposed to be only one digit + + // -- Fast cases + if (exponent == 0) return 1; + + size_t shift_count = logn - 1; + // printf("shift count: %zu\n", shift_count); + if (exponent == (1 << shift_count)) return operand; + + // -- Perform binary exponentiation. + ZZ power = operand; + ZZ product = 0; + ZZ intermediate = 1; + // print_zz("exponent begin", exponent); + + // -- Initially: power = operand and intermediate = 1, product is irrelevant. + while (true) + { + if (exponent & (ZZ)(1 << shift_count)) + { + // printf("1 "); + product = mul_mod(power, intermediate, mod); + + // -- Swap product and intermediate + ZZ temp = product; + product = intermediate; + intermediate = temp; + } + // else printf("0 "); + exponent &= (ZZ)(~(1 << shift_count)); + // exponent <<= 1; + if (exponent == 0) break; + // print_zz("exponent", exponent); + product = mul_mod(power, power, mod); + + // -- Swap product and power + ZZ temp = product; + product = power; + power = temp; + shift_count--; + } + // printf("\n"); + return intermediate; +} + +/** +Exponentiates a ZZ-type value with respect to a modulus. + +@param[in] operand Value to exponentiate +@param[in] exponent Exponent to raise operand to +@param[in] mod Modulus +@returns [(operand)^(exponent)]_mod +*/ +static inline ZZ exponentiate_uint_mod(ZZ operand, ZZ exponent, const Modulus *mod) +{ + // -- Result is supposed to be only one digit + + // -- Fast cases + if (exponent == 0) return 1; + if (exponent == 1) return operand; + + // -- Perform binary exponentiation. + ZZ power = operand; + ZZ product = 0; + ZZ intermediate = 1; + // print_zz("exponent begin", exponent); + + // Initially: power = operand and intermediate = 1, product is irrelevant. + while (true) + { + if (exponent & 1) + { + // printf("1 "); + product = mul_mod(power, intermediate, mod); + + // -- Swap product and intermediate + ZZ temp = product; + product = intermediate; + intermediate = temp; + } + // else printf("0 "); + exponent >>= 1; + if (exponent == 0) break; + // print_zz("exponent", exponent); + product = mul_mod(power, power, mod); + + // -- Swap product and power + ZZ temp = product; + product = power; + power = temp; + } + // printf("\n"); + return intermediate; +} + +/** +This struct contains: + - an operand + - a precomputed quotient: floor((operand << 64)/q) = floor(operand * (2^64)/q) for a +specific modulus q. + +When passed to mul_mod, a faster variant of Barrett reduction will be performed. +When used in the ntt, 'operand' stores a particular power of a root of unity. + +Note: This struct is the same as "MultiplyUIntModOperand" in SEAL. +Requires: operand < q. + +@param operand The operand +@param quotient The precomputed quotient +*/ +typedef struct MUMO +{ + ZZ operand; // The operand + ZZ quotient; // The precomputed quotient +} MUMO; + +/** +Modular mult. using a highly-optimized variant of Barrett reduction. Reduces result to [0, +2q- 1]. Correctness: q <= 63-bit, y < q. + +@param[in] x Operand 1 +@param[in] y Operand 2 +@param[in] q Modulus. Must be <= 63 bits +@returns ((x * y) mod q) or (((x * y) mod q) + q) +*/ +static inline ZZ mul_mod_mumo_lazy(ZZ x, const MUMO *y, const Modulus *modulus) +{ + ZZ q = modulus->value; + + // -- We want to calculate [x*y]q (i.e. (x*y) mod q) + // We will use Barrett reduction to calculate r = [x*y]q or [x*y]2q + // The formula for this is: + // r = [ [x*y]b - [floor(x*u/t) * q]b ]b + // = [ [op1]b - [op2 * q]b ]b + // where u = floor(y*t/q), b = 2^64, t = 2^64 + + // -- This will implicitly give us [x*y]b by only returning the lower 64 bits of x*y + ZZ op1 = x * y->operand; + + // -- We can get op2 = floor(x*u/t) by multiplying x with floor(u) and taking the high + // word + ZZ op2 = mul_uint_high(x, y->quotient); + + // -- Use the same trick two more times to get result. + ZZ r = op1 - op2 * q; + + // -- Note that we don't need to call 'shift_result' here in this lazy version + return r; +} + +/** +Modular mult. using a highly-optimized variant of Barrett reduction. Reduces result to [0, +q). Correctness: q <= 63-bit, y < q. + +@param[in] x Operand 1 +@param[in] y Operand 2 +@param[in] q Modulus. Must be <= 63 bits. +@returns (x * y) mod q +*/ +static inline ZZ mul_mod_mumo(ZZ x, const MUMO *y, const Modulus *q) +{ + ZZ r = mul_mod_mumo_lazy(x, y, q); + return shift_result(r, q->value); +} diff --git a/device/lib/uintops.h b/device/lib/uintops.h new file mode 100644 index 0000000..30ac106 --- /dev/null +++ b/device/lib/uintops.h @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file uintops.h +*/ + +#pragma once + +#include // uint64_t + +#include "defines.h" +#include "uint_arith.h" + +/** +Adds two ZZ-type unsigned integers together but does not keep track of the carry. + +@param[in] op1 A pointer to a ZZ-type value +@param[in] op2 A pointer to another ZZ-type value +@param[out] res Pointer to sizeof(ZZ)-sized result op1 + op2 (w/o the carry) +*/ +static inline void add_uint_nocarry(ZZ op1, ZZ op2, ZZ *res) +{ + *res = op1 + op2; +} + +/** +Adds two ZZ-type unsigned integers together. + +@param[in] op1 A pointer to a ZZ-type value +@param[in] op2 A pointer to another ZZ-type value +@param[out] res Pointer to sizeof(ZZ)-sized result of op1 + op2 (w/o the carry) +@returns The carry value +*/ +static inline uint8_t add_uint(ZZ op1, ZZ op2, ZZ *res) +{ + return add_uint32(op1, op2, res); +} + +/** +Multiplies two ZZ-type values and returns the full sized-(2*sizeof(ZZ)) result. + +@param[in] op1 A type-ZZ value +@param[in] op2 Another type-ZZ value +@param[out] res Pointer to full (2*sizeof(ZZ))-sized result of op1 * op2 +*/ +static inline void mul_uint_wide(ZZ op1, ZZ op2, ZZ *res) +{ + mul_uint32_wide(op1, op2, res); +} + +/** +Multiplies two ZZ-type values and returns the upper sizeof(ZZ) bytes of the +sized-(2*sizeof(ZZ)) result. + +@param[in] op1 A type-ZZ value +@param[in] op2 Another type-ZZ value +@returns Upper sizeof(ZZ) bytes of op1 * op2 +*/ +static inline ZZ mul_uint_high(ZZ op1, ZZ op2) +{ + return mul_uint32_high(op1, op2); +} + +/** +Multiplies two ZZ-type values and returns the lower sizeof(ZZ) bytes of the +sized-(2*sizeof(ZZ)) result. + +@param[in] op1 A type-ZZ value +@param[in] op2 Another type-ZZ value +@returns Lower sizeof(ZZ) bytes of op1 * op2 +*/ +static inline ZZ mul_uint_low(ZZ op1, ZZ op2) +{ + return mul_uint32_low(op1, op2); +} + +/** +Adds two 128-bit unsigned integers together. + +@param[in] op1 A pointer to a 128-bit value +@param[in] op2 A pointer to another 128-bit value +@param[out] res Pointer to the 128-bit result of op1 + op2 (w/o the carry) +@returns The carry value +*/ +static inline unsigned char add_uint128(const uint64_t *op1, const uint64_t *op2, + uint64_t *res) +{ + uint64_t res_right; + uint64_t carry_right = (uint64_t)add_uint64(op1[0], op2[0], (uint64_t *)&res_right); + + uint64_t res_left; + unsigned char carry_left = add_uint64(op1[1], op2[1], (uint64_t *)&res_left); + + // -- Carry for below should be 0, so don't need to save it + add_uint64(res_left, carry_right, (uint64_t *)&res_left); + + res[1] = res_left; + res[0] = res_right; + + return carry_left; +} diff --git a/device/lib/user_defines.h b/device/lib/user_defines.h new file mode 100644 index 0000000..e652dc5 --- /dev/null +++ b/device/lib/user_defines.h @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file user_defines.h +*/ + +#pragma once + +#include +#include // PRIu32, PRIu64 // not even sure this is needed... +#include // uint64_t, UINT64_MAX +#include +#include // size_t +#include // memset + +#ifdef SE_ON_SPHERE_M4 +#include "mt3620.h" +#include "os_hal_dma.h" +#include "os_hal_gpt.h" +#include "os_hal_uart.h" +#endif + +// ============================================================================== +// Basic configurations +// +// Instructions: Configure each of the following by choosing a numbered value. +// These must match the platform's capabilities or library will not work. +// ============================================================================== +/** +Assert type. Will be force-set to 0 if in cmake release mode. + +0 = none (will define NDEBUG) +1 = standard using +2 = custom. +*/ +#define SE_ASSERT_TYPE 1 + +/** +Randomness generation type. + +Note: Tests will only work if set to 0 on the Azure sphere M4 -- Not secure! +To use this library on the Sphere M4, you need to build a partner application +to sample randomness using an A7 application and pass it to this library. + +0 = none (For debugging only. Will not pass all tests) +1 = use getrandom() +2 = nrf5 rng +*/ +#define SE_RAND_TYPE 1 + +// ============================================================================== +// Basic configurations: Memory +// +// Instructions: Configure each of the following by choosing a numbered value +// for more control over memory allocation. +// ============================================================================== + +/** +Inverse FFT type. + +Note: On the Sphere M4, will not work if this is non-otf for n = 4K and in Debug mode. + +0 = compute "on-the-fly" +1 = load +2 = compute "one-shot" (not yet supported) +*/ +#define SE_IFFT_TYPE 0 + +/** +NTT type. + +Note: On the Sphere M4, will not work if this is load-fast for n = 4K and in Debug mode. + +0 = compute "on-the-fly" +1 = compute "one-shot" +2 = load +3 = load fast +*/ +#define SE_NTT_TYPE 0 + +/** +Index map type. + +0 = compute "on-the-fly" +1 = compute persistant +2 = load +3 = load persistant +*/ +#define SE_INDEX_MAP_TYPE 0 + +/** +Secret key type. (Ignored in asymmetric mode). + +Note: If SE_IFFT_TYPE is set as 0, a setting of 1 here will be reset to 2 (same amount of +memory for better performance). + +0 = not persistent +1 = persistent across primes +2 = persistent +*/ +#define SE_SK_TYPE 0 + +/** +Data load type. Load method for precomputed keys, roots, and index map. + +Note: On M4, this will be force set to 1. + +TODO: Error when this is 1 and index map is loaded or ifft is loaded + +0 = from file (uses file I/O) +1 = copy from headers into separate buffer (no file I/O) +2 = load from code directly (not supported) +*/ +#define SE_DATA_LOAD_TYPE 0 + +// ============================================================================= +// Optional / Advanced Configurations +// +// Instructions: Configure each of the following by commenting/uncommenting +// ============================================================================= + +/** +Include memory for "values" allocation as part of the initial memory pool. +Uncomment to use. +*/ +#define SE_MEMPOOL_ALLOC_VALUES + +/** +Uncomment to use predefine implementation for conj(), creal(), and cimag(). Otherwise, +treats complex values as two doubles (a real part followed by an imaginary part). +*/ +#define SE_USE_PREDEF_COMPLEX_FUNCS + +/** +Sometimes malloc is not available or stack memory allocation is more desireable. +Comment out to use stack allocation instead. +TODO: Not fully working as yet to comment this out. +*/ +#define SE_USE_MALLOC + +/** +Optimization to generate prime components in reverse order every other time. Enables more +optimal memory usage and performance. Will be ignored if IFFT type is "compute on-the-fly" +or NTT type is "compute on-the-fly" or "compute one-shot". Uncomment to use. +TODO: Enable this feature in adapter. +*/ +// #define SE_REVERSE_CT_GEN_ENABLED + +/** +Explicitly sets some unnecessary function arguments (i.e., arguments only necessary for +testing) to 0 at the start of the function for better performance. If defined, testing +will no longer function correctly. Uncomment to use. +*/ +// #define SE_DISABLE_TESTING_CAPABILITY + +// ============================================================================= +// Malloc off configurations +// +// Instructions: Configurations that apply when SE_USE_MALLOC is undefined +// Uncomment/comment as needed and set desired values for n, # of primes. +// ============================================================================= +/** +Encryption type to use if malloc is not enabled (uncomment to use asymmetric encryption) +*/ +#define SE_ENCRYPT_TYPE_SYMMETRIC + +/** +Number of primes to use if malloc is not enabled. +*/ +#define SE_NPRIMES 3 + +/** +Polynomial ring degree to use if malloc is not enabled +*/ +#define SE_DEGREE_N 4096 + +// ============================================================================= +// Bare-metal configurations +// +// Instructions: Configure each of the following by commenting/uncommenting +// These configurations will be ignored if platform type is A7 or none. +// ============================================================================= +/** +Include the code file containing hard-coded public key values. +This file can be generated using the SEAL-Embedded adapter. +(Ignored if SE_DATA_LOAD_TYPE is "from file".) +*/ +// #define SE_DEFINE_PK_DATA + +/** +Include the code file containing hard-coded secret key values. +This file can be generated using the SEAL-Embedded adapter. +(Ignored if SE_DATA_LOAD_TYPE is "from file".) +*/ +#define SE_DEFINE_SK_DATA + +/** +Print using UART (will be disabled if platform type != NRF5). + +If you are on NRF5 and want output to go to RTT, make sure RETARGET_ENABLED is set to 0 in +sdk_config.h (and that RTT is enabled in the configuration window) and comment out this +preprocessor. + +If you don not want output to go to RTT, do the opposite of above and uncomment this +preprocessor. +*/ +// #define SE_NRF5_UART_PRINTF_ENABLED diff --git a/device/lib/util_print.h b/device/lib/util_print.h new file mode 100644 index 0000000..4896345 --- /dev/null +++ b/device/lib/util_print.h @@ -0,0 +1,892 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file util_print.h + +Helpful print functions +*/ + +#pragma once + +#include +#include // fabs +#include // bool +#include // uint64_t +#include // size_t + +#include "defines.h" +#include "sample.h" // get_small_poly_idx + +// clang-format off +#ifdef SE_ON_SPHERE_A7 + #include // Log_Debug + #define printf(...) Log_Debug(__VA_ARGS__) +#elif defined(SE_ON_SPHERE_M4) + #include "printf.h" + // #include "mt3620.h" + // #include "os_hal_uart.h" + // #include "os_hal_gpt.h" +#elif defined(SE_ON_NRF5) + #include "sdk_config.h" +#else + #include +#endif + +#ifdef SE_NRF5_UART_PRINTF_ENABLED + #include "app_error.h" + #include "app_uart.h" + #include "bsp.h" // defines for UART_PRESENT and UARTE_PRESENT + #include "nrf.h" + #ifdef UART_PRESENT + #include "nrf_uart.h" + #endif + #ifdef UARTE_PRESENT + #include "nrf_uarte.h" + #endif +#endif +// clang-format on + +#ifdef SE_ON_SPHERE_M4 +extern void _putchar(char character); +#endif + +/** +The printf string for printing floating point values. Modify to change the printing +precision. +*/ +#define SE_PRINT_PREC_STR "%0.2f" + +/** +The printf string for printing complex floating point values. Modify to change the +printing precision. +*/ +#define SE_PRINT_PREC_CMPLX_STR "%0.2f + %0.2fi" + +/** +Helper function to return the value of the print length == min(len, PRINT_LEN_SMALL) + +@param[in] len +@returns Print length +*/ +static inline size_t get_print_len(size_t len) +{ +#ifdef SE_PRINT_SMALL + if (PRINT_LEN_SMALL < len) return PRINT_LEN_SMALL; +#endif + return len; +} + +/** +Helper function to print a comma + +@param[in] idx Current idx +@param[in] len +*/ +static inline void print_comma(size_t idx, size_t len) +{ + if (idx < (len - 1)) + printf(", "); + else + printf(" "); +} + +/** +Helper function to print the end string. + +@param[in] print_len +@param[in] len +*/ +static inline void print_end_string(size_t print_len, size_t len) +{ + const char *end_str = (len == print_len) ? "}\n" : "... }\n"; + printf("%s", end_str); +} + +/** +Prints an unsigned integer value with name. + +@param[in] name Name of value +@param[in] val Value to print +*/ +static inline void print_zz(const char *name, ZZ val) +{ + printf("%s: %" PRIuZZ "\n", name, val); +} + +/** +Prints an unsigned integer value. + +@param[in] val Value to print +*/ +static inline void print_zz_plain(ZZ val) +{ + printf("%" PRIuZZ, val); +} + +/** +Prints a signed integer value. + +@param[in] val Value to print +*/ +static inline void print_zzi_plain(ZZsign val) +{ + printf("%" PRIiZZ, val); +} + +/** +Print banner for memory pool size. + +@param[in] mempool_size Size of the memory pool +*/ +static inline void print_mem_use(size_t mempool_size) +{ + printf("Total memory required: %zu bytes (= %zu x sizeof(uint32_t) = %zu KB)\n", + mempool_size * sizeof(ZZ), mempool_size, mempool_size * sizeof(ZZ) / 1024); +} + +/** +Prints an array of double complex values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' double complex values (or min(PRINT_LEN_SMALL, len) +double complex values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_double_complex(const char *name, const double complex *a, + PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + if (fabs(se_cimag(a[i])) > 0.0001) + printf(SE_PRINT_PREC_CMPLX_STR, se_creal(a[i]), se_cimag(a[i])); + else + printf(SE_PRINT_PREC_STR, se_creal(a[i])); + + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of double complex values + +Size_req: 'a' must have at least 'len' double complex values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_double_complex_full(const char *name, const double complex *a, + PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + if (fabs(se_cimag(a[i])) > 0.0001) + printf(SE_PRINT_PREC_CMPLX_STR, se_creal(a[i]), se_cimag(a[i])); + else + printf(SE_PRINT_PREC_STR, se_creal(a[i])); + + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of type flpt values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' flpt values (or min(PRINT_LEN_SMALL, len) flpt +values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_flpt(const char *name, const flpt *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + se_assert(print_len == 8); + for (PolySizeType i = 0; i < print_len; i++) + { + printf(SE_PRINT_PREC_STR, (flpt)a[i]); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of type flpt values + +Size_req: 'a' must have at least 'len' flpt values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_flpt_full(const char *name, const flpt *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + printf(SE_PRINT_PREC_STR, (flpt)a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of doubles + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' double values (or min(PRINT_LEN_SMALL, len) double +values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_double(const char *name, const double *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + printf(SE_PRINT_PREC_STR, a[i]); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of doubles + +Size_req: 'a' must have at least 'len' double values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_double_full(const char *name, const double *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + printf(SE_PRINT_PREC_STR, a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of type ZZsign values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' ZZsign values (or min(PRINT_LEN_SMALL, len) ZZsign +values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_sign(const char *name, const ZZsign *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + print_zzi_plain((ZZsign)(a[i])); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of type ZZsign values + +Size_req: 'a' must have at least 'len' ZZsign values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_sign_full(const char *name, const ZZsign *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + print_zzi_plain((ZZsign)(a[i])); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of int8_t values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' int8_t values (or min(PRINT_LEN_SMALL, len) int8_t +values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_int8(const char *name, const int8_t *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + printf("%" PRIi8, a[i]); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of uint8_t values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' uint8_t values (or min(PRINT_LEN_SMALL, len) uint8_t +values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_uint8(const char *name, const uint8_t *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + printf("%" PRIu8, a[i]); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of int8_t values + +Size_req: 'a' must have at least 'len' int8_t values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_int8_full(const char *name, const int8_t *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + printf("%" PRIi8, a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of uint8_t values + +Size_req: 'a' must have at least 'len' uint8_t values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_uint8_full(const char *name, const uint8_t *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + printf("%" PRIu8, a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of int64_t values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' int64_t values (or min(PRINT_LEN_SMALL, len) +int64_t values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_int64(const char *name, const int64_t *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + printf("%" PRIi64, a[i]); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of int64_t values + +Size_req: 'a' must have at least 'len' int64_t values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_int64_full(const char *name, const int64_t *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + printf("%" PRIi64, a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of type-ZZ values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' type-ZZ values (or min(PRINT_LEN_SMALL, len) +type-ZZ values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly(const char *name, const ZZ *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + print_zz_plain(a[i]); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of type-ZZ values + +Size_req: 'a' must have at least 'len' type-ZZ values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_full(const char *name, const ZZ *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + print_zz_plain(a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of type-ZZ values along with their indices. + +Size_req: 'a' must have at least 'len' type-ZZ values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_debug_full(const char *name, const ZZ *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + print_zz_plain(a[i]); + printf(" (%zu)", i); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of values stored in a compressed (2 bits per value) form. + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 2*'len' bits (or 2*min(PRINT_LEN_SMALL, len) bits, if +SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_small(const char *name, const ZZ *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + uint8_t val = get_small_poly_idx(a, i); + print_zz_plain((ZZ)val); + print_comma(i, len); + } + print_end_string(print_len, len); +} + +/** +Prints an array of values stored in a compressed (2 bits per value) form. + +Size_req: 'a' must have at least 2*'len' bits + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_small_full(const char *name, const ZZ *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + uint8_t val = get_small_poly_idx(a, i); + print_zz_plain((ZZ)val); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints the array storing the values of a ternary polynomial. +Calls the corrrect print function depending on if 'a' is in small/compressed form. + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: If small == 1, 'a' must have at least 2*'len' bits (or 2*min(PRINT_LEN_SMALL, +len) bits, if SE_PRINT_SMALL is defined). Else, 'a' must have at least 'len' type-ZZ +values (or min(PRINT_LEN_SMALL, len) type-ZZ values, if SE_PRINT_SMALL is defined). + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +@param[in] small Set to 1 if 'a' is in small/compressed form +*/ +static inline void print_poly_ternary(const char *name, const ZZ *a, PolySizeType len, bool small) +{ + if (small) + print_poly_small(name, a, len); + else + print_poly(name, a, len); +} + +/** +Prints the array storing the values of a ternary polynomial. +Calls the corrrect print function depending on if 'a' is in small/compressed form. + +Size_req: If small == 1, 'a' must have at least 2*'len' bits. +Else, 'a' must have at least 'len' type-ZZ values. + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +@param[in] small Set to 1 if 'a' is in small/compressed form +*/ +static inline void print_poly_ternary_full(const char *name, const ZZ *a, PolySizeType len, + bool small) +{ + if (small) + print_poly_small_full(name, a, len); + else + print_poly_full(name, a, len); +} + +/** +Prints an array of uint16_t values + +Note: If SE_PRINT_SMALL is defined, will only print up to PRINT_LEN_SMALL elements of 'a' + +Size_req: 'a' must have at least 'len' uint16_t values (or min(PRINT_LEN_SMALL, len) +uint16_t values, if SE_PRINT_SMALL is defined) + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_uint16(const char *name, const uint16_t *a, PolySizeType len) +{ + size_t print_len = get_print_len(len); + printf("%s : { ", name); + for (PolySizeType i = 0; i < print_len; i++) + { + printf("%" PRIu16, (uint16_t)a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints an array of uint16_t values + +Size_req: 'a' must have at least 'len' uint16_t values + +@param[in] name Name of array +@param[in] a Array to print +@param[in] len Number of elements of 'a' to print +*/ +static inline void print_poly_uint16_full(const char *name, const uint16_t *a, PolySizeType len) +{ + printf("%s : { ", name); + for (PolySizeType i = 0; i < len; i++) + { + printf("%" PRIu16, (uint16_t)a[i]); + print_comma(i, len); + } + printf("}\n"); +} + +/** +Prints a banner with chosen configuration options. + +@param[in] sym Set to 1 if in symmetric mode (will just toggle whether secret key type +option is printed) +*/ +static inline void print_config(bool sym) +{ + const char *platform_str = " Platform target :"; + const char *debug_str = " Mode :"; + const char *values_str = " Values allocation :"; + const char *assembly_str = "Using inline assembly? :"; + const char *malloc_str = " Using malloc? :"; + const char *predef_cplx_str = " Using predef complex? :"; + const char *timers_str = " Timers enabled? :"; + const char *getrand_str = " Randomness enabled? :"; + const char *ct_rev_str = " Reverse ct enabled? :"; + const char *data_load_str = " Data load type :"; + const char *assert_str = " Assert type :"; + const char *ifft_str = " IFFT type :"; + const char *ntt_str = " NTT type :"; + const char *index_map_str = " Index map type :"; + const char *s_str = " Secret key type :"; + // const char *zz_str = " ZZ type :"; + +#ifdef SE_ON_SPHERE_A7 + printf("%s Azure Sphere A7 Core (#define SE_ON_SPHERE_A7)\n", platform_str); +#elif defined(SE_ON_SPHERE_M4) + printf("%s Azure Sphere M4 Core (#define SE_ON_SPHERE_M4)\n", platform_str); +#else + printf("%s Generic\n", platform_str); +#endif + +// -- Debug type +#if defined(NDEBUG) && !defined(SE_DEBUG_WITH_ZEROS) && !defined(SE_VERBOSE_TESTING) + printf( + "%s Non-Debug (#define NDEBUG, but SE_DEBUG_WITH_ZEROS and " + "SE_VERBOSE_TESTING not defined)\n", + debug_str); +#elif defined(NDEBUG) && (defined(SE_DEBUG_WITH_ZEROS) || defined(SE_VERBOSE_TESTING)) + printf("%s Debug (#define NDEBUG, SE_DEBUG_WITH_ZEROS, SE_VERBOSE_TESTING)\n", debug_str); +#elif !defined(NDEBUG) + printf("%s Debug (NDEBUG is not defined)\n", debug_str); +#endif + +#ifdef SE_MEMPOOL_ALLOC_VALUES + printf("%s Part of memory pool (#define SE_MEMPOOL_ALLOC_VALUES)\n", values_str); +#else + printf("%s Not part of memory pool\n", values_str); +#endif + +#ifdef SE_USE_ASM_ARITH + printf("%s Yes (#define SE_USE_ASM_ARITH)\n", assembly_str); +#else + printf("%s No\n", assembly_str); +#endif + +#ifdef SE_USE_MALLOC + printf("%s Yes (#define SE_USE_MALLOC)\n", malloc_str); +#else + printf("%s No\n", malloc_str); + printf("Degree N: %zu\n", (size_t)SE_DEGREE_N); + printf("Number of primes: %zu\n", (size_t)SE_NPRIMES); +#endif + +#ifdef SE_USE_PREDEF_COMPLEX_FUNCS + printf("%s Yes (#define SE_USE_PREDEF_COMPLEX_FUNCS)\n", predef_cplx_str); +#else + printf("%s No\n", predef_cplx_str); +#endif + +#ifdef SE_ENABLE_TIMERS + printf("%s Yes (#define SE_ENABLE_TIMERS)\n", timers_str); +#else + printf("%s No\n", timers_str); +#endif + +#ifdef SE_RAND_GETRANDOM + printf("%s Yes (#define SE_RAND_GETRANDOM)\n", getrand_str); +#elif defined(SE_RAND_NRF5) + printf("%s Yes (#define SE_RAND_NRF5)\n", getrand_str); +#else + printf("%s No\n", getrand_str); +#endif + +#ifdef SE_REVERSE_CT_GEN_ENABLED + printf("%s Yes (#define SE_REVERSE_CT_GEN_ENABLED)\n", ct_rev_str); +#else + printf("%s No\n", ct_rev_str); +#endif + +#ifdef SE_DATA_FROM_CODE_COPY + printf("%s from code copy (#define SE_DATA_FROM_CODE_COPY)\n", data_load_str); +#elif defined(SE_DATA_FROM_CODE_DIRECT) + printf("%s from code direct (#define SE_DATA_FROM_CODE_DIRECT)\n", data_load_str); +#else + printf("%s from file (requires an operating system)\n", data_load_str); +#endif + +// -- Assert type +#ifdef SE_ASSERT_STANDARD +#ifdef NDEBUG + printf( + "%s Standard/None (with NDEBUG defined) (assert.h, #define " + "SE_ASSERT_STANDARD)\n", + assert_str); +#else + printf( + "%s Standard (without NDEBUG defined) (assert.h, #define " + "SE_ASSERT_STANDARD)\n", + assert_str); +#endif +#elif defined(SE_ASSERT_CUSTOM) + printf("%s Custom (se_assert(), #define SE_ASSERT_CUSTOM)\n", assert_str); +#else + printf("%s None\n", assert_str); +#endif + + // printf("%s uint32_t\n", zz_str); + +#ifdef SE_IFFT_OTF + printf("%s compute on-the-fly (#define SE_IFFT_OTF)\n", ifft_str); +#elif defined(SE_IFFT_ONE_SHOT) + printf("%s compute one-shot (#define SE_IFFT_ONE_SHOT)\n", ifft_str); +#elif defined(SE_IFFT_LOAD_FULL) + printf("%s pre-alloc & load (#define SE_IFFT_LOAD_FULL)\n", ifft_str); +#else + printf("%s Error! IFFT type not chosen\n"); + while (1) + ; +#endif + +#ifdef SE_NTT_OTF + printf("%s compute on-the-fly (#define SE_NTT_OTF)\n", ntt_str); +#elif defined(SE_NTT_ONE_SHOT) + printf("%s compute one-shot (#define SE_NTT_ONE_SHOT)\n", ntt_str); +#elif defined(SE_NTT_REG) + printf("%s load (from flash) (#define SE_NTT_REG)\n", ntt_str); +#elif defined(SE_NTT_FAST) + printf("%s load fast (aka \"lazy\") (from flash) (#define SE_NTT_FAST)\n", ntt_str); +#elif defined(SE_NTT_NONE) + printf("%s None (i.e.. Toom-Cook Karatsuba) (#define SE_NTT_NONE)\n", ntt_str); +#else + printf("%s Error! NTT type not chosen\n"); + while (1) + ; +#endif + +#ifdef SE_INDEX_MAP_OTF + printf("%s compute on-the-fly (#define SE_INDEX_MAP_OTF)\n", index_map_str); +#elif defined(SE_INDEX_MAP_LOAD) + printf("%s load (not persistent) (#define SE_INDEX_MAP_LOAD)\n", index_map_str); +#elif defined(SE_INDEX_MAP_PERSIST) + printf("%s compute persistent (#define SE_INDEX_MAP_PERSIST)\n", index_map_str); +#elif defined(SE_INDEX_MAP_LOAD_PERSIST) + printf("%s load + persistent (#define SE_INDEX_MAP_LOAD_PERSIST)\n", index_map_str); +#else + printf("%s Error! Index map type not chosen\n"); + while (1) + ; +#endif + + if (sym) + { +#ifdef SE_SK_NOT_PERSISTENT + printf( + "%s not persistent (L loads per sequence) (#define " + "SE_SK_NOT_PERSISTENT)\n", + s_str); +#elif defined(SE_SK_PERSISTENT_ACROSS_PRIMES) + printf("%s persistent across primes (1 load per sequence) ", s_str); + printf("(#define SE_SK_PERSISTENT_ACROSS_PRIMES)\n"); +#elif defined(SE_SK_PERSISTENT) + printf("%s truly persistent (1 load only) (#define SE_SK_PERSISTENT)\n", s_str); +#else + printf("%s Error! Index map type not chosen\n"); + while (1) + ; +#endif + } +} + +#ifdef SE_NRF5_UART_PRINTF_ENABLED +/** +Error handler for uart, required for uart on the NRF5. + +@param p_event +*/ +static inline void uart_error_handle(app_uart_evt_t *p_event) +{ + if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR) + { + APP_ERROR_HANDLER(p_event->data.error_communication); + } + else if (p_event->evt_type == APP_UART_FIFO_ERROR) + { + APP_ERROR_HANDLER(p_event->data.error_code); + } +} + +// -- When UART is used for communication with the host do not use flow control +#define UART_TX_BUF_SIZE 256 +#define UART_RX_BUF_SIZE 256 +#define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED + +/** +Sets up UART on the NRF. Required for printing. +*/ +static inline void se_setup_uart() +{ + // bsp_board_init(BSP_INIT_LEDS); + uint32_t err_code; + const app_uart_comm_params_t comm_params = { + RX_PIN_NUMBER, TX_PIN_NUMBER, RTS_PIN_NUMBER, CTS_PIN_NUMBER, UART_HWFC, false, +#ifdef UART_PRESENT + NRF_UART_BAUDRATE_115200 +#else + NRF_UARTE_BAUDRATE_115200 +#endif + }; + + APP_UART_FIFO_INIT(&comm_params, UART_RX_BUF_SIZE, UART_TX_BUF_SIZE, uart_error_handle, + APP_IRQ_PRIORITY_LOWEST, err_code); + // APP_ERROR_CHECK(err_code); + se_assert(err_code == NRF_SUCCESS); +} +#endif diff --git a/device/linker.ld b/device/linker.ld new file mode 100644 index 0000000..5055ae7 --- /dev/null +++ b/device/linker.ld @@ -0,0 +1,80 @@ +/** + * This code is based on a sample from Microsoft (see license below), + * with modifications made by MediaTek. + * Modified version of linker.ld from Microsoft Azure Sphere sample code: + * https://github.com/Azure/azure-sphere-samples/blob/master/Samples/HelloWorld/HelloWorld_RTApp_MT3620_BareMetal/linker.ld + **/ + +/* Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the MIT License. */ + +MEMORY +{ + TCM (rwx) : ORIGIN = 0x00100000, LENGTH = 192K + SYSRAM (rwx) : ORIGIN = 0x22000000, LENGTH = 64K + FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 1M +} + +/* The data and BSS regions can be placed in TCM or SYSRAM. The code and read-only regions can + be placed in TCM, SYSRAM, or FLASH. See + https://docs.microsoft.com/en-us/azure-sphere/app-development/memory-latency for information + about which types of memory which are available to real-time capable applications on the + MT3620, and when they should be used. */ + +/* REGION_ALIAS("CODE_REGION", TCM); */ +REGION_ALIAS("CODE_REGION", FLASH); +/* REGION_ALIAS("CODE_REGION", SYSRAM); */ + +/* REGION_ALIAS("RODATA_REGION", TCM); */ +REGION_ALIAS("RODATA_REGION", FLASH); +/* REGION_ALIAS("RODATA_REGION", SYSRAM); */ + +/* REGION_ALIAS("DATA_REGION", TCM); */ +REGION_ALIAS("DATA_REGION", SYSRAM); + +REGION_ALIAS("BSS_REGION", TCM); +/* REGION_ALIAS("BSS_REGION", SYSRAM); */ + +ENTRY(__isr_vector) +SECTIONS +{ + /* The exception vector's virtual address must be aligned to a power of two, + which is determined by its size and set via CODE_REGION. See definition of + ExceptionVectorTable in main.c. + + When the code is run from XIP flash, it must be loaded to virtual address + 0x10000000 and be aligned to a 32-byte offset within the ELF file. */ + .text : ALIGN(32) { + __vector_table_start__ = .; + KEEP(*(.vector_table)) + __vector_table_end__ = .; + *(.text) + } >CODE_REGION + + .isr_vector_tcm : ALIGN(512) { + KEEP(*(.vector_table_tcm)) + } >DATA_REGION + + .rodata : { + *(.rodata) + } >RODATA_REGION + + .data : { + *(.data) + } >DATA_REGION + + .bss : { + __bss_start__ = .; + *(.bss) + __bss_end__ = .; + } >BSS_REGION + + . = ALIGN(4); + end = .; + + .sysram : { + *(.sysram) + } >SYSRAM + + StackTop = ORIGIN(TCM) + LENGTH(TCM); +} diff --git a/device/scripts/app_manifest_sphere_a7.txt b/device/scripts/app_manifest_sphere_a7.txt new file mode 100644 index 0000000..f5d8805 --- /dev/null +++ b/device/scripts/app_manifest_sphere_a7.txt @@ -0,0 +1,13 @@ +{ + "SchemaVersion": 1, + "Name": "SealEmbeddedApp", + "ComponentId": "5a4b43d1-a3b0-4f9f-a9c9-30a4cc52a2f2", + "EntryPoint": "/bin/app", + "CmdArgs": [], + "Capabilities": { + "Gpio": [ 9 ], + "AllowedApplicationConnections": [], + "AllowedConnections": [ "www.neverssl.com", "neverssl.com", "httpstat.us"] + }, + "ApplicationType": "Default" + } diff --git a/device/scripts/app_manifest_sphere_m4.txt b/device/scripts/app_manifest_sphere_m4.txt new file mode 100644 index 0000000..b73d60d --- /dev/null +++ b/device/scripts/app_manifest_sphere_m4.txt @@ -0,0 +1,13 @@ +{ + "SchemaVersion": 1, + "Name": "SealEmbeddedApp", + "ComponentId": "5a4b43d1-a3b0-4f9f-a9c9-30a4cc52a2f2", + "EntryPoint": "/bin/app", + "CmdArgs": [], + "Capabilities": { + "Gpio": [ 9 ], + "AllowedApplicationConnections": [], + "AllowedConnections": [ "www.neverssl.com", "neverssl.com", "httpstat.us"] + }, + "ApplicationType": "RealTimeCapable" + } diff --git a/device/scripts/commands_sphere_a7.gdb b/device/scripts/commands_sphere_a7.gdb new file mode 100644 index 0000000..de2f866 --- /dev/null +++ b/device/scripts/commands_sphere_a7.gdb @@ -0,0 +1,4 @@ +set tcp connect-timeout unlimited +set pagination off +target remote 192.168.35.2:2345 +c diff --git a/device/scripts/commands_sphere_m4.gdb b/device/scripts/commands_sphere_m4.gdb new file mode 100644 index 0000000..c96603f --- /dev/null +++ b/device/scripts/commands_sphere_m4.gdb @@ -0,0 +1,3 @@ +set tcp connect-timeout unlimited +set pagination off +target remote :4444 diff --git a/device/scripts/gdb_launch_sphere_a7.sh b/device/scripts/gdb_launch_sphere_a7.sh new file mode 100644 index 0000000..d09837b --- /dev/null +++ b/device/scripts/gdb_launch_sphere_a7.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +# ***************************************************************************** +# -- File: gdb_launch_sphere_a7.sh +# +# -- Description: +# This script runs the azure sphere gdb debug tool for debugging SEAL-Embedded +# a SEAL-Embedded application on an azure sphere device and follows the +# instructions listen under 'Debug the Sample' at the following URL: +# +# https://docs.microsoft.com/en-us/azure-sphere/install/qs-blink-application +# +# This script is meant to be run in conjuction with sphere_a7_launch_cl.sh +# (from another terminal) and targets the a7 core. +# +# -- Instructions: +# Navigate to folder with "SEAL_EMBEDDED.out" and run this script. +# Make sure to replace 'sysroot' with the desired sysroot version to use. +# and edit path variables according to your setup as needed. +# ***************************************************************************** + +sysroot=10 + +SCRIPT_DIR_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +AZSPHERE_SDK_PATH="/opt/azurespheresdk" + +# -- Lines 1, 2, 3 +$AZSPHERE_SDK_PATH/Sysroots/$sysroot/tools/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-musleabi/arm-poky-linux-musleabi-gdb \ +--command=$SCRIPT_DIR_PATH/commands_sphere_a7.gdb \ +./SEAL_EMBEDDED.out diff --git a/device/scripts/gdb_launch_sphere_m4.sh b/device/scripts/gdb_launch_sphere_m4.sh new file mode 100644 index 0000000..a8a567d --- /dev/null +++ b/device/scripts/gdb_launch_sphere_m4.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +# ***************************************************************************** +# -- File: gdb_launch_sphere_m4.sh +# +# -- Description: +# This script runs the azure sphere gdb debug tool for debugging SEAL-Embedded +# a SEAL-Embedded application on an azure sphere device and follows the +# instructions listen under 'Debug the Sample' at the following URL: +# +# https://docs.microsoft.com/en-us/azure-sphere/install/qs-real-time-application + +# This script is meant to be run in conjuction with sphere_m4_launch_cl.sh +# (from another terminal) and targets the m4 core. +# +# -- Instructions: +# Navigate to folder with "SEAL_EMBEDDED.out" and run this script. +# Make sure to replace 'sysroot' with the desired sysroot version to use. +# and edit path variables according to your setup as needed. +# ***************************************************************************** + +sysroot=10 + +SCRIPT_DIR_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +GNU_TOOLCHAIN_PATH="${HOME}/gcc-arm-none-eabi-10.3-2021.07" + +$GNU_TOOLCHAIN_PATH/bin/arm-none-eabi-gdb \ +--command=$SCRIPT_DIR_PATH/commands_sphere_m4.gdb \ +./SEAL_EMBEDDED.out diff --git a/device/scripts/minirc_sphere_m4.dfl b/device/scripts/minirc_sphere_m4.dfl new file mode 100644 index 0000000..1ee7086 --- /dev/null +++ b/device/scripts/minirc_sphere_m4.dfl @@ -0,0 +1,8 @@ +# Machine-generated file - use "minicom -s" to change parameters. +pu port /dev/ttyUSB3 +pu baudrate 115200 +pu bits 8 +pu parity N +pu stopbits 1 +pu minit +pu rtscts No diff --git a/device/scripts/ocd_launch_sphere_m4.sh b/device/scripts/ocd_launch_sphere_m4.sh new file mode 100644 index 0000000..a8dbf5b --- /dev/null +++ b/device/scripts/ocd_launch_sphere_m4.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +# ***************************************************************************** +# -- File: gdb_launch_sphere_m4.sh +# +# -- Description: +# This script runs the azure sphere gdb debug tool for debugging SEAL-Embedded +# a SEAL-Embedded application on an azure sphere device and follows the +# instructions listen under 'Debug the Sample' at the following URL: +# +# https://docs.microsoft.com/en-us/azure-sphere/install/qs-real-time-application + +# This script is meant to be run in conjuction with sphere_m4_launch_cl.sh +# (from another terminal) and targets the m4 core. +# +# -- Instructions: +# Navigate to folder with "SEAL_EMBEDDED.out" and run this script. +# Make sure to replace 'sysroot' with the desired sysroot version to use. +# and edit path variables according to your setup as needed. +# ***************************************************************************** + +sysroot=10 +AZSPHERE_SDK_PATH="/opt/azurespheresdk" +core="targets io0" + +# -- Replace above line with this if targetting Core 1 instead +# core="targets io1" + +# -- Save current path +# dir=$(pwd) + +# -- Change directories +cd $AZSPHERE_SDK_PATH/Sysroots/$sysroot/tools/sysroots/x86_64-pokysdk-linux/ + +# -- Run the on-chip debugger +./usr/bin/openocd -f mt3620-rdb-ftdi.cfg -f mt3620-io0.cfg -c "gdb_memory_map disable" -c "gdb_breakpoint_override hard" -c init -c "${core}" -c halt -c "targets" diff --git a/device/scripts/sphere_a7_launch_cl.sh b/device/scripts/sphere_a7_launch_cl.sh new file mode 100644 index 0000000..2b620a7 --- /dev/null +++ b/device/scripts/sphere_a7_launch_cl.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +# ***************************************************************************** +# -- File: sphere_a7_launch_cl.sh +# +# -- Description: +# This script runs some azure sphere commands to launch a SEAL-Embedded +# application in debug mode on an azure sphere and follows the instructions +# listed under 'Run the Sample' and 'Debug the Sample' at the following URL: +# +# https://docs.microsoft.com/en-us/azure-sphere/install/qs-blink-application +# +# This script is meant to be run in conjuction with gdb_launch_a7.sh (from +# another terminal) and targets the a7 core. +# +# Note that the component ID for the SEAL Embedded image package can be found +# in app_manifest.json or via the following command: +# +# azsphere image-package show --image-package SEAL_EMBEDDED.imagepackage +# +# -- Instructions: +# Nagivate to the folder where SEAL_EMBEDDED.imagepackage is created and run +# this script. +# ***************************************************************************** + +# -- Application component ID, found in app_manifest.json +comp_id=5a4b43d1-a3b0-4f9f-a9c9-30a4cc52a2f2 + +AZSPHERE_SDK_PATH="/opt/azurespheresdk" + +SCRIPT_DIR_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +cp $SCRIPT_DIR_PATH/app_manifest_sphere_a7.txt app_manifest.json + +# -- Make sure we are connected +$AZSPHERE_SDK_PATH/DeviceConnection/azsphere_connect.sh + +# -- Uncomment this to delete any applications that were previously loaded +# on the device first +azsphere device sideload delete + +# -- If the application is already running, stop it first +azsphere device app stop --component-id $comp_id + +# -- Load the applicaiton onto the device +azsphere device sideload deploy --image-package SEAL_EMBEDDED.imagepackage + +# -- Start running the application in debug mode +azsphere device app start --debug-mode --component-id $comp_id +#azsphere device app start --component-id $comp_id + +# -- Connect to the output port +telnet 192.168.35.2 2342 diff --git a/device/scripts/sphere_m4_launch_cl.sh b/device/scripts/sphere_m4_launch_cl.sh new file mode 100644 index 0000000..c3f65b6 --- /dev/null +++ b/device/scripts/sphere_m4_launch_cl.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +# ***************************************************************************** +# -- File: sphere_m4_launch_cl.sh +# +# -- Description: +# This script runs some azure sphere commands to launch a SEAL-Embedded +# application in debug mode on an azure sphere and follows the instructions +# listed under 'Run the Sample' and 'Debug the Sample' at the following URL: +# +# https://docs.microsoft.com/en-us/azure-sphere/install/qs-blink-application +# +# Note that the component ID for the SEAL Embedded image package can be found +# in app_manifest.json or via the following command: +# +# azsphere image-package show --image-package SEAL_EMBEDDED.imagepackage +# +# -- Instructions: +# Nagivate to the folder where SEAL_EMBEDDED.imagepackage is created and run +# this script. +# ***************************************************************************** + +# -- Application component ID, found in app_manifest.json +comp_id=5a4b43d1-a3b0-4f9f-a9c9-30a4cc52a2f2 + +AZSPHERE_SDK_PATH="/opt/azurespheresdk" + +SCRIPT_DIR_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +cp $SCRIPT_DIR_PATH/app_manifest_sphere_m4.txt app_manifest.json + +# -- Make sure we are connected +$AZSPHERE_SDK_PATH/DeviceConnection/azsphere_connect.sh + +# -- Uncomment this to delete any applications that were previously loaded +# on the device first +azsphere device sideload delete + +# -- If the application is already running, stop it first +# azsphere device app stop --component-id $comp_id + +# -- Load the applicaiton onto the device +azsphere device sideload deploy --image-package SEAL_EMBEDDED.imagepackage + +# -- Start running the application in debug mode +azsphere device app start --debug-mode --component-id $comp_id +#azsphere device app start --component-id $comp_id diff --git a/device/test/CMakeLists.txt b/device/test/CMakeLists.txt new file mode 100644 index 0000000..a1454bd --- /dev/null +++ b/device/test/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +set(SE_TESTS_SOURCE_FILES ${SE_TESTS_SOURCE_FILES} + ${CMAKE_CURRENT_LIST_DIR}/fft_tests.c + ${CMAKE_CURRENT_LIST_DIR}/modulo_tests.c + ${CMAKE_CURRENT_LIST_DIR}/network_tests.c + ${CMAKE_CURRENT_LIST_DIR}/sample_tests.c + ${CMAKE_CURRENT_LIST_DIR}/uintmodarith_tests.c + ${CMAKE_CURRENT_LIST_DIR}/uintops_tests.c + ${CMAKE_CURRENT_LIST_DIR}/ntt_tests.c + ${CMAKE_CURRENT_LIST_DIR}/ckks_tests_encode.c + ${CMAKE_CURRENT_LIST_DIR}/ckks_tests_common.c + ${CMAKE_CURRENT_LIST_DIR}/ckks_tests_asym.c + ${CMAKE_CURRENT_LIST_DIR}/ckks_tests_sym.c + ${CMAKE_CURRENT_LIST_DIR}/api_tests.c + ${CMAKE_CURRENT_LIST_DIR}/main.c +) + +set(SE_TESTS_SOURCE_FILES ${SE_TESTS_SOURCE_FILES} PARENT_SCOPE) diff --git a/device/test/api_tests.c b/device/test/api_tests.c new file mode 100644 index 0000000..bed434c --- /dev/null +++ b/device/test/api_tests.c @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file api_tests.c +*/ + +#include +#include + +#include "ckks_tests_common.h" +#include "defines.h" +#include "seal_embedded.h" +#include "test_common.h" +#include "util_print.h" + +// -- Comment out to run true test +// #define SE_API_TESTS_DEBUG + +/** +Function to print ciphertext values with the same function signature as SEND_FNCT_PTR. +Used in place of a networking function for testing. + +Size req: v must be at least vlen_bytes + +@param[in] v Input polynomial (ciphertext) to be printed +@param[in] vlen_bytes Number of bytes of v to print +@returns Then number of bytes of v that were printed (equal to vlen_bytes on +success) +*/ +size_t test_print_ciphertexts(void *v, size_t vlen_bytes) +{ + static int idx = 0; + size_t vlen = vlen_bytes / sizeof(ZZ); + const char *name = idx ? "c1" : "c0"; +#ifdef SE_API_TESTS_DEBUG + print_poly(name, (ZZ *)v, vlen); +#else + print_poly_full(name, (ZZ *)v, vlen); +#endif + idx = idx ? 0 : 1; + return vlen_bytes; +} + +/** +Helper function to tests the default SEAL-Embedded API for by printing values of a +ciphertext. If SE_DISABLE_TESTING_CAPABILITY is not defined, throws an error on failure. +On success, resulting output can be verified with the SEAL-Embedded adapter. +*/ +void test_ckks_api_base(SE_PARMS *se_parms) +{ + se_assert(se_parms); + se_assert(se_parms->parms); + se_assert(se_parms->se_ptrs); + + SEND_FNCT_PTR fake_network_func = (void *)&test_print_ciphertexts; + + size_t vlen = se_parms->parms->coeff_count / 2; +#ifdef SE_USE_MALLOC + flpt *v = calloc(vlen, sizeof(flpt)); +#else + flpt v[SE_DEGREE_N / 2]; + se_assert(SE_DEGREE_N / 2 == se_parms->parms->coeff_count / 2); +#endif + + for (size_t testnum = 0; testnum < 9; testnum++) + { + // -- Set up buffer v with testing values + set_encode_encrypt_test(testnum, vlen, &(v[0])); + + // -- Print the expected output. This will help the adapter with verification. +#ifdef SE_API_TESTS_DEBUG + print_poly_flpt("v (cleartext)", v, vlen); +#else + print_poly_flpt_full("v (cleartext)", v, vlen); +#endif + + // -- Call the main encryption function! + /* + // -- Seed for testing + uint8_t share_seed[SE_PRNG_SEED_BYTE_COUNT]; + uint8_t seed[SE_PRNG_SEED_BYTE_COUNT]; + memset(&(share_seed[0]), 0, SE_PRNG_SEED_BYTE_COUNT); + memset(&(seed[0]), 0, SE_PRNG_SEED_BYTE_COUNT); + share_seed[0] = 1; + seed[0] = 1; + bool ret = se_encrypt_seeded(share_seed, seed, fake_network_func, &(v[0]), + vlen * sizeof(flpt), false, se_parms); + se_assert(ret); + */ + se_encrypt(fake_network_func, &(v[0]), vlen * sizeof(flpt), false, se_parms); + } +#ifdef SE_USE_MALLOC + free(v); +#endif + delete_parameters(se_parms->parms); +} + +/** +Tests the default SEAL-Embedded API for symmetric encryption by printing values of a +ciphertext. If SE_DISABLE_TESTING_CAPABILITY is not defined, throws an error on failure. +On success, resulting output can be verified with the SEAL-Embedded adapter. +*/ +void test_ckks_api_sym(void) +{ + printf("Beginning tests for ckks api symmetric encrypt...\n"); +#ifndef SE_USE_MALLOC + static ZZ mempool_local[MEMPOOL_SIZE]; + *mempool_ptr_global = &(mempool_local[0]); + // -- Debugging + // printf("Address of mempool_local: %p\n", &(mempool_local[0])); + // printf("Address of mempool_ptr_global: %p\n", &mempool_ptr_global); + // printf("Value of mempool_ptr_global (should be address of mempool_local): %p\n", + // mempool_ptr_global); +#endif + SE_PARMS *se_parms = se_setup_default(SE_SYM_ENCR); + print_test_banner("Symmetric Encryption (API)", se_parms->parms); + test_ckks_api_base(se_parms); +} + +/** +Tests the default SEAL-Embedded API for asymmetric encryption by printing values of a +ciphertext. If SE_DISABLE_TESTING_CAPABILITY is not defined, throws an error on failure. +On success, resulting output can be verified with the SEAL-Embedded adapter. +*/ +void test_ckks_api_asym(void) +{ + printf("Beginning tests for ckks api asymmetric encrypt...\n"); +#ifndef SE_USE_MALLOC + static ZZ mempool_local[MEMPOOL_SIZE]; + *mempool_ptr_global = &(mempool_local[0]); + // -- Debugging + // printf("Address of mempool_local: %p\n", &(mempool_local[0])); + // printf("Address of mempool_ptr_global: %p\n", &mempool_ptr_global); + // printf("Value of mempool_ptr_global (should be address of mempool_local): %p\n", + // mempool_ptr_global); +#endif + SE_PARMS *se_parms = se_setup_default(SE_ASYM_ENCR); + print_test_banner("Asymmetric Encryption (API)", se_parms->parms); + test_ckks_api_base(se_parms); +} diff --git a/device/test/applibs_versions.h b/device/test/applibs_versions.h new file mode 100644 index 0000000..11517b6 --- /dev/null +++ b/device/test/applibs_versions.h @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file applibs_versions.h +*/ + +#pragma once + +/// +/// This identifier should be defined before including any of the +/// networking-related header files. It indicates which version of the Wi-Fi +/// data structures the application uses. +/// +#define NETWORKING_STRUCTS_VERSION 1 + +/// +/// This identifier must be defined before including any of the Wi-Fi related +/// header files. It indicates which version of the Wi-Fi data structures the +/// application uses. +/// +#define WIFICONFIG_STRUCTS_VERSION 1 + +/// +/// This identifier must be defined before including any of the UART-related +/// header files. It indicates which version of the UART data structures the +/// application uses. +/// +#define UART_STRUCTS_VERSION 1 + +/// +/// This identifier must be defined before including any of the SPI-related +/// header files. It indicates which version of the SPI data structures the +/// application uses. +/// +#define SPI_STRUCTS_VERSION 1 diff --git a/device/test/ckks_tests_asym.c b/device/test/ckks_tests_asym.c new file mode 100644 index 0000000..e4592ec --- /dev/null +++ b/device/test/ckks_tests_asym.c @@ -0,0 +1,326 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_tests_asym.c +*/ + +#include // pow +#include +#include + +#include "ckks_asym.h" +#include "ckks_common.h" +#include "ckks_sym.h" +#include "ckks_tests_common.h" +#include "defines.h" +#include "ntt.h" +#include "polymodarith.h" +#include "sample.h" +#include "test_common.h" +#include "util_print.h" + +#if !((defined(SE_ON_SPHERE_M4) || defined(SE_ON_NRF5)) && defined(SE_ENCRYPT_TYPE_SYMMETRIC)) + +/** +Asymmetric test core. If debugging is enabled, throws an error if a test fails. + +@param[in] test_message If 0, sets the message 0 +*/ +void test_ckks_asym_base(bool test_message) +{ +#ifdef SE_USE_MALLOC + const size_t n = 4096; + size_t nprimes = 3; +#else + const size_t n = SE_DEGREE_N; + size_t nprimes = SE_NPRIMES; +#endif + + Parms parms; + parms.pk_from_file = 0; + parms.sample_s = 0; + parms.scale = (n > 1024) ? pow(2, 25) : pow(2, 20); + // parms.scale = pow(2, 40); + bool encode_only = 0; + // ========================= + parms.is_asymmetric = 1; + parms.small_s = 1; // try this first + parms.small_u = 1; // try this first + + // -- Make sure we didn't set this accidentally + if (!parms.sample_s) se_assert(parms.small_s); + +#ifdef SE_USE_MALLOC + print_ckks_mempool_size(n, 0); + ZZ *mempool = ckks_mempool_setup_asym(n); +#else + print_ckks_mempool_size(); + ZZ mempool_local[MEMPOOL_SIZE]; + ZZ *mempool = &(mempool_local[0]); +#endif + + // -- Get pointers + SE_PTRS se_ptrs_local; + ckks_set_ptrs_asym(n, mempool, &se_ptrs_local); + double complex *conj_vals = se_ptrs_local.conj_vals; + int64_t *conj_vals_int = se_ptrs_local.conj_vals_int_ptr; + double complex *ifft_roots = se_ptrs_local.ifft_roots; + ZZ *pk_c0 = se_ptrs_local.c0_ptr; + ZZ *pk_c1 = se_ptrs_local.c1_ptr; + uint16_t *index_map = se_ptrs_local.index_map_ptr; + ZZ *ntt_roots = se_ptrs_local.ntt_roots_ptr; + ZZ *ntt_u_e1_pte = se_ptrs_local.ntt_pte_ptr; + ZZ *u = se_ptrs_local.ternary; + flpt *v = se_ptrs_local.values; + int8_t *e1 = se_ptrs_local.e1_ptr; + size_t vlen = n / 2; + + if (!test_message) + { + memset(v, 0, vlen * sizeof(flpt)); + // v = 0; + } + + // -- Additional pointers required for testing. +#ifdef SE_USE_MALLOC + ZZ *s = calloc(n / 16, sizeof(ZZ)); + int8_t *ep_small = calloc(n, sizeof(int8_t)); + ZZ *ntt_s_save = calloc(n, sizeof(ZZ)); // ntt(expanded(s)) or expanded(s) + printf(" s addr: %p\n", s); + printf(" ep_small addr: %p\n", ep_small); + printf(" ntt_s_save addr: %p\n", ntt_s_save); +#else + ZZ s_vec[SE_DEGREE_N / 16]; + int8_t ep_small_vec[SE_DEGREE_N]; + ZZ ntt_s_save_vec[SE_DEGREE_N]; // ntt(expanded(s)) or expanded(s) + ZZ *s = &(s_vec[0]); + int8_t *ep_small = &(ep_small_vec[0]); + ZZ *ntt_s_save = &(ntt_s_save_vec[0]); // ntt(expanded(s)) or expanded(s) +#endif + +#if defined(SE_USE_MALLOC) && !(defined(SE_ON_NRF5) || defined(SE_ON_SPHERE_M4)) + // -- This is too much memory for the NRF5, so just allocate as needed later in that + // case + ZZ *ntt_ep_save = calloc(n, sizeof(ZZ)); // ntt(reduced(ep)) or reduced(ep) + ZZ *ntt_e1_save = calloc(n, sizeof(ZZ)); + ZZ *ntt_u_save = calloc(n, sizeof(ZZ)); + ZZ *temp_test_mem = calloc(4 * n, sizeof(ZZ)); + printf(" ntt_ep_save addr: %p\n", ntt_ep_save); + printf(" ntt_e1_save addr: %p\n", ntt_e1_save); + printf(" ntt_u_save addr: %p\n", ntt_u_save); + printf("temp_test_mem addr: %p\n", temp_test_mem); +#elif !defined(SE_USE_MALLOC) + ZZ ntt_ep_save_vec[SE_DEGREE_N]; // ntt(reduced(ep)) or reduced(ep) + ZZ ntt_e1_save_vec[SE_DEGREE_N]; + ZZ ntt_u_save_vec[SE_DEGREE_N]; + ZZ temp_test_mem_vec[4 * SE_DEGREE_N]; + ZZ *ntt_ep_save = &(ntt_ep_save_vec[0]); // ntt(reduced(ep)) or reduced(ep) + ZZ *ntt_e1_save = &(ntt_e1_save_vec[0]); + ZZ *ntt_u_save = &(ntt_u_save_vec[0]); + ZZ *temp_test_mem = &(temp_test_mem_vec[0]); +#endif + + SE_PRNG prng; + SE_PRNG shareable_prng; + + // -- Set up parameters and index_map if applicable + ckks_setup(n, nprimes, index_map, &parms); + + print_test_banner("Asymmetric Encryption", &parms); + + // -- If s is allocated space ahead of time, can load ahead of time too. + // -- (If we are testing and sample s is set, this will also sample s) + ckks_setup_s(&parms, NULL, &prng, s); + size_t s_size = parms.small_s ? n / 16 : n; + if (encode_only) clear(s, s_size); // TODO: What do to here?? + + for (size_t testnum = 0; testnum < 9; testnum++) + { + printf("-------------------- Test %zu -----------------------\n", testnum); + ckks_reset_primes(&parms); + + if (test_message) + { + // -- Set test values + set_encode_encrypt_test(testnum, vlen, v); + print_poly_flpt("v ", v, vlen); + } + + // ------------------------------------------ + // ----- Begin encode-encrypt sequence ------ + // ------------------------------------------ + + // -- First, encode + if (test_message) + { + bool ret = ckks_encode_base(&parms, v, vlen, index_map, ifft_roots, conj_vals); + se_assert(ret); + } + else + memset(conj_vals_int, 0, n * sizeof(conj_vals_int[0])); + // print_poly_int64("conj_vals_int ", conj_vals_int, n); + se_assert(v); + + // -- Sample u, ep, e0, e1. While sampling e0, add in-place to base message. + if (!encode_only) + { + // -- Sample ep here for generating the public key later +#ifdef SE_DEBUG_NO_ERRORS + memset(ep_small, 0, n * sizeof(int8_t)); +#else + sample_poly_cbd_generic_prng_16(n, &prng, ep_small); +#endif + // print_poly_int8_full("ep_small", ep_small, n); + // printf("About to init\n"); + ckks_asym_init(&parms, NULL, &prng, conj_vals_int, u, e1); + // printf("Back from init\n"); + } + + // -- Debugging + // size_t u_size = parms.small_u ? n/16 : n; + // memset(u, 0, u_size * sizeof(ZZ)); + // if (parms.small_u) set_small_poly_idx(0, 1, u); + // else u[0] = 1; + + print_poly_ternary("u ", u, n, true); + print_poly_ternary("s ", s, n, true); + // print_poly_int8_full("e1 ", e1_ptr, n); + + for (size_t i = 0; i < parms.nprimes; i++) + { + const Modulus *mod = parms.curr_modulus; + print_zz(" ***** Modulus", mod->value); + +#ifdef SE_ON_NRF5 + ZZ *ntt_ep_save = calloc(n, sizeof(ZZ)); // ntt(reduced(ep)) or reduced(ep) + ZZ *ntt_e1_save = calloc(n, sizeof(ZZ)); + ZZ *ntt_u_save = calloc(n, sizeof(ZZ)); +#endif + // -- We can't just load pk if we are testing because we need ep to + // check the "decryption". Need to generate pk here instead to keep + // track of the secret key error term. + se_assert(!parms.pk_from_file); + printf("generating pk...\n"); + gen_pk(&parms, s, ntt_roots, NULL, &shareable_prng, ntt_s_save, ep_small, ntt_ep_save, + pk_c0, pk_c1); + printf("...done generating pk.\n"); + + // -- Debugging + print_poly("pk0 ", pk_c0, n); + print_poly("pk1 ", pk_c1, n); + print_poly_ternary("u ", u, n, 1); + // print_poly_int8_full("e1 ", e1_ptr, n); + // if (ntt_e1_ptr) print_poly_full("ntt e1 ", ntt_e1_ptr, n); + + // -- Per prime Encode + Encrypt + ckks_encode_encrypt_asym(&parms, conj_vals_int, u, e1, ntt_roots, ntt_u_e1_pte, + ntt_u_save, ntt_e1_save, pk_c0, pk_c1); + print_poly_int64("conj_vals_int ", conj_vals_int, n); + + // -- Debugging + // print_poly_int8_full("e1 ", e1, n); + // if (ntt_e1_ptr) print_poly_full("ntt e1 ", ntt_e1, n); + print_poly_ternary("u ", u, n, 1); + // if (ntt_u_save) print_poly("ntt_u_save ", ntt_u_save, n); + + // -- Decryption does the following: + // (c1 = pk1*u + e1)*s + (c0 = pk0*u + e0 + m) --> + // ((pk1 = c1 = a)*u + e1)*s + ((pk0 = c1 = -a*s+ep)*u + e0 + m) --> + // a*u*s + e1*s -a*s*u + ep*u + e0 ===> e1*s + ep*u + e0 + m + print_poly("c0 ", pk_c0, n); + print_poly("c1 ", pk_c1, n); + print_poly("ntt(u) ", ntt_u_save, n); + print_poly("ntt(ep) ", ntt_ep_save, n); + poly_mult_mod_ntt_form_inpl(ntt_u_save, ntt_ep_save, n, mod); + print_poly("ntt(u) * ntt(ep)", ntt_u_save, n); + + print_poly("ntt(s) ", ntt_s_save, n); + print_poly("ntt(e1) ", ntt_e1_save, n); + poly_mult_mod_ntt_form_inpl(ntt_e1_save, ntt_s_save, n, mod); + print_poly("ntt(s) * ntt(e1)", ntt_e1_save, n); + + print_poly("ntt(u) * ntt(ep)", ntt_u_save, n); + poly_add_mod_inpl(ntt_u_save, ntt_e1_save, n, mod); + print_poly("ntt(u) * ntt(ep) + ntt(s) * ntt(e1)", ntt_u_save, n); + + print_poly("ntt(m + e0)", ntt_u_e1_pte, n); + poly_add_mod_inpl(ntt_u_e1_pte, ntt_u_save, n, mod); + print_poly("ntt(u) * ntt(ep) + ntt(s) * ntt(e1) + ntt(m + e0)", ntt_u_e1_pte, n); + ZZ *pterr = ntt_u_e1_pte; + +#ifdef SE_ON_NRF5 + free(ntt_ep_save); + free(ntt_e1_save); + free(ntt_u_save); + ZZ *temp_test_mem = calloc(4 * n, sizeof(ZZ)); +#endif + + // -- Check that decrypt gives back the pt+err and decode gives back v. + // -- Note: This will only decode if values is non-zero. Otherwise, will + // just decrypt. + // -- Note: sizeof(max(ntt_roots, ifft_roots)) must be passed as temp memory + // to undo ifft + bool s_test_save_small = 0; // TODO: make this set better... + check_decode_decrypt_inpl(pk_c0, pk_c1, v, vlen, ntt_s_save, s_test_save_small, pterr, + index_map, &parms, temp_test_mem); + + // -- Done checking this prime, now try next prime if requested + // -- Note: This does nothing to u if u is in small form + bool ret = ckks_next_prime_asym(&parms, u); + se_assert(ret || (!ret && i + 1 == parms.nprimes)); + +#ifdef SE_ON_NRF5 + free(temp_test_mem); +#endif + } + + // -- Can exit now if rlwe testing only + if (!test_message) break; + } + +#ifdef SE_USE_MALLOC + free(mempool); + free(s); + free(ep_small); + free(ntt_s_save); +#if !(defined(SE_ON_NRF5) || defined(SE_ON_SPHERE_M4)) + free(ntt_ep_save); + free(ntt_e1_save); + free(ntt_u_save); + free(temp_test_mem); +#endif +#endif + delete_parameters(&parms); +} + +/** +Full encode + asymmetric encrypt test +*/ +void test_ckks_encode_encrypt_asym(void) +{ + printf("Beginning tests for ckks encode + asymmetric encrypt...\n"); + bool test_message = 1; + test_ckks_asym_base(test_message); +} + +/** +Asymmetric rlwe test only (message is the all-zeros vector) +*/ +void test_enc_zero_asym(void) +{ + printf("Beginning tests for rlwe asymmetric encryption of 0...\n"); + bool test_message = 0; + test_ckks_asym_base(test_message); +} +#else +void test_ckks_encode_encrypt_asym(void) +{ + printf("Error! Did you choose the wrong configuration settings?\n"); +} + +void test_enc_zero_asym(void) +{ + printf("Error! Did you choose the wrong configuration settings?\n"); +} +#endif diff --git a/device/test/ckks_tests_common.c b/device/test/ckks_tests_common.c new file mode 100644 index 0000000..4e6428d --- /dev/null +++ b/device/test/ckks_tests_common.c @@ -0,0 +1,237 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_tests_common.c +*/ + +#include "ckks_tests_common.h" + +#include +#include +#include + +#include "ckks_common.h" +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "intt.h" +#include "ntt.h" +#include "polymodarith.h" +#include "polymodmult.h" +#include "test_common.h" +#include "util_print.h" + +void set_encode_encrypt_test(size_t testnum, size_t vlen, flpt *v) +{ + se_assert(v); + clear_flpt(v, vlen); + if (testnum > 8) testnum = 8; // currently only have 9 tests + switch (testnum) + { + case 0: v[0] = 1; break; + case 1: v[0] = 2; break; + case 2: set_flpt(v, vlen, 1); break; + case 3: set_flpt(v, vlen, 2); break; + case 4: set_flpt(v, vlen, (flpt)1.1); break; + case 5: set_flpt(v, vlen, (flpt)-2.1); break; + case 6: + set_flpt(v, vlen, (flpt)1); + for (size_t i = 0; i < vlen; i += 2) + { + v[i] = 0; + if ((i + 1) < vlen) v[i + 1] = 1; + } + break; + case 7: gen_flpt_eighth_poly(v, -100, vlen); break; + case 8: gen_flpt_quarter_poly(v, -10, vlen); break; + + // -- This test works when n = 1024 with -1000 + // -- But not for most other cases + // case 9: gen_flpt_half_poly(v, 512, vlen); break; + + // -- This test does not work with 1000 + // case 10: gen_flpt_poly(v, 10000000, vlen); break; + default: break; + } +} + +void ckks_decode(const ZZ *pt, size_t values_len, uint16_t *index_map, const Parms *parms, + double complex *temp, flpt *values_decoded) +{ + se_assert(pt && parms && parms->curr_modulus && temp && values_decoded); + size_t n = parms->coeff_count; + size_t logn = parms->logn; + ZZ q = parms->curr_modulus->value; + double scale = parms->scale; + + double complex *res = temp; + printf("scale: %0.5f\n", scale); + print_poly("pt", pt, n); + + for (size_t i = 0; i < n; i++) + { + // -- Get representative element in (-q/2, q/2] from element in [0, q). + // -- Note: q, val are both unsigned, so dval = (double)(val - q) won't work. + ZZ val = pt[i]; + double dval = (val > q / 2) ? -(double)(q - val) : (double)val; + // if(i < 5) printf("dval: %0.6f\n", dval); + // if(i < 5) printf("dval/scale: %0.6f\n", dval/scale); + + // -- We scale by 1/scale here + res[i] = (double complex)_complex(dval / scale, 0); + // printf("value: %zu\n", i); + } + print_poly_double_complex("res", res, n); + + fft_inpl(res, n, logn, NULL); + + print_poly_double_complex("res", res, n); + +#ifdef SE_INDEX_MAP_OTF + // -- Here we are making room for calculating the index map. Since we are just + // testing, we can just load in the values all at once, assuming we have space. + // A more accurate test would be to calculate the index map truly on-the-fly. + se_assert(!index_map); + + // -- We no longer need the second half of res + double *res_double = (double *)res; + for (size_t i = 0; i < n; i++) { res_double[i] = se_creal(res[i]); } + print_poly_double("res double", res_double, n); + + uint16_t *index_map_ = (uint16_t *)(&(res[n / 2])); + ckks_calc_index_map(parms, index_map_); + + for (size_t i = 0; i < values_len; i++) + { + values_decoded[i] = (flpt)(res_double[index_map_[i]]); + } + // print_poly_flpt("decoded", values_decoded, n); +#else + se_assert(index_map); + #ifdef SE_INDEX_MAP_LOAD + // -- Load or setup here, doesn't matter, since we are just testing... + load_index_map(parms, index_map); + #endif + for (size_t i = 0; i < values_len; i++) + { + values_decoded[i] = (flpt)se_creal(res[index_map[i]]); + } +#endif +} + +void check_decode_inpl(ZZ *pt, const flpt *values, size_t values_len, uint16_t *index_map, + const Parms *parms, ZZ *temp) +{ + size_t n = parms->coeff_count; + se_assert(values_len <= n / 2 && values_len > 0); + + // -- We don't need enough space for the ifft_roots in this version since we don't + // care how long it takes + // -- Check: original values should be close to: + // Decode(Decrypt(Encrypt(Encode(original values)))) + const char *n1 = "values "; + const char *n2 = "values_decoded"; + ckks_decode_inpl(pt, values_len, index_map, parms, (double complex *)temp); + print_poly_flpt(n2, (flpt *)pt, n); + bool err = compare_poly_flpt(n1, values, n2, (flpt *)pt, values_len, (flpt)0.1); + se_assert(!err); +} + +void ckks_decrypt(const ZZ *c0, const ZZ *c1, const ZZ *s, bool small_s, + const Parms *parms, ZZ *pt) +{ + // -- c0 = [-a*s + e + pt]_Rq ; c1 = a + // Encryption is correct if [c0 + c1*s]_Rq = ([-a*s + e + pt]_Rq) + [(a)*s]_Rq + // = [a*s - a*s + pt + e]_Rq + // = [pt + e]_Rq + PolySizeType n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + se_assert(!small_s); + + // -- Step 1: pt = [c1 * s]_Rq + poly_mult_mod_ntt_form(c1, s, n, mod, pt); + // print_poly("c1*s ", pt, n); + + // -- Step 2: pt = [c0 + c1*s]_Rq + poly_add_mod_inpl(pt, c0, n, mod); +} + +void ckks_decrypt_inpl(ZZ *c0, ZZ *c1, const ZZ *s, bool small_s, const Parms *parms) +{ + // -- c0 = [-a*s + e + pt]_Rq ; c1 = a + // Encryption is correct if c0 + c1*s == pt + errors (all operations mod q) + // because c0 + c1*s = ([-a*s + e + pt]_Rq) + [(a)*s]_Rq + // = [a*s - a*s + pt + e]_Rq + // = [pt +e]_Rq (in the symmetric case) + PolySizeType n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + + // -- Step 1: pt = [c1 * s]_Rq + se_assert(!small_s); + poly_mult_mod_ntt_form_inpl(c1, s, n, mod); + + // -- Step 2: pt = [c0 + c1*s]_Rq + poly_add_mod_inpl(c0, c1, n, mod); +} + +void check_decode_decrypt_inpl(ZZ *c0, ZZ *c1, const flpt *values, size_t values_len, + const ZZ *s, bool small_s, const ZZ *pte_calc, + uint16_t *index_map, const Parms *parms, ZZ *temp) +{ + se_assert(c0 && c1 && values && s && pte_calc && parms && temp); + se_assert(!small_s); + print_poly_flpt("values", values, values_len); + + size_t n = parms->coeff_count; + se_assert(values_len <= n / 2 && values_len > 0); + + print_poly("c0", c0, n); + print_poly("c1", c1, n); + print_poly("s", s, n); + + // -- Calculate: c0 := [c0 + c1*s] + ckks_decrypt_inpl(c0, c1, s, small_s, parms); + + // -- Check that decrypt works by comparing to pterr + const char *s1 = "pte calculated"; + const char *s2 = "pte decrypted "; + print_poly(s1, pte_calc, n); + print_poly(s2, c0, n); + /* + // -- Debugging + intt_roots_initialize(parms, temp); + //print_poly_full(" c0 ", c0, n); + intt(parms, temp, c0); + intt(parms, temp, pterr); + const char *s1 = "pte calculated"; print_poly(s1, pte, n); + const char *s2 = "pte decrypted "; print_poly(s2, c0, n); + */ + + compare_poly(s1, pte_calc, s2, c0, n); + + // -- Make sure we didn't accidentally check an all-zeros vector + if (n > 16) se_assert(!all_zeros(c0, n)); + + // -- Then, test decode if requested + if (values) + { + print_poly(" c0 ", c0, n); + intt_roots_initialize(parms, temp); + se_assert(temp); + intt_inpl(parms, temp, c0); + print_poly("pt = intt(c0) ", c0, n); + + // -- We don't need enough space for the ifft_roots in this version since we don't + // care how long it takes + // -- Check: original values should be close to: + // Decode(Decrypt(Encrypt(Encode(original values))) + const char *n1 = "values "; + const char *n2 = "values_decoded"; + ckks_decode_inpl(c0, values_len, index_map, parms, (double complex *)temp); + print_poly_flpt(n2, (flpt *)c0, n); + bool err = compare_poly_flpt(n1, values, n2, (flpt *)c0, values_len, (flpt)0.1); + se_assert(!err); + } +} diff --git a/device/test/ckks_tests_common.h b/device/test/ckks_tests_common.h new file mode 100644 index 0000000..1c7c8d6 --- /dev/null +++ b/device/test/ckks_tests_common.h @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_tests_common.h +*/ + +#pragma once + +#include + +#include "defines.h" +#include "parameters.h" +#include "test_common.h" + +/** +Sets the values of 'v' according to the particular test number. +If testnum > 8, will set testnum to 8. + +@param[in] testnum Test number in range [0,9] +@param[in] vlen Number of flpt elements in v +@param[out] v Array to set +*/ +void set_encode_encrypt_test(size_t testnum, size_t vlen, flpt *v); + +/** +(Pseudo) ckks decode. 'values_decoded' and 'pt' may share the same starting address for +in-place computation (see: ckks_decode_inpl) + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: 'temp' must contain space for n double complex values +'values_decoded' must contain space for n/2 flpt elements. +If 'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t elements. + +@param[in] pt Plaintext to decode +@param[in] values_len True number of entries in initial values message. Must be <= +n/2 +@param[in] index_map [Optional]. If passed in, can avoid 1 flash read +@param[in] parms Parameters set by ckks_setup +@param temp Scratch space +@param[out] values_decoded Decoded message +*/ +void ckks_decode(const ZZ *pt, size_t values_len, uint16_t *index_map, const Parms *parms, + double complex *temp, flpt *values_decoded); + +/** +(Pseudo) in-place ckks decode. + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: 'temp' must contain space for n double complex values +'values_decoded' must contain space for n/2 flpt elements +If 'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t elements. + +@param[in,out] pt In: Plaintext to decode; Out: Decoded message +@param[in] values_len True number of entries in initial values message. Must be <= +n/2 +@param[in] index_map [Optional]. If passed in, can avoid 1 flash read +@param[in] parms Parameters set by ckks_setup +@param temp Scratch space +*/ +static inline void ckks_decode_inpl(ZZ *pt, size_t values_len, uint16_t *index_map, + const Parms *parms, double complex *temp) +{ + ckks_decode(pt, values_len, index_map, parms, temp, (flpt *)pt); +} + +/** +Checks that the pseudo-decoding of a ciphertext is correct. + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: 'temp' must contain space for n double complex values +If 'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t elements. + +@param[in,out] pt In: Plaintext to decode; Out: Decoded message +@param[in] values Cleartext input message +@param[in] values_len True number of entries in initial values message. Must be <= +n/2 +@param[in] index_map [Optional]. If passed in, can avoid 1 flash read +@param[in] parms Parameters instance +@param temp Scratch space +*/ +void check_decode_inpl(ZZ *pt, const flpt *values, size_t values_len, uint16_t *index_map, + const Parms *parms, ZZ *temp); + +/** +Pseudo-decrypts a ciphertext in place (for a particular prime component). Currently only +works if 'small_s' is 0. 'c0' and 'pt' may share the same starting address for in-place +computation (see: ckks_decrypt_inpl). + +Size req: 'pt' must have space for n elements + +@param[in] c0 1st ciphertext component for this prime +@param[in] c1 2nd ciphertext component for this prime +@param[in] s Secret key. Must be in expanded form! +@param[in] small_s If true, secret key is in small form; Else, is in expanded form +@param[in] parms Parameters instance +@param[out] pt Decypted result (a ckks plaintext) +*/ +void ckks_decrypt(const ZZ *c0, const ZZ *c1, const ZZ *s, bool small_s, + const Parms *parms, ZZ *pt); + +/** +Pseudo-decrypts a ciphertext in place (for a particular prime component). Currently only +works if small_s is 0. Note that this function will also corrupt the memory pointed to by +c1. + +@param[in,out] c0 In: First ciphertext component for this prime; + Out: Decrypted result (a CKKS plaintext) +@param[in] c1 Second ciphertext component for this prime. Will be corrupted +@param[in] s Secret key. Must be in expanded form! +@param[in] small_s If true, secret key is in small form; Else, is in expanded form +@param[in] parms Parameters instance +*/ +void ckks_decrypt_inpl(ZZ *c0, ZZ *c1, const ZZ *s, bool small_s, const Parms *parms); + +/** +Checks that the pseudo-decoding and pseudo-decryption of a ciphertext is correct (for a +particular prime ciphertext component). Currently only works if 'small_s' is 0. + +Correctness: 'small_s' must be true (and 's' must be in small form) + +Note: index_map is non-const in case SE_INDEX_MAP_LOAD is defined. + +Size req: 'temp' must contain space for n double complex values +If 'SE_INDEX_MAP_LOAD' is defined, index_map must constain space for n uint16_t elements. + +@param[in] c0 In: 1st component of ciphertext to check; + Out: Decrypted and decoded message +@param[in] c1 2nd component of ciphertext to check. Will be corrupted! +@param[in] values Cleartext input message +@param[in] values_len True number of entries in initial values message. Must be <= n/2 +@param[in] s Secret key. Must be in expanded form! +@param[in] small_s If true, secret key is in small form; Else, is in expanded form +@param[in] pte_calc Plaintext + error (in NTT form) (output by ckks_encode for +testing) +@param[in] index_map [Optional]. If passed in, can avoid 1 flash read +@param[in] parms Parameters instance +@param temp Scratch space +*/ +void check_decode_decrypt_inpl(ZZ *c0, ZZ *c1, const flpt *values, size_t values_len, + const ZZ *s, bool small_s, const ZZ *pte_calc, + uint16_t *index_map, const Parms *parms, ZZ *temp); diff --git a/device/test/ckks_tests_encode.c b/device/test/ckks_tests_encode.c new file mode 100644 index 0000000..fcedefc --- /dev/null +++ b/device/test/ckks_tests_encode.c @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_tests_encode.c +*/ + +#include // pow +#include +#include + +#include "ckks_common.h" +#include "ckks_sym.h" +#include "ckks_tests_common.h" +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "ntt.h" +#include "polymodarith.h" +#include "polymodmult.h" +#include "sample.h" +#include "test_common.h" +#include "util_print.h" + +void test_ckks_encode(void) +{ +#ifdef SE_USE_MALLOC + size_t n = 4096; +#else + size_t n = SE_DEGREE_N; +#endif + + Parms parms; + parms.scale = (n > 1024) ? pow(2, 25) : pow(2, 20); + + // TODO: Make a malloc function to include test memory +#ifdef SE_USE_MALLOC + print_ckks_mempool_size(n, 1); + ZZ *mempool = ckks_mempool_setup_sym(n); +#else + print_ckks_mempool_size(); + ZZ mempool_local[MEMPOOL_SIZE]; + ZZ *mempool = &(mempool_local[0]); +#endif + + // -- Get pointers + SE_PTRS se_ptrs_local; + ckks_set_ptrs_sym(n, mempool, &se_ptrs_local); + double complex *conj_vals = se_ptrs_local.conj_vals; + int64_t *conj_vals_int = se_ptrs_local.conj_vals_int_ptr; + double complex *ifft_roots = se_ptrs_local.ifft_roots; + uint16_t *index_map = se_ptrs_local.index_map_ptr; + // -- Note that, since we are not adding the error, the pt will not be in NTT form. + ZZ *pt = se_ptrs_local.ntt_pte_ptr; + flpt *v = se_ptrs_local.values; + size_t vlen = n / 2; + + // -- Additional pointers required for testing. +#ifdef SE_USE_MALLOC + ZZ *temp = calloc(n, sizeof(double complex)); +#else + ZZ temp[SE_DEGREE_N * sizeof(double complex) / sizeof(ZZ)]; +#endif + + // -- Set up parameters and index_map if applicable + ckks_setup(n, 1, index_map, &parms); + print_test_banner("Encode", &parms); + + for (size_t testnum = 0; testnum < 9; testnum++) + { + printf("-------------------- Test %zu -----------------------\n", testnum); + const Modulus *mod = parms.curr_modulus; + print_zz("\n ***** Modulus", mod->value); + + // -- Get test values + set_encode_encrypt_test(testnum, vlen, v); + print_poly_flpt("v ", v, vlen); + + // -- Begin encode-encrypt sequence + // -- First, encode base. Afer this, we should only use the pointer values + bool ret = ckks_encode_base(&parms, v, vlen, index_map, ifft_roots, conj_vals); + se_assert(ret); + // print_poly_int64_full("conj_vals_int ", conj_vals_int, n); + + // -- Now, need to reduce the plaintext + reduce_set_pte(&parms, conj_vals_int, pt); + // print_poly_int64_full("conj_vals_int ", conj_vals_int, n); + + // -- Check that decoding works + check_decode_inpl(pt, v, vlen, index_map, &parms, temp); + } +#ifdef SE_USE_MALLOC + free(mempool); + free(temp); +#endif + delete_parameters(&parms); +} diff --git a/device/test/ckks_tests_sym.c b/device/test/ckks_tests_sym.c new file mode 100644 index 0000000..e0ac35a --- /dev/null +++ b/device/test/ckks_tests_sym.c @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ckks_test_sym.c +*/ + +#include +#include +#include + +#include "ckks_common.h" +#include "ckks_sym.h" +#include "ckks_tests_common.h" +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "ntt.h" +#include "polymodarith.h" +#include "polymodmult.h" +#include "sample.h" +#include "test_common.h" +#include "util_print.h" + +#if !((defined(SE_ON_SPHERE_M4) || defined(SE_ON_NRF5)) && !defined(SE_ENCRYPT_TYPE_SYMMETRIC)) +/** +Symmetric test core. If debugging is enabled, throws an error if a test fails. + +@param[in] test_message If 0, sets the message 0 +*/ +void test_ckks_sym_base(bool test_message) +{ +#ifdef SE_USE_MALLOC + size_t n = 4096; + size_t nprimes = 3; +#else + size_t n = SE_DEGREE_N; + size_t nprimes = SE_NPRIMES; +#endif + + Parms parms; + parms.scale = (n > 1024) ? pow(2, 25) : pow(2, 20); + // parms.scale = pow(2, 40); + // parms.scale = n * n; + bool encode_only = 0; // this only works if s is not not persistent + // ========================= + parms.is_asymmetric = 0; + parms.small_s = 1; + parms.sample_s = 0; + + // -- Make sure we didn't set this accidentally + if (!parms.sample_s) se_assert(parms.small_s); + +#ifdef SE_USE_MALLOC + print_ckks_mempool_size(n, 1); + ZZ *mempool = ckks_mempool_setup_sym(n); +#else + print_ckks_mempool_size(); + ZZ mempool_local[MEMPOOL_SIZE]; + ZZ *mempool = &(mempool_local[0]); +#endif + + // -- Get pointers + SE_PTRS se_ptrs_local; + ckks_set_ptrs_sym(n, mempool, &se_ptrs_local); + double complex *conj_vals = se_ptrs_local.conj_vals; + int64_t *conj_vals_int = se_ptrs_local.conj_vals_int_ptr; + double complex *ifft_roots = se_ptrs_local.ifft_roots; + ZZ *c0 = se_ptrs_local.c0_ptr; + ZZ *c1 = se_ptrs_local.c1_ptr; + uint16_t *index_map = se_ptrs_local.index_map_ptr; + ZZ *ntt_roots = se_ptrs_local.ntt_roots_ptr; + ZZ *ntt_pte = se_ptrs_local.ntt_pte_ptr; + ZZ *s = se_ptrs_local.ternary; + flpt *v = se_ptrs_local.values; + size_t vlen = n / 2; + + if (!test_message) + { + memset(v, 0, vlen * sizeof(flpt)); + // v = 0; + } + +// -- Additional pointers required for testing. +#ifdef SE_USE_MALLOC + ZZ *s_test_save = calloc(n, sizeof(ZZ)); // ntt(expanded(s)) or expanded(s) + ZZ *c1_test_save = calloc(n, sizeof(ZZ)); + ZZ *temp_test_mem = calloc(4 * n, sizeof(ZZ)); +#else + ZZ s_test_save_vec[SE_DEGREE_N]; // ntt(expanded(s)) or expanded(s) + ZZ c1_test_save_vec[SE_DEGREE_N]; + ZZ temp_test_mem_vec[4 * SE_DEGREE_N]; + ZZ *s_test_save = &(s_test_save_vec[0]); + // printf("s_save address: %p\n", s_test_save); + ZZ *c1_test_save = &(c1_test_save_vec[0]); + ZZ *temp_test_mem = &(temp_test_mem_vec[0]); +// printf("temp_test_mem address: %p\n", temp_test_mem); +// printf("temp_test_mem address: %p\n", &(temp_test_mem[4*SE_DEGREE_N-1])); +#endif + + SE_PRNG prng; + SE_PRNG shareable_prng; + + // -- Set up parameters and index_map if applicable + ckks_setup(n, nprimes, index_map, &parms); + + print_test_banner("Symmetric Encryption", &parms); + + // -- If s is allocated space ahead of time, can load ahead of time too + // -- If we are testing and sample s is set, this will also sample s + ckks_setup_s(&parms, NULL, &prng, s); + size_t s_size = parms.small_s ? n / 16 : n; + if (encode_only) clear(s, s_size); + + for (size_t testnum = 0; testnum < 9; testnum++) + { + printf("-------------------- Test %zu -----------------------\n", testnum); + ckks_reset_primes(&parms); + + // -- Set test values + if (test_message) + { + set_encode_encrypt_test(testnum, vlen, v); + print_poly_flpt("v ", v, vlen); + } + + // -- Begin encode-encrypt sequence + // -- First, calculate m + e (not fully reduced, not in ntt form) + if (test_message) + { + bool ret = ckks_encode_base(&parms, v, vlen, index_map, ifft_roots, conj_vals); + se_assert(ret); + } + else + memset(conj_vals_int, 0, n * sizeof(conj_vals_int[0])); + // print_poly_int64("conj_vals_int ", conj_vals_int, n); + se_assert(v); + + // -- Sample error e. While sampling e, add it in place to the base message. + if (!encode_only) + { + ckks_sym_init(&parms, NULL, NULL, &shareable_prng, &prng, conj_vals_int); + } + + for (size_t i = 0; i < parms.nprimes; i++) + { + print_zz("\n ***** Modulus", parms.curr_modulus->value); + + // -- Per prime Encode + Encrypt + ckks_encode_encrypt_sym(&parms, conj_vals_int, NULL, &shareable_prng, s, ntt_pte, + ntt_roots, c0, c1, s_test_save, c1_test_save); + // print_poly_int64("conj_vals_int", conj_vals_int, n); + // print_poly_ternary("s", s, n, true); + // print_poly_ternary("s_save", s, n, false); + + // -- Check that decrypt gives back the pt+err and decode gives back v. + // -- Note: This will only decode if values is non-zero. Otherwise, will + // just decrypt. + // -- Note: sizeof(max(ntt_roots, ifft_roots)) must be passed as temp memory + // to undo ifft + bool s_test_save_small = false; + check_decode_decrypt_inpl(c0, c1_test_save, v, vlen, s_test_save, s_test_save_small, + ntt_pte, index_map, &parms, temp_test_mem); + + // -- Done checking this prime. Now try next prime if requested + bool ret = ckks_next_prime_sym(&parms, s); + se_assert(ret || (!ret && i + 1 == parms.nprimes)); + } + + // -- Can exit now if rlwe testing only + if (!test_message) break; + } + +#ifdef SE_USE_MALLOC + free(mempool); + free(s_test_save); + free(c1_test_save); + free(temp_test_mem); +#endif + delete_parameters(&parms); +} + +/** +Full encode + symmetric encrypt test +*/ +void test_ckks_encode_encrypt_sym(void) +{ + printf("Beginning tests for ckks encode + symmetric encrypt...\n"); + bool test_message = 1; + test_ckks_sym_base(test_message); +} + +/** +Symmetric rlwe test only (message is the all-zeros vector) +*/ +void test_enc_zero_sym(void) +{ + printf("Beginning tests for rlwe symmetric encryption of 0...\n"); + bool test_message = 0; + test_ckks_sym_base(test_message); +} +#else + +void test_ckks_encode_encrypt_sym(void) +{ + printf("Error! Did you choose the wrong configuration settings?\n"); +} + +void test_enc_zero_sym(void) +{ + printf("Error! Did you choose the wrong configuration settings?\n"); +} + +#endif diff --git a/device/test/fft_tests.c b/device/test/fft_tests.c new file mode 100644 index 0000000..4fbe655 --- /dev/null +++ b/device/test/fft_tests.c @@ -0,0 +1,350 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file fft_tests.c +*/ + +#include +#include // log2 +#include // memcpy + +#include "defines.h" +#include "fft.h" +#include "fileops.h" +#include "test_common.h" +#include "util_print.h" + +#ifndef SE_USE_MALLOC +#if defined(SE_IFFT_LOAD_FULL) || defined(SE_IFFT_ONE_SHOT) +#define IFFT_TEST_ROOTS_MEM SE_DEGREE_N +#else +#define IFFT_TEST_ROOTS_MEM 0 +#endif +#if defined(SE_FFT_LOAD_FULL) || defined(SE_FFT_ONE_SHOT) +#define FFT_TEST_ROOTS_MEM SE_DEGREE_N +#else +#define FFT_TEST_ROOTS_MEM 0 +#endif +#endif + +/** +Multiplies two complex-valued polynomials using schoolbook multiplication. + +Space req: 'res' must constain space for n double complex elements, where each input +polynomial consists of n double complex elements (each). + +@param[in] a Input polynomial 1 +@param[in] b Input polynomial 2 +@param[in] n Number of coefficients to multiply +@param[out] res Result of [a * b] +*/ +void poly_mult_sb_complex(const double complex *a, const double complex *b, PolySizeType n, + double complex *res) +{ + for (PolySizeType i = 0; i < n; i++) + { + for (PolySizeType j = 0; j < n; j++) { res[i + j] += a[i] * b[j]; } + } +} + +/** +In-place divides each element of a polynomial by a divisor. + +@param[in,out] poly In: Input polynomial; Out: Result polynomial +@param[in] n Number of coefficients to multiply +@param[in] divisor Divisor value +*/ +static inline void poly_div_inpl_complex(double complex *poly, size_t n, size_t divisor) +{ + for (size_t i = 0; i < n; i++) poly[i] = (double)poly[i] / (double)divisor; +} + +/** +Pointwise multiplies two complex-valued polynomials. 'a' and 'res' starting address may +overlap for in-place computation (see: pointwise_mult_inpl_complex) + +Space req: 'res' must constain space for n double complex elements, where each input +polynomial consists of n double complex elements (each). + +@param[in] a Input polynomial 1 +@param[in] b Input polynomial 2 +@param[in] n Number of coefficients to multiply +@param[out] res Result of [a . b] +*/ +static inline void pointwise_mult_complex(const double complex *a, const double complex *b, + PolySizeType n, double complex *res) +{ + for (size_t i = 0; i < n; i++) res[i] = a[i] * b[i]; +} + +/** +In-place pointwise multiplies two complex-valued polynomials. + +@param[in] a Input polynomial 1 +@param[in] b Input polynomial 2 +@param[in] n Number of coefficients to multiply +@param[out] res Result of [a . b] +*/ +static inline void pointwise_mult_inpl_complex(double complex *a, const double complex *b, + PolySizeType n) +{ + for (size_t i = 0; i < n; i++) a[i] *= b[i]; +} + +void test_fft_mult_helper(size_t degree, double complex *v1, double complex *v2, + double complex *v_exp, double complex *temp, double complex *roots) +{ + size_t n = degree; + size_t logn = (size_t)log2(degree); + + // -- Correctness: ifft(fft(vec) .* fft(v2))*(1/n) = vec * vec2 + print_poly_double_complex("v1 ", v1, n); + print_poly_double_complex("v2 ", v2, n); + + double complex *v_res = temp; + clear_double_complex(v_res, n); + + poly_mult_sb_complex(v1, v2, n / 2, v_res); + print_poly_double_complex("vec_res (expected)", v_res, n); + +#ifdef SE_FFT_LOAD_FULL + se_assert(roots); + load_fft_roots(n, roots); +#elif defined(SE_FFT_ONE_SHOT) + se_assert(roots); + calc_fft_roots(n, logn, roots); +#endif + + fft_inpl(v1, n, logn, roots); + // print_poly_double_complex("v1 (after fft) ", v1, n); + fft_inpl(v2, n, logn, roots); + // print_poly_double_complex("v2 (after fft) ", v2, n); + + pointwise_mult_inpl_complex(v1, v2, n); + // print_poly_double_complex("vec (after mult) ", v1, n); + +#ifdef SE_IFFT_LOAD_FULL + se_assert(roots); + load_ifft_roots(n, roots); +#elif defined(SE_IFFT_ONE_SHOT) + se_assert(roots); + calc_ifft_roots(n, logn, roots); +#endif + + ifft_inpl(v1, n, logn, roots); + // print_poly_double_complex("vec (after ifft) ", v1, n); + + poly_div_inpl_complex(v1, n, n); + print_poly_double_complex("vec_res (actual) ", v1, n); + + double maxdiff = 0.0001; + bool err = compare_poly_double_complex(v1, v_res, n, maxdiff); + se_assert(!err); + + if (v_exp) + { + print_poly_double_complex("v_exp2 ", v_exp, n); + err = compare_poly_double_complex(v_exp, v_res, n, maxdiff); + se_assert(!err); + } +} + +void test_fft_helper(size_t degree, const double complex *v, double complex *temp, + double complex *roots) +{ + size_t n = degree; + size_t logn = (size_t)log2(degree); + + // -- Correctness: ifft(fft(vec)) * (1/n) = vec + // -- Save vec for comparison later. Write to temp to apply fft/ifft in-place + print_poly_double_complex("vec ", v, n); + double complex *v_fft = temp; + memcpy(v_fft, v, n * sizeof(double complex)); + print_poly_double_complex("vec ", v_fft, n); + +#ifdef SE_FFT_LOAD_FULL + se_assert(roots); + load_fft_roots(n, roots); +#elif defined(SE_FFT_ONE_SHOT) + se_assert(roots); + calc_fft_roots(n, logn, roots); +#endif + + // -- Note: 'roots' will be ignored if SE_FFT_OTF is chosen + fft_inpl(v_fft, n, logn, roots); + print_poly_double_complex("vec (after fft) ", v, n); + +#ifdef SE_IFFT_LOAD_FULL + se_assert(roots); + load_ifft_roots(n, roots); +#elif defined(SE_IFFT_ONE_SHOT) + se_assert(roots); + calc_ifft_roots(n, logn, roots); +#endif + + // -- Note: 'roots' will be ignored if SE_IFFT_OTF is chosen + ifft_inpl(v_fft, n, logn, roots); + print_poly_double_complex("vec (after ifft) ", v_fft, n); + + poly_div_inpl_complex(v_fft, n, n); + print_poly_double_complex("vec (after *(1/n))", v_fft, n); + + double maxdiff = 0.0001; + bool err = compare_poly_double_complex(v_fft, v, n, maxdiff); + se_assert(!err); +} + +void test_fft(void) +{ + // -- Note: For multiplication tests, v1 and v2 must not be + // larger than length n/2 in data and should be zero + // padded up to length n. Result vector will be length n. + +#ifdef SE_USE_MALLOC + const size_t n = 4096; + size_t ifft_roots_size = 0; +#if defined(SE_IFFT_LOAD_FULL) || defined(SE_IFFT_ONE_SHOT) + ifft_roots_size = n; +#endif + size_t fft_roots_size = 0; +#if defined(SE_FFT_LOAD_FULL) || defined(SE_FFT_ONE_SHOT) + fft_roots_size = n; +#endif + size_t roots_size = ifft_roots_size ? ifft_roots_size : fft_roots_size; + size_t mempool_size = 4 * n + roots_size; + double complex *mempool = calloc(mempool_size, sizeof(double complex)); +#else + const size_t n = SE_DEGREE_N; + size_t roots_size = IFFT_TEST_ROOTS_MEM + FFT_TEST_ROOTS_MEM; + double complex mempool[4 * SE_DEGREE_N + IFFT_TEST_ROOTS_MEM + FFT_TEST_ROOTS_MEM]; + size_t mempool_size = 4 * n + roots_size; +#endif + + size_t start_idx = 0; + double complex *v1 = &(mempool[start_idx]); + start_idx += n; + double complex *v2 = &(mempool[start_idx]); + start_idx += n; + double complex *v_exp = &(mempool[start_idx]); + start_idx += n; + double complex *temp = &(mempool[start_idx]); + start_idx += n; + double complex *roots = roots_size ? &(mempool[start_idx]) : 0; + start_idx += roots_size; + se_assert(start_idx == mempool_size); + + Parms parms; + set_parms_ckks(n, 1, &parms); + print_test_banner("fft/ifft", &parms); + + for (size_t testnum = 0; testnum < 15; testnum++) + { + printf("\n--------------- Test: %zu -----------------\n", testnum); + clear_double_complex(mempool, mempool_size); + switch (testnum) + { + case 0: set_double_complex(v1, n, 1); break; // {1, 1, 1, ... } + case 1: set_double_complex(v1, n, 2); break; // {2, 2, 2, ... } + case 2: // {0, 1, 2, 3, ...} + for (size_t i = 0; i < n; i++) { v1[i] = (double complex)_complex(i, 0); } + break; + case 3: + for (size_t i = 0; i < n; i++) + { + v1[i] = (double complex)(gen_double_eighth(10), 0); + } + break; + case 4: + for (size_t i = 0; i < n; i++) + { + v1[i] = (double complex)(gen_double_quarter(100), 0); + } + break; + case 5: + for (size_t i = 0; i < n; i++) + { + v1[i] = (double complex)(gen_double_half(-100), 0); + } + break; + case 6: + for (size_t i = 0; i < n; i++) { v1[i] = (double complex)(gen_double(1000), 0); } + break; + case 7: // {1, 0, 0, ...} * {2, 2, 2, ...} = {2, 2, 2, ..., 0, 0, ...} + v1[0] = (double complex)_complex(1, 0); + set_double_complex(v2, n / 2, 2); + set_double_complex(v_exp, n / 2, 2); + break; + case 8: // {-1, 0, 0, ...} * {2, 2, 2, ...} = {-2, -2, -2, ..., 0, 0, + // ...} + v1[0] = (double complex)_complex(-1, 0); + set_double_complex(v2, n / 2, 2); + set_double_complex(v_exp, n / 2, -2); + break; + case 9: // {1, 0, 0, ...} * {-2, -2, -2, ...} = {-2, -2, -2, ..., 0, 0, + // ...} + v1[0] = (double complex)_complex(1, 0); + set_double_complex(v2, n / 2, -2); + set_double_complex(v_exp, n / 2, -2); + break; + case 10: // {1, 1, 1, ...} * {2, 2, 2, ...} = {2, 4, 8, ..., 4, 2, 0} + set_double_complex(v1, n / 2, 1); + set_double_complex(v2, n / 2, 2); + for (size_t i = 0; i < n / 2; i++) + { + v_exp[i] = (double complex)_complex(2 * (i + 1), 0); + } + for (size_t i = 0; i < (n / 2) - 1; i++) + { + v_exp[i + n / 2] = v_exp[n / 2 - (i + 2)]; + } + break; + case 11: + for (size_t i = 0; i < n / 2; i++) + { + v1[i] = (double complex)_complex(gen_double_eighth(pow(10, 1)), 0); + v2[i] = (double complex)_complex(gen_double_eighth(pow(10, 1)), 0); + } + break; + case 12: + for (size_t i = 0; i < n / 2; i++) + { + v1[i] = (double complex)_complex(gen_double_quarter(-pow(10, 2)), 0); + v2[i] = (double complex)_complex(gen_double_quarter(-pow(10, 2)), 0); + } + break; + case 13: + for (size_t i = 0; i < n / 2; i++) + { + v1[i] = (double complex)(gen_double_half(pow(10, 3)), 0); + v2[i] = (double complex)(gen_double_half(pow(10, 3)), 0); + } + break; + case 14: + for (size_t i = 0; i < n / 2; i++) + { + v1[i] = (double complex)(gen_double(pow(10, 6)), 0); + v2[i] = (double complex)(gen_double(pow(10, 6)), 0); + } + break; + } + if (testnum < 7) + test_fft_helper(n, v1, temp, roots); + else if (testnum < 11) + test_fft_mult_helper(n, v1, v2, v_exp, temp, roots); + else + test_fft_mult_helper(n, v1, v2, 0, temp, roots); + } +#ifdef SE_USE_MALLOC + free(mempool); +#endif +} + +#ifndef SE_USE_MALLOC +#ifdef IFFT_TEST_ROOTS_MEM +#undef IFFT_TEST_ROOTS_MEM +#endif +#ifdef FFT_TEST_ROOTS_MEM +#undef FFT_TEST_ROOTS_MEM +#endif +#endif diff --git a/device/test/main.c b/device/test/main.c new file mode 100644 index 0000000..d89d7f7 --- /dev/null +++ b/device/test/main.c @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file main.c + +Note: Currently, you can only run one test at a time. Uncomment the test you want to run (and +uncomment all others) before compiling. Note: Not all the tests will run on the devices since tests +may consume much more memory than regular API usage. +*/ + +#include "defines.h" +#include "util_print.h" + +extern void test_add_uint(void); +extern void test_mult_uint(void); +extern void test_add_mod(void); +extern void test_neg_mod(void); +extern void test_mul_mod(void); +extern void test_sample_poly_uniform(void); +extern void test_sample_poly_ternary(void); +extern void test_sample_poly_ternary_small(void); +extern void test_barrett_reduce(void); +extern void test_barrett_reduce_wide(void); +extern void test_poly_mult_ntt(void); +extern void test_fft(void); +extern void test_enc_zero_sym(void); +extern void test_enc_zero_asym(void); +extern void test_ckks_encode(void); +extern void test_ckks_encode_encrypt_sym(void); +extern void test_ckks_encode_encrypt_asym(void); +extern void test_ckks_api_sym(void); +extern void test_ckks_api_asym(void); + +#ifdef SE_ON_SPHERE_M4 +#include "mt3620.h" +#include "os_hal_gpt.h" +#include "os_hal_uart.h" + +static const uint8_t uart_port_num = OS_HAL_UART_PORT0; +// static const uint8_t uart_port_num = OS_HAL_UART_ISU0; + +/** +Hook for "printf" +@param[in] character Character to print +*/ +// /* +void _putchar(char character) +{ + mtk_os_hal_uart_put_char(uart_port_num, character); + if (character == '\n') mtk_os_hal_uart_put_char(uart_port_num, '\r'); +} +// */ + +void RTCoreMain(void) +{ + // -- Init Vector Table -- + NVIC_SetupVectorTable(); + + // -- Init UART -- + mtk_os_hal_uart_ctlr_init(uart_port_num); + printf("\nUART Inited (port_num=%d)\n", uart_port_num); + + // -- Init GPT -- + // gpt0_int.gpt_cb_hdl = Gpt0Callback; + // gpt0_int.gpt_cb_data = (void *)gpt_cb_data; + mtk_os_hal_gpt_init(); +#else + +extern int test_network_basic(void); +extern void test_network(void); + +int main(void) +{ +#ifdef SE_NRF5_UART_PRINTF_ENABLED + se_setup_uart(); +#endif +#endif + // while(1) + // { + // printf("Beginning tests...\n"); + // } + printf("Beginning tests...\n"); + se_randomness_init(); // required for nrf. does nothing if not on nrf + + // test_sample_poly_uniform(); + // test_sample_poly_ternary(); + // test_sample_poly_ternary_small(); // Only useful when SE_USE_MALLOC is defined + + // test_add_uint(); + // test_mult_uint(); + + // test_barrett_reduce(); + // test_barrett_reduce_wide(); + + // test_add_mod(); + // test_neg_mod(); + // test_mul_mod(); + + // -- Note: This test sometimes takes a while to run + // because it uses schoolbook multiplication + // test_poly_mult_ntt(); + + test_fft(); + + // test_enc_zero_sym(); + // test_enc_zero_asym(); + + // test_ckks_encode(); + + // -- Main tests + test_ckks_encode_encrypt_sym(); + // test_ckks_encode_encrypt_asym(); + + // -- Run these tests to verify api + // -- Check the result with the adapter by writing output to a text file + // and passing that to the adapter "verify ciphertexts" functionality + // test_ckks_api_sym(); + // test_ckks_api_asym(); + + // test_network_basic(); + // test_network(); + + printf("...done with all tests. All tests passed.\n"); + exit(0); +} diff --git a/device/test/modulo_tests.c b/device/test/modulo_tests.c new file mode 100644 index 0000000..b95b6b4 --- /dev/null +++ b/device/test/modulo_tests.c @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file modulo_tests.c +*/ + +#include + +#include "defines.h" +#include "inttypes.h" // PRIu64 +#include "modulo.h" +#include "modulus.h" +#include "test_common.h" +#include "util_print.h" + +/** +Helper function to test barret wide reduction. Note that this tests the '%' operator +too, which may not always be desirable. + +@param input +@param modulus +@param res_exp + */ +void test_barrett_reduce_wide_helper(ZZ *input, const Modulus *modulus, ZZ res_exp) +{ + ZZ q = modulus->value; + printf("-------------------------------------\n"); + ZZ res = barrett_reduce_wide(input, modulus); + + printf("{ %" PRIuZZ " , %" PRIuZZ "} mod %" PRIuZZ "\n", input[1], input[0], q); + + uint64_t actual_input = (uint64_t)(((uint64_t)input[1] << 32) | ((input[0]) & 0xFFFFFFFF)); + printf("--> input = %" PRIu64 "\n", actual_input); + + // -- If you don't want to test '%', must comment this out + ZZ res_default = (ZZ)(actual_input % q); + print_zz("Result default", res_default); + se_assert(res == res_default); + + print_zz("Result expected", res_exp); + print_zz("Result barrett", res); + se_assert(res == res_exp); +} + +/** +Helper function to test barret reduction. Note that this tests the '%' operator too, +which may not always be desirable. + +@param input +@param modulus +@param res_exp + */ +void test_barrett_reduce_helper(ZZ input, const Modulus *modulus, ZZ res_exp) +{ + ZZ q = modulus->value; + printf("-------------------------------------\n"); + ZZ res = barrett_reduce(input, modulus); + + printf("%" PRIuZZ " mod %" PRIuZZ "\n", input, q); + + // If you don't want to test '%', must comment this out + ZZ res_default = input % q; + print_zz("Result default", res_default); + se_assert(res == res_default); + + print_zz("Result expected", res_exp); + print_zz("Result barrett", res); + se_assert(res == res_exp); +} + +void test_barrett_reduce(void) +{ + printf("\n**************************************\n"); + printf("Beginning tests for barrett_reduce...\n\n"); + ZZ input; + Modulus modulus; + + // modulus.value = q + // modulus.const_ratio = floor(2^128/q) + + // ------------------------------ + // Set 1: q = 2 (min value of q) + // ------------------------------ + set_modulus(2, &modulus); + + test_barrett_reduce_helper(1, &modulus, 1); // 1 = 1 (mod 2) + test_barrett_reduce_helper(2, &modulus, 0); // 2 = 0 (mod 2) + test_barrett_reduce_helper(3, &modulus, 1); // 3 = 1 (mod 2) + test_barrett_reduce_helper(MAX_ZZ, &modulus, 1); // max odd = 1 (mod 2) + test_barrett_reduce_helper(MAX_ZZ - 1, &modulus, 0); // max even = 0 (mod 2) + + input = 0xda9b246b; + + test_barrett_reduce_helper(input, &modulus, 1); // random odd = 1 (mod 2) + input--; + test_barrett_reduce_helper(input, &modulus, 0); // random even = 0 (mod 2) + + // ------------------ + // Set 2: q = max q + // ------------------ + set_modulus(MAX_Q, &modulus); + test_barrett_reduce_helper(1, &modulus, 1); // 1 % MAXQ = 1 + test_barrett_reduce_helper(2, &modulus, 2); // 2 % MAXQ = 2 + + // -- Since MAX_ZZ = MAX_Q*2 + 1, MAXZZ % MAX_q = 1 + test_barrett_reduce_helper(MAX_ZZ, &modulus, 1); + + // -- random x < q ---> x = x (mod q), 32 bit + input = 0x76774403; + test_barrett_reduce_helper(input, &modulus, input); + input = 0x958b2d91; // random > q, 32 bit + test_barrett_reduce_helper(input, &modulus, 361442706); + + // -------------------------------------------- + // Set 3: q = 1559578058 (random 31-bit number) + // -------------------------------------------- + set_modulus(0x5cf545ca, &modulus); + test_barrett_reduce_helper(1, &modulus, 1); // 1 = 1 (mod q) + test_barrett_reduce_helper(2, &modulus, 2); // 2 = 2 (mod q) + test_barrett_reduce_helper(MAX_ZZ, &modulus, 1175811179); + + // random x < q ---> x = x (mod q) + input = 0x16774403; + test_barrett_reduce_helper(input, &modulus, input); + // random > q, 32 bit + test_barrett_reduce_helper(0x958b2d91, &modulus, 949348295); + printf("\n... all tests for barrett_reduce passed.\n"); + printf("**************************************\n"); +} + +void test_barrett_reduce_wide(void) +{ + printf("\n***************************************\n"); + printf("Beginning tests for barrett_reduce_wide...\n\n"); + + ZZ input[2]; + Modulus modulus; + // -- q must also be > 1 ----> where is this enforced?? + + // ------------------------------ + // Set 1: q = 2 (min value of q) + // ------------------------------ + set_modulus(2, &modulus); + + input[1] = 0; // 1 = 1 (mod 2) + input[0] = 1; + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[1] = 0; // 2 = 0 (mod 2) + input[0] = 2; + test_barrett_reduce_wide_helper(input, &modulus, 0); + input[1] = 0; // 3 = 1 (mod 2) + input[0] = 3; + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[1] = MAX_ZZ; // max (odd) = 1 (mod 2) + input[0] = MAX_ZZ; + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[1] = MAX_ZZ; // max (even) = 0 (mod 2) + input[0] = MAX_ZZ - 1; + test_barrett_reduce_wide_helper(input, &modulus, 0); + + input[1] = random_zz(); + input[0] = 0xda9b246b; // random (odd) = 1 (mod 2) + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[0]--; // random (even) = 0 (mod 2) + test_barrett_reduce_wide_helper(input, &modulus, 0); + + // ------------- + // Set 2: q = 3 + // ------------- + set_modulus(3, &modulus); + + input[1] = 0; + input[0] = 0; + test_barrett_reduce_wide_helper(input, &modulus, 0); + input[1] = 0; + input[0] = 1; + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[1] = 456; + input[0] = 123; + test_barrett_reduce_wide_helper(input, &modulus, 0); + input[1] = MAX_ZZ; + input[0] = MAX_ZZ; + test_barrett_reduce_wide_helper(input, &modulus, 0); + + // ------------- + // Set 3: max q + // ------------- + set_modulus(MAX_Q, &modulus); + + input[1] = 0; // 1 = 1 (mod q) + input[0] = 1; + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[1] = 0; // 2 = 2 (mod q) + input[0] = 2; + test_barrett_reduce_wide_helper(input, &modulus, 2); + + // -- Since MAX_ZZ = MAX_Q*2 + 1, MAXZZ % MAX_q = 1 + input[1] = 0; + input[0] = MAX_ZZ; + test_barrett_reduce_wide_helper(input, &modulus, 1); + + input[1] = MAX_ZZ; + input[0] = MAX_ZZ; + test_barrett_reduce_wide_helper(input, &modulus, 3); + + // -- random x < q ---> x = x (mod q), 32 bit + input[1] = 0; + input[0] = 0x76774403; + test_barrett_reduce_wide_helper(input, &modulus, input[0]); + + input[1] = 0x958b2d91; // random > q, 32 bit + input[0] = 0x654b2d21; // random > q, 32 bit + test_barrett_reduce_wide_helper(input, &modulus, 274827334); + + // -------------------------------------------- + // Set 3: random q, 32 bit (q = 1559578058) + // -------------------------------------------- + set_modulus(0x5cf545ca, &modulus); + + input[1] = 0; // 1 = 1 (mod q) + input[0] = 1; + test_barrett_reduce_wide_helper(input, &modulus, 1); + input[1] = 0; // 2 = 2 (mod q) + input[0] = 2; + test_barrett_reduce_wide_helper(input, &modulus, 2); + input[1] = 0; // random x < q ---> x = x (mod q) + input[0] = 0x16774403; + test_barrett_reduce_wide_helper(input, &modulus, input[0]); + + input[1] = MAX_ZZ; + input[0] = MAX_ZZ; + test_barrett_reduce_wide_helper(input, &modulus, 112593495); + input[1] = 0xd9582d91; // random > q + input[0] = 0x7b813c5b; + test_barrett_reduce_wide_helper(input, &modulus, 25773121); + printf("\n... all tests for barrett_reduce_wide passed.\n"); + printf("***************************************\n"); +} diff --git a/device/test/network_tests.c b/device/test/network_tests.c new file mode 100644 index 0000000..ae6a54c --- /dev/null +++ b/device/test/network_tests.c @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file network_tests.c + +Adapted from Sphere example +*/ + +#include "defines.h" + +#ifdef SE_ON_SPHERE_A7 + #include + #include + #include + #include + #include + #include // strerror + + #include "network.h" + #include "util_print.h" + +void test_network_basic(void) +{ + CURL *curl = NULL; + CURLcode res = CURLE_OK; + + // First, check if we are connected to the internet + if (!is_network_connected()) return; + + res = curl_global_init(CURL_GLOBAL_ALL); + if (is_curl_error(&res, "init")) return; + + curl = curl_easy_init(); + if (is_curl_error(curl, "init")) { goto cleanup; } + + // Specify URL to download. Important: any change + // in the domain name must be reflected in the + // AllowedConnections capability in app_manifest.json. + res = curl_easy_setopt(curl, CURLOPT_URL, url); + if (is_curl_error(&res, "url")) { goto cleanup; } + + res = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if (is_curl_error(&res, "verbose")) { goto cleanup; } + + // Let cURL follow any HTTP 3xx redirects.Important: + // any redirection to different domain names requires + // that domain name to be added to app_manifest.json. + res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + if (is_curl_error(&res, "follow")) { goto cleanup; } + + res = curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + if (is_curl_error(&res, "user agent")) { goto cleanup; } + + // + // First, try a GET + // + res = curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + if (is_curl_error(&res, "httpget")) { goto cleanup; } + + res = curl_easy_perform(curl); + if (is_curl_error(&res, "get")) { goto cleanup; } + + // + // Then, try a POST + // + size_t num_data_bytes = 1 * sizeof(ZZ); + ZZ *data = malloc(num_data_bytes); + data[0] = 5; + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: vector"); + + res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + if (is_curl_error(&res, "postfields")) { goto cleanup2; } + + res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, num_data_bytes); + if (is_curl_error(&res, "postfieldsize")) { goto cleanup2; } + + res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + if (is_curl_error(&res, "headers")) { goto cleanup2; } + + res = curl_easy_perform(curl); + if (is_curl_error(&res, "post")) { goto cleanup2; } + +cleanup2: + free(data); + curl_slist_free_all(curl); + +cleanup: + if (curl) curl_easy_cleanup(curl); + curl_global_cleanup(); +} + +void test_network(void) +{ + size_t len = 1024; + size_t num_data_bytes = len * sizeof(ZZ); + ZZ *data = malloc(num_data_bytes); + + for (size_t i = 0; i < len; i++) data[i] = (ZZ)i; + send_over_network(data, num_data_bytes); + free(data); +} +#endif diff --git a/device/test/ntt_tests.c b/device/test/ntt_tests.c new file mode 100644 index 0000000..00dd653 --- /dev/null +++ b/device/test/ntt_tests.c @@ -0,0 +1,314 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file ntt_tests.c +*/ + +#include "defines.h" + +#ifndef SE_NTT_NONE + +#include +#include +#include // memset + +#include "intt.h" +#include "ntt.h" +#include "parameters.h" +#include "polymodmult.h" +#include "test_common.h" +#include "uintmodarith.h" +#include "util_print.h" + +#ifndef SE_USE_MALLOC +#ifdef SE_NTT_FAST +#define NTT_TESTS_ROOTS_MEM 2 * SE_DEGREE_N +#elif defined(SE_NTT_REG) || defined(SE_NTT_ONE_SHOT) +#define NTT_TESTS_ROOTS_MEM SE_DEGREE_N +#else +#define NTT_TESTS_ROOTS_MEM 0 +#endif +#ifdef SE_INTT_FAST +#define INTT_TESTS_ROOTS_MEM 2 * SE_DEGREE_N +#elif defined(SE_INTT_REG) || defined(SE_INTT_ONE_SHOT) +#define INTT_TESTS_ROOTS_MEM SE_DEGREE_N +#else +#define INTT_TESTS_ROOTS_MEM 0 +#endif +#endif + +// sb_res stores schoolbook results and must have 2n space +// ntt_roots either has space for n or 2n roots, depending on ntt option chosen +void test_poly_mult_ntt_only_helper(const Parms *parms, const ZZ *ntt_roots, ZZ *sb_res, ZZ *a, + ZZ *b) +{ + size_t n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + print_poly("a ", a, n); + print_poly("b ", b, n); + + // -- We could get the ntt by the following equation: + // intt(ntt(a) . ntt(b)) = [a * b]_Rq + // -- However, we do not necessarily have an intt implementation. + // -- Therefore, can check ntt in the following way, by balancing + // the above equation: + // --> ntt(intt(ntt(a) . ntt(b))) = ntt([a * b]_Rq) + // --> ntt(a) . ntt(b) = ntt([a * b]_Rq) + // We can use either the schoolbook alg for a * b and reduce it mod Rq + const char *left_side_str = "ntt(a) . ntt(b)"; + const char *right_side_str = "ntt([a * b]_Rq)"; + + poly_mult_mod_sb(a, b, n, mod, sb_res); + print_poly(" [a * b]_Rq ", sb_res, n); + + // -- Calculate right side: ntt([a * b]_Rq) + ntt_inpl(parms, ntt_roots, sb_res); + print_poly(right_side_str, sb_res, n); + ntt_inpl(parms, ntt_roots, a); + // print_poly("ntt(a) ", a, n); + ntt_inpl(parms, ntt_roots, b); + // print_poly("ntt(b) ", b, n); + + // -- Calculate left side: ntt(a) . ntt(b) + poly_mult_mod_ntt_form_inpl(a, b, n, mod); + print_poly(left_side_str, a, n); + + // -- Compare left side with right side + compare_poly(right_side_str, sb_res, left_side_str, a, n); +} + +void test_poly_mult_ntt_intt_helper(const Parms *parms, const ZZ *ntt_roots, const ZZ *intt_roots, + ZZ *sb_res, ZZ *a, ZZ *b) +{ + size_t n = parms->coeff_count; + Modulus *mod = parms->curr_modulus; + print_poly("a ", a, n); + print_poly("b ", b, n); + + // -- intt(ntt(a) . ntt(b)) = [a * b]_Rq + const char *left_side_str = "ntt(a) . ntt(b)"; + const char *right_side_str = " [a * b]_Rq "; + + // -- First, make sure we can get back the original vector + memcpy(sb_res, a, n * sizeof(a[0])); // sb_res = a + + ntt_inpl(parms, ntt_roots, sb_res); // sb_res = ntt(a) + print_poly(" ntt(a) ", sb_res, n); + intt_inpl(parms, intt_roots, sb_res); // sb_res = intt(ntt(a)) + print_poly("a ", a, n); + print_poly("intt(ntt(a))", sb_res, n); + compare_poly("a ", a, "intt(ntt(a))", sb_res, n); + + // -- Now, test multiplication + // printf("About to perform schoolbook multiplication\n"); + poly_mult_mod_sb(a, b, n, mod, sb_res); // This is very slow! + // printf("Back from performing schoolbook multiplication\n"); + print_poly(" [a * b]_Rq ", sb_res, n); + + ntt_inpl(parms, ntt_roots, a); + // print_poly("ntt(a) ", a, n); + + ntt_inpl(parms, ntt_roots, b); + // print_poly("ntt(b) ", b, n); + + // -- Left side: intt(ntt(a) . ntt(b)) + poly_mult_mod_ntt_form_inpl(a, b, n, mod); + intt_inpl(parms, intt_roots, a); + print_poly(left_side_str, a, n); + + // -- Finally, compare left side with right side + compare_poly(right_side_str, sb_res, left_side_str, a, n); +} + +void test_poly_mult_ntt(void) +{ + printf("**********************************\n\n"); + printf("Beginning tests for poly_mult_mod_ntt"); + printf("....\n\n"); + + // ================================ + // Configuration + // ================================ + PolySizeType n = 4096; + bool intt_mult_test = true; + // ================================ + Parms parms; + set_parms_ckks(n, 1, &parms); + print_test_banner("Ntt", &parms); + +#ifdef SE_NTT_OTF + size_t ntt_roots_size = 0; +#elif defined(SE_NTT_REG) || defined(SE_NTT_ONE_SHOT) + size_t ntt_roots_size = n; +#else // defined(SE_NTT_FAST) + size_t ntt_roots_size = 2 * n; +#endif + +#ifdef SE_INTT_OTF + size_t intt_roots_size = 0; +#elif defined(SE_INTT_REG) || defined(SE_INTT_ONE_SHOT) + size_t intt_roots_size = n; +#else // defined(SE_INTT_FAST) + size_t intt_roots_size = 2 * n; +#endif + + // ------------------ + // Initialize memory + // ------------------ + size_t mempool_size = 4 * n + ntt_roots_size + intt_roots_size; +#ifdef SE_USE_MALLOC + ZZ *mempool = calloc(mempool_size, sizeof(ZZ)); +#else + ZZ mempool_local[4 * SE_DEGREE_N + NTT_TESTS_ROOTS_MEM + INTT_TESTS_ROOTS_MEM]; + ZZ *mempool = &(mempool_local[0]); +#endif + + // clang-format off + size_t idx = 0; // start index + ZZ *a = &(mempool[idx]); idx += n; + ZZ *b = &(mempool[idx]); idx += n; + ZZ *sb_res = &(mempool[idx]); idx += 2*n; + ZZ *ntt_roots = ntt_roots_size ? &(mempool[idx]) : 0; idx += ntt_roots_size; + ZZ *intt_roots = intt_roots_size ? &(mempool[idx]) : 0; idx += intt_roots_size; + se_assert(idx == mempool_size); + // clang-format on + + while (1) + { + // ---------------------- + // Initialize ntt roots + // ---------------------- + ntt_roots_initialize(&parms, ntt_roots); +#ifdef SE_NTT_FAST + for (size_t i = 0; i < 3; i++) + { + printf("\nNtt Mumo[%zu]: ", i); + print_zz("operand", ((MUMO *)ntt_roots)[i].operand); + print_zz("quotient", ((MUMO *)ntt_roots)[i].quotient); + } +#elif !defined(SE_NTT_OTF) + print_poly("ntt_roots", ntt_roots, n); +#endif + if (intt_mult_test) + { + intt_roots_initialize(&parms, intt_roots); +#ifdef SE_INTT_FAST + for (size_t i = 0; i < 3; i++) + { + printf("\nIntt Mumo[%zu]: ", i); + print_zz("operand", ((MUMO *)intt_roots)[i].operand); + print_zz("quotient", ((MUMO *)intt_roots)[i].quotient); + } +#elif !defined(SE_INTT_OTF) + print_poly("intt_roots", intt_roots, n); +#endif + } + + // ------------------- + // Run through tests + // ------------------- + print_zz("Modulus", parms.curr_modulus->value); + for (int testnum = 1; testnum < 14; testnum++) + { + printf("--------------- Test %d ------------------\n", testnum); + reset_primes(&parms); + clear(mempool, mempool_size - ntt_roots_size - intt_roots_size); // Reset for each test + switch (testnum) + { + case 0: break; // 0 * 0 = 0 + case 1: a[0] = b[0] = 1; break; // 1 * 1 = 1 + case 2: a[1] = b[0] = 1; break; // x * 1 = 1 + case 3: + a[n / 4] = 2; + b[0] = 1; + break; // x * 1 = x + + case 4: + set(a, n, 2); // {2, 2, 2, ...} + b[0] = 1; // * 1 + break; + + case 5: + set(a, n, 1); // {1, 1, 1, ...} + b[0] = 2; // * 2 + break; + + case 6: { + ZZ v[] = {1, 1, 0}; + memcpy(a, v, sizeof(v)); + } + { + ZZ v[] = {1, 0, 0}; + memcpy(b, v, sizeof(v)); + } + break; + + case 7: { + ZZ v[] = {1, 1, 0}; + memcpy(a, v, sizeof(v)); + memcpy(b, v, sizeof(v)); + } + break; + + case 8: { + ZZ v[] = {1, 1, 1, 0, 0}; + memcpy(a, v, sizeof(v)); + memcpy(b, v, sizeof(v)); + } + break; + + case 9: + a[1] = 1; // {0, 1} + b[0] = 1; // * 1 + break; + + case 10: + a[n - 1] = 1; // {0, ..., 0, 1} + b[0] = 1; // * 1 + break; + + case 11: + a[n - 1] = 1; // {0, ..., 0, 1} + b[1] = 1; // * {0, 1} + break; + + case 12: + set(a, n, 1); // {1, 1, 1, ...} + set(b, n, 1); // * {1, 1, 1, ...} + break; + + case 13: + random_zzq_poly(a, n, parms.curr_modulus); // rand_x + random_zzq_poly(b, n, parms.curr_modulus); // * rand_y + break; + default: break; + } + if (intt_mult_test) + test_poly_mult_ntt_intt_helper(&parms, ntt_roots, intt_roots, sb_res, a, b); + else + test_poly_mult_ntt_only_helper(&parms, ntt_roots, sb_res, a, b); + } + if ((parms.curr_modulus_idx + 1) < parms.nprimes) + { + bool ret = next_modulus(&parms); + se_assert(ret); + } + else + break; + } +#ifdef SE_USE_MALLOC + free(mempool); +#endif + delete_parameters(&parms); +} +#endif + +#ifdef SE_USE_MALLOC +#ifdef NTT_TESTS_ROOTS_MEM +#undef NTT_TESTS_ROOTS_MEM +#endif +#ifdef INTT_TESTS_ROOTS_MEM +#undef INTT_TESTS_ROOTS_MEM +#endif +#endif diff --git a/device/test/sample_tests.c b/device/test/sample_tests.c new file mode 100644 index 0000000..0ea529a --- /dev/null +++ b/device/test/sample_tests.c @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/* +@file sample_tests.c + +Various tests for sampling a polynomial from a distribution. +*/ + +#include // memcmp + +#include "ckks_common.h" +#include "defines.h" +#include "parameters.h" +#include "sample.h" +#include "test_common.h" +#include "util_print.h" // printf + +#ifdef SE_USE_MALLOC +#define TEST_SAMPLE_DEGREE 4096 +#else +#define TEST_SAMPLE_DEGREE SE_DEGREE_N +#endif + +void test_ternary_poly_stats(ZZ *s, size_t n) +{ + size_t num_zero = 0, num_one = 0, num_other = 0; + + for (size_t i = 0; i < n; i++) + { + if (s[i] == 0) + num_zero++; + else if (s[i] == 1) + num_one++; + else + num_other++; + } + + size_t percent_zero = 100 * num_zero / n; + size_t percent_one = 100 * num_one / n; + size_t percent_other = 100 * num_other / n; + + size_t threshold_lower = 29; + size_t threshold_upper = 37; + + printf("Percent \'0\' values (should be ~33%%) : %0.1f\n", + ((double)num_zero / (double)n) * 100); + printf("Percent \'1\' values (should be ~33%%) : %0.1f\n", + ((double)num_one / (double)n) * 100); + printf("Percent \'other\' values (should be ~33%%) : %0.1f\n", + ((double)num_other / (double)n) * 100); + + if (n > 64) + { + se_assert(percent_zero > threshold_lower); + se_assert(percent_zero < threshold_upper); + se_assert(percent_one > threshold_lower); + se_assert(percent_one < threshold_upper); + se_assert(percent_other > threshold_lower); + se_assert(percent_other < threshold_upper); + } +} + +// TODO: Create tests for cbd sampling + +void test_sample_poly_uniform(void) +{ + printf("\n******************************************\n"); + printf("Beginning test for sample_poly_uniform...\n"); + size_t n = TEST_SAMPLE_DEGREE; + Parms parms; + set_parms_ckks(n, 1, &parms); + ZZ q = parms.curr_modulus->value; + print_zz("q", q); + printf("\n"); + SE_PRNG prng; + prng_randomize_reset(&prng, NULL); + + // -- Sample +#ifdef SE_USE_MALLOC + ZZ *a = calloc(n, sizeof(ZZ)); +#else + ZZ a[SE_DEGREE_N * sizeof(ZZ)]; +#endif + sample_poly_uniform(&parms, &prng, a); + + // -- Test + size_t num_above = 0; + size_t num_below_eq = 0; + for (int i = 0; i < n; i++) + { + if (a[i] > q / 2) + num_above++; + else + num_below_eq++; + } + size_t percent_above = 100 * num_above / n; + size_t percent_below_eq = 100 * num_below_eq / n; + size_t threshold_lower = 47; + size_t threshold_upper = 53; + se_assert(percent_above > threshold_lower); + se_assert(percent_below_eq < threshold_upper); + + print_poly_sign("sampled a", (ZZsign *)a, n); +#ifdef SE_USE_MALLOC + delete_parameters(&parms); + free(a); +#endif + printf("... done with tests for sample_poly_uniform.\n"); + printf("******************************************\n"); +} + +void test_sample_poly_ternary(void) +{ + printf("\n******************************************\n"); + printf("Beginning test for sample_poly_ternary...\n"); + size_t n = TEST_SAMPLE_DEGREE; + Parms parms; + set_parms_ckks(n, 1, &parms); + print_zz("q", parms.curr_modulus->value); + printf("\n"); + SE_PRNG prng; + prng_randomize_reset(&prng, NULL); + + // -- Sample +#ifdef SE_USE_MALLOC + ZZ *s = malloc(n * sizeof(ZZ)); +#else + ZZ s[SE_DEGREE_N * sizeof(ZZ)]; +#endif + sample_poly_ternary(&parms, &prng, s); + + // -- Test + test_ternary_poly_stats(s, n); + + print_poly("sampled s", s, n); +#ifdef SE_USE_MALLOC + delete_parameters(&parms); + free(s); +#endif + printf("... done with tests for sample_poly_ternary.\n"); + printf("******************************************\n"); +} + +#ifndef SE_USE_MALLOC +void test_sample_poly_ternary_small(void) +{ + printf("Error. This test is not runnable because SE_USE_MALLOC is not defined.\n"); +} +#else +void test_sample_poly_ternary_small(void) +{ + printf("\n******************************************\n"); + printf("Beginning test for sample_poly_ternary_small...\n"); + + size_t n = TEST_SAMPLE_DEGREE; + Parms parms; + set_parms_ckks(n, 3, &parms); + print_zz("q", parms.curr_modulus->value); + printf("\n"); + + SE_PRNG prng; + prng_randomize_reset(&prng, NULL); + + // -- Sample a polynomial in compressed form + // -- Note: n = # of elements = # of 2-bit slots + // --> # of bits required = 2n + // --> # of bytes required = 2n/8 = n/4 + size_t s_small_nbytes = n / 4; + ZZ *s_small = calloc(s_small_nbytes, 1); + sample_small_poly_ternary_prng_96(n, &prng, s_small); + print_poly_small("s ", s_small, n); + ZZ *s_small_save = malloc(s_small_nbytes); + memcpy(s_small_save, s_small, s_small_nbytes); + + // -- Test expansion + size_t s_expanded_nbytes = n * sizeof(ZZ); + ZZ *s_expanded = calloc(s_expanded_nbytes, 1); + expand_poly_ternary(s_small, &parms, s_expanded); + print_poly("s_expanded ", s_expanded, n); + test_ternary_poly_stats(s_expanded, n); + + // -- Test in-place expansion + ZZ *s_inplace = realloc(s_small, s_expanded_nbytes); + expand_poly_ternary_inpl(s_inplace, &parms); + print_poly("sk expanded inpl", s_inplace, n); + test_ternary_poly_stats(s_inplace, n); + + se_assert(!memcmp(s_inplace, s_expanded, s_expanded_nbytes)); + + // -- Test conversion to next modulus prime + for (size_t j = 0; j < 2; j++) + { + for (size_t nprimes = 0; nprimes < parms.nprimes; nprimes++) + { + print_zz("q", parms.curr_modulus->value); + if (j == 1) + { + convert_poly_ternary_inpl(s_inplace, &parms); + print_poly("s converted ", s_inplace, n); + } + else + { + expand_poly_ternary(s_small_save, &parms, s_inplace); + print_poly("s expanded ", s_inplace, n); + } + for (size_t i = 0; i < n; i++) + { + if (s_inplace[i] == 0 || s_inplace[i] == 1) + { + se_assert(s_inplace[i] == s_expanded[i]); + } + else + { + ZZ q = parms.curr_modulus->value; + if (s_inplace[i] != q - 1) + { + printf("s_inplace[%zu]: %" PRIuZZ "\n", i, s_inplace[i]); + printf("q: %" PRIuZZ "\n", q); + } + se_assert(s_inplace[i] == q - 1); + } + } + if ((nprimes + 1) < parms.nprimes) next_modulus(&parms); + } + reset_primes(&parms); + } + + delete_parameters(&parms); + free(s_inplace); + free(s_expanded); + free(s_small_save); + printf("... done with tests for sample_poly_ternary_small.\n"); + printf("******************************************\n"); +} +#endif diff --git a/device/test/test_common.h b/device/test/test_common.h new file mode 100644 index 0000000..d382a5f --- /dev/null +++ b/device/test/test_common.h @@ -0,0 +1,352 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file test_common.h +*/ + +#pragma once + +#include +#include // uint64_t, UINT64_MAX +#include // memset + +#include "defines.h" +#include "modulo.h" +#include "sample.h" +#include "util_print.h" + +//#define TEST_DEBUG + +#define BILLION 1000000000L + +#ifndef MAX64 +#define MAX64 0xFFFFFFFFFFFFFFFFULL +#endif + +#ifndef MAX63 +#define MAX63 0x7FFFFFFFFFFFFFFFULL +#endif + +#ifndef MAX32 +#define MAX32 0xFFFFFFFFUL +#endif + +#ifndef MAX31 +#define MAX31 0x7FFFFFFFUL +#endif + +#ifndef MAX16 +#define MAX16 0xFFFFU +#endif + +#define MAX_ZZ MAX32 +#define MAX_Q MAX31 + +// ----------------------------------------------- +// Generate "random" uint +// Note: random_zz is already defined in sample.h +// ----------------------------------------------- +/** +@param[in] q Modulus value +@returns A random ZZ value mod q +*/ +static inline ZZ random_zzq(Modulus *q) +{ + return barrett_reduce(random_zz(), q); +} + +/** +@returns a random sized-(sizeof(ZZ)/2) value +*/ +static inline ZZ random_zz_half(void) +{ + // -- Don't do this. It doesn't work: + // ZZ result; + // getrandom((void*)&result, sizeof(result)/2, 0); + + return random_zz() & 0xFFFF; +} + +/** +@returns a random sized-(sizeof(ZZ)/4) value +*/ +static inline ZZ random_zz_quarter(void) +{ + return random_zz() & 0xFF; +} + +/** +@returns a random sized-(sizeof(ZZ)/8) value +*/ +static inline ZZ random_zz_eighth(void) +{ + return random_zz() & 0xF; +} + +// ------------------------- +// Generate "random" double +// ------------------------- +static inline double gen_double(int64_t div) +{ + return (double)random_zz() / (double)div; +} +static inline double gen_double_half(int64_t div) +{ + return (double)random_zz_half() / (double)div; +} +static inline double gen_double_quarter(int64_t div) +{ + return (double)random_zz_quarter() / (double)div; +} +static inline double gen_double_eighth(int64_t div) +{ + return (double)random_zz_eighth() / (double)div; +} + +// ----------------------- +// Generate "random" flpt +// ----------------------- +static inline flpt gen_flpt(int64_t div) +{ + return (flpt)gen_double(div); +} +static inline flpt gen_flpt_half(int64_t div) +{ + return (flpt)gen_double_half(div); +} +static inline flpt gen_flpt_quarter(int64_t div) +{ + return (flpt)gen_double_quarter(div); +} +static inline flpt gen_flpt_eighth(int64_t div) +{ + return (flpt)gen_double_eighth(div); +} + +// ------------------ +// Compare functions +// ------------------ +static inline void compare_poly(const char *a_name, const ZZ *a, const char *b_name, const ZZ *b, + size_t len) +{ + for (size_t i = 0; i < len; i++) + { + if (a[i] != b[i]) + { + printf("\n"); + printf("Mismatched index: %zu\n", i); + print_zz(a_name, a[i]); + print_zz(b_name, b[i]); + } + se_assert(a[i] == b[i]); + } +} + +static inline bool compare_poly_flpt(const char *a_name, const flpt *a, const char *b_name, + const flpt *b, size_t len, flpt max_diff) +{ + printf("Comparing...\n"); + for (size_t i = 0; i < len; i++) + { + flpt diff = (flpt)fabs(a[i] - b[i]); + if (diff >= max_diff) + { + printf("%s[%zu]: %0.9f\n", a_name, i, a[i]); + printf("%s[%zu]: %0.9f\n", b_name, i, b[i]); + return 1; + } + } + return 0; +} + +static inline bool all_zeros(ZZ *vec, size_t n) +{ + size_t num_zeros = 0; + for (size_t i = 0; i < n; i++) + { + if (!vec[i]) num_zeros++; + } + if (n == num_zeros) + return true; + else + return false; +} + +static inline bool compare_poly_double_complex(const double complex *a, const double complex *b, + size_t n, double maxdiff) +{ + for (size_t i = 0; i < n; i++) + { + // printf("Checking index %zu: \n", i); + double diff = cabs(a[i] - b[i]); + if (diff >= maxdiff) + { + printf("vec1[%zu]: %0.9f + %0.9fi\n", i, se_creal(a[i]), se_cimag(a[i])); + printf("vec2[%zu]: %0.9f + %0.9fi\n", i, se_creal(b[i]), se_cimag(b[i])); + return 1; + } + } + return false; +} + +// ------------------------------------------------- +// Set uint +// (Note that clear is already defined in defines.h) +// ------------------------------------------------- +static inline void set(ZZ *vec, size_t vec_len, ZZ val) +{ + for (size_t i = 0; i < vec_len; i++) vec[i] = val; +} + +// ------------------ +// Set/clear flpt +// ------------------- +static inline void clear_flpt(flpt *poly, PolySizeType n) +{ + for (PolySizeType i = 0; i < n; i++) poly[i] = 0; +} + +static inline void set_flpt(flpt *vec, size_t vec_len, flpt val) +{ + for (size_t i = 0; i < vec_len; i++) vec[i] = val; +} + +// ---------------------- +// Set/clear double poly +// ---------------------- +static inline void clear_double(double *vec, PolySizeType n) +{ + memset(vec, 0, sizeof(double) * n); + // for (PolySizeType i = 0; i < n; i++) vec[i] = 0; +} + +static inline void set_double(double *vec, size_t vec_len, double val) +{ + for (size_t i = 0; i < vec_len; i++) vec[i] = val; +} + +// ------------------------------ +// Set/clear double complex poly +// ------------------------------ +static inline void clear_double_complex(double complex *vec, size_t n) +{ + memset(vec, 0, sizeof(double complex) * n); + // for (size_t i = 0; i < n; i++) vec[i] = double complex(0, 0); +} + +static inline void set_double_complex(double complex *vec, size_t n, flpt val) +{ + for (size_t i = 0; i < n; i++) vec[i] = (double complex)_complex(val, 0); +} + +// ---------------------- +// "Random" uint poly +// ---------------------- +static inline void random_zz_quarter_poly(ZZ *poly, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = random_zz_quarter(); +} + +static inline void random_zz_half_poly(ZZ *poly, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = random_zz_half(); +} + +static inline void random_zz_poly(ZZ *poly, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = random_zz(); +} + +static inline void random_zzq_poly(ZZ *poly, size_t n, Modulus *q) +{ + for (size_t i = 0; i < n; i++) poly[i] = random_zzq(q); +} + +// ------------------------- +// "Random" double complex +// ------------------------- +static inline void gen_double_complex_eighth_vec(double complex *vec, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + vec[i] = (double complex)(gen_double_eighth(div), gen_double_eighth(div)); + } +} + +static inline void gen_double_complex_quarter_vec(double complex *vec, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + vec[i] = (double complex)(gen_double_quarter(div), gen_double_quarter(div)); + } +} + +static inline void gen_double_complex_half_vec(double complex *vec, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + vec[i] = (double complex)(gen_double_half(div), gen_double_half(div)); + } +} + +static inline void gen_double_complex_vec(double complex *vec, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) { vec[i] = (double complex)(gen_double(div), gen_double(div)); } +} + +// ---------------------- +// "Random" double poly +// ---------------------- +static inline void gen_double_eighth_poly(double *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_double_eighth(div); +} + +static inline void gen_double_quarter_poly(double *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_double_quarter(div); +} + +static inline void gen_double_half_poly(double *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_double_half(div); +} + +static inline void gen_double_poly(double *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_double(div); +} + +// ---------------------- +// "Random" float poly +// ---------------------- +static inline void gen_flpt_eighth_poly(flpt *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_flpt_eighth(div); +} + +static inline void gen_flpt_quarter_poly(flpt *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_flpt_quarter(div); +} + +static inline void gen_flpt_half_poly(flpt *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_flpt_half(div); +} + +static inline void gen_flpt_poly(flpt *poly, int64_t div, size_t n) +{ + for (size_t i = 0; i < n; i++) poly[i] = gen_flpt(div); +} + +static inline void print_test_banner(const char *test_name, const Parms *parms) +{ + printf("***************************************************\n"); + printf("Running Test: %s\n", test_name); + printf("n: %zu, nprimes: %zu, scale: %0.2f\n", parms->coeff_count, parms->nprimes, + parms->scale); + print_config(!parms->is_asymmetric); + printf("***************************************************\n"); +} diff --git a/device/test/uintmodarith_tests.c b/device/test/uintmodarith_tests.c new file mode 100644 index 0000000..8298b59 --- /dev/null +++ b/device/test/uintmodarith_tests.c @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file uintmodarith_tests.c +*/ + +#include "defines.h" +#include "modulo.h" +#include "modulus.h" +#include "test_common.h" +#include "uintmodarith.h" +#include "util_print.h" + +void test_add_mod_helper(ZZ val1, ZZ val2, Modulus *modulus, ZZ res_exp) +{ + // -- Recall correctness requirement: val1 + val2 < 2q-1 + ZZ q = modulus->value; + printf("---------------------------------\n"); + for (int i = 0; i < 2; i++) + { + printf("( %" PRIuZZ " + %" PRIuZZ " ) %% %" PRIuZZ "\n", val1, val2, q); + ZZ res = add_mod(val1, val2, modulus); + print_zz("Result ", res); + print_zz("Result expected", res_exp); + se_assert(res == res_exp); + + ZZ res_default = (val1 + val2) % q; + print_zz("Result default ", res_default); + se_assert(res == res_default); + + // -- Swap val1 and val2 and try again + ZZ temp = val1; + val1 = val2; + val2 = temp; + printf("(After swap)\n"); + } +} + +void test_mul_mod_helper(ZZ val1, ZZ val2, Modulus *modulus, ZZ res_exp) +{ + ZZ q = modulus->value; + printf("---------------------------------\n"); + for (int i = 0; i < 2; i++) + { + printf("( %" PRIuZZ " * %" PRIuZZ " ) %% %" PRIuZZ "\n", val1, val2, q); + ZZ res = mul_mod(val1, val2, modulus); + print_zz("Result expected", res_exp); + print_zz("Result ", res); + se_assert(res == res_exp); + + ZZ res_default = (val1 * val2) % q; + print_zz("Result default ", res_default); + se_assert(res == res_default); + + // -- Swap val1 and val2 and try again + ZZ temp = val1; + val1 = val2; + val2 = temp; + printf("(After swap)\n"); + } +} + +void test_neg_mod_helper(ZZ input, const Modulus *modulus, ZZ res_exp) +{ + ZZ q = modulus->value; + printf("---------------------------------\n"); + ZZ res = neg_mod(input, modulus); + + printf("( -%" PRIuZZ ") %% %" PRIuZZ "\n", input, q); + print_zz("Result ", res); + print_zz("Result expected", res_exp); + se_assert(res == res_exp); + + // -- Don't use the remainder operator, it does not work well for modular negation + // ZZ res_default = (ZZ)((int64_t)(-input) % q); + // print_zz("Result default ", res_default); + // se_assert(res == res_default); +} + +void test_add_mod_basic(Modulus *modulus) +{ + // -- Recall correctness requirement: val1 + val2 < 2q-1 + ZZ q = modulus->value; + + test_add_mod_helper(0, 0, modulus, 0); // 0+0 = 0 (mod q) + test_add_mod_helper(0, 1, modulus, 1); // 0+1 = 1 (mod q) + test_add_mod_helper(0, q, modulus, 0); // 0+q = 0 (mod q) + test_add_mod_helper(1, q, modulus, 1); // 1+q = 1 (mod q) + + test_add_mod_helper(1, q - 1, modulus, 0); // 1+q-1 = 0 (mod q) + test_add_mod_helper(q, q - 1, modulus, q - 1); // q+q-1 = q-1 (mod q) + + test_add_mod_helper(q - 1, q - 1, modulus, q - 2); // q-1+q-1 = q-2 (mod q) + test_add_mod_helper(0, 2 * q - 1, modulus, q - 1); // 0+2q-1 = q-1 (mod q) +} + +void test_mul_mod_basic(Modulus *modulus) +{ + ZZ q = modulus->value; + test_mul_mod_helper(0, 0, modulus, 0); // 0*0 % q = 0 + test_mul_mod_helper(1, 1, modulus, 1); // 1*1 % q = 1 + + test_mul_mod_helper(1, q, modulus, 0); // 1*q % q = 0 + test_mul_mod_helper(q + 1, 1, modulus, 1); // (q + 1)*1 % q = 1 + test_mul_mod_helper(q - 1, 1, modulus, q - 1); // (q - 1)*1 % q = q - 1 + test_mul_mod_helper(0, 12345, modulus, 0); // 0*x % q = 0 + + // Can't really calculate expected for these... + test_mul_mod_helper(1, MAX_ZZ, modulus, + MAX_ZZ % q); // 1*MAX64 % q = MAX64 % q + test_mul_mod_helper(1, 12345, modulus, 12345 % q); // 1*x % q = x % q +} + +void test_neg_mod_basic(Modulus *modulus) +{ + ZZ q = modulus->value; + test_neg_mod_helper(0, modulus, 0); // -0 % q = 0 + test_neg_mod_helper(1, modulus, q - 1); // -1 % q = q-1 + test_neg_mod_helper(q - 1, modulus, 1); // -(q-1) % q = 1 + test_neg_mod_helper(q, modulus, 0); // -q % q = 0 +} + +void test_add_mod(void) +{ + printf("\n*******************************************\n"); + printf("Beginning tests for add_mod...\n\n"); + Modulus modulus; + + // ------------------------------- + // Set 1: q = 2 (min value of q) + // ------------------------------- + set_modulus(2, &modulus); + test_add_mod_basic(&modulus); + + // ---------------- + // Set 2: q = 10 + // ---------------- + set_modulus(10, &modulus); + test_add_mod_basic(&modulus); + test_add_mod_helper(7, 7, &modulus, 4); // 7+7 % 10 = 4 + test_add_mod_helper(6, 7, &modulus, 3); // 6+7 % 10 = 3 + + // ----------------------- + // Set 3: max q + // ----------------------- + set_modulus(MAX_Q, &modulus); + test_add_mod_basic(&modulus); + test_add_mod_basic(&modulus); + + printf("\n...all tests for add_mod passed.\n"); + printf("*******************************************\n"); +} + +void test_neg_mod(void) +{ + printf("\n*******************************************\n"); + printf("Beginning tests for neg_mod...\n\n"); + Modulus modulus; + + set_modulus(2, &modulus); + test_neg_mod_basic(&modulus); + + set_modulus(0xFFFF, &modulus); + test_neg_mod_basic(&modulus); + test_neg_mod_helper(1234, &modulus, 64301); + + set_modulus(0x10000, &modulus); + test_neg_mod_basic(&modulus); + test_neg_mod_helper(1234, &modulus, 64302); + + printf("\n...all tests for neg_mod passed.\n"); + printf("*******************************************\n"); +} + +void test_mul_mod(void) +{ + printf("\n*******************************************\n"); + printf("Beginning tests for mul_mod...\n\n"); + Modulus modulus; + + // ------------------------------- + // Set 1: q = 2 (min value of q) + // ------------------------------- + set_modulus(2, &modulus); + test_mul_mod_basic(&modulus); + + // ---------------- + // Set 2: q = 10 + // ---------------- + set_modulus(10, &modulus); + test_mul_mod_basic(&modulus); + test_mul_mod_helper(7, 7, &modulus, 9); + test_mul_mod_helper(6, 7, &modulus, 2); + + // ----------------------- + // Set 3: max q + // ----------------------- + set_modulus(MAX_Q, &modulus); + test_mul_mod_basic(&modulus); + + printf("\n...all tests for mul_mod passed.\n"); + printf("*******************************************\n"); +} + +// TODO: Add more test cases for above functions + +// TODO: Add this test +// void test_mod_mumo_helper(); diff --git a/device/test/uintops_tests.c b/device/test/uintops_tests.c new file mode 100644 index 0000000..1d9ab67 --- /dev/null +++ b/device/test/uintops_tests.c @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +/** +@file uintops_tests.c +*/ + +#include +#include // memset + +#include "defines.h" +#include "test_common.h" +#include "uint_arith.h" +#include "uintops.h" +#include "util_print.h" + +void test_add_uint_helper(ZZ val1, ZZ val2, ZZ sum_exp, uint8_t carry_exp) +{ + for (int i = 0; i < 2; i++) + { + ZZ sum; + uint8_t carry = add_uint(val1, val2, &sum); + se_assert(carry == carry_exp); + se_assert(sum == sum_exp); + + if (i == 0) + { + // -- Swap val1 and val2 and try again + ZZ temp = val1; + val1 = val2; + val2 = temp; + } + } +} + +void test_add_uint(void) +{ + printf("\n**********************************\n"); + printf("\nBeginnning tests for add_uint...\n\n"); + ZZ val1, val2, sum_exp; + uint8_t c_exp; + + test_add_uint_helper(0, 0, 0, 0); + test_add_uint_helper(1, 1, 2, 0); + test_add_uint_helper(MAX_ZZ, 0, MAX_ZZ, 0); + test_add_uint_helper(MAX_ZZ, 1, 0, 1); + test_add_uint_helper(MAX_ZZ, MAX_ZZ, MAX_ZZ - 1, 1); + test_add_uint_helper(MAX_ZZ - 1, 1, MAX_ZZ, 0); + test_add_uint_helper(MAX_ZZ - 1, 2, 0, 1); + + ZZ val = random_zz(); + test_add_uint_helper(val, 0, val, 0); + + // -- Test that regular addition still works + for (size_t i = 0; i < 5; i++) + { + printf("\n-------------\n"); + printf("\nTest number: %zu\n\n", i); + switch (i) + { + case 0: // max half * 2 = shift everything by 1 + val1 = 0xFFFF; + val2 = 0xFFFF; + sum_exp = val1 << 1; + c_exp = 0; + break; + case 1: + val1 = 0xFFFFF; + val2 = 0xFFFFF; + sum_exp = val1 << 1; + c_exp = 0; + break; + case 2: + val1 = 0x0F00F00F; + val2 = ~val1; + sum_exp = 0xFFFFFFFF; + c_exp = 0; + case 3: + val1 = 0x37281295; + val2 = 0x15720382; + sum_exp = 0x4c9a1617; + c_exp = 0; + case 4: + val1 = 0xd7281295; + val2 = 0xa5720382; + sum_exp = 0x7c9a1617; + c_exp = 1; + } + test_add_uint_helper(val1, val2, sum_exp, c_exp); + } + printf("**********************************\n"); +} + +void test_mult_uint_helper(ZZ val1, ZZ val2, ZZ *result_exp) +{ + ZZ result[2]; + for (int i = 0; i < 2; i++) + { + mul_uint_wide(val1, val2, result); + se_assert(result[0] == result_exp[0]); + se_assert(result[1] == result_exp[1]); + + if (i == 0) + { + // -- Swap inputs and try again + memset(&result, 0, sizeof(result)); + ZZ temp = val1; + val1 = val2; + val2 = temp; + } + } +} + +void test_mult_uint(void) +{ + printf("\n************************************\n"); + printf("\nBeginnning tests for mult_uint...\n\n"); + ZZ val1, val2; + ZZ result_exp[2]; + + memset(result_exp, 0, sizeof(result_exp)); + test_mult_uint_helper(0, 0, result_exp); + + memset(result_exp, 0, sizeof(result_exp)); + test_mult_uint_helper(1, 0, result_exp); + + for (size_t i = 0; i < 3; i++) + { + printf("\n-------------\n"); + printf("\nTest number: %zu\n\n", i); + switch (i) + { + case 0: + val1 = 0x10000; + val2 = 0x0FABA; + result_exp[1] = 0; + result_exp[0] = 0xFABA0000; + break; + case 1: + val1 = 0x100000; + val2 = 0x00FABA; + result_exp[1] = 0xF; + result_exp[0] = 0xABA00000; + break; + + case 2: + val1 = 11223344; + val2 = 55667788; + result_exp[1] = 0x2383b; + result_exp[0] = 0xa2879a40; + break; + } + test_mult_uint_helper(val1, val2, result_exp); + } + printf("************************************\n"); +} diff --git a/pipelines/device_docker.yml b/pipelines/device_docker.yml new file mode 100644 index 0000000..b9e549f --- /dev/null +++ b/pipelines/device_docker.yml @@ -0,0 +1,40 @@ +trigger: + batch: true + branches: + include: + - main + paths: + exclude: + - 'tools/*' + - CODE_OF_CONDUCT.md + - CONTRIBUTING.md + - ISSUES.md + - LICENSE + - NOTICE + - README.md + - SECURITY.md + +stages: +- stage: adapter + displayName: 'adapter' + jobs: + - job: build + displayName: 'Build SEAL-Embedded test image' + pool: + vmImage: 'ubuntu-latest' + steps: + - task: CMake@1 + displayName: 'CMake SEAL-Embedded Adapter' + inputs: + workingDirectory: '$(Build.SourcesDirectory)' + cmakeArgs: '-DCMAKE_BUILD_TYPE=Release adapter' + - script: | + cd $BUILD_SOURCESDIRECTORY + make + displayName: 'Build SEAL-Embedded Adapter' + - script: | + cd $BUILD_SOURCESDIRECTORY + ./bin/se_adapter <<< 1 + displayName: 'Run SEAL-Embedded Adapter' + - bash: docker build --target build -t hlbuildimage -f .devcontainer/Dockerfile . && docker run --name hlbuildcontainer hlbuildimage && docker cp hlbuildcontainer:/out $(Build.ArtifactStagingDirectory)/HLOutput + displayName: Build SEAL-Embedded image diff --git a/pipelines/device_local.yml b/pipelines/device_local.yml new file mode 100644 index 0000000..e488f2f --- /dev/null +++ b/pipelines/device_local.yml @@ -0,0 +1,87 @@ +trigger: + batch: true + branches: + include: + - main + paths: + exclude: + - 'tools/*' + - CODE_OF_CONDUCT.md + - CONTRIBUTING.md + - ISSUES.md + - LICENSE + - NOTICE + - README.md + - SECURITY.md + +stages: +- stage: adapter + displayName: 'adapter' + jobs: + - job: build + displayName: 'Build SEAL-Embedded test image' + pool: + vmImage: 'windows-latest' + steps: + - task: CMake@1 + displayName: 'CMake SEAL-Embedded Adapter' + inputs: + workingDirectory: '$(Build.SourcesDirectory)/build-adapter' + cmakeArgs: '-DCMAKE_BUILD_TYPE=Release ../adapter' + - task: MSBuild@1 + displayName: 'Build SEAL-Embedded Adapter' + inputs: + solution: '$(Build.SourcesDirectory)/build-adapter/seal_embedded_adapter.sln' + msbuildArchitecture: 'x64' + platform: 'x64' + configuration: 'Release' + - task: CmdLine@2 + displayName: 'Run SEAL-Embedded Adapter' + inputs: + script: | + echo 1 >> test.txt + $(Build.SourcesDirectory)/build-adapter/bin/Release/se_adapter.exe < test.txt +# - task: CMake@1 +# displayName: 'CMake SEAL-Embedded' +# inputs: +# workingDirectory: '$(Build.SourcesDirectory)/build-device' +# cmakeArgs: '-DCMAKE_BUILD_TYPE=Release -DSE_BUILD_LOCAL=ON -DSE_BUILD_M4=OFF -DSE_M4_IS_SPHERE=OFF -DSE_BUILD_TYPE="Tests" ../device' +# - task: MSBuild@1 +# displayName: 'Build SEAL-Embedded' +# inputs: +# solution: '$(Build.SourcesDirectory)/build-device/seal_embedded.sln' +# msbuildArchitecture: 'x64' +# platform: 'x64' +# configuration: 'Release' + - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 + displayName: 'Run CredScan' + inputs: + toolMajorVersion: 'V2' + outputFormat: sarif + debugMode: false + - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@2 + displayName: 'Run Roslyn Analyzers' + - task: Semmle@0 + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + sourceCodeDirectory: '$(Build.SourcesDirectory)' + language: 'cpp' + includeNodeModules: true + querySuite: 'Recommended' + timeout: '1800' + ram: '16384' + addProjectDirToScanningExclusionList: true + buildCommands: '"%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsMSBuildCmd.bat" && msbuild $(Build.SourcesDirectory)/build-adapter/seal_embedded_adapter.sln' + cleanupBuildCommands: '"%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsMSBuildCmd.bat" && msbuild $(Build.SourcesDirectory)/build-adapter/seal_embedded_adapter.sln /t:Clean' + - task: ComponentGovernanceComponentDetection@0 + inputs: + scanType: 'Register' + verbosity: 'Verbose' + alertWarningLevel: 'High' + - task: PublishSecurityAnalysisLogs@2 + inputs: + ArtifactName: 'CodeAnalysisLogs' + ArtifactType: 'Container' + AllTools: true + ToolLogsNotFoundAction: 'Standard' diff --git a/tools/scripts/clang-format-all.sh b/tools/scripts/clang-format-all.sh new file mode 100644 index 0000000..47d21dd --- /dev/null +++ b/tools/scripts/clang-format-all.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +BASE_DIR=$(dirname "$0") +SE_ROOT_DIR=$BASE_DIR/../../ +shopt -s globstar +clang-format -i $SE_ROOT_DIR/device/**/*.h +clang-format -i $SE_ROOT_DIR/device/**/*.c +clang-format -i $SE_ROOT_DIR/adapter/**/*.h +clang-format -i $SE_ROOT_DIR/adapter/**/*.cpp +clang-format -i $SE_ROOT_DIR/example/**/*.h +clang-format -i $SE_ROOT_DIR/example/**/*.cpp