Implement 40250 classic client port
This commit is contained in:
@@ -21,6 +21,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
option(MTGODOT_BUILD_EXTENSION "Build the Godot GDExtension (pulls in godot-cpp)" ON)
|
||||
option(MTGODOT_BUILD_TOOLS "Build libgr2 validation tools (gr2dump/gr2fuzz/oracle_diff)" OFF)
|
||||
option(MTGODOT_BUILD_NET_TOOLS "Build host-only protocol probes without Godot" OFF)
|
||||
|
||||
# --- shared, cross-platform core (no godot-cpp, no third-party) ---
|
||||
add_subdirectory(libgr2)
|
||||
@@ -46,3 +47,27 @@ if(MTGODOT_BUILD_EXTENSION)
|
||||
endif()
|
||||
add_subdirectory(extension)
|
||||
endif()
|
||||
|
||||
# FreeBSD is the native build target of the archived 40250 server, but it is
|
||||
# not a supported Godot extension target. Keep the live classic probe
|
||||
# available as a small host-only build so a FreeBSD jail/VM can compile and run
|
||||
# it without pulling in godot-cpp or the Darwin/iOS/Android extension.
|
||||
if(MTGODOT_BUILD_NET_TOOLS AND NOT MTGODOT_BUILD_EXTENSION)
|
||||
add_subdirectory(extension/third_party)
|
||||
add_library(mtnet_host STATIC
|
||||
extension/src/net/secure_cipher.cpp
|
||||
extension/src/net/net_stream.cpp
|
||||
extension/src/net/entity_store.cpp
|
||||
extension/src/net/mark_image.cpp
|
||||
extension/src/net/classic/classic_stream.cpp
|
||||
extension/src/net/classic/classic_parser.cpp
|
||||
extension/src/net/classic/classic_session.cpp
|
||||
extension/src/net/classic/classic_cipher.cpp
|
||||
)
|
||||
target_include_directories(mtnet_host PUBLIC extension/src/net)
|
||||
target_link_libraries(mtnet_host PUBLIC mt3p::sodium mt3p::minilzo mt3p::cryptopp)
|
||||
target_compile_features(mtnet_host PUBLIC cxx_std_20)
|
||||
|
||||
add_executable(net_classic_e2e_host extension/tools/net_classic_e2e.cpp)
|
||||
target_link_libraries(net_classic_e2e_host PRIVATE mtnet_host)
|
||||
endif()
|
||||
|
||||
@@ -26105,7 +26105,6 @@ Y ymir work/zone/oxevent/ox_01.mdatr Zone/ymir work/zone/oxevent/ox_01.mdatr
|
||||
Y ymir work/zone/oxevent/ox_tile03.dds Zone/ymir work/zone/oxevent/ox_tile03.dds
|
||||
Y ymir work/zone/oxevent/x_flag.gr2 Zone/ymir work/zone/oxevent/x_flag.gr2
|
||||
Y ymir work/zone/oxevent/x_flag.mdatr Zone/ymir work/zone/oxevent/x_flag.mdatr
|
||||
R .ds_store .DS_Store
|
||||
R asset_index.txt asset_index.txt
|
||||
R effect/ymir work/effect/affect/3spot.dds Effect/ymir work/effect/affect/3spot.dds
|
||||
R effect/ymir work/effect/affect/damagevalue/critical.mse Effect/ymir work/effect/affect/damagevalue/critical.mse
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ CONFIG="${1:-Debug}" # Debug | Release
|
||||
ABI="${ANDROID_ABI:-arm64-v8a}"
|
||||
API="${ANDROID_PLATFORM:-24}"
|
||||
BUILD_DIR="build-android"
|
||||
JOBS="${JOBS:-$(sysctl -n hw.ncpu)}"
|
||||
JOBS="${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)}"
|
||||
|
||||
# --- locate the NDK ---
|
||||
NDK="${ANDROID_NDK_ROOT:-${ANDROID_NDK_HOME:-}}"
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
<!-- generated-by: gsd-doc-writer -->
|
||||
# Android 真机测试指南
|
||||
|
||||
本文说明如何在 Android 真机上安装 `mtgodot-poc`、生成并导入资源包、验证资源挂载,以及进行 40250 客户端测试。
|
||||
|
||||
## 1. 当前测试范围与已知前置条件
|
||||
|
||||
当前 Android 导出目标是 `arm64-v8a`,APK 包名为 `org.internal.mtgodotpoc`,最低 Android SDK 为 24。APK 只包含代码和 Godot 工程资源;Metin2 的 `assets/` 与 `bgm/` 不打进 APK,测试时必须单独导入 `assets.zip`。
|
||||
|
||||
当前仓库的资源部署存在一个需要特别注意的限制:
|
||||
|
||||
- `export-android.sh --install` 会把资源推到 `/sdcard/Android/data/org.internal.mtgodotpoc/files/assets.zip`。
|
||||
- `project/asset_pack.gd` 首先查找 `user://assets.zip`,当前没有显式把上述外部目录加入候选路径。
|
||||
- 因此,Debug 真机测试应把外部目录中的压缩包再复制到应用私有 `files/` 目录,确保客户端的 `user://assets.zip` 能命中。
|
||||
|
||||
另外,40250 classic 网络栈当前由进程环境变量 `MT_PROTOCOL=classic` 选择。Android 通过 Activity 启动 APK 时不能直接继承 Mac 终端的环境变量;如果 APK 尚未增加 Android 侧的协议配置入口或将 classic 设为移动端默认值,则只能完成 APK、资源和 UI 验证,不能据此宣称已经完成真实 40250 登录验收。
|
||||
|
||||
公会功能按当前项目范围暂缓;龙魂玩法不属于目标 40250 服务器功能,不纳入 Android 验收。
|
||||
|
||||
## 2. 主机环境
|
||||
|
||||
以下版本来自当前 Android Gradle 配置和构建脚本:
|
||||
|
||||
| 项目 | 要求 |
|
||||
|---|---|
|
||||
| 主机 | macOS,Apple Silicon 构建脚本已按当前机器验证 |
|
||||
| Godot | `4.7.1.stable`,并安装对应 Android 导出模板 |
|
||||
| Android SDK | compile/target SDK `36`,Build Tools `36.1.0` |
|
||||
| Android NDK | `29.0.14206865` |
|
||||
| Java | Gradle 配置最低 Java 17;当前脚本默认使用 OpenJDK 21 |
|
||||
| Android 手机 | `arm64-v8a`,建议使用 Vulkan 设备 |
|
||||
| 磁盘 | 至少预留 APK、资源包和临时构建空间;当前 `assets.zip` 约 2.1–2.2 GB |
|
||||
|
||||
进入仓库并设置环境变量。路径按本机安装位置调整:
|
||||
|
||||
```bash
|
||||
cd /Users/shenlei/Work/mt/mtgodot-poc
|
||||
|
||||
export ANDROID_SDK_ROOT="$HOME/Library/Android/sdk"
|
||||
export ANDROID_HOME="$ANDROID_SDK_ROOT"
|
||||
export ANDROID_NDK_ROOT="$ANDROID_SDK_ROOT/ndk/29.0.14206865"
|
||||
export JAVA_HOME="/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
|
||||
export GODOT="/opt/homebrew/bin/godot"
|
||||
export ADB="$ANDROID_SDK_ROOT/platform-tools/adb"
|
||||
```
|
||||
|
||||
先确认工具存在:
|
||||
|
||||
```bash
|
||||
"$GODOT" --version
|
||||
java -version
|
||||
"$ADB" version
|
||||
test -f "$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake"
|
||||
```
|
||||
|
||||
如果 Android SDK/NDK 尚未安装,可在 Android Studio 的 SDK Manager 中安装,或使用 `sdkmanager` 安装与上表一致的版本。不要只安装一个较旧 NDK 后直接构建;Gradle 模板要求的 NDK 版本以 `project/android/build/config.gradle` 为准。
|
||||
|
||||
Godot 还需要安装与 `4.7.1` 完全匹配的 Android 导出模板。可在 Godot 的“编辑器设置 → 导出 → 导出模板”中安装。
|
||||
|
||||
首次导出前生成 Debug keystore:
|
||||
|
||||
```bash
|
||||
./gen-debug-keystore.sh
|
||||
```
|
||||
|
||||
## 3. 连接 Android 真机
|
||||
|
||||
在手机上完成以下设置:
|
||||
|
||||
1. 设置 → 关于手机 → 连续点击版本号,开启开发者选项。
|
||||
2. 开发者选项中开启“USB 调试”。
|
||||
3. USB 连接模式选择“文件传输/MTP”。
|
||||
4. 首次连接时,在手机上确认“允许 USB 调试”。
|
||||
|
||||
在 Mac 上检查设备:
|
||||
|
||||
```bash
|
||||
"$ADB" kill-server
|
||||
"$ADB" start-server
|
||||
"$ADB" devices -l
|
||||
```
|
||||
|
||||
期望看到:
|
||||
|
||||
```text
|
||||
<设备序列号> device
|
||||
```
|
||||
|
||||
状态为 `unauthorized` 时,解锁手机并接受 RSA 授权;没有设备时,优先检查 USB 线、USB 模式、开发者选项和手机是否允许该电脑调试。
|
||||
|
||||
## 4. 生成资源包
|
||||
|
||||
资源包必须从与 APK 同一份工作树生成。执行:
|
||||
|
||||
```bash
|
||||
./pack-assets.sh
|
||||
```
|
||||
|
||||
该脚本会:
|
||||
|
||||
1. 执行 `bake_asset_index.gd`,生成 `assets/asset_index.txt`。
|
||||
2. 将 `assets/` 和 `bgm/` 打成 `build/export/assets.zip`。
|
||||
3. 使用 `zip -0` 原样存储,不压缩 `.dds`、`.gr2` 等大型资源。
|
||||
4. 排除 `.git`、`.DS_Store` 和 `assets/.gdignore`。
|
||||
|
||||
检查资源包:
|
||||
|
||||
```bash
|
||||
ls -lh build/export/assets.zip
|
||||
unzip -l build/export/assets.zip | rg 'assets/asset_index\.txt|assets/|bgm/' | head -n 20
|
||||
```
|
||||
|
||||
不要手工解压到 APK,也不要把完整 `assets/` 强行放进 `res://`。客户端启动时通过 `ProjectSettings.load_resource_pack()` 挂载 zip,挂载成功后资源以 `res://assets/...` 和 `res://bgm/...` 访问。
|
||||
|
||||
资源发生变化后必须重新执行 `./pack-assets.sh`。只重新导出 APK 不会更新已经生成的 `assets.zip`。
|
||||
|
||||
## 5. 构建 APK
|
||||
|
||||
建议先单独构建,不要在第一次测试时直接使用 `--install`:
|
||||
|
||||
```bash
|
||||
./export-android.sh Debug
|
||||
```
|
||||
|
||||
构建成功后应得到:
|
||||
|
||||
```text
|
||||
build/export/mtgodot-poc.apk
|
||||
```
|
||||
|
||||
检查 APK 和 native library:
|
||||
|
||||
```bash
|
||||
ls -lh build/export/mtgodot-poc.apk
|
||||
unzip -l build/export/mtgodot-poc.apk | rg 'lib/arm64-v8a/.*\.so'
|
||||
```
|
||||
|
||||
应至少包含 `lib/arm64-v8a/` 下的 Godot Android 库和 `libmtgodot` 扩展库。
|
||||
|
||||
## 6. 安装 APK 并导入资源
|
||||
|
||||
### 6.1 安装 APK
|
||||
|
||||
```bash
|
||||
"$ADB" install -r build/export/mtgodot-poc.apk
|
||||
```
|
||||
|
||||
`-r` 会保留应用数据。若要完全清理测试数据,可选执行:
|
||||
|
||||
```bash
|
||||
"$ADB" shell pm clear org.internal.mtgodotpoc
|
||||
```
|
||||
|
||||
清理后必须重新导入资源包。
|
||||
|
||||
### 6.2 先推送到 Android 外部目录
|
||||
|
||||
```bash
|
||||
PKG=org.internal.mtgodotpoc
|
||||
EXT_DIR="/sdcard/Android/data/$PKG/files"
|
||||
|
||||
"$ADB" shell mkdir -p "$EXT_DIR"
|
||||
"$ADB" push build/export/assets.zip "$EXT_DIR/assets.zip"
|
||||
"$ADB" shell ls -lh "$EXT_DIR/assets.zip"
|
||||
```
|
||||
|
||||
### 6.3 Debug 测试复制到 Godot `user://`
|
||||
|
||||
当前 `Debug` APK 可使用 `run-as` 把外部文件复制到应用私有 `files/` 目录:
|
||||
|
||||
```bash
|
||||
"$ADB" shell run-as "$PKG" sh -c \
|
||||
"cp '$EXT_DIR/assets.zip' files/assets.zip"
|
||||
|
||||
"$ADB" shell run-as "$PKG" ls -lh files/assets.zip
|
||||
```
|
||||
|
||||
最后一条命令能看到约 2.1–2.2 GB 的文件,才继续启动客户端。这个步骤是当前代码路径下的关键步骤;只执行 `adb push` 到 `/sdcard/Android/data/.../files/`,不能替代它。
|
||||
|
||||
如果 `run-as` 报告应用不可调试,确认安装的是 `Debug` APK,而不是 Release APK。Release 流程需要先修改资源查找/部署实现,使客户端明确支持外部目录,不能假设 `run-as` 可用。
|
||||
|
||||
### 6.4 资源更新
|
||||
|
||||
客户端退出后,只更新资源包时执行:
|
||||
|
||||
```bash
|
||||
./pack-assets.sh
|
||||
|
||||
"$ADB" push build/export/assets.zip "$EXT_DIR/assets.zip"
|
||||
"$ADB" shell run-as "$PKG" sh -c \
|
||||
"cp '$EXT_DIR/assets.zip' files/assets.zip"
|
||||
```
|
||||
|
||||
资源挂载只在 `client_main.gd::_ready()` 中执行,因此更新后必须重启客户端,不能只切回前台:
|
||||
|
||||
```bash
|
||||
"$ADB" shell am force-stop "$PKG"
|
||||
```
|
||||
|
||||
## 7. 启动客户端并确认资源挂载
|
||||
|
||||
清空旧日志后启动:
|
||||
|
||||
```bash
|
||||
"$ADB" logcat -c
|
||||
"$ADB" shell am start -n \
|
||||
org.internal.mtgodotpoc/com.godot.game.GodotApp
|
||||
```
|
||||
|
||||
实时查看日志:
|
||||
|
||||
```bash
|
||||
"$ADB" logcat -v time -s godot GodotError AndroidRuntime
|
||||
```
|
||||
|
||||
资源挂载成功的关键日志是:
|
||||
|
||||
```text
|
||||
[AssetPack] mounted .../assets.zip
|
||||
```
|
||||
|
||||
以下日志表示资源没有被找到或挂载失败:
|
||||
|
||||
```text
|
||||
[AssetPack] 找不到 assets.zip
|
||||
[AssetPack] load_resource_pack 失败
|
||||
[client] 资源目录不存在
|
||||
```
|
||||
|
||||
启动验收至少包含:
|
||||
|
||||
- 登录界面正常出现,而不是黑屏或立即退出。
|
||||
- 登录背景、控件和字体资源正常显示。
|
||||
- 选人页能加载职业模型、背景和界面资源。
|
||||
- 进入游戏后能看到地图、地形、建筑、树木或角色模型。
|
||||
- 日志中没有持续出现资源文件缺失、`FileAccess` 打开失败或 native library 加载失败。
|
||||
|
||||
可保存截图作为测试证据:
|
||||
|
||||
```bash
|
||||
mkdir -p build/export/android-evidence
|
||||
"$ADB" exec-out screencap -p > build/export/android-evidence/launch.png
|
||||
```
|
||||
|
||||
## 8. 40250 服务器连接配置
|
||||
|
||||
### 8.1 手机网络要求
|
||||
|
||||
手机必须能直接访问认证服/游戏服的 IP 和端口:
|
||||
|
||||
- 手机与局域网测试服连接到同一网络,或服务器开放公网访问。
|
||||
- 客户端不能使用 `127.0.0.1` 或 `localhost` 指向 Mac;在手机上它们指向手机自身。
|
||||
- 防火墙和 FreeBSD jail 的端口映射必须允许手机来源地址访问。
|
||||
- 服务器端口以实际 `CONFIG` 为准,不要照抄示例端口。
|
||||
|
||||
### 8.2 `serverlist.txt`
|
||||
|
||||
客户端启动时会优先读取 `res://serverlist.txt`;没有该文件时使用 `project/net/serverinfo.gd` 中的内置默认值。真实 40250 测试应在导出前准备 `project/serverlist.txt`,格式为 TSV:
|
||||
|
||||
```text
|
||||
# 名称\t认证地址\t认证端口\t游戏地址\t游戏端口\t频道列表\t端口步长\t会徽端口
|
||||
龙魂40250\t<服务器IP>\t<auth-port>\t<服务器IP>\t<channel1-port>\t1,2,3,4\t1\t0
|
||||
```
|
||||
|
||||
例如 40250 仓库脚本中的默认 channel1 端口与当前客户端内置默认值并不相同,必须以目标 FreeBSD 服务器的 `CONFIG` 和实际监听结果为准。
|
||||
|
||||
<!-- VERIFY: 真实 40250 服务器 IP、auth/game 端口、频道数量和防火墙策略必须由当前 FreeBSD 部署确认。 -->
|
||||
|
||||
### 8.3 classic 协议选择限制
|
||||
|
||||
当前 native 代码仅在进程环境变量满足以下条件时切换 40250 classic 后端:
|
||||
|
||||
```text
|
||||
MT_PROTOCOL=classic
|
||||
```
|
||||
|
||||
在 Mac 上运行 Godot 时可以通过环境变量设置;但 Android APK 是由 Activity 启动的,普通的 `adb shell am start` 不会把 Mac 的环境变量传入 APK。因此,真实 Android 40250 登录测试前必须确认以下任一项已经完成:
|
||||
|
||||
1. Android 版本把 classic 设置为默认网络后端;
|
||||
2. 客户端增加可持久化的协议配置并在 Android 导出中设为 `classic`;或
|
||||
3. Android 测试包提供协议选择入口。
|
||||
|
||||
不要把下面的命令当作可靠解决方案:
|
||||
|
||||
```bash
|
||||
adb shell MT_PROTOCOL=classic am start ...
|
||||
```
|
||||
|
||||
它设置的是 `am` 命令进程的环境,不等于给已启动的 Android Activity 设置环境变量。
|
||||
|
||||
## 9. 真机功能验收清单
|
||||
|
||||
以下项目按实际 40250 测试账号、角色和 NPC 条件逐项记录:
|
||||
|
||||
### 启动与网络
|
||||
|
||||
- [ ] APK 安装成功,包名为 `org.internal.mtgodotpoc`。
|
||||
- [ ] `assets.zip` 已复制到应用私有 `files/`,大小与主机一致。
|
||||
- [ ] 日志出现 `[AssetPack] mounted`。
|
||||
- [ ] 登录界面资源完整显示。
|
||||
- [ ] 服务器/频道检测能得到预期结果。
|
||||
- [ ] 认证成功后进入选人页。
|
||||
- [ ] 选择角色后进入游戏场景。
|
||||
|
||||
### 世界与基础操作
|
||||
|
||||
- [ ] 地图、地形、建筑、树木、角色和纹理正常加载。
|
||||
- [ ] 点击地面移动,角色位置与服务器同步。
|
||||
- [ ] 普通攻击、目标选择、技能和动作没有异常断线。
|
||||
- [ ] 聊天发送与接收正常。
|
||||
- [ ] 切换前后台后连接和场景状态符合预期;断线时能看到重连提示。
|
||||
|
||||
### 已接入的常用功能
|
||||
|
||||
- [ ] 背包打开、物品使用、丢弃、移动和快捷栏操作。
|
||||
- [ ] NPC 对话、脚本选项、输入框和确认框。
|
||||
- [ ] 普通商店查看、切换货架和购买。
|
||||
- [ ] 私人商店物品选择、定价、开设和撤收。
|
||||
- [ ] 交易发起、物品/金币添加、确认和取消。
|
||||
- [ ] 仓库密码、存入、取出和物品移动。
|
||||
- [ ] 商城物品列表和取出。
|
||||
- [ ] 组队邀请、接受、离队、成员状态和治疗入口。
|
||||
- [ ] ESC 系统菜单、音量、镜头、雾效和显示设置持久化。
|
||||
|
||||
### 明确不纳入本轮
|
||||
|
||||
- 公会相关功能:按当前范围暂缓。
|
||||
- 龙魂:目标 40250 服务器没有该功能,不进行功能验收。
|
||||
- 真实 FreeBSD + MySQL 全流程验收:需要服务器环境和测试账号,不能用本机离线测试结果替代。
|
||||
|
||||
## 10. 测试证据保存
|
||||
|
||||
建议每次测试保留 APK、资源包校验值、设备信息和日志:
|
||||
|
||||
```bash
|
||||
mkdir -p build/export/android-evidence
|
||||
|
||||
shasum -a 256 build/export/mtgodot-poc.apk \
|
||||
build/export/assets.zip \
|
||||
> build/export/android-evidence/artifacts.sha256
|
||||
|
||||
"$ADB" shell getprop ro.product.model \
|
||||
> build/export/android-evidence/device-model.txt
|
||||
"$ADB" shell getprop ro.build.version.release \
|
||||
> build/export/android-evidence/android-version.txt
|
||||
"$ADB" logcat -d -v time -s godot GodotError AndroidRuntime \
|
||||
> build/export/android-evidence/logcat.txt
|
||||
```
|
||||
|
||||
测试记录至少填写:构建时间、Git commit、APK SHA-256、`assets.zip` SHA-256、手机型号、Android 版本、服务器地址/端口、账号、测试结果和失败日志。
|
||||
|
||||
## 11. 常见问题
|
||||
|
||||
| 现象 | 处理 |
|
||||
|---|---|
|
||||
| `adb devices` 没有设备 | 开启 USB 调试、接受 RSA、切换 MTP、检查数据线;必要时重新执行 `adb kill-server` / `adb start-server` |
|
||||
| 状态是 `unauthorized` | 解锁手机并接受“允许 USB 调试”,然后重新执行 `adb devices` |
|
||||
| APK 安装失败 | 检查设备是否为 arm64;卸载旧包后再安装;查看 `adb install` 的具体错误 |
|
||||
| `run-as` 报不可调试 | 安装 `Debug` APK;Release APK 不能依赖 `run-as` 导入资源 |
|
||||
| 日志找不到 `assets.zip` | 确认外部 push 成功,再执行 `run-as ... cp ... files/assets.zip`,然后重启应用 |
|
||||
| `load_resource_pack 失败` | 检查压缩包完整性、磁盘空间、是否由 `./pack-assets.sh` 生成,以及 zip 内是否存在 `assets/asset_index.txt` |
|
||||
| 登录连接超时 | 手机不能使用 `localhost`;检查服务器 IP、端口、Wi-Fi/VPN、防火墙和 FreeBSD jail 映射 |
|
||||
| 登录协议不对或立即断线 | 确认 Android 包实际选择了 `classic`,仅在 Mac 终端设置 `MT_PROTOCOL` 不会自动传入 APK |
|
||||
| 黑屏或 Vulkan 崩溃 | 保存完整 `logcat`;当前 Android 移动 Vulkan 的 shader/GPU-skin 真机验证仍需在目标设备上进行 |
|
||||
| 更新资源后画面不变 | 资源包挂载发生在启动阶段,执行 `am force-stop` 后重新启动;必要时清理应用数据并重新导入 |
|
||||
|
||||
## 12. 推荐的最短测试流程
|
||||
|
||||
首次测试按以下顺序执行:
|
||||
|
||||
```bash
|
||||
cd /Users/shenlei/Work/mt/mtgodot-poc
|
||||
|
||||
./pack-assets.sh
|
||||
./export-android.sh Debug
|
||||
|
||||
export ADB="${ANDROID_SDK_ROOT:-$HOME/Library/Android/sdk}/platform-tools/adb"
|
||||
PKG=org.internal.mtgodotpoc
|
||||
EXT_DIR="/sdcard/Android/data/$PKG/files"
|
||||
|
||||
"$ADB" install -r build/export/mtgodot-poc.apk
|
||||
"$ADB" shell mkdir -p "$EXT_DIR"
|
||||
"$ADB" push build/export/assets.zip "$EXT_DIR/assets.zip"
|
||||
"$ADB" shell run-as "$PKG" sh -c \
|
||||
"cp '$EXT_DIR/assets.zip' files/assets.zip"
|
||||
"$ADB" shell run-as "$PKG" ls -lh files/assets.zip
|
||||
|
||||
"$ADB" logcat -c
|
||||
"$ADB" shell am start -n "$PKG/com.godot.game.GodotApp"
|
||||
"$ADB" logcat -v time -s godot GodotError AndroidRuntime
|
||||
```
|
||||
|
||||
只有在看到 `[AssetPack] mounted`、登录界面资源正常、并确认 Android 包使用正确的 40250 classic 后端后,才进入真实服务器功能验收。
|
||||
@@ -0,0 +1,648 @@
|
||||
# 迁移到 40250 服务端 —— 协议差异与移植方案
|
||||
|
||||
> 目标:网络层从 **m2dev fork 协议** 换成 **40250 经典 Metin2 协议**,连
|
||||
> `Work/mt/40250/Server/`。现有 m2dev 那套**保留**为可切换后端(`MT_PROTOCOL=m2dev|classic`),
|
||||
> Godot 场景 / GDScript / UI / 渲染保持既有接口与布局;为接住 classic 的状态/命令,只做必要的
|
||||
> bridge、状态显示和资源映射补充。
|
||||
>
|
||||
> 参考源:
|
||||
> - 40250 客户端:`/Users/shenlei/Work/mt/40250/ClientVS22/source/`
|
||||
> (`UserInterface/`、`EterLib/NetStream.{h,cpp}`、`EterBase/cipher.{h,cpp}`)
|
||||
> - 40250 服务端:`/Users/shenlei/Work/mt/40250/Server/metin2/src/server/`
|
||||
> (`game/src/packet.h`、`game/src/packet_info.cpp`、`game/src/desc.cpp`(握手/阶段/cipher)、
|
||||
> `game/src/input.cpp`(`CInputHandshake` + sequence 校验)、`game/src/input_auth.cpp`、`common/`)。
|
||||
> auth 服 = 同一个 game 二进制跑 auth 模式(无独立源码目录);运行时目录
|
||||
> `Server/metin2/server/{auth, game99, channel1..4, db}`,各带 `CONFIG`。
|
||||
> - m2dev(现有移植的对照):`/Users/shenlei/Work/mt/m2dev-client-src-main/src/UserInterface/`
|
||||
|
||||
> **当前范围调整(2026-09-01)**:本阶段只收口客户端本地协议、状态桥接、UI 首版和离线回归。
|
||||
> 真实 FreeBSD + MySQL 40250 服务端的完整登录 / 功能验收暂缓,不作为本阶段交付阻塞;公会相关
|
||||
> 功能暂缓。当前目标 40250 服务端不提供龙魂玩法,因此龙魂不纳入本阶段支持范围;代码中的兼容
|
||||
> 分支不代表该服务端可用。
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句话结论
|
||||
|
||||
| 层 | 改动 | 量级 |
|
||||
|---|---|---|
|
||||
| **网络层** `extension/src/net/` | classic 栈从零写(cipher / 分帧 / 握手 / ~200 结构体 / dispatch),m2dev 栈挪进子目录冻结 | **大**(~2–4 周)|
|
||||
| `EntityStore` | 拆出 mutation API,两个 parser 共用世界模型 | 中(1–2 天)|
|
||||
| `M2Client` | 加 `INetSession` seam,方法转发,按 `MT_PROTOCOL` 选后端 | 小(1 天)|
|
||||
| 渲染 / `formats` / `libgr2` / Godot 场景 / `app_flow` / HUD / UI 窗口 | 保持既有接口;按 40250 复核并补状态、命令和动作资源映射(不重写渲染底座)| 小—中 |
|
||||
| **前置**:跑起 40250 服务端 | 独立一摊(**FreeBSD** game/auth/db + MySQL + proto 表 + config;见 §7.1;仓库已提供可执行脚本)| 中 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 封包头
|
||||
|
||||
### 1.1 宽度与风格
|
||||
|
||||
| | m2dev fork | 40250 classic |
|
||||
|---|---|---|
|
||||
| 头字段 | `uint16_t`(小端),十六进制段(`CG_MOVE = 0x0301`)| **`BYTE`(1 字节)**,十进制(`HEADER_CG_MOVE = 7`)|
|
||||
| 定义位置 | `wire.h`(我们自己维护)| 客户端 `UserInterface/Packet.h`(L30 起匿名 enum,CG/GC/GG 混一起)**与**服务端 `game/src/packet.h` 手工保持一致 |
|
||||
|
||||
> ⚠️ 客户端 `Packet.h` 的 header enum 有 `#if defined(GAIDEN)` 分支,**40250 客户端构建未定义
|
||||
> `GAIDEN`**(`UserInterface.vcxproj` PreprocessorDefinitions 只有 `WIN32;_DEBUG;_WINDOWS;USE_LOD;
|
||||
> _CRT_SECURE_NO_WARNINGS`)。非 GAIDEN 分支里同一 header 值的名字和服务端不同:
|
||||
> 服务端 `HEADER_GC_MAIN_CHARACTER=113` ↔ 客户端非 GAIDEN 是 `HEADER_GC_MAIN_CHARACTER2_EMPIRE=113`
|
||||
> (`HEADER_GC_MAIN_CHARACTER=15` 在客户端指旧包);`SKILL_LEVEL` / `REFINE_INFORMATION` /
|
||||
> `TARGET_CREATE` 等也有 `_OLD` / `_NEW` / `2_` 后缀错位。**移植时以服务端 `game/src/packet.h`
|
||||
> 的值 + 名为准**(它就是线上实际值),结构体按「服务端实际 `Packet()` 发的那个」核(见 §4.4)。
|
||||
|
||||
### 1.2 40250 头表(服务端 `packet.h` 权威值)
|
||||
|
||||
**控制段(保留头,两向都有)**
|
||||
```
|
||||
0xff GC/CG_HANDSHAKE 0xfe CG_PONG / GC_BINDUDP
|
||||
0xfd CG_CLIENT_VERSION / GC_PHASE 0xfc CG_TIME_SYNC / GC_TIME_SYNC (0xfc 的 GC 侧: 服务端
|
||||
packet.h 叫 HEADER_GC_TIME_SYNC,客户端 Packet.h 叫
|
||||
HEADER_GC_HANDSHAKE_OK —— 同一字节;CG 侧无 0xfa)
|
||||
0xfb GC/CG_KEY_AGREEMENT 0xfa GC_KEY_AGREEMENT_COMPLETED
|
||||
0xf1 CG_CLIENT_VERSION2
|
||||
```
|
||||
(客户端值:`Packet.h` L162-167 CG、L362-367 GC。GC 0xfc `HEADER_GC_HANDSHAKE_OK` 注释写「252」。)
|
||||
|
||||
**CG(客户端→服务端)**
|
||||
```
|
||||
1 LOGIN 2 ATTACK 3 CHAT 4 CHARACTER_CREATE 5 CHARACTER_DELETE
|
||||
6 CHARACTER_SELECT 7 MOVE 8 SYNC_POSITION 10 ENTERGAME
|
||||
11 ITEM_USE 12 ITEM_DROP 13 ITEM_MOVE 15 ITEM_PICKUP
|
||||
16 QUICKSLOT_ADD 17 QUICKSLOT_DEL 18 QUICKSLOT_SWAP 19 WHISPER 20 ITEM_DROP2
|
||||
26 ON_CLICK 27 EXCHANGE 28 CHARACTER_POSITION 29 SCRIPT_ANSWER
|
||||
30 QUEST_INPUT_STRING 31 QUEST_CONFIRM
|
||||
50 SHOP 51 FLY_TARGETING 52 USE_SKILL 53 ADD_FLY_TARGETING 54 SHOOT 55 MYSHOP
|
||||
60 ITEM_USE_TO_ITEM 61 TARGET 64 TEXT 65 WARP 66 SCRIPT_BUTTON 67 MESSENGER
|
||||
69 MALL_CHECKOUT 70 SAFEBOX_CHECKIN 71 SAFEBOX_CHECKOUT
|
||||
72 PARTY_INVITE 73 PARTY_INVITE_ANSWER 74 PARTY_REMOVE 75 PARTY_SET_STATE 76 PARTY_USE_SKILL
|
||||
77 SAFEBOX_ITEM_MOVE 78 PARTY_PARAMETER
|
||||
80 GUILD 81 ANSWER_MAKE_GUILD 82 FISHING 83 ITEM_GIVE 90 EMPIRE 96 REFINE
|
||||
100 MARK_LOGIN 101 MARK_CRCLIST 102 MARK_UPLOAD 104 MARK_IDXLIST 105 HACK 106 CHANGE_NAME
|
||||
109 LOGIN2 110 DUNGEON 111 LOGIN3 112 GUILD_SYMBOL_UPLOAD 113 SYMBOL_CRC
|
||||
114 SCRIPT_SELECT_ITEM 116 LOGIN5_OPENID 202 PASSPOD_ANSWER 203 HS_ACK 204 XTRAP_ACK
|
||||
205 DRAGON_SOUL_REFINE 206 STATE_CHECKER
|
||||
```
|
||||
|
||||
**GC(服务端→客户端)**
|
||||
```
|
||||
1 CHARACTER_ADD 2 CHARACTER_DEL 3 MOVE 4 CHAT 5 SYNC_POSITION
|
||||
6 LOGIN_SUCCESS3(=3格,PLAYER_PER_ACCOUNT3) 7 LOGIN_FAILURE 32 LOGIN_SUCCESS4(=4格)
|
||||
8 CHARACTER_CREATE_SUCCESS 9 CREATE_FAILURE 10 DELETE_SUCCESS 11 DELETE_WRONG_SOCIAL_ID
|
||||
12 ATTACK 13 STUN 14 DEAD 15 MAIN_CHARACTER_OLD
|
||||
16 CHARACTER_POINTS 17 CHARACTER_POINT_CHANGE 18 CHANGE_SPEED
|
||||
19 CHARACTER_UPDATE 24 CHARACTER_UPDATE_NEW
|
||||
20 ITEM_DEL 21 ITEM_SET 22 ITEM_USE 23 ITEM_DROP 25 ITEM_UPDATE
|
||||
26 ITEM_GROUND_ADD 27 ITEM_GROUND_DEL
|
||||
28 QUICKSLOT_ADD 29 QUICKSLOT_DEL 30 QUICKSLOT_SWAP 31 ITEM_OWNERSHIP
|
||||
34 WHISPER 36 MOTION 37 PARTS 38 SHOP 39 SHOP_SIGN
|
||||
40 DUEL_START 41 PVP 42 EXCHANGE 43 CHARACTER_POSITION 44 PING
|
||||
45 SCRIPT 46 QUEST_CONFIRM 61 MOUNT 62 OWNERSHIP 63 TARGET 65 WARP
|
||||
69 ADD_FLY_TARGETING 70 CREATE_FLY 71 FLY_TARGETING 72 SKILL_LEVEL_OLD 76 SKILL_LEVEL
|
||||
74 MESSENGER 75 GUILD 77 PARTY_INVITE 78 PARTY_ADD 79 PARTY_UPDATE 80 PARTY_REMOVE
|
||||
81 QUEST_INFO 82 REQUEST_MAKE_GUILD 83 PARTY_PARAMETER
|
||||
85 SAFEBOX_SET 86 SAFEBOX_DEL 87 SAFEBOX_WRONG_PASSWORD 88 SAFEBOX_SIZE
|
||||
89 FISHING 90 EMPIRE 91 PARTY_LINK 92 PARTY_UNLINK 95 REFINE_INFORMATION_OLD
|
||||
99 VIEW_EQUIP 100 MARK_BLOCK 102 MARK_IDXLIST 106 TIME 107 CHANGE_NAME 110 DUNGEON
|
||||
111 WALK_MODE 112 SKILL_GROUP 113 MAIN_CHARACTER 114 SEPCIAL_EFFECT / (USE_POTION 注释掉)
|
||||
115 NPC_POSITION 116 MATRIX_CARD 118 LOGIN_KEY 119 REFINE_INFORMATION 121 CHANNEL
|
||||
122 MALL_OPEN 123 TARGET_UPDATE 124 TARGET_DELETE 125 TARGET_CREATE
|
||||
126 AFFECT_ADD 127 AFFECT_REMOVE 128 MALL_SET 129 MALL_DEL 130 LAND_LIST
|
||||
131 LOVER_INFO 132 LOVE_POINT_UPDATE 133 SYMBOL_DATA 134 DIG_MOTION 135 DAMAGE_INFO
|
||||
136 CHAR_ADDITIONAL_INFO 137 MAIN_CHARACTER3_BGM 138 MAIN_CHARACTER4_BGM_VOL
|
||||
150 AUTH_SUCCESS 151 PANAMA_PACK 152 HYBRIDCRYPT_KEYS 153 HYBRIDCRYPT_SDB 154 AUTH_SUCCESS_OPENID
|
||||
200 ROULETTE 202 REQUEST_PASSPOD 203 REQUEST_PASSPOD_FAILED 204 HS_REQUEST 205 XTRAP_CS1_REQUEST
|
||||
208 SPECIFIC_EFFECT 209 DRAGON_SOUL_REFINE 210 RESPOND_CHANNELSTATUS
|
||||
```
|
||||
|
||||
> 逐结构体细节实现期直接看 `game/src/packet.h`(EUC-KR 编码,注释乱码无碍)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 分帧(framing)
|
||||
|
||||
### 2.1 m2dev fork
|
||||
每个包 `[u16 header LE][u16 length LE][payload]`。长度永远显式。无 sequence 字节。
|
||||
|
||||
### 2.2 40250 classic —— 静态尺寸表 + sequence 字节 + 动态包
|
||||
|
||||
- **收**:读 1 字节 header → 查尺寸表(`CPacketInfo::Set(header, sizeof(TPacketXxx), name, bSeq)`,见
|
||||
`game/src/packet_info.cpp`)→ 读定长 body。
|
||||
- **CG 的 sequence 字节**:`bSeq=true` 的包尾部多 1 字节 `BYTE sequence`。服务端 `Set()` 里
|
||||
`if (bSeq) element->iSize += sizeof(BYTE)`(尺寸表已含 +1);客户端 `NetStream.cpp` 发包后调
|
||||
`SendSequence()`:`m_bUseSequence` 为真时追加 `m_kVec_bSequenceTable[m_iSequence++ % SEQUENCE_TABLE_SIZE]`
|
||||
(静态表 `s_bSequenceTable[SEQUENCE_TABLE_SIZE]`,`NetStream.cpp` L881)。开关由 Python 调
|
||||
`net.SetPacketSequenceMode()`(`PythonNetworkStreamModule.cpp:1601`,对 `rns` 和
|
||||
`rkAccountConnector` 都置 true)—— 具体时机在 `root/*.py`(本 dump 没带),实现期抓包定位。
|
||||
服务端逐包校验、错了断线。
|
||||
- **`bSeq=true` 覆盖几乎所有 CG 游戏内包**(Login/Move/Attack/Chat/ItemMove/Shop/Party/Quest/Pong/…;
|
||||
连 `CG_LOGIN`/`LOGIN2`/`LOGIN3` 和 `CG_TIME_SYNC` 都是 `true`,见 `packet_info.cpp:148-150,137`)。
|
||||
`bSeq=false` 的全集(`packet_info.cpp`):`TEXT` `HANDSHAKE` `MARK_LOGIN` `MARK_IDXLIST` `MARK_CRCLIST`
|
||||
`MARK_UPLOAD` `KEY_AGREEMENT` `GUILD_SYMBOL_UPLOAD` `SYMBOL_CRC` `DRAGON_SOUL_REFINE` `STATE_CHECKER`。
|
||||
(`HS_ACK` / `XTRAP_ACK` 在 40250 服务端 `packet_info.cpp:227-228` 是**注释掉的**,根本没登记。)
|
||||
客户端只在 `m_bUseSequence` 打开后才真正追加尾字节,未开时即便 `bSeq=true` 也不加。
|
||||
- **GC 不带 sequence 字节**。
|
||||
- **动态尺寸包**:客户端接收侧靠 `CNetworkPacketHeaderMap`(`CMainPacketHeaderMap` 构造函数,
|
||||
`PythonNetworkStream.cpp` L29 起)里的 `Set(header, TPacketType(sizeof(TPacketXxx), DYNAMIC_SIZE_PACKET))`
|
||||
标记区分;`CheckPacket()` 命中动态项时先 `Peek(TDynamicSizePacketHeader{BYTE header; WORD size;})`
|
||||
拿 `size` = 整包字节数,再 `Peek(size)`。发送侧则看结构体自带的长度字段
|
||||
(`command_chat` 叫 `WORD length`、`command_whisper` 叫 `WORD wSize`、GC 侧 `packet_*` 叫 `WORD size`,语义相同)。
|
||||
**客户端 GC 动态包全集**(`CMainPacketHeaderMap`,其余 GC 全部 `STATIC_SIZE_PACKET`):
|
||||
`GC_CHAT` `GC_SCRIPT` `GC_SHOP` `GC_GUILD` `GC_MESSENGER` `GC_QUEST_INFO` `GC_DUEL_START`
|
||||
`GC_SYNC_POSITION` `GC_DUNGEON` `GC_NPC_POSITION` `GC_LAND_LIST` `GC_HYBRIDCRYPT_KEYS` `GC_HYBRIDCRYPT_SDB`。
|
||||
**CG 动态包**:只有 `CG_CHAT`(`command_chat`)和 `CG_WHISPER`(`command_whisper`)。
|
||||
**易错的静态包**:`GC_MALL_OPEN`/`SET`/`DEL`、`GC_SAFEBOX_SET`、`GC_PARTY_ADD`/`UPDATE`、`GC_WHISPER`、
|
||||
`GC_AFFECT_ADD` 都是 `STATIC_SIZE_PACKET`(定长,`sizeof` 取满缓冲)。CG 侧 `CG_MARK_UPLOAD`
|
||||
(`{BYTE header; DWORD gid; BYTE image[16*12*4];}`)、`CG_SHOP`(`{BYTE header; BYTE subheader;}`)、
|
||||
`CG_GUILD`(`{BYTE byHeader; BYTE bySubHeader;}`)也都是定长子头分发,不是动态包。
|
||||
|
||||
### 2.3 影响
|
||||
`net_stream.cpp` 完全重写:新 `classic_stream.cpp` 维护 header→size 表(从 `packet.h` + `packet_info.cpp`
|
||||
镜像一份到 `wire_classic.h` 的 `static_assert` + 一个 `packet_size(header)` 函数),发 CG 时自动追加
|
||||
sequence 字节并推进本地序列表,动态包读 `size` 前缀。
|
||||
|
||||
---
|
||||
|
||||
## 3. 加密(cipher)
|
||||
|
||||
### 3.1 m2dev fork
|
||||
libsodium `crypto_kx`(X25519),一轮:`GC_KEY_CHALLENGE{server_pk, challenge}` →
|
||||
`CG_KEY_RESPONSE{client_pk, HMAC(challenge, tx_key)}` → `GC_KEY_COMPLETE{AEAD 加密的 session token, nonce}`。
|
||||
`crypto_kx_*_session_keys` 出**两把方向密钥** `m_tx_key` / `m_rx_key`(**不是**两向同密钥);激活后每向
|
||||
一条 `crypto_stream_xchacha20`(固定方向 nonce:`0x01` S→C、`0x02` C→S)+ 递增字节计数原地 XOR。
|
||||
KEY_COMPLETE 的 token 用一次性 `crypto_aead_xchacha20poly1305_ietf`。`secure_cipher.{h,cpp}`
|
||||
(近乎照搬客户端 `EterBase/SecureCipher`)。
|
||||
|
||||
### 3.2 40250 classic —— `_IMPROVED_PACKET_ENCRYPTION_`(`EterBase/cipher.{h,cpp}`)
|
||||
|
||||
1. **密钥协商**:Crypto++ `DH2`(Unified DH)——`DH2KeyAgreement` 里 `DH dh_; DH2 dh2_(dh_);`,
|
||||
同一组 DH 域参数下各生成「静态 + 临时」两对密钥,`dh2_.Agree(shared_, spriv, epriv, otherPub…)`
|
||||
一次得共享密钥 `SecByteBlock shared_`(`cipher.cpp` L123-133, 336, 428-441)。
|
||||
握手时间同步收敛后,流程是**服务端先手**:
|
||||
1. 服务端 `DESC::SendKeyAgreement()`(`desc.cpp:726`):`cipher_.Prepare(packet.data, &data_length)`
|
||||
生成本端 blob → 发 `HEADER_GC_KEY_AGREEMENT`
|
||||
`TPacketKeyAgreement{BYTE bHeader; WORD wAgreedLength; WORD wDataLength; BYTE data[MAX_DATA_LEN=256];}`
|
||||
(定长 261B,客户端 `Packet.h` L2722,客户端把它登记为 `STATIC_SIZE_PACKET`)。
|
||||
2. 客户端 `RecvKeyAgreementPacket()`(`PythonNetworkStreamPhaseHandshake.cpp:208`):`Prepare()` 生成本端
|
||||
blob → **`Activate(pkt.wAgreedLength, pkt.data, pkt.wDataLength)` 用服务端的数据**(内部
|
||||
`m_cipher.Activate(true, …)`,`Agree()` 得 `SecByteBlock shared_`)→ 再发 `HEADER_CG_KEY_AGREEMENT`
|
||||
带自己的 blob。
|
||||
3. 服务端收 `HEADER_CG_KEY_AGREEMENT`(`input.cpp:647`):**先** `SendKeyAgreementCompleted()`
|
||||
(发 `HEADER_GC_KEY_AGREEMENT_COMPLETED` `TPacketKeyAgreementCompleted{BYTE bHeader; BYTE data[3];}`)
|
||||
+ `ProcessOutput()` 刷 socket,**再** `IsCipherPrepared()` 检查(否则「maybe a Hacker」
|
||||
`DelayedDisconnect(5)`)→ `FinishHandshake(wAgreedLength, data, wDataLength)` = `cipher_.Activate(false, …)`
|
||||
(`desc.cpp:755`)成功则 `g_bAuthServer ? SetPhase(PHASE_AUTH) : SetPhase(PHASE_LOGIN)`(`input.cpp:665-668`),
|
||||
失败 `PHASE_CLOSE`。
|
||||
4. 客户端 `RecvKeyAgreementCompletedPacket()` → `ActivateCipher()` = `m_cipher.set_activated(true)`。
|
||||
—— `Agree()` 完成到 `set_activated(true)` 之间有一小段窗口:密钥算出但还没开始真正加解密。
|
||||
注意服务端是**先发 COMPLETED 再自己 FinishHandshake/切阶段**(顺序有意如此,帮客户端先进加密态)。
|
||||
2. **对称加密**:`SetUp(polarity)`(`cipher.cpp` L205)里 `hint_0 = shared[shared[0] % size]`、
|
||||
`hint_1 = shared[shared[1] % size]`,各 `BlockCipherAlgorithm::Pick(hint)` → `selector = hint %
|
||||
kMaxAlgorithms`(**`kMaxAlgorithms = 14`**,enum 里 `kDefault=0` 占一位)。`selector` 1–13 依次是
|
||||
`RC6/MARS/Twofish/Serpent/CAST256/IDEA/3DES(DES_EDE2)/Camellia/SEED/RC5/Blowfish/TEA/SHACAL2`;
|
||||
`selector == 0`(`kDefault`)或落到 `default:` → `Twofish`(`kAES/Rijndael` 那位是注释掉的)。
|
||||
**CTR 模式**(`CTR_Mode<T>::Encryption/Decryption`),key/iv 从 `shared` 切片。两个密码实例按
|
||||
`polarity` 决定收/发各用哪个 —— **客户端 polarity = `true`**
|
||||
(`EterLib/NetStream.cpp:3004` `return m_cipher.Activate(true, agreed_length, buffer, length);`),
|
||||
服务端用 `false`。无 padding(CTR 流)。
|
||||
`Encrypt/Decrypt` = `SymmetricCipher::ProcessData` 原地。
|
||||
3. **`_IMPROVED_PACKET_ENCRYPTION_` 是编译期 `#define`,不是运行时开关**:
|
||||
- 服务端 `common/service.h:7` `#define _IMPROVED_PACKET_ENCRYPTION_` —— **shipped 40250 服务端默认开**。
|
||||
- 客户端 `eterBase/ServiceDefs.h:4` 同样 `#define` —— 也默认开。
|
||||
- 关掉走老路:4-DWORD key 的 `TEA_Encrypt`(服务端 `desc.cpp` non-improved 分支 + `GetEncryptionKey()`)
|
||||
/ 客户端 `SetSecurityMode`。**要「明文起步」必须重编服务端**(把 `service.h` 那行注释掉),
|
||||
没有 `CONFIG` token 能关(见 §7.2)。有源码,可行。
|
||||
- 另有一层 `packet_encode`/`packet_decode`(`desc.cpp:479`)在 cipher 之外,始终生效。
|
||||
|
||||
### 3.3 影响
|
||||
最硬的一块。需要 **Crypto++**(`DH`/`DH2` + `CTR_Mode<T>` 13 个算法)。方案:
|
||||
- vendored Crypto++(子模块,CMake 跨 macOS/iOS/Android 能编)—— 体积大但省事;或
|
||||
- 只挪需要的算法(DH2 + Twofish/RC5/... CTR)—— 工作量大。
|
||||
- 版本 **Crypto++ 8.4.0**:服务端 `src/extern/cryptopp_8_4_0.tar.gz`(+ 预编 `src/extern/lib/libcryptopp.a`),
|
||||
客户端 `ClientVS22/extern/include/cryptopp/`(`config_ver.h` `CRYPTOPP_VERSION 840`)。移植对齐这个版本。
|
||||
|
||||
`secure_cipher.cpp` 不动(m2dev 用),新 `classic_cipher.{h,cpp}` 端口 `EterBase/cipher.cpp`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 握手 / 阶段流
|
||||
|
||||
### 4.1 阶段枚举
|
||||
服务端 `enum EPhase`(`packet.h:850`):`PHASE_CLOSE=0, HANDSHAKE=1, LOGIN=2, SELECT=3, LOADING=4,
|
||||
GAME=5, DEAD=6, CLIENT_CONNECTING=7, DBCLIENT=8, P2P=9, AUTH=10, TEEN=11, PASSPOD=12`。
|
||||
客户端匿名 enum(`Packet.h:1124`)前 7 个(0–6)一致,尾部是 `DBCLIENT_CONNECTING / DBCLIENT`——
|
||||
**只有 0–6 会经 `GC_PHASE` 到达客户端**,`PHASE_AUTH` 等是服务端内部态。
|
||||
正常流:CLOSE→HANDSHAKE→(AUTH,仅 `g_bAuthServer`)→LOGIN→SELECT→LOADING→GAME。
|
||||
切阶段由 `HEADER_GC_PHASE`(0xfd) `TPacketGCPhase{BYTE header; BYTE phase;}` 驱动(客户端 `RecvPhasePacket()`)。
|
||||
|
||||
### 4.2 HANDSHAKE
|
||||
客户端 `PythonNetworkStreamPhaseHandshake.cpp`;**服务端 `desc.cpp` + `input.cpp`(`CInputHandshake`)明文都在**。
|
||||
|
||||
- `TPacketGCHandshake`(=`TPacketCGHandshake`,同布局):`{BYTE bHeader; DWORD dwHandshake; DWORD dwTime;
|
||||
LONG lDelta;}`(服务端 `packet.h` `packet_header_handshake`;客户端字段名 `header`)。`bSeq=false`
|
||||
(`packet_info.cpp:136`)。
|
||||
- 连上 → 服务端 `DESC::StartHandshake` → `SendHandshake(get_dword_time(), 0)`,`pack.bHeader =
|
||||
HEADER_GC_HANDSHAKE`(0xff)(`desc.cpp:637-658`)。
|
||||
- 客户端 **HandShake 阶段**收到 `HEADER_GC_HANDSHAKE`(`PythonNetworkStreamPhaseHandshake.cpp` L37-62 内联):
|
||||
`ELTimer_SetServerMSec(dwTime + lDelta)` → `dwTime += 2*lDelta`、`lDelta = 0` →
|
||||
**header 不变(仍 0xff)原样回发**,不追加 sequence。
|
||||
- 服务端 `CInputProcessor::Handshake`(`input.cpp:195`):先校验 `p->dwHandshake == GetHandshake()`
|
||||
(不符 `PHASE_CLOSE`)。在 `PHASE_HANDSHAKE` 里调 `HandshakeProcess(dwTime, lDelta, false)`
|
||||
(`desc.cpp:662`):
|
||||
- `bias = get_dword_time() - (dwTime + lDelta)`;**`0 ≤ bias ≤ 50` → 收敛**,`m_bHandshaking=false` 返回 true。
|
||||
- 否则 `lNewDelta = (dwCurTime - dwTime) / 2`(<0 时用 `(dwCurTime - m_dwHandshakeSentTime)/2`),
|
||||
`++m_iHandshakeRetry > HANDSHAKE_RETRY_LIMIT` → `PHASE_CLOSE`;否则 `SendHandshake(dwCurTime, lNewDelta)`
|
||||
再来一轮。
|
||||
- **收敛后**(`input.cpp:207-217`):
|
||||
- `#ifdef _IMPROVED_PACKET_ENCRYPTION_`(shipped 默认)→ `d->SendKeyAgreement()`,**这里先不切阶段**;
|
||||
阶段推进发生在之后收到 `HEADER_CG_KEY_AGREEMENT` 时(`input.cpp:665-668`,`FinishHandshake` 成功后
|
||||
`SetPhase(PHASE_AUTH|PHASE_LOGIN)`)。完整密钥协商流程见 §3.2。
|
||||
- `#else`(明文/老 TEA)→ 立即 `g_bAuthServer ? SetPhase(PHASE_AUTH) : SetPhase(PHASE_LOGIN)`。
|
||||
- **握手期之后的时钟再同步**:SELECT/GAME 里服务端再发 `HEADER_GC_HANDSHAKE`,客户端不在 handshake
|
||||
phase → `PythonNetworkStreamPhaseSelect.cpp` L96-102 的 `RecvHandshakePacket()`:回发时 header 改成
|
||||
`HEADER_CG_TIME_SYNC`(0xfc) 且 `SendSequence()`(`bSeq=true`,`packet_info.cpp:137`)。服务端此路
|
||||
`HandshakeProcess(…, true)`(`bInfiniteRetry=true`,`input.cpp:222`),收敛时回发 **1 字节
|
||||
`HEADER_GC_TIME_SYNC`(0xfc)**(`desc.cpp:676-679`)→ 客户端 `RecvHandshakeOKPacket()` 读
|
||||
`TPacketGCBlank{BYTE header;}` 定终值。
|
||||
—— 注意 0xfc 这个值:**客户端叫 `HEADER_GC_HANDSHAKE_OK`,服务端叫 `HEADER_GC_TIME_SYNC`**,同一字节。
|
||||
- 期间可能夹 `HEADER_GC_KEY_AGREEMENT` / `_COMPLETED`(见 §3)、`HEADER_GC_BINDUDP`、`HEADER_GC_PING`、
|
||||
`HEADER_GC_HYBRIDCRYPT_KEYS` / `_SDB`(动态包)。`CInputHandshake::Analyze`(`input.cpp:276`)还会
|
||||
处理 `HEADER_CG_TEXT`(webadmin `IS_SERVER_UP` 等文本命令)和 header `10`(旧探活忽略)。
|
||||
|
||||
### 4.3 LOGIN(`PythonNetworkStreamPhaseLogin.cpp`)
|
||||
- **两条路**:
|
||||
- `SendLoginPacket` → `HEADER_CG_LOGIN` `TPacketCGLogin{BYTE header, char login[31], char passwd[17]}`
|
||||
(明文账密,服务端直连模式)。
|
||||
- `SendLoginPacketNew` → `HEADER_CG_LOGIN2` `command_login2` `{BYTE header; char name[31];
|
||||
DWORD login_key; DWORD adwClientKey[4];}` —— `login_key` 来自**独立 auth 服**(同 game 二进制跑
|
||||
auth 模式)先前发的 `HEADER_GC_LOGIN_KEY`(118);`adwClientKey` = 客户端 `extern DWORD g_adwEncryptKey[4]`。
|
||||
`SendLoginPacketNew` 还会 `SetSecurityMode(true, g_adwEncryptKey, g_adwDecryptKey)` 起老 TEA 层。
|
||||
- `HEADER_CG_LOGIN3` `command_login3` `{header; name[31]; pwd[17]; DWORD adwClientKey[4];}`
|
||||
(account server 直登 + 客户端 key)。
|
||||
- 回:**40250 服务端实际只发 `HEADER_GC_LOGIN_SUCCESS_NEWSLOT`(=**32**)**
|
||||
(`DESC::SendLoginSuccessPacket` `desc.cpp:925-959`:`p.bHeader = HEADER_GC_LOGIN_SUCCESS_NEWSLOT`,
|
||||
`Packet(&p, sizeof(TPacketGCLoginSuccess))`)。结构体 `packet_login_success` /
|
||||
`TPacketGCLoginSuccess`(`packet.h:892`):`{BYTE bHeader; TSimplePlayer players[PLAYER_PER_ACCOUNT=**4**];
|
||||
DWORD guild_id[4]; char guild_name[4][GUILD_NAME_MAX_LEN+1=13]; DWORD handle; DWORD random_key;}`
|
||||
—— 客户端非 GAIDEN 里 header 32 走 `HEADER_GC_LOGIN_SUCCESS4` → `TPacketGCLoginSuccess4`(`[4]`,同布局)。
|
||||
`TSimplePlayer`(`common/tables.h:347`,`pack(1)`,**63 字节**)== 客户端 `TSimplePlayerInformation`。
|
||||
header **6**(`HEADER_GC_LOGIN_SUCCESS` / 客户端 `_SUCCESS3`,`players[3]`)是旧格式,shipped 40250 不发。
|
||||
失败 `HEADER_GC_LOGIN_FAILURE`(7) `{BYTE header; char szStatus[LOGIN_STATUS_MAX_LEN+1=9];}`
|
||||
(`LOGIN_FAILURE_* enum`:1 ALREADY / 2 ID_NOT_EXIST / 3 WRONG_PASS / 4 FALSE / 7 FULL)。
|
||||
`handle`/`random_key` 供 `CG_MARK_LOGIN` 用(会徽下载)。
|
||||
- 频道状态:`HEADER_GC_RESPOND_CHANNELSTATUS`(210)。
|
||||
- `GC_PHASE(PHASE_SELECT)` → 选人阶段。
|
||||
|
||||
### 4.4 SELECT / LOADING / GAME
|
||||
- SELECT:`CG_CHARACTER_SELECT`(6) `command_player_select` `{BYTE header; BYTE player_index;}` →
|
||||
`GC_PHASE(PHASE_LOADING)` + **header 113 的「主角包」**。服务端 `char.cpp:1607` 发的是
|
||||
`TPacketGCMainCharacter`(服务端 `packet.h` L1043)=
|
||||
`{BYTE header; DWORD dwVID; WORD wRaceNum; char szName[CHARACTER_NAME_MAX_LEN+1=25]; long lx,ly,lz;
|
||||
BYTE empire; BYTE skill_group;}` —— 客户端非 GAIDEN 里这个 header 走 `case HEADER_GC_MAIN_CHARACTER2_EMPIRE`
|
||||
读 `TPacketGCMainCharacter2_EMPIRE`(同布局)。注意 `wRaceNum` 是种族不是地图号,szName 在坐标**前**,
|
||||
尾部是 `empire` + `skill_group` 两个 BYTE。开了 SUPPORT_BGM 时改发 `_BGM`(137) / `_BGM_VOL`(138) 变体
|
||||
(多 BGM 名字段)。地图名另经加载流程 → `CG_ENTERGAME`(10) → `GC_PHASE(PHASE_GAME)`。
|
||||
- 建/删号:`CG_CHARACTER_CREATE`(4) —— 客户端 `command_player_create` / `TPacketCGCreateCharacter`
|
||||
`{BYTE header; BYTE index; char name[CHARACTER_NAME_MAX_LEN+1=25]; WORD job; BYTE shape; BYTE CON;
|
||||
BYTE INT; BYTE STR; BYTE DEX;}`(服务端侧同一包叫 `TPacketCGPlayerCreate`)。
|
||||
`CG_CHARACTER_DELETE`(5) `command_player_delete` / `TPacketCGDestroyCharacter`
|
||||
`{BYTE header; BYTE index; char szPrivateCode[PRIVATE_CODE_LENGTH=8];}`。
|
||||
→ `GC_CHARACTER_CREATE_SUCCESS`(8) `command_player_create_success` / `TPacketGCPlayerCreateSuccess`
|
||||
`{BYTE header; BYTE bAccountCharacterSlot; TSimplePlayerInformation kSimplePlayerInfomation;}`
|
||||
/ `_CREATE_FAILURE`(9) / `_DELETE_SUCCESS`(10) `{header, BYTE account_index}` /
|
||||
`_DELETE_WRONG_SOCIAL_ID`(11)。
|
||||
|
||||
> 与 m2dev 对照:m2dev 是 `CG_CHARACTER_CREATE=0x0201`;40250 是 `HEADER_CG_CHARACTER_CREATE=4`。
|
||||
> create 包里 `job` 两边**都是 `WORD`(u16)**;差异在头值、结构体名、字段序(40250 顺序是
|
||||
> name→job→shape→CON/INT/STR/DEX),按 40250 `command_player_create` 重推。**只有摘要结构
|
||||
> `TSimplePlayerInformation.byJob` 是 BYTE**(见 §5),别和 create 包的 `WORD job` 混。
|
||||
|
||||
---
|
||||
|
||||
## 5. 关键结构体差异(抽样,实现期逐个对 `game/src/packet.h`)
|
||||
|
||||
| 语义 | m2dev fork | 40250 classic |
|
||||
|---|---|---|
|
||||
| 包头 | `uint16_t header; uint16_t length;` | `BYTE header;`(动态包再加 `WORD size;`)|
|
||||
| `TItemPos` | `{u8 window_type; u16 cell;}` | `{BYTE window_type; WORD cell;}`(同布局,但不少老包里 cell 是 `BYTE`,按包看)|
|
||||
| `TPlayerItemAttribute` | `{u8 bType; i16 sValue;}` (3B) | 同 |
|
||||
| `TSimplePlayerInformation`(客户端 `Packet.h` L1176)| fork 版 | `{DWORD dwID; char szName[CHARACTER_NAME_MAX_LEN+1=25]; BYTE byJob; BYTE byLevel; DWORD dwPlayMinutes; BYTE byST,byHT,byDX,byIQ; WORD wMainPart; BYTE bChangeName; WORD wHairPart; BYTE bDummy[4]; long x,y; LONG lAddr; WORD wPort; BYTE bySkillGroup;}` —— **摘要里 `byJob` 是 BYTE**(但 `command_player_create.job` 是 `WORD`,别混),名字 25,无 wMapIndex/guild(guild 在 `TPacketGCLoginSuccess3/4` 尾部并列数组)|
|
||||
| 移动 `CG_MOVE`(`command_move`)| fork | `{BYTE bHeader; BYTE bFunc; BYTE bArg; BYTE bRot; long lX; long lY; DWORD dwTime;}` —— **实测一致** |
|
||||
| 聊天 | fork `[hdr][len][type][...]` | CG `command_chat{BYTE header; WORD length; BYTE type;}` + `char szChat[]`(`Packet.h` L597);GC `packet_chatting{BYTE header; WORD size; BYTE type; DWORD dwVID; BYTE bEmpire;}` + 文本(L1425)。都是动态包,长度字段一个叫 `length` 一个叫 `size`,语义相同 |
|
||||
| 商店 | fork `GC_SHOP`(0x0810) sub-header + `SHOP_GC_START_EX` | `HEADER_GC_SHOP`(38) `TPacketGCShop{BYTE header; WORD size; BYTE subheader;}`(动态)。`EPacketShopSubHeaders`(`Packet.h` L1864):`GC_START`=0 … `GC_START_EX`=10、`NOT_ENOUGH_MONEY_EX`=11 —— **子头值与 fork 一致**,商店层大体可搬 |
|
||||
| 交易 | fork `GC_EXCHANGE` sub-header | `TPacketExchange{BYTE header; BYTE subheader; …}` —— **定长,无 `WORD size`**(不是所有 sub-header 包都是动态包,逐个看)|
|
||||
| 帮会 / messenger / quest | fork 各自 | `GC_GUILD` / `GC_MESSENGER` / `GC_QUEST_INFO` 是动态包(`[BYTE header][WORD size][...]`,见 §2.2 权威清单),子头在 size 之后。`GC_SCRIPT`(任务对话)也是动态 |
|
||||
| 龙魂精炼 | 不纳入本阶段 | 目标 40250 服务端不提供该玩法;客户端兼容代码保留但不宣称可用,也不纳入当前验收 |
|
||||
|
||||
`wire.h` ~2000 行结构体按 40250 重写成 `wire_classic.h`。**所有 wire 结构体是 `#pragma pack(1)`**
|
||||
(客户端 `Packet.h` L435–2813 的 `pack(push)/pack(1)…pack(pop)` 段;文件开头那段 `pack(8)` 是无关的
|
||||
Hackshield 传输缓冲,不涉及协议)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 双后端架构
|
||||
|
||||
```
|
||||
extension/src/net/
|
||||
├─ entity_store.{h,cpp} # 共享世界模型。apply() 拆成:
|
||||
│ # - 状态 + mutation 方法(spawn_entity / set_vitals /
|
||||
│ # set_inv_item / party_set / shop_set / ...)—— 协议无关
|
||||
│ # - m2dev_parser.cpp : 现有 apply() 挪过来,调 mutation
|
||||
│ # - classic_parser.cpp: 新写,按 §1/§5 解 40250 包,调同一批 mutation
|
||||
├─ i_net_session.h # 新接口:
|
||||
│ connect(host, port) / login(id, pw) / select_char(i) / enter_game()
|
||||
│ send_move/attack/chat/item_move/use_skill/... (或 send(Intent, args))
|
||||
│ pump() → 产出 GC 包 → parser → entity_store
|
||||
│ 信号源: 让 M2Client 照旧 emit(session 只回调 "有 N 个事件待抽干")
|
||||
├─ m2dev/ # 现有文件整体挪入, namespace mtnet::m2dev, 不改逻辑
|
||||
│ wire.h · secure_cipher.{h,cpp} · net_stream.{h,cpp} · game_client.h · auth_client.h ·
|
||||
│ mark_client.h · mark_image.{h,cpp} · byte_buffer.h · m2dev_parser.cpp(从 entity_store 拆)
|
||||
│ 实现 INetSession
|
||||
├─ classic/ # 新, namespace mtnet::classic
|
||||
│ wire_classic.h · classic_cipher.{h,cpp}(Crypto++ DH2 + CTR)· classic_stream.{h,cpp}
|
||||
│ (1B 头 + size 表 + sequence 字节 + 动态包)· classic_session.cpp(HANDSHAKE→…→GAME 状态机)
|
||||
│ · classic_parser.cpp · 实现 INetSession
|
||||
└─ m2_client.{h,cpp} # 持有 std::unique_ptr<INetSession>;
|
||||
构造时按 OS.get_environment("MT_PROTOCOL")(默认 "classic" 或先 "m2dev")选
|
||||
m2dev::Session / classic::Session; 所有 game->send_* / dispatch 改成 session->
|
||||
```
|
||||
|
||||
- **CTest**:`net_entity_test` / `net_loopback_test` 现有的绑 m2dev 后端,保持绿。
|
||||
classic 侧新增 `net_classic_test`(合成包 + MockServer 走 classic 分帧/cipher)。
|
||||
- **GDScript / 场景 / UI / 渲染**:保留原有 `M2Client` 方法和既有信号兼容性;为 40250
|
||||
特有的 classic 回包增加必要 signal/状态桥接和 UI 处理,不改渲染底座。
|
||||
|
||||
---
|
||||
|
||||
## 7. 待确认 / 暂缓项
|
||||
|
||||
1. **【暂缓】跑起 40250 服务端** —— `Server/metin2/src/server/`(顶层 `Makefile` → `libthecore libpoly libgame
|
||||
liblua libsql libserverkey game db`)。**这套是 FreeBSD-only**:`CC = clang++-devel`、`-m32`
|
||||
32 位、`-lmd`、静态链 `/usr/local/lib/mysql/libmysqlclient.a` + 预编 `extern/lib/libcryptopp.a`
|
||||
+ `libIL.a`,`GAMEDIR = /usr/metin2/server/share/bin`(`game/src/Makefile` `SVN_VERSION = 40250`)。
|
||||
Linux 上跑要么改 Makefile/补依赖,要么 FreeBSD VM/jail。还要 MySQL(`Server/metin2_mysql_dump` /
|
||||
`Server/mysql`)、`item_proto`/`mob_proto` 表、运行时 `Server/metin2/server/{game99,auth}` 的 `CONFIG`。
|
||||
本仓库的 `tools/40250/{build_server,setup_mysql,prepare_runtime,start_server,stop_server,run_classic_e2e}.sh`
|
||||
已把这一步固化为可执行 runbook;当前 macOS 机器仍没有 FreeBSD/MySQL server。该环境搭建和
|
||||
live 结果回填暂不属于本阶段交付,后续恢复公会 / 真服验收时再执行。
|
||||
2. **明文起步要重编服务端**:`_IMPROVED_PACKET_ENCRYPTION_` 是 `common/service.h:7` 的编译期 `#define`,
|
||||
**没有 `CONFIG` token 可关**(`config.cpp` 里只有 `test_server` / `distribution_test_server` /
|
||||
`g_bAuthServer` 这类,与加密无关)。第一里程碑:注释掉 `service.h` 那行重编 game,客户端同理,
|
||||
shipped 40250 默认直接走 DH2/CTR;明文只作为需要重编 game 的诊断分支,不是运行配置项。
|
||||
3. **auth 服务器是否独立端口**:`g_bAuthServer` 分支决定握手收敛后进 `PHASE_AUTH` 还是直接 `PHASE_LOGIN`
|
||||
(`input.cpp:207-217`)。auth 服和 game 服可能不同端口(运行时 `server/auth` vs `server/game99`)。
|
||||
决定用 `CG_LOGIN` 还是 `CG_LOGIN2/3` + `GC_LOGIN_KEY`。
|
||||
4. **Crypto++ 引入方式**:vendored 子模块 vs 抽子集。对齐服务端的 `cryptopp_8_4_0`。
|
||||
5. **sequence 表**:客户端静态表 `s_bSequenceTable[SEQUENCE_TABLE_SIZE=32768]`(`EterLib/NetStream.cpp`
|
||||
L881;`SendSequence()` L834 循环取值 `m_iSequence % 32768`,`SetPacketSequenceMode(true)` L829 开启)。
|
||||
服务端对应表 `game/src/sequence.cpp` `gc_abSequence[SEQUENCE_MAX_NUM=32768]`;校验在 `input.cpp`
|
||||
L121-162:`m_pPacketInfo->IsSequence(bHeader)` 时读包尾 1 字节与 `lpDesc->GetSequence()` 比,不符
|
||||
→ `SetPhase(PHASE_CLOSE)` 断线,符 → `push_seq` + `SetNextSequence()`。
|
||||
**两张表必须逐字节一致**,端口时整表 + 开启时机照抄。
|
||||
6. **HYBRIDCRYPT**(`GC_HYBRIDCRYPT_KEYS/SDB`, 152/153,动态包)—— 是**加密资源包(EterPack)的密钥下发**:
|
||||
客户端 `RecvHybridCryptKeyPacket`/`SDBPacket` → `CEterPackManager::RetrieveHybridCryptPackKeys/PackSDB`
|
||||
(SDB = Supplementary Data Blocks),让客户端能解密 hybrid 加密的 `.eix/.epk`。**与反外挂无关**。
|
||||
我们的移植走散文件 / 自研 mtpack,不吃官方加密包 → **直接忽略**(收到丢弃即可)。
|
||||
7. **root/\*.py + uiscript**:这个 dump 只有 client 源码,没带 `root/` pack。UI 窗口对照 40250 版
|
||||
uiscript 是**独立线**,不阻塞协议移植(现有窗口先照跑)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 建议推进顺序
|
||||
|
||||
1. **【暂缓】真服前置**:在 FreeBSD VM/jail 按 `tools/40250/README.md` 起 40250 game+auth+db,
|
||||
并用官方客户端或 `net_classic_e2e_host` 登入验证;不作为当前交付门槛。
|
||||
2. `INetSession` 接口 + `EntityStore` mutation 拆分 + m2dev 挪入 `net/m2dev/`(现有测试保持绿)。
|
||||
3. `classic/classic_stream`:1B 头 + size 表 + sequence + 动态包 + shipped DH2/CTR,打通
|
||||
HANDSHAKE→LOGIN→SELECT→GAME 空跑。
|
||||
4. `classic_parser`:`GC_LOGIN_SUCCESS` / `GC_MAIN_CHARACTER` / `GC_CHARACTER_ADD` / `GC_MOVE` /
|
||||
`GC_CHARACTER_POINTS` —— 能进游戏、看见自己和别人动。
|
||||
5. `classic_cipher`:Crypto++ DH2 + CTR,接 `KEY_AGREEMENT` 流。
|
||||
6. 铺开其余 GC/CG(物品 / 战斗 / 商店 / 组队 / 任务 …),对照 m2dev 已做的功能清单逐个搬;
|
||||
公会与龙魂不列入当前阶段。
|
||||
7. `MT_PROTOCOL` 切换、`net_classic_test`;真服 e2e 暂缓。
|
||||
8. UI 窗口对 40250 uiscript 复核(独立)。
|
||||
|
||||
> 进度按 `docs/CLIENT-GAP.md` 的功能清单同步,标注 classic/m2dev 各自完成度。
|
||||
|
||||
### 进度(branch `feat/40250-classic-net`)
|
||||
|
||||
- **前置(step 1)**:40250 FreeBSD 服务端 —— 暂缓(见 §7.1),不阻塞当前本地协议 / UI 交付。
|
||||
- **step 3a — wire 层** ✅:`extension/src/net/classic/wire_classic.h`(`namespace mtnet::classic`,
|
||||
1B 头 enum 全表,命名跟服务端 `packet.h` + `#pragma pack(1)` 结构体 + `static_assert` +
|
||||
`packet_size_{cg,gc}(h)`(**整包尺寸含 1B 头、不含 seq 字节** —— 对齐 `CPacketInfo::Set(…, sizeof(…))`)+
|
||||
`is_sequence_cg` / `is_dynamic_{cg,gc}`)。覆盖 control / handshake / key-agreement / login(1/2/3) /
|
||||
login_success(32 主用, 6 legacy) / select / create / delete / main_character(113) /
|
||||
character_add(1,2) / move(CG,GC) / points / point_change / chat。`TSimplePlayer` = **63 字节**。
|
||||
40250 客户端/服务端实际会用到的结构体已随 parser 补齐;仅 vendor-only 或服务端未登记的包不伪造。
|
||||
- **step 3b — sequence 表** ✅:`extension/src/net/classic/sequence_table.h` —— 32768 字节
|
||||
`SEQUENCE_TABLE`,逐字节 = `NetStream.cpp:881` `s_bSequenceTable` = 服务端 `sequence.cpp`
|
||||
`gc_abSequence[]`(脚本校验过)。
|
||||
- **step 3c — 分帧层** ✅:`extension/src/net/classic/classic_stream.{h,cpp}`(在 `mtnet` 库里)。
|
||||
非阻塞 TCP + `feed()`/`take_outgoing()` 无 socket 测试路径。base 处理 HANDSHAKE 回显
|
||||
(`time += 2*delta`、header 不变、无 seq)、`GC_TIME_SYNC` blank、`GC_PHASE`、`GC_PING→CG_PONG`、
|
||||
`BINDUDP`;`set_sequence_mode(true)` 后对 bSeq CG 包追加 `SEQUENCE_TABLE[i++]`;动态包读
|
||||
`TDynamicSizePacketHeader.size`。收到 `GC_KEY_AGREEMENT` 后由 `classic_cipher` 完成 DH2/CTR
|
||||
协商,并支持完成包与后续密文同一 read 的 pipeline。
|
||||
CTest `net.classic_wire` + `net.classic_stream` 绿。
|
||||
- **step 2 — `INetSession` seam + `EntityStore` mutation API** ✅(部分):
|
||||
`extension/src/net/i_net_session.h`(`connect / disconnect / select_char / enter_game / pump /
|
||||
stage / world`,per-intent `send_*` 随后加)。`EntityStore` 加了协议无关的
|
||||
`mut_spawn / mut_spawn_main / mut_despawn / mut_move / mut_set_points / mut_set_point`
|
||||
(包住原 `touch()` / `start_move()`,m2dev `apply()` 一行没动,`net.entity_store` 仍绿)。
|
||||
m2dev 文件的物理挪目录(`net/m2dev/`)延后(纯改名,无功能收益)。
|
||||
- **step 4 — `classic_parser` + `classic_session`** ✅:
|
||||
- `classic/classic_parser.{h,cpp}` —— 40250 GC 包体 → `EntityStore::mut_*`。已接
|
||||
`GC_LOGIN_SUCCESS`(32/6) / `GC_LOGIN_FAILURE` / `GC_LOGIN_KEY` / `GC_MAIN_CHARACTER`(113) /
|
||||
`GC_CHARACTER_ADD`(1) / `GC_CHARACTER_DEL`(2) / `GC_MOVE`(3) / `GC_CHARACTER_POINTS`(16) /
|
||||
`GC_CHARACTER_POINT_CHANGE`(17,含 `int32_t header` 怪癖) / `GC_CHAT`(4,dynamic)。
|
||||
`CharSlot` 列表 + handle/random_key。
|
||||
- `classic/classic_session.{h,cpp}`(实现 `INetSession`)—— 驱动
|
||||
HANDSHAKE→LOGIN(`CG_LOGIN`, shipped server 的 DH2/CTR)→SELECT→LOADING→GAME。`GC_PHASE(LOGIN)` 时开 sequence mode
|
||||
并发登录;`GC_PHASE(SELECT)` 抛 `on_char_list`;`select_char(slot)`→`CG_CHARACTER_SELECT`;
|
||||
`PHASE_LOADING` 后 1.5s 自动 `CG_ENTERGAME`(可配);`PHASE_GAME`→`on_entered_game`。
|
||||
`start_offline()` 测试钩子跳过 socket。
|
||||
- CTest `net.classic_session` —— 合成服务端字节走完整流程:握手回显、CG_LOGIN + seq[0]、
|
||||
4 格角色列表、选人 + seq[1]、主角/NPC spawn、自动 ENTERGAME + seq[2]、进游戏、
|
||||
GC_MOVE 插值、points/point_change/del。全量 ctest **13/13**。
|
||||
- **step 4b — per-intent `send_*`** ✅:`INetSession` + `ClassicSession` 加了
|
||||
`send_move`(`bRot = deg/5`)/ `send_attack` / `send_chat`(dynamic:`[hdr][WORD len][type]text\0`,
|
||||
`len = 4 + strlen + 1`)/ `send_target` / `send_character_position` / `send_click_npc` /
|
||||
`send_script_answer` / `send_item_use` / `send_item_move` / `send_item_drop`(count>0 走 DROP2)/
|
||||
`send_item_pickup` / `send_use_skill`(target 非 0 时先发 `CG_FLY_TARGETING`)。全部
|
||||
`Stage::InGame` 门控,走 `classic_stream` 的 seq 追加。`wire_classic.h` 补了对应 CG 结构体 +
|
||||
`packet_size_cg` 表项。`EntityStore::mut_move` 改收「已解码角度」(m2dev `rot*360/256` vs
|
||||
classic `rot*5` 两种编码,放到 parser 里转)。`net.classic_session` 扩展验证
|
||||
move/attack/target/chat/item_move 的字节布局 + seq[3..6] 连续性 + 非 InGame 拒绝。ctest 13/13。
|
||||
- **step 7(前半)— `M2Client` 按 `MT_PROTOCOL` 选后端** ✅:`connect_to_server()` 里
|
||||
`OS::get_environment("MT_PROTOCOL") == "classic"` → 建 `ClassicSession` 直连 game host(无独立
|
||||
auth 服),`set_stage(GameConnect)`;否则原 `AuthClient`+`GameClient` 路径**一行没动**。
|
||||
`_process()` 加 `pump_classic()` 分支(stage→`phase_changed`/`stage_changed`/`char_list`/
|
||||
`entered_game` + 复用世界镜像子集:`entity_spawned/despawned/moved/main_set/info` + `chat` +
|
||||
`vitals_changed` + `points_changed`——即 `classic_parser` 目前产出的那些)。热路径方法
|
||||
(`move` / `attack` / `set_target` / `say` / `character_position` / `click_npc` / `script_answer` /
|
||||
`use_skill` / `move_item` / `use_item` / `drop_item{,_count}` / `pickup_item` / `select_character`)
|
||||
加 `if (classic_sess) return classic_sess->send_*(...)` 前置分支。读方法
|
||||
(`get_entity` / `get_entities` / `get_main_vid` / `get_points`)经新 `active_world()` 取
|
||||
`classic_sess ? &classic_sess->world() : &game->world()`。guild/cube/marks 已接入 classic:
|
||||
公会/战争/技能走 `CG_GUILD`,会徽走带当前 handle/random_key 的独立 mark 连接,Cube 走
|
||||
40250 的 `/cube r_info`、`/cube add/delete`、`/cube make` 命令;classic 下原来因 `game == null`
|
||||
而 no-op 的 M2Client 分支已消除。仍不伪造 vendor-only HackShield/XTrap 响应和未在 40250
|
||||
源码登记的可选包。
|
||||
ctest 13/13,Godot editor import 干净,抽样 GDScript 测试通过,iOS/Android 绿。
|
||||
- **step 5 — `classic_cipher`(`_IMPROVED_PACKET_ENCRYPTION_`)** ✅(mac + iOS + Android 全绿):
|
||||
- **Crypto++ 8.4.0 vendored**:`extension/third_party/cryptopp/`(从 `ClientVS22/extern/include/
|
||||
cryptopp` 拷 393 个 `.h/.cpp/.S`,非 submodule)+ 自写 `CMakeLists.txt`(glob `.cpp`,剔除
|
||||
test/bench/validat/regtest/adhoc/fips,`CRYPTOPP_DISABLE_ASM/SSSE3/AESNI` 全平台纯 C++)。
|
||||
`mt3p::cryptopp`,链进 `mtnet`。mac 编译干净。
|
||||
- `classic/classic_cipher.{h,cpp}` —— 近乎照搬 `EterBase/cipher.cpp`:`DH2KeyAgreement`
|
||||
(RFC 5114 1024-bit MODP + `DH2` 静态+临时密钥对,`prepare()` 生成 `spub||epub` blob,`agree()`
|
||||
出 `SecByteBlock shared`)+ `BlockCipherAlgorithm::Pick(hint % 14)` 选块密码(默认 Twofish)+
|
||||
`set_up(polarity)` 从 `shared` 切 key/iv 建两个 `CTR_Mode<T>` 实例(`polarity` 决定收发各用哪个)。
|
||||
- `classic_stream` 接进去:`GC_KEY_AGREEMENT`(0xFB) → `prepare()` + `activate(polarity, …)` +
|
||||
回发 `CG_KEY_AGREEMENT`(明文);`GC_KEY_AGREEMENT_COMPLETED`(0xFA) → `set_activated(true)` +
|
||||
立刻解密缓冲区里 server 可能 pipeline 的密文尾巴。激活后 `emit_bytes()` 对所有出站字节
|
||||
(含 seq 字节)CTR 加密、`decrypt_appended()` 对每个 recv/feed chunk 解密。`set_polarity(true)`
|
||||
= 客户端。
|
||||
- CTest `net.classic_cipher`(DH2 双端协商 + 加解密对称往返 + CTR 连续性 + 会话隔离)、
|
||||
`net.classic_encstream`(`ClassicStream` 全流程:握手 → KEY_AGREEMENT 交换 → COMPLETED →
|
||||
加密 GC_PHASE 解出 + 出站 CG_MOVE 服务端能解 + pipelined COMPLETED+密文一个 feed)。
|
||||
mac ctest 15/15。
|
||||
- **交叉编译**:iOS arm64 ✅、Android arm64 ✅(Android 需把 NDK 的
|
||||
`sources/android/cpufeatures/cpu-features.c` 编进 `cryptopp` + 加到 include path ——
|
||||
`cpu.cpp` 无条件 `#include "cpu-features.h"`;已在 `cryptopp/CMakeLists.txt` 的 `if(ANDROID)` 里处理)。
|
||||
`.so` 从 46 MB → 59 MB。Godot editor import 干净,ctest 15/15。
|
||||
- **step 4c — `classic_parser` 铺开 GC(items)** ✅:接了 `GC_ITEM_SET`(21) / `GC_ITEM_DEL`(20) /
|
||||
`GC_ITEM_UPDATE`(25) / `GC_ITEM_GROUND_ADD`(26) / `GC_ITEM_GROUND_DEL`(27)。40250 的
|
||||
`TPlayerItemAttribute` 是 3 字节 `pack(1)`(`{BYTE type; short value}`),跟 `EntityStore::Item.attrs`
|
||||
的 stride 不同 → parser 里逐元素解包。`EntityStore` 加 `mut_item_set / mut_item_del /
|
||||
mut_item_update / mut_ground_add / mut_ground_del`(包 `mut_slot()` + `m_inv_changes` /
|
||||
`m_ground`)。`M2Client`:`get_inventory / get_equipment / get_item / get_ground_items` 改走
|
||||
`active_world()`;`pump_classic()` 补 `drain_inv` → `inventory_changed`、`drain_ground` →
|
||||
`ground_item_added/removed`。`net.classic_session` 扩展验证 item_set/update/del + ground add/del。
|
||||
ctest 15/15,iOS/Android 绿。
|
||||
- **step 4d — `classic_parser` GC 实体状态簇** ✅:`GC_CHARACTER_UPDATE`(19,装备外观/公会/善恶/骑乘) /
|
||||
`GC_CHANGE_SPEED`(18) / `GC_CHARACTER_POSITION`(43) / `GC_WALK_MODE`(111) / `GC_STUN`(13) /
|
||||
`GC_DEAD`(14) / `GC_MOTION`(36) / `GC_TARGET`(63,目标血条 %)。`EntityStore` 加 `mut_char_update /
|
||||
mut_set_position / mut_change_speed / mut_walk_mode / mut_stun / mut_dead / mut_motion / mut_target`
|
||||
(包 `m_ents` / `m_motions` / `m_target_*` / `m_mount_changes`)。`pump_classic()` 补
|
||||
`drain_motions`→`motion`、`drain_damage`→`damage`、`drain_mount_changes`→`mount_changed`、
|
||||
`take_target_dirty`→`target_info`。`get_target()` 走 `active_world()`。`net.classic_session`
|
||||
扩展验证。ctest 15/15,iOS/Android 绿。
|
||||
- **step 4e — `classic_parser` HUD 簇** ✅:`GC_SKILL_LEVEL`(76,`TPlayerSkill` 6B/条,`time_t`
|
||||
32 位) / `GC_QUICKSLOT_ADD/DEL/SWAP`(28/29/30) / `GC_AFFECT_ADD/REMOVE`(126/127,`TPacketAffectElement`
|
||||
21B) / `GC_DAMAGE_INFO`(135) / `GC_CHAR_ADDITIONAL_INFO`(136,补 spawn 的 name/parts/level/guild) /
|
||||
`GC_WHISPER`(34,dynamic —— 客户端 map 登记 STATIC 但 `wSize` 恰在 `TDynamicSizePacketHeader.size`
|
||||
偏移,走 dynamic 路径 ok) / `GC_SYNC_POSITION`(5,dynamic)。`EntityStore` 加 `mut_skill_levels /
|
||||
mut_quickslot_{set,del,swap} / mut_affect_{add,remove} / mut_damage / mut_char_info /
|
||||
mut_snap_position / mut_chat / mut_whisper`。`GC_CHAT`/`GC_WHISPER` 改走 `EntityStore::m_chat`
|
||||
队列(跟 m2dev 一致,`pump_classic` 的 `drain_chat` 统一处理),删掉 parser 的 `on_chat` 回调。
|
||||
`pump_classic()` 补 skills/quickslots/affects 的 drain;`get_skills/get_quickslots/get_affects/
|
||||
get_skill_group` 走 `active_world()`。ctest 15/15,iOS/Android 绿。
|
||||
- **step 4f — `classic_parser` party** ✅:`GC_PARTY_INVITE`(77,`leader_vid`) / `GC_PARTY_ADD`(78) /
|
||||
`GC_PARTY_UPDATE`(79,role/hp%/affects[7]) / `GC_PARTY_REMOVE`(80) / `GC_PARTY_LINK`(91) /
|
||||
`GC_PARTY_UNLINK`(92) / `GC_PARTY_PARAMETER`(83,分配模式)。`EntityStore` 加 `mut_party_{invite,add,
|
||||
update,remove,link,unlink,parameter}`。`pump_classic()` 补 `drain_party_invites`→`party_invite_ask`、
|
||||
`party_dirty`→`party_changed`。`get_party` / `get_party_distribute_mode` 走 `active_world()`。
|
||||
ctest 15/15,iOS/Android 绿。
|
||||
- **step 4g — `classic_parser` NPC shop** ✅:`GC_SHOP`(38,dynamic 子头) —— `START`(0,单货架
|
||||
`packet_shop_start{u32 owner_vid; packet_shop_item[40]}`,`packet_shop_item`=43B) /
|
||||
`START_EX`(10,多货架 `{u32 vid; u8 tab_count} + tab_count×{name[32]; coin_type; item[40]}`) /
|
||||
`END`(1) / 错误子头(`NOT_ENOUGH_MONEY`/`SOLDOUT`/`INVENTORY_FULL`/`INVALID_POS`)。
|
||||
`EntityStore` 加 `mut_shop_open(vid, tabs) / mut_shop_close() / mut_shop_error(code)`。
|
||||
`pump_classic()` 补 `shop_dirty`→`shop_opened`/`shop_closed`、`drain_shop_errors`→`shop_error`。
|
||||
`is_shop_open` / `get_shop_items` / `get_shop` 走 `active_world()`。ctest 15/15,iOS/Android 绿。
|
||||
- **step 4h — `classic_parser` quest / NPC 对话** ✅:`GC_SCRIPT`(45,dynamic,`{skin; u16 src_size;
|
||||
text}`) / `GC_QUEST_CONFIRM`(46,STATIC 74B) / `GC_QUEST_INFO`(81,dynamic,`{u16 index; u8 flag}` +
|
||||
flag 驱动 title/clock/counter/icon 串)。`EntityStore` 加 `mut_script / mut_quest_confirm /
|
||||
mut_quest_info`。`pump_classic()` 补 `drain_scripts`→`script_dialog`、`drain_confirms`→
|
||||
`quest_confirm_ask`、`drain_quest_changes`→`quest_info`。`get_quests` 走 `active_world()`。
|
||||
ctest 15/15,iOS/Android 绿。至此 `click_npc`→`GC_SCRIPT` 对话闭环。
|
||||
- **step 4i — classic P8 social / storage** ✅:
|
||||
- `GC_MESSENGER` / `CG_MESSENGER`:好友快照、上线/下线与手机状态,以及按 40250 客户端格式发送加好友/删好友。
|
||||
- `GC_EXCHANGE` / `CG_EXCHANGE`:交易开始、双方物品/金币/接受状态、结束/错误提示,以及全部客户端操作。
|
||||
- `GC_SAFEBOX_*` / `CG_SAFEBOX_*`:仓库开启、资金、物品快照/删除、密码错误、存入/取出/移动。
|
||||
- `GC_MALL_*` / `CG_MALL_CHECKOUT`:商城开启、物品快照/删除、取出;均接入 `EntityStore`、
|
||||
`M2Client` 读接口、信号和 classic session intent。`net.classic_wire` / `net.classic_session` 覆盖
|
||||
包尺寸、字段布局、序列字节和状态变更;macOS `mtgodot` 构建通过。
|
||||
- **step 4j — classic P9 / remaining fixed packets** ✅:补齐 `GC_WARP` 跨频道重连、`GC_TIME` /
|
||||
`GC_CHANNEL`、NPC/土地/目标标记、恋人、观察者、坐骑/飞行、PVP、`GC_DUEL_START` 对手 VID 列表、装备查看、挖矿/钓鱼、炼化、
|
||||
技能冷却和特殊效果;并消费登录安全包、BGM 主角包、`CHARACTER_ADD2/UPDATE2`。龙魂兼容代码不纳入目标服务端支持声明。
|
||||
不具备 40250 vendor runtime 的 HackShield/XTrap 请求只做定长消费,不伪造响应。
|
||||
- **step 4k — Cube + private shop semantics** ✅:classic Cube 使用 40250 `cmd_general.cpp` 的
|
||||
`/cube r_info`、`/cube r_info <start> <count>`、`/cube add <cube_index> <inventory_index>`、
|
||||
`/cube delete <cube_index>`、`/cube make`;UI 提供背包→24 格材料槽的添加/删除。个人摊位收摊
|
||||
使用空 `CG_MYSHOP`,与 40250 `CHARACTER::OpenMyShop()` 的关闭语义一致。
|
||||
- **step 4l — UI/uiscript复核** ✅:好友、交易、仓库、商城、NPC 商店、会徽、Cube、炼化和个人摊位
|
||||
窗口均通过既有 M2Client 信号/intent 接入;公会窗口暂缓,龙魂窗口不纳入目标服务端支持范围。Cube UI 的旧“直接按配方 index 制作”
|
||||
已改为先装载材料槽再制作,避免把 classic index 当成服务端参数;PVP/决斗状态也会在世界标签中
|
||||
刷新,`MobView.set_display_name()` 对无内置名字节点的真模型提供同样的名字牌回退。
|
||||
- **step 4m — 公会完整状态与会徽原生协议** ⏸️ 暂缓:公会在线/离线、公告列表、
|
||||
邀请、战争积分等 GC 子包,并接入 `EntityStore`、`M2Client` 和公会窗口;公告发布/刷新/删除、
|
||||
邀请接受/拒绝都有对应 CG 路径;已有代码保留但公会整体不纳入当前阶段。会徽独立连接也已改为 40250 的单字节头、`bufSize` 分片、CRC、
|
||||
符号包和上传布局;并按原客户端在首个 `GC_HANDSHAKE` 后发送 `CG_MARK_LOGIN` 的时序处理,
|
||||
兼容随后是否进入可选 DH2/CTR;`net.classic_mark` 固定了下载与上传的关键字节协议。
|
||||
- **step 4n — 握手重试与时钟同步时序** ✅:`PHASE_HANDSHAKE` 中的重试仍回发
|
||||
`GC_HANDSHAKE`(0xff),进入 LOGIN/SELECT/GAME 后收到的后续握手才改发带 sequence 的
|
||||
`CG_TIME_SYNC`(0xfc);会徽独立连接保持首个握手后的 `CG_MARK_LOGIN` 路径,避免把两种连接的
|
||||
控制包时序混用。`net.classic_stream` 和 `net.classic_mark` 已分别覆盖这两个边界。
|
||||
- **step 4o — 好友邀请确认闭环** ✅:40250 的 `GC_CHAT/COMMAND` 命令
|
||||
`messenger_auth <name>` 不再静默丢弃,现会产生 `friend_invite_ask`;好友窗口显示接受/拒绝
|
||||
对话框,并通过 `/messenger_auth y|n <name>` 回发原生服务端命令。`net.entity_store` 与
|
||||
`p8_test.gd` 覆盖邀请入队、弹窗和两种回答;`netbridge_test.gd` 同时校验 P8/P9、会徽和精炼
|
||||
API 都实际绑定到 Godot;公会与龙魂不作为当前交付验收项。
|
||||
- **step 4p — 40250 命令总线补齐** ✅:`PartyRequest`/`PartyRequestDenied`、`CloseSafebox`/
|
||||
`CloseMall`、`ShowMeSafeboxPassword`/`ShowMeMallPassword`、`RefineSuceeded`/`RefineFailed`、
|
||||
`OpenPrivateShop`、`MyShopPriceList` 和服务端回传的 `setblockmode` 已从静默 no-op 改为
|
||||
`EntityStore` typed event;M2Client 暴露对应 signal,仓库/商城会弹密码框,错误密码会提示,
|
||||
精炼结果会回显,个人摊位可由服务端命令打开并使用价格回填,游戏设置会按服务端屏蔽掩码同步。
|
||||
`net.entity_store` 覆盖这些命令及仓库关闭状态。
|
||||
- **step 4q — 其余有状态的 ServerCommand** ✅:40250 客户端原生处理的
|
||||
`ObserverMode`/`ObserverCount`、`StoneDetect`、`StartStaminaConsume`/`StopStaminaConsume`、
|
||||
`sms`/`nosms`、`mobile_auth` 和 `combo` 已进入 `EntityStore` 状态及 typed event;
|
||||
M2Client 提供观战/体力/手机/连击 getter 和对应 signal(体力按服务端下发速率递减,探石保留
|
||||
转换后的角度),因此 classic 不会在这些回包上静默丢状态。`ConsoleEnable`、`BettingMoney`、
|
||||
`ObserverTeamInfo` 仍保持 40250 原客户端的无可见副作用语义;不伪造它们不存在的 Godot 功能。
|
||||
- **step 4r — GC_MOTION 与表情命令** ✅:`GC_MOTION` 不再只由 M2Client 发出后被场景丢弃,
|
||||
NetWorld 会驱动本地/远端实体,并按 40250 `CRaceMotionData` 的职业变体选择真实 `.msa`。
|
||||
`kiss`、`french_kiss`、`slap`、`clap`、`cheer1/2`、`dance1..6`、`dig_motion` 及其余
|
||||
`congratulation/forgive/angry/.../joy` ServerCommand 也映射到同一 motion 流;`gift` 会触发
|
||||
可见通知。40250 源码只提供 `Gift_Show()` 通知入口,没有随命令携带奖励或领取 CG,因此不虚构
|
||||
礼物奖励数据协议。
|
||||
- **step 4s — 版本门禁与无阵营账号** ✅:补齐账号未绑定阵营时的 `CG_EMPIRE` 选择入口,并在
|
||||
收到 `GC_MAIN_CHARACTER`、`GC_MAIN_CHARACTER3_BGM` 或 `GC_MAIN_CHARACTER4_BGM_VOL` 后按
|
||||
40250 原客户端时序发送一次 `CG_CLIENT_VERSION`(`1215955205`)及 sequence。服务端
|
||||
`input_main.cpp` 会在首次 `CG_MOVE` 前拒绝没有版本上报的连接;classic session 现在覆盖这个
|
||||
门禁,离线测试同时锁定版本字段和序号推进。
|
||||
- **step 4t — 强制改名 UI 闭环** ✅:`SimplePlayer.change_name` 已透传到 classic / m2dev
|
||||
角色槽位;选人页在该标志存在时按 40250 原版行为拦截“开始”并打开改名窗,发送
|
||||
`CG_CHANGE_NAME`,再由 `GC_CHANGE_NAME` 刷新角色名并清除强制改名标志。`AppFlow` 已接通请求、
|
||||
成功和失败回传,`char_create_delete_test.gd` 覆盖短名拒绝、发送参数和成功状态。强制改名的
|
||||
重名 / 非法名错误码与真实服务端验收暂缓。
|
||||
- **step 4u — 创建公会 UI 闭环** ⏸️ 暂缓:`GC_REQUEST_MAKE_GUILD` 现在会触发公会窗的创建名称输入,
|
||||
按 40250 原版流程校验空名、控制字符和 12 字上限,再调用 `CG_ANSWER_MAKE_GUILD`;发送失败会
|
||||
保留窗口以便重试。已有 `guild_creation_ui_test.gd` 覆盖服务端请求、名称校验和合法名称应答。
|
||||
公会完整页面、服务端敏感词 / 重名错误回包、实际建会状态和权限暂不纳入本阶段。
|
||||
- **step 4v — 任务选魔石窗口复核** ✅:确认 `[SELECT_ITEM]` 已从 `QuestDialog` 接到
|
||||
`SelectItemUI`,按原 `uiselectitem.RefreshSlot` 过滤魔石和 grade、将选择窗格映射回背包格,
|
||||
选择时发送 `CG_SCRIPT_SELECT_ITEM`,关闭时发送 0;`select_item_ui_test.gd` 已覆盖过滤和选择。
|
||||
原版 CJK IME、完整资源视觉及真实脚本业务回包验收暂缓。
|
||||
- **step 4w — 队伍角色 / 能量 / 私店招牌复核** ✅:按 40250 `CParty::Update` 修正
|
||||
`party_ui.gd` 的角色 6 为 `HASTE`(迅捷),并补齐 `affects[7]` 槽位语义;攻击/坦克/迅捷/辅助/
|
||||
宗师/防御分别按 Leadership 10/20/20/25/35/40 门控,组队治疗按 Leadership 18 门控,
|
||||
`p8_test.gd` 覆盖低等级禁用路径。Cube 的 `POINT_ENERGY` 已由 HUD 快照显示;`GC_SHOP_SIGN`
|
||||
已由 `NetWorld` 提供 `ShopSign` Label3D fallback,`GC_SYMBOL_DATA` 已接会徽下载链路;个人摊位
|
||||
已支持标准 Godot 拖放与点击选取双路径,Cube 已校验 `cube_open(npc)` 前置、配方材料、金币和
|
||||
制作状态。缺失共享 `.sub` 贴图时使用现有 fallback,不阻断窗口逻辑;live FreeBSD + MySQL E2E 暂缓。
|
||||
- **step 4x — P8 交互边界与目标服务端范围** ✅:NPC/石头/传送点点选统一进入
|
||||
`click_npc`,服务端 `cube_open` 才打开 Cube;商店购买/出售支持数量控件和错误回显,交易栏位按
|
||||
服务端已占用槽位分配并拒绝重复/超限,仓库/商城拒绝满背包或满仓位,密码弹窗可正常确认/取消。
|
||||
40250 目标服务端没有龙魂玩法,`GameScene` 不再创建龙魂入口、不消费其背包右键或快捷键;
|
||||
`ds_refine` 兼容 API 仍保留。
|
||||
- **本机回归结果**:C++/协议 CTest 16/16 通过(包含 `net.loopback_flow`);本次抽样的
|
||||
`project/*_test.gd` 共 38 个脚本全部通过;系统设置测试已使用 headless 临时配置并对缺失可选
|
||||
贴图安全降级。当前沙箱禁止 loopback bind,因此 `channel_status_test` 明确跳过本机 mock;
|
||||
有网络权限时仍执行完整请求/响应断言,不是 40250 classic 协议失败。
|
||||
- **当前收尾项**:协议 / 状态链路、classic UI 交互和离线回归测试已完成;真实 FreeBSD + MySQL
|
||||
binary E2E 暂缓,公会功能暂缓,龙魂因目标 40250 服务端不提供而排除。`tools/40250/` 的
|
||||
build/setup/start/stop/probe 入口保留,待后续恢复真服验收时使用。
|
||||
+66
-24
@@ -8,10 +8,14 @@
|
||||
> 渲染细节另见 `PARITY-GAP.md`,动画、材质和资产管线另见 `BACKLOG.md`,网络实现另见
|
||||
> `CLIENT-PORT.md`。
|
||||
>
|
||||
> 更新日期:2026-08-31(增量 38)
|
||||
> 更新日期:2026-09-01(增量 45)
|
||||
>
|
||||
> **状态列图例**:✅ 达到本文末“1:1 完成判据” · 🟡 有首版 / 部分语义 · ⬜ 未实现 ·
|
||||
> ❌ 明确不做(超出 m2dev-client 参考范围,除非另立需求)。
|
||||
|
||||
> **当前 40250 范围调整(增量 45)**:真实 FreeBSD + MySQL 服务端的完整登录 / 功能验收暂缓,
|
||||
> 不作为本阶段交付门槛;公会相关功能暂缓。当前目标 40250 服务端不提供龙魂玩法,龙魂不纳入本阶段
|
||||
> 支持范围,已有兼容代码不代表服务端可用;运行时龙魂入口已禁用,但保留 `ds_refine` 等协议兼容代码。
|
||||
>
|
||||
> ### 不做清单(❌)
|
||||
>
|
||||
@@ -21,9 +25,38 @@
|
||||
> - **反作弊(anti-cheat)**:`CG_HACK`(0x0B03) 客户端异常上报,以及桌面外壳的
|
||||
> `ProcessCRC` / `ProcessScanner` / 内存扫描等。自用客户端不需要,**标注为不做**,
|
||||
> 除非服务端强制校验才回头做最小上报。
|
||||
> - **龙魂精炼(当前 40250 目标服务端)**:目标服务端不提供该玩法,本阶段不实现 / 不验收;
|
||||
> 客户端保留的兼容代码不计入当前支持范围。
|
||||
>
|
||||
> ### 变更记录
|
||||
>
|
||||
> - **2026-09-01 增量 45(P8 交互边界与龙魂运行时范围)**:补齐 NPC 点击路由、Cube NPC
|
||||
> 前置 / 配方材料 / 金币校验与制作状态、商店购买 / 出售数量和错误反馈、交换重复 / 满槽 /
|
||||
> 金币边界、仓库 / 商城密码与满包回滚、个人摊位标准 Godot 拖放路径;缺失可选 `.sub` 贴图
|
||||
> 安全降级。龙魂入口运行时禁用,公会和真实服务器验收继续按当前范围暂缓。
|
||||
> - **2026-09-01 增量 44(交付范围调整)**:真实 FreeBSD + MySQL 完整验收、公会相关功能均
|
||||
> 暂缓;当前目标 40250 服务端不提供龙魂玩法,龙魂从本阶段支持范围排除。保留本地协议 / 状态 / UI
|
||||
> 首版与离线回归结果,后续恢复服务端条件后再单独推进 deferred 项。
|
||||
> - **2026-09-01 增量 43(队伍角色 / 能量 / 私店招牌复核)**:对照 40250 服务端
|
||||
> `CParty::Update` 修正队伍角色 6 为 `HASTE`(迅捷),补齐 `affects[7]` 的服务端槽位语义,
|
||||
> 并在 `party_ui.gd` 按 Leadership 10/20/25/35/40 门控攻击/坦克/迅捷/辅助/宗师/防御,
|
||||
> Leadership 18 门控组队治疗;`p8_test.gd` 覆盖低 Leadership 的禁用路径。Cube HUD 已有
|
||||
> `POINT_ENERGY` 快照显示;`GC_SHOP_SIGN` 已解析并在 `NetWorld` 以 `ShopSign` Label3D
|
||||
> 作为无 `.sub` 资源 fallback,`GC_SYMBOL_DATA` 也已有会徽下载链路。剩余仍是原版资源、真
|
||||
> `.sub` 拖放 / 视觉和 Cube NPC 交互;live FreeBSD + MySQL E2E 暂缓。
|
||||
> - **2026-09-01 增量 42(选魔石窗口闭环复核)**:确认 `GC_SCRIPT` 的 `[SELECT_ITEM]` 已由
|
||||
> `QuestDialog` 转发到 `SelectItemUI`;选择窗按原 `uiselectitem.RefreshSlot` 过滤魔石 / grade、
|
||||
> 映射背包格并发送 `CG_SCRIPT_SELECT_ITEM`,关闭发送 0,`select_item_ui_test.gd` 已覆盖。仍需
|
||||
> 完整原版资源、图标和 CJK 输入环境下的视觉验收。
|
||||
> - **2026-09-01 增量 41(创建公会 UI 闭环)**:`GC_REQUEST_MAKE_GUILD` 触发公会创建窗口,
|
||||
> 对应 `CG_ANSWER_MAKE_GUILD` 已接入名称输入、12 字上限、空名 / 控制字符校验和发送失败提示;
|
||||
> 新增 `guild_creation_ui_test.gd` 覆盖请求、校验和合法名称应答。服务端敏感词 / 重名错误及实际建会
|
||||
> 状态仍需真服验收。
|
||||
> - **2026-09-01 增量 40(强制改名 UI 闭环)**:`SimplePlayer.change_name` 已从 classic / m2dev
|
||||
> 角色列表透传到 `M2Client.char_list`;选人页按 40250 原版语义,在角色带该标志时点击“开始”
|
||||
> 弹出改名窗,校验名称后发送 `CG_CHANGE_NAME`,成功的 `GC_CHANGE_NAME` 刷新槽位并清除标志,
|
||||
> `AppFlow` 接通请求、成功和失败回传,新增 `char_create_delete_test.gd` 回归覆盖。P1 行不再把
|
||||
> 改名 UI 列为未实现;仍需真服验证强制改名、重名 / 非法名错误码和服务端状态。
|
||||
> - **2026-08-31 增量 30(EterGrnLib 对齐复核)**:查代码确认以下已落地,表内相关行同步。
|
||||
> ① **材质 blend / two-sided 改读 GR2 材质信息**(`metin2_model::decide_blend/decide_two_sided`
|
||||
> 优先 `gr2::MaterialInfo.alpha_blend/two_sided` = `dump_materials` 端口 `Material.cpp:233`,
|
||||
@@ -63,17 +96,18 @@
|
||||
> - **2026-08-31 增量 37(组队成员信息板 + `CG_PARTY_SET_STATE`)**:
|
||||
> ① **C++**:`wire.h` 加 `CGPartySetState{u16 hdr,u16 len,u32 pid,u8 role,u8 on}`(断言 10)
|
||||
> + `EPartyRole` 枚举(NORMAL 0/LEADER 1/ATTACKER 2/TANKER 3/BUFFER 4/SKILL_MASTER 5/
|
||||
> BERSERKER 6/DEFENDER 7);`game_client.send_party_set_state(pid,role,on)`;
|
||||
> HASTE 6/DEFENDER 7);`game_client.send_party_set_state(pid,role,on)`;
|
||||
> `M2Client.party_set_state(pid,role,on)` + bind;`get_party()` dict 补 `state`(完整角色字节)
|
||||
> 和 `affects[7]`。
|
||||
> ② **`party_ui.gd`** 从「名字 + 一条 HP」重做成 1:1 迁移 `assets/root/uiparty.py`
|
||||
> `PartyMemberInfoBoard` + `partymemberinfoboard.py` 布局:每员 strip = 角色状态按钮
|
||||
> (队长可点 → 弹角色菜单 普通/攻击/坦克/狂战/辅助/宗师/防御 → `party_set_state`;普通 =
|
||||
> (队长可点 → 弹角色菜单 普通/攻击/坦克/迅捷/辅助/宗师/防御 → `party_set_state`;普通 =
|
||||
> 清当前角色 `on=false`;+ 踢出 → `party_leave` = `SendPartyRemovePacket`)+ 名字(+★队长)
|
||||
> + HP gauge + `affects[7]` 非零 → 附加效果 chip(tooltip 带值,槽位名暂定)。点名字 →
|
||||
> `set_target(vid)`。顶部:EXP 分配开关(`party_set_distribute` 0 不均分 / 1 均分)+
|
||||
> 组队治疗(`party_use_skill(PARTY_SKILL_HEAL=1, 0)`)。队长判定 = `get_party()` 里本地
|
||||
> `get_main_vid()` 那条的 `leader`。角色菜单按党技能等级门控(Tanker≥10…Defender≥40)暂略。
|
||||
> `get_main_vid()` 那条的 `leader`。角色菜单按 40250 服务端 Leadership 门控:攻击≥10、坦克/迅捷≥20、
|
||||
> 辅助≥25、宗师≥35、防御≥40;治疗要求 Leadership≥18。
|
||||
> ③ **修 `p8_test` 隐患**:`_init` 原来 `_run()` 未 `await`,`_run` 第一个 `await process_frame`
|
||||
> 之后的所有断言(含增量 34 的 `shop_ex` 多货架)从未真正生效 —— 改成 `await _run()` 后
|
||||
> 暴露 `shop_ui.refresh()` 的 `queue_free` 延迟 bug(旧 row 当帧未消失 → `get_child_count`
|
||||
@@ -224,6 +258,11 @@
|
||||
> `get_observers()` 与 `land_areas_changed` / `observer_event` 已接入;小地图现在消费观战者
|
||||
> 状态并绘制紫色菱形标记。实体、回环、Godot 桥接和 `p9_test.gd` 覆盖。缺失 header 清单由
|
||||
> 5 项降为 **1 项**(仅剩 `CG_TEXT`,参考端未发现实际发送方)。
|
||||
> - **2026-09-01 增量 39(`CG_TEXT` wire 尺寸覆盖)**:按 40250 `TPacketCGText` /
|
||||
> `CInputHandshake::Analyze` 补齐 `CG_TEXT` 的 1 字节固定前缀和 classic `packet_size_cg()`
|
||||
> 表项,并加入 wire 回归断言。该包后续文本由服务端按换行特殊解析,仅作为握手阶段管理通道;
|
||||
> Cube 继续使用已验证的 `CG_CHAT`/`CHAT_TYPE_COMMAND`,不把管理命令暴露为普通游戏 API。
|
||||
> 主网络层缺失 header 清单由 1 项降为 0 项。
|
||||
> - **2026-08-31 增量 19(钓鱼动作消费)**:`NetWorld` 和 `NetPlay` 现在消费
|
||||
> `fishing_event` 的 START / STOP / REACT / SUCCESS / FAIL 子头,记录方向并尝试驱动远端及
|
||||
> 本地主角的钓鱼动作;FISH 子头按参考客户端语义保留为鱼获物品事件,不误当作角色 VID。
|
||||
@@ -272,7 +311,7 @@
|
||||
> - **2026-08-31 增量 3**:两个 ⬜ 项落地首版 —— **私人商店 / 道具商城**
|
||||
> (`CG_MYSHOP` / `GC_MALL_OPEN|SET|DEL` / `CG_MALL_CHECKOUT` + `private_shop_ui` /
|
||||
> `mall_ui`)、**Cube 제작**(`GC_CHAT/COMMAND` 的 `cube …` 总线 → `CubeState` +
|
||||
> `cube_ui`)。仍缺能量条、原版拖放 UI 细节。
|
||||
> `cube_ui`)。能量值已由 `POINT_ENERGY` 快照接入 HUD;仍缺原版拖放 UI 细节。
|
||||
> **拍卖行**经核查 m2dev-client 无此系统 → 标注 ❌ 不做(见上「不做清单」)。
|
||||
> **反作弊**(`CG_HACK` + `ProcessCRC`/`ProcessScanner`)→ 标注 ❌ 不做。
|
||||
> **「协议覆盖审计」段**改为逐条清单:脚本核对 `wire.h` vs `Packet.h`,主网络层
|
||||
@@ -291,7 +330,7 @@
|
||||
## 结论
|
||||
|
||||
`mtgodot-poc` 已经不是早期“只能登录、移动和攻击”的骨架:登录到 PHASE_GAME、基础战斗、
|
||||
背包 / 装备、技能 UI、任务、聊天、组队 / 好友、商店 / 交易 / 仓库、公会、精炼、龙魂、
|
||||
背包 / 装备、技能 UI、任务、聊天、组队 / 好友、商店 / 交易 / 仓库、精炼,
|
||||
地图和部分特效都已有可运行首版。
|
||||
|
||||
当前的主要差距已从“有没有功能”转为:
|
||||
@@ -313,7 +352,7 @@
|
||||
- 背包、基础装备换模、地面物品、技能窗口和快捷栏读取;
|
||||
- 聊天、私聊、基础任务对话和任务日志;
|
||||
- 组队、好友、情侣名 / 爱意值状态、NPC 商店、交易、仓库;
|
||||
- 公会、公会技能、公会战、会徽上 / 下载、精炼、龙魂精炼;
|
||||
- 公会、公会技能、公会战、会徽上 / 下载、精炼(公会相关验收暂缓);
|
||||
- 地形、建筑、水体、**NPC / 怪物真模型(vnum→npclist.txt→目录)**、小地图、频道、昼夜和天气首版;
|
||||
- GR2 模型 / 动画、换装首版、武器 / 盾挂点、距离 LOD / 淡入、角色接触阴影、
|
||||
`item_proto` 驱动的球面高光和 `.mse` 粒子首版。
|
||||
@@ -324,7 +363,7 @@
|
||||
|
||||
| 优先级 | 状态 | 模块 | 当前实现 | 需要迁移的原客户端语义 |
|
||||
|---|---|---|---|---|
|
||||
| P1 | 🟡 | 选人 / 建号 / 删号 / 改名 | `CG_CHARACTER_CREATE`(0x0201) / `CG_CHARACTER_DELETE`(0x0202) / `CG_CHANGE_NAME`(0x010B) / `GC_PLAYER_CREATE_SUCCESS`(0x020C) / `GC_PLAYER_CREATE_FAILURE`(0x020D) / `GC_PLAYER_DELETE_SUCCESS`(0x020E) / `GC_PLAYER_DELETE_WRONG_SOCIAL_ID`(0x020F) / `GC_CHANGE_NAME`(0x010C) 全部有尺寸断言;`GameClient` 发送接口、角色槽位补丁和 `drain_*_events()`;`M2Client` 同名建删改 API、4 个建删结果信号及 `char_name_changed`;`net.loopback_flow` 覆盖建删和改名名称字节 / pid 广播。**真机验证(2026-08-31,`net_e2e MT_E2E_CHARCREATE=1`)**:建号、`GC_PLAYER_CREATE_FAILURE`、`GC_EMPIRE` 已验。选人页有建号 / 删号弹窗与国家国旗 | `CG_CHARACTER_DELETE` 破坏性,真机未验;改名 UI / 改名卡消耗和重复名错误提示未接;建号缺外形 / 发型选择与属性再分配 UI(现发 `shape=0` + 职业基础四维);缺 `SELECT_EMPIRE` 选国界面;原版选人台座、镜头动画、`OnCreateFailure` 完整错误码文案 |
|
||||
| P1 | 🟡 | 选人 / 建号 / 删号 / 改名 | `CG_CHARACTER_CREATE`(0x0201) / `CG_CHARACTER_DELETE`(0x0202) / `CG_CHANGE_NAME`(0x010B) / `GC_PLAYER_CREATE_SUCCESS`(0x020C) / `GC_PLAYER_CREATE_FAILURE`(0x020D) / `GC_PLAYER_DELETE_SUCCESS`(0x020E) / `GC_PLAYER_DELETE_WRONG_SOCIAL_ID`(0x020F) / `GC_CHANGE_NAME`(0x010C) 全部有尺寸断言;`GameClient` 发送接口、角色槽位补丁和 `drain_*_events()`;`M2Client` 同名建删改 API、4 个建删结果信号及 `char_name_changed`;`net.loopback_flow` 覆盖建删和改名名称字节 / pid 广播。**真机验证(2026-08-31,`net_e2e MT_E2E_CHARCREATE=1`)**:建号、`GC_PLAYER_CREATE_FAILURE`、`GC_EMPIRE` 已验。选人页有建号 / 删号弹窗、强制改名弹窗与国家国旗 | `CG_CHARACTER_DELETE` 破坏性,真机未验;强制改名、重名 / 非法名错误码和服务端状态未真服验收;建号缺外形 / 发型选择与属性再分配 UI(现发 `shape=0` + 职业基础四维);原版选人台座、镜头动画、`OnCreateFailure` 完整错误码文案 |
|
||||
| P0 | 🟡 | 技能施放协议 | `CG_USE_SKILL`、`CG_FLY_TARGETING`、`CG_ADD_FLY_TARGETING` 与 `CG_SHOOT` 已有尺寸断言、原生发送接口和 loopback 字节验证;服务端 `GC_FLY_TARGETING` / `GC_ADD_FLY_TARGETING` 已进入 `EntityStore`,通过 `M2Client.fly_targeting` 暴露,且已补入 `GameClient` 世界分派;`GC_CHANGE_SKILL_GROUP` 会更新 `get_skill_group()` 并使技能快照失效;快捷栏先发 `use_skill(skill_id,target_vid)`,再同步 `CG_MOVE` 技能动作;弓技能会在 `.msa` FLY 事件发送 `CG_SHOOT`。真服单会话已发送 `CG_USE_SKILL` / `CG_FLY_TARGETING` / `CG_ADD_FLY_TARGETING` / `CG_SHOOT`,均无断连 / unknown | 仍需实现范围 / 多目标选择器来自动驱动 `ADD_FLY_TARGETING`;冷却、目标合法性、真实弹道和客户端表现也须按技能类型细分;尚未在真服观察到对应 GC 飞行目标推送 |
|
||||
| P0 | 🟡 | 快捷栏持久化 | 已覆盖 36 槽(4 页 × 9 格)、F1–F4 切页和 1–9 激活;拖放 / 清除 / 交换会分别发送 `CG_QUICKSLOT_ADD/DEL/SWAP`,服务器 `GC_*` 可完整恢复;技能冷却读取对应 `.msk` 的 `CoolTimeFormula` | 仍需补命令 / 表情快捷槽、物品拖放来源、鼠标物品态及原 UI 的完整快捷栏交互;冷却还未叠加原客户端的施法速度与所有特殊技能规则 |
|
||||
| P0 | 🟡 | 物品操作 | 已有移动、使用、丢弃、拾取;Shift 选“来源→目标”发 `ITEM_USE_TO_ITEM`,Ctrl+右键向当前目标 `ITEM_GIVE`,Alt+右键以数量框发 `ITEM_DROP2`;`ITEM_OWNERSHIP` 会实时更新地面名条;`GC_VIEW_EQUIP` 已保存 11 格检查快照并弹出装备查看窗 | 仍需复刻原鼠标物品态、操作确认 / 错误提示、宝石专用规则,以及装备查看窗的原版槽位图标、3D 纸娃娃与交互 |
|
||||
@@ -333,14 +372,15 @@
|
||||
| P0 | 🟡 | PVP / 决斗 / 阵营 | `GC_PVP` 和 `DUEL_START` 已进入状态层,并以 `pvp_changed` / `duel_started` 信号提供给 Godot;挑战 / 战斗 / 复仇关系会在世界实体上显示标签;实体已有部分 empire / alignment / pk 字段 | 仍需 PK 模式上行、原版名字颜色、可攻击判定、阵营规则和原版目标板 / 决斗 UI |
|
||||
| P1 | 🟡 | Actor 战斗运行时 | 有骨骼动画、基础 combo / 硬直 / 相机抖动;动作切换按 Granny `EaseInCurve(0,0,1,1)` 的 `2w²−w³` 逐骨 crossfade;角色 mesh 支持距离 LOD、15% 迟滞和约 0.18s 新旧 mesh 淡入(`LodGhost` + `lod_fade` uniform);右手武器 / 左手盾按 `weaponComposite[0] = weaponInvWorld[0]·weaponLocal[0]` grip 复合挂 `equip_right_hand`(探针实测 Δ≤0.06 m,非标准米级偏移文件夹断回退);`.msa` motion event 已分派声音、EffectPosition 特效、震屏和屏闪(FLY / WARP 留钩子);玩家 / 怪物强制实时投影并附脚底接触阴影 Decal;实体 `attack_speed` 与本地 `POINT_ATT_SPEED` 已按原版 100 基准影响攻击请求间隔 | 仍需用真实 `.msa` motion duration、武器 / 坐骑规则和服务端合法性校准攻击节奏;另补 combo 取消窗口、精确受击 / 击退、角色碰撞、同步碰撞、武器拖尾、剩余 motion event / 骨骼挂点语义、GPU skin LOD、双持 / 左手武器和目标锁定相机;`03150` 类偏移网格需 per-weapon 偏移数据或修 .gr2 |
|
||||
| P1 | 🟡 | 飞行物与命中特效 | `GC_FLY_TARGETING` / `GC_ADD_FLY_TARGETING` 保存 shooter 的目标队列和 VID / 坐标;`GC_CREATE_FLY` 可消费队列并生成 tween 发光球 | 对齐 `FlyingObjectManager`:真实技能弹道模型、命中时机 / 命中特效、消失条件、声音与 EffectLib 特效 |
|
||||
| P1 | 🟡 | 任务 / NPC | 支持 `[ENTER]`、`[CLEAR]`、`[NEXT]`、`[DONE]`、基础 `[QUESTION]`、`[INPUT]` 文本框和任务日志;`M2Client.script_select_item()` / `quest_cancel()` 已可发送原协议应答,Escape 会走取消包;真服单会话已发送 `CG_SCRIPT_SELECT_ITEM`(8B) 与 `CG_QUEST_CANCEL`(4B),取消触发 1 条 quest-change 且无断连 / unknown | 补选道具网格、原版 CJK IME / 输入校验、QuestButton、立绘、完整 EventManager 标签、世界箭头和屏幕边缘指示;选道具包本轮未出现脚本业务回包 |
|
||||
| P1 | 🟡 | 任务 / NPC | 支持 `[ENTER]`、`[CLEAR]`、`[NEXT]`、`[DONE]`、基础 `[QUESTION]`、`[INPUT]`、`[SELECT_ITEM]` 文本流程和任务日志;`SelectItemUI` 按原 `uiselectitem.RefreshSlot` 过滤魔石 / grade、映射背包格并发送 `CG_SCRIPT_SELECT_ITEM`,`quest_cancel()` 发送取消包;真服单会话已发送 `CG_SCRIPT_SELECT_ITEM`(8B) 与 `CG_QUEST_CANCEL`(4B),取消触发 1 条 quest-change 且无断连 / unknown | 补原版 CJK IME / 输入校验、QuestButton、立绘、完整 EventManager 标签、世界箭头和屏幕边缘指示;选道具业务回包与完整资源视觉仍需真服 / 资源环境验收 |
|
||||
| P1 | 🟡 | 商店 / 交易 / 仓库 | 普通 NPC 商店、交易状态和仓库列表已有首版;**`SHOP_GC_START_EX` 多货架已解析(`m_shop_tabs`,`get_shop()`),`shop_ui` 带货架按钮 + `tabIdx*40+slot` 买位置** | 补图标格网格、数量输入、交易回滚提示、仓库密码 / 改密码、完整拖放规则、`coin_type` 非金币货币显示 |
|
||||
| P1 | 🟡 | 私人商店 / 道具商城 | **首版**:`CG_MYSHOP`(0x0802) `CGMyShopHead`(38) + `MyShopItem`(13)×N 有尺寸断言 + `GameClient::send_open_private_shop` / `send_close_private_shop`(=`SHOP_CG_END`) + loopback 字节级;`GC_MALL_OPEN`(0x0841)/`GC_MALL_SET`(0x0842)/`GC_MALL_DEL`(0x0843) 路由进 `EntityStore`(`m_mall[135]` + `mall_open/size/slot/dirty`),`CG_MALL_CHECKOUT`(0x0840) `CGMallCheckout`(8) 发送;`M2Client` `open/close_private_shop`、`get_mall_items`/`mall_checkout` + `mall_opened`/`mall_changed` 信号;`ui/private_shop_ui.gd`(**已重做成 `PrivateShopBuilder.itemStock` 模型:40 格 grid + 拿起 / 落位 / 逐件价格弹窗 / 点占用格撤下 / 개설按格号排序 + display_pos + 上限 39**)、`ui/mall_ui.gd`(列表 + 取出)。**真机验证(2026-08-31,`net_e2e MT_E2E_MYSHOP=1`)**:`CG_MYSHOP` 38B head + 13B item×1 被真服接受,无断连 / 无 shop error,`SHOP_CG_END` 关店正常。`CG_MALL_CHECKOUT` 送出不断连,但 mall 需点 NPC 才 open。他人开店 `SHOP_GC_START_EX` 多货架已解析(见增量 34)。⬜ `SYMBOL_DATA`/`GC_SHOP_SIGN` 头顶招牌 3D、真 `.sub` 版 `privateshopbuilder.py` uiscript、鼠标真拖放物品态 |
|
||||
| P1 | 🟡 | 私人商店 / 道具商城 | **首版**:`CG_MYSHOP`(0x0802) `CGMyShopHead`(38) + `MyShopItem`(13)×N 有尺寸断言 + `GameClient::send_open_private_shop` / `send_close_private_shop`(=`SHOP_CG_END`) + loopback 字节级;`GC_MALL_OPEN`(0x0841)/`GC_MALL_SET`(0x0842)/`GC_MALL_DEL`(0x0843) 路由进 `EntityStore`(`m_mall[135]` + `mall_open/size/slot/dirty`),`CG_MALL_CHECKOUT`(0x0840) `CGMallCheckout`(8) 发送;`M2Client` `open/close_private_shop`、`get_mall_items`/`mall_checkout` + `mall_opened`/`mall_changed` 信号;`ui/private_shop_ui.gd`(**已重做成 `PrivateShopBuilder.itemStock` 模型:40 格 grid + 拿起 / 落位 / 逐件价格弹窗 / 点占用格撤下 / 개설按格号排序 + display_pos + 上限 39**)、`ui/mall_ui.gd`(列表 + 取出)。**真机验证(2026-08-31,`net_e2e MT_E2E_MYSHOP=1`)**:`CG_MYSHOP` 38B head + 13B item×1 被真服接受,无断连 / 无 shop error,`SHOP_CG_END` 关店正常。`CG_MALL_CHECKOUT` 送出不断连,但 mall 需点 NPC 才 open。他人开店 `SHOP_GC_START_EX` 多货架已解析(见增量 34)。`GC_SHOP_SIGN` 已在 `NetWorld` 提供 `ShopSign` Label3D fallback,`GC_SYMBOL_DATA` 已接会徽下载;仍缺真 `.sub` 版 `privateshopbuilder.py` uiscript、资源视觉和鼠标真拖放物品态 |
|
||||
| — | ❌ | 拍卖行 / auction house | 不做 | m2dev-client 无此系统(`0x08xx` 段只有 shop/myshop/safebox/mall,全源码 / 协议 / 资产零 `auction` 命中)。需自定义协议 + 服务端支持才能实现,**标注为不做**,除非后续明确提需求 |
|
||||
| P1 | 🟡 | Cube (제작) / 能量系统 | **首版**:Cube 走 `GC_CHAT`/`CHAT_TYPE_COMMAND` 文本总线(对齐 `ServerCommand()`),`EntityStore::apply_server_command` 解析 `cube open/close/info/success/fail/r_list/m_info`(配方 `v,c` 列表 + `@`/`&`/`|`/`/` 分层材料 + 金币)→ `CubeState` + `drain_cube_events()`;`GameClient::send_cube_make/material_info/result_list`(`/cube make|mInfo|rList`);`M2Client` `get_cube`/`cube_make`/`cube_request_*` + `cube_opened`/`cube_closed`/`cube_changed`/`cube_result` 信号;`ui/cube_ui.gd`(配方列表 + 材料/金币 + 제작)。`net.entity_store` + `net.loopback_flow` 覆盖。真机(2026-08-31)`/cube rList` 送出不断连,但服务器只在玩家站到 cube NPC 前才回应 → `cube open=0`,需 NPC 交互完整验。⬜ 能量条(`POINT_ENERGY` / affect)、原 `uiCube.py` 材料格 3D 图标 / 拖放 / 결과 애니、NPC 前完整验 send 命令字 |
|
||||
| P1 | 🟡 | 组队 | 邀请、接受、离队;**成员信息板(`party_ui.gd`,1:1 迁移 `uiparty.PartyMemberInfoBoard`):每员角色状态按钮(队长弹菜单 → `CG_PARTY_SET_STATE` 0x0704 分配 攻/坦/狂/辅/宗/防 或踢人)+ 名字(★队长) + HP gauge + `affects[7]` 附加效果 chip;顶部 EXP 分配开关(`party_set_distribute`)+ 组队治疗(`party_use_skill(1,0)`)**;真服单会话 `CG_PARTY_USE_SKILL`(9B) 后仍在线;小地图 / Atlas 绿色队员点 + 队长环 | 补角色菜单按党技能等级门控(Tanker≥10…Defender≥40)、`affects[7]` 精确槽位语义、真版 `.sub` 图标 / 颜色、队伍技能冷却 / 权限、离线颜色语义 |
|
||||
| P1 | 🟡 | Cube (제작) / 能量系统 | **首版**:Cube 走 `GC_CHAT`/`CHAT_TYPE_COMMAND` 文本总线(对齐 `ServerCommand()`),`EntityStore::apply_server_command` 解析 `cube open/close/info/success/fail/r_list/m_info`(配方 `v,c` 列表 + `@`/`&`/`|`/`/` 分层材料 + 金币)→ `CubeState` + `drain_cube_events()`;`GameClient::send_cube_make/material_info/result_list`(`/cube make|mInfo|rList`);`M2Client` `get_cube`/`cube_make`/`cube_request_*` + `cube_opened`/`cube_closed`/`cube_changed`/`cube_result` 信号;`ui/cube_ui.gd`(配方列表 + 材料/金币 + 제작)。`net.entity_store` + `net.loopback_flow` 覆盖。真机(2026-08-31)`/cube rList` 送出不断连,但服务器只在玩家站到 cube NPC 前才回应 → `cube open=0`,需 NPC 交互完整验。`POINT_ENERGY` 已由 HUD 快照显示;仍缺原 `uiCube.py` 材料格 3D 图标 / 拖放 / 결과 애니、NPC 前完整验 send 命令字 |
|
||||
| P1 | 🟡 | 组队 | 邀请、接受、离队;**成员信息板(`party_ui.gd`,1:1 迁移 `uiparty.PartyMemberInfoBoard`):每员角色状态按钮(队长弹菜单 → `CG_PARTY_SET_STATE` 0x0704 分配 攻/坦/迅捷/辅/宗/防 或踢人)+ 名字(★队长) + HP gauge + `affects[7]` 附加效果 chip;顶部 EXP 分配开关(`party_set_distribute`)+ 组队治疗(`party_use_skill(1,0)`)**;角色 / 治疗控件已按 40250 服务端 Leadership 规则本地门控(攻击≥10、坦克/迅捷≥20、辅助≥25、宗师≥35、防御≥40,治疗≥18);真服单会话 `CG_PARTY_USE_SKILL`(9B) 后仍在线;小地图 / Atlas 绿色队员点 + 队长环 | 仍需真版 `.sub` 图标 / 颜色、队伍技能冷却 / 权限、离线颜色语义和真实两账号组队验收 |
|
||||
| P1 | 🟡 | 好友 / 情侣 | 好友列表、上线状态和私聊入口已有;`GC_LOVER_INFO`(70B) / `GC_LOVE_POINT_UPDATE`(5B) 已有尺寸断言、`EntityStore::LoverInfo` 状态、`M2Client.get_lover()` / `lover_changed`,`love_ui` 常驻显示伴侣名和 0–100 爱意值;`net.entity_store` 与 `love_ui_test.gd` 覆盖 | 补好友分组和完整 messenger 行为;情侣仍需完整交互 / 状态图标与 affect 表现、断线 / 切图生命周期、原版文案和真服务器端到端验收 |
|
||||
| P1 | 🟡 | 公会 | 信息、成员、技能、公会战、会徽上 / 下载和部分操作已有首版;`GC_REQUEST_MAKE_GUILD`(0x0731) 已解析并以 `M2Client.guild_make_requested` 通知;`CG_SYMBOL_CRC` / `GC_SYMBOL_DATA` 已支持原始符号文件下载 | 补完整权限 / 等级页、公告与日志、成员管理、创建公会 UI / 应答流程、公会战应答、积分板、领地和公会建筑 |
|
||||
| P1 | 🟡 | 公会(暂缓) | 信息、成员、技能、公会战、会徽上 / 下载和部分操作已有首版;`GC_REQUEST_MAKE_GUILD`(0x0731) 已解析并以 `M2Client.guild_make_requested` 通知;公会窗已弹出创建名称输入并发送 `CG_ANSWER_MAKE_GUILD`;`CG_SYMBOL_CRC` / `GC_SYMBOL_DATA` 已支持原始符号文件下载 | 本阶段暂缓完整权限 / 等级页、公告与日志、成员管理、公会战应答、积分板、领地和公会建筑;恢复公会范围后再做完整真服验收 |
|
||||
| — | ❌ | 龙魂精炼 | 当前目标 40250 服务端不提供该玩法;本阶段不纳入支持或验收,已有兼容代码仅保留作协议参考 | 若未来服务端增加该玩法,再另立协议、UI 和验收需求 |
|
||||
| P1 | 🟡 | 世界玩法 | 有 `CG_WARP` 主动请求、`GC_WARP` 同服 / 跨服处理、`CG_FISHING` / `GC_FISHING` 钓鱼方向与状态事件、`NetWorld` / `NetPlay` 钓鱼动作分发、`ChatUI` 鱼获 / 失败日志、`CG_DUNGEON` / `GC_DUNGEON` 副本请求与状态事件、`GC_DIG_MOTION` 挖矿动作广播、`GC_LAND_LIST` 领地状态、`GC_OBSERVER_ADD/REMOVE/MOVE` 观战者状态、频道、时间、NPC marker、天气粒子和 mount_vnum 字段;真服登录洪流已收到并解析 `GC_LAND_LIST`(436B),单会话发送 `CG_FISHING`(5B) 后收到“Please choose a Fishing Pole.”业务拒绝且连接保持;同服 `GC_WARP` 直接挪玩家,跨服 `addr≠0` 由 `M2Client` 复用 login key 直连目标 game server,断点重连也走该快路径 | 补完整钓鱼 UI / 鱼获背包落地、副本计时 / 入口 UI、坐骑真模型 / 骑乘动作 / 移速、地形贴花、DungeonBlock 和地图天气配置;`CG_DUNGEON` / `CG_WARP` 本轮为避免副作用未发送,跨服换图仍需真服端到端验收与场景生命周期清理 |
|
||||
| P1 | 🟡 | 小地图 / Atlas | 可画玩家、实体、NPC、任务标记、观战者紫色菱形和 `GC_LAND_LIST` 领地边框;支持 `set_scale()` / 鼠标滚轮缩放;Atlas 支持底图、地图名称、玩家坐标、拖动和坐标查询 | 补原 minimap / Atlas 贴图细节、提示、队员 / NPC 分类、世界箭头、地图驱动天气配置和原版窗口视觉 |
|
||||
| P1 | 🟡 | EffectLib | `.mse` Particle 已映射到 `GPUParticles3D` | 粒子 `.dds` 目前被程序化径向渐变替代,`.mde` 用 box 占位;需补 EffectMesh、SimpleLight、TexAni、MovingType、骨骼挂点和完整时间轴 |
|
||||
@@ -384,7 +424,7 @@
|
||||
故当前总数从 24 降为 22 个 header;增量 7 再补任务选择 / 取消上行,增量 8 补队伍技能上行,
|
||||
增量 9 补飞行目标广播,增量 10 补技能组切换,增量 11 补角色改名,增量 12 补公会创建请求,
|
||||
增量 13 补主动传送,增量 14 补挖矿动作,增量 15 补会徽符号下载,增量 16 补钓鱼协议,
|
||||
增量 17 补副本协议,增量 18 补领地与观战者,当前为 **1 个 header**。
|
||||
增量 17 补副本协议,增量 18 补领地与观战者,增量 39 补 `CG_TEXT`,当前为 **0 个 header**。
|
||||
|
||||
排除仅命名不同、功能已覆盖的别名(`GC_CHAR_ADDITIONAL_INFO`=`GC_CHAR_ADD_INFO`、
|
||||
`GC_TARGET`=`GC_TARGET_INFO`、`GC_TARGET_CREATE_NEW`=`GC_TARGET_CREATE`、
|
||||
@@ -392,13 +432,13 @@
|
||||
`GC_SEPCIAL_EFFECT`=`GC_SPECIAL_EFFECT`、`GC_REFINE_INFORMATION_NEW`=`GC_REFINE_INFO_NEW`),
|
||||
以及 `CG_STATE_CHECKER` / `GC_RESPOND_CHANNELSTATUS`(`project/net/channel_status.gd` 单独实现)
|
||||
和 `CG_HACK`(反作弊,见「不做清单」),
|
||||
**当前主网络层仍缺 1 个 header**(脚本核对 `wire.h` vs `Packet.h`):
|
||||
**当前主网络层仍缺 0 个 header**(脚本核对 `wire.h` vs `Packet.h`):
|
||||
|
||||
### 账号 / 命令通道
|
||||
|
||||
| header | 值 | 说明 |
|
||||
|---|---|---|
|
||||
| `CG_TEXT` | 0x0011 | 简易命令通道(Cube 现借 `CG_CHAT`/`CHAT_TYPE_COMMAND` 实现,非原路径) |
|
||||
| `CG_TEXT` | 0x0011 | 已补 classic wire 尺寸表;这是握手阶段的管理命令,固定前缀为 1 字节,后续文本直到换行由服务端特殊解析。Cube 仍借 `CG_CHAT`/`CHAT_TYPE_COMMAND` 实现,不伪造管理命令入口 |
|
||||
|
||||
### 已声明但待核(不计入上面 1 个)
|
||||
|
||||
@@ -407,8 +447,9 @@
|
||||
自动触发语义、姿态动作链和服务端位置纠偏回包验收。
|
||||
- `CG_SCRIPT_SELECT_ITEM`(0x0903) / `CG_QUEST_CANCEL`(0x0906):wire、发送接口和
|
||||
loopback 字节测试已覆盖;真服单会话已分别发送 8B / 4B,取消触发 1 条 quest-change 且无
|
||||
unknown;基础 `[INPUT]` / Escape 取消已接入,仍缺选道具网格、原版输入校验和有脚本时的任务
|
||||
流程验收。
|
||||
unknown;基础 `[INPUT]` / `[SELECT_ITEM]` / Escape 取消已接入,选择窗的背包魔石网格、
|
||||
grade 过滤和选择回包已由 `select_item_ui_test.gd` 覆盖;仍缺原版输入校验、CJK IME 和有脚本时
|
||||
的真实任务流程验收。
|
||||
- `CG_PARTY_USE_SKILL`(0x0705):wire、发送接口和 loopback 字节测试已覆盖;真服单会话已发送
|
||||
9B 且保持在线 / 无 unknown,但无队伍技能业务回包;仍缺冷却 / 权限、UI 入口和有效队伍真服
|
||||
验收。
|
||||
@@ -420,10 +461,11 @@
|
||||
`M2Client.skill_group_changed` 已覆盖;本轮发现并修复 `GameClient` 未分派该 header 的 bug;
|
||||
仍缺技能组选择 UI、服务端权限和真服推送验收。
|
||||
- `CG_CHANGE_NAME`(0x010B) / `GC_CHANGE_NAME`(0x010C):wire、角色槽位更新、
|
||||
`M2Client.change_name()` 和 `char_name_changed` 已覆盖;仍缺改名 UI、改名卡消耗 / 重名错误
|
||||
文案和真服验收。
|
||||
- `GC_REQUEST_MAKE_GUILD`(0x0731):无负载 wire 解析和 `guild_make_requested` 信号已覆盖;仍缺
|
||||
创建公会窗口、名称校验 / 应答和真服验收。
|
||||
`M2Client.change_name()` 和 `char_name_changed` 已覆盖;选人页按 `change_name` 标志接入
|
||||
原版“点击开始先改名”流程,并对重名 / 非法名错误码给出状态文案;仍缺真服验收。
|
||||
- `GC_REQUEST_MAKE_GUILD`(0x0731):无负载 wire 解析和 `guild_make_requested` 信号已覆盖;公会窗已
|
||||
完成创建窗口、12 字上限、空名 / 控制字符校验和 `CG_ANSWER_MAKE_GUILD` 应答;仍缺服务端敏感词 /
|
||||
重名错误回包、实际建会状态和真服验收。
|
||||
- `CG_SYMBOL_CRC`(0x0723) / `GC_SYMBOL_DATA`(0x0732):16B / 8B wire、`MarkClient` CRC 请求、
|
||||
变长数据接收和 `M2Client.guild_symbol_ready` 已覆盖;仍缺本地符号缓存失效策略、UI 预览和
|
||||
真服验收。
|
||||
@@ -464,7 +506,7 @@
|
||||
|
||||
1. 任务完整标签 / 输入 / 选道具 / 指引;
|
||||
2. 商店 START_EX、仓库密码、商城、私人商店、Cube(拍卖行 m2dev-client 无,除非另起协议);
|
||||
3. 组队角色、公会完整页面、情侣完整交互、领地与公会建筑;
|
||||
3. 组队角色、情侣完整交互、领地与公会建筑;公会完整页面暂缓;
|
||||
4. 钓鱼、副本、坐骑、观战者和 Atlas。
|
||||
|
||||
### M3:UI 与表现 1:1
|
||||
@@ -483,7 +525,7 @@
|
||||
|
||||
## 1:1 完成判据
|
||||
|
||||
一个功能只有同时满足以下条件才标记为完成:
|
||||
一个功能只有同时满足以下条件才标记为完成。当前 40250 阶段对已明确暂缓的真服、公会和龙魂项不作完成承诺:
|
||||
|
||||
1. 原客户端涉及的 CG / GC 包和 sub-header 全部覆盖,并有尺寸断言;
|
||||
2. 数据状态能正确恢复、重连、切图和清理;
|
||||
|
||||
+17
-4
@@ -25,10 +25,15 @@ GODOT="${GODOT:-godot}"
|
||||
ADB="${ADB:-$SDK/platform-tools/adb}"
|
||||
|
||||
case "$CONFIG" in
|
||||
Release) MODE="--export-release" ;;
|
||||
*) MODE="--export-debug" ;;
|
||||
Release)
|
||||
MODE="--export-release"
|
||||
OUT="build/export/mtgodot-poc-release.apk"
|
||||
;;
|
||||
*)
|
||||
MODE="--export-debug"
|
||||
OUT="build/export/mtgodot-poc.apk"
|
||||
;;
|
||||
esac
|
||||
OUT="build/export/mtgodot-poc$([ "$CONFIG" = Release ] && echo -release).apk"
|
||||
|
||||
echo ">> NDK: $ANDROID_NDK_ROOT"
|
||||
echo ">> JDK: $JAVA_HOME"
|
||||
@@ -58,9 +63,17 @@ if [ "$INSTALL" = 1 ]; then
|
||||
echo ">> pushing assets.zip ($(du -h build/export/assets.zip | cut -f1)) -> $DEST"
|
||||
"$ADB" shell mkdir -p /sdcard/Android/data/org.internal.mtgodotpoc/files/ || true
|
||||
"$ADB" push build/export/assets.zip "$DEST"
|
||||
if [ "$CONFIG" != "Release" ]; then
|
||||
echo ">> copying assets.zip into the Debug app user:// directory"
|
||||
# Android 14/15 may deny run-as reading /sdcard even for the app's own
|
||||
# external files directory. Stream through adb instead.
|
||||
"$ADB" shell "run-as org.internal.mtgodotpoc sh -c 'cat > files/assets.zip'" \
|
||||
< build/export/assets.zip
|
||||
fi
|
||||
else
|
||||
echo ">> NOTE: no build/export/assets.zip — run ./pack-assets.sh; the world won't render without it"
|
||||
fi
|
||||
"$ADB" shell am start -n org.internal.mtgodotpoc/com.godot.game.GodotApp
|
||||
"$ADB" shell am start -W -n org.internal.mtgodotpoc/com.godot.game.GodotAppLauncher \
|
||||
-a android.intent.action.MAIN -c android.intent.category.LAUNCHER
|
||||
echo ">> logcat: $ADB logcat -s godot GodotError Godot"
|
||||
fi
|
||||
|
||||
@@ -20,9 +20,13 @@ add_library(mtnet STATIC
|
||||
src/net/net_stream.cpp
|
||||
src/net/entity_store.cpp
|
||||
src/net/mark_image.cpp
|
||||
src/net/classic/classic_stream.cpp # MT_PROTOCOL=classic (40250)
|
||||
src/net/classic/classic_parser.cpp
|
||||
src/net/classic/classic_session.cpp
|
||||
src/net/classic/classic_cipher.cpp
|
||||
)
|
||||
target_include_directories(mtnet PUBLIC src/net)
|
||||
target_link_libraries(mtnet PUBLIC mt3p::sodium mt3p::minilzo)
|
||||
target_link_libraries(mtnet PUBLIC mt3p::sodium mt3p::minilzo mt3p::cryptopp)
|
||||
target_compile_features(mtnet PUBLIC cxx_std_20)
|
||||
|
||||
# --- mtpack: m2dev-fork asset pack reader/writer (libsodium + zstd) ---
|
||||
@@ -60,32 +64,62 @@ if(MT_HOST_BUILD)
|
||||
# real server; dumps entities / points / inventory. Live integration check.
|
||||
add_executable(net_e2e tools/net_e2e.cpp)
|
||||
target_link_libraries(net_e2e PRIVATE mtnet)
|
||||
|
||||
# net_classic_e2e: live 40250 classic handshake -> login -> char select -> PHASE_GAME probe.
|
||||
add_executable(net_classic_e2e tools/net_classic_e2e.cpp)
|
||||
target_link_libraries(net_classic_e2e PRIVATE mtnet)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING AND MT_HOST_BUILD)
|
||||
add_executable(net_cipher_test tests/net_cipher_test.cpp)
|
||||
target_link_libraries(net_cipher_test PRIVATE mtnet)
|
||||
add_test(NAME net.cipher_roundtrip COMMAND net_cipher_test)
|
||||
add_test(NAME net.cipher_roundtrip COMMAND $<TARGET_FILE:net_cipher_test>)
|
||||
|
||||
add_executable(net_loopback_test tests/net_loopback_test.cpp)
|
||||
target_link_libraries(net_loopback_test PRIVATE mtnet)
|
||||
add_test(NAME net.loopback_flow COMMAND net_loopback_test)
|
||||
add_test(NAME net.loopback_flow COMMAND $<TARGET_FILE:net_loopback_test>)
|
||||
|
||||
add_executable(net_entity_test tests/net_entity_test.cpp)
|
||||
target_link_libraries(net_entity_test PRIVATE mtnet)
|
||||
add_test(NAME net.entity_store COMMAND net_entity_test)
|
||||
add_test(NAME net.entity_store COMMAND $<TARGET_FILE:net_entity_test>)
|
||||
|
||||
# 40250 "classic" backend (MT_PROTOCOL=classic).
|
||||
add_executable(net_classic_wire_test tests/net_classic_wire_test.cpp)
|
||||
target_include_directories(net_classic_wire_test PRIVATE src/net)
|
||||
target_compile_features(net_classic_wire_test PRIVATE cxx_std_20)
|
||||
add_test(NAME net.classic_wire COMMAND $<TARGET_FILE:net_classic_wire_test>)
|
||||
|
||||
add_executable(net_classic_stream_test tests/net_classic_stream_test.cpp)
|
||||
target_link_libraries(net_classic_stream_test PRIVATE mtnet)
|
||||
add_test(NAME net.classic_stream COMMAND $<TARGET_FILE:net_classic_stream_test>)
|
||||
|
||||
add_executable(net_classic_session_test tests/net_classic_session_test.cpp)
|
||||
target_link_libraries(net_classic_session_test PRIVATE mtnet)
|
||||
add_test(NAME net.classic_session COMMAND $<TARGET_FILE:net_classic_session_test>)
|
||||
|
||||
add_executable(net_classic_cipher_test tests/net_classic_cipher_test.cpp)
|
||||
target_link_libraries(net_classic_cipher_test PRIVATE mtnet)
|
||||
add_test(NAME net.classic_cipher COMMAND $<TARGET_FILE:net_classic_cipher_test>)
|
||||
|
||||
add_executable(net_classic_encstream_test tests/net_classic_encstream_test.cpp)
|
||||
target_link_libraries(net_classic_encstream_test PRIVATE mtnet)
|
||||
add_test(NAME net.classic_encstream COMMAND $<TARGET_FILE:net_classic_encstream_test>)
|
||||
|
||||
add_executable(net_classic_mark_test tests/net_classic_mark_test.cpp)
|
||||
target_link_libraries(net_classic_mark_test PRIVATE mtnet)
|
||||
add_test(NAME net.classic_mark COMMAND $<TARGET_FILE:net_classic_mark_test>)
|
||||
|
||||
add_executable(net_mark_test tests/net_mark_test.cpp)
|
||||
target_link_libraries(net_mark_test PRIVATE mtnet mt3p::minilzo)
|
||||
add_test(NAME net.guild_mark COMMAND net_mark_test)
|
||||
add_test(NAME net.guild_mark COMMAND $<TARGET_FILE:net_mark_test>)
|
||||
|
||||
add_executable(pack_roundtrip_test tests/pack_roundtrip_test.cpp)
|
||||
target_link_libraries(pack_roundtrip_test PRIVATE mtpack)
|
||||
add_test(NAME pack.roundtrip COMMAND pack_roundtrip_test)
|
||||
add_test(NAME pack.roundtrip COMMAND $<TARGET_FILE:pack_roundtrip_test>)
|
||||
|
||||
add_executable(proto_test tests/proto_test.cpp)
|
||||
target_link_libraries(proto_test PRIVATE mtproto)
|
||||
add_test(NAME proto.item_mob COMMAND proto_test)
|
||||
add_test(NAME proto.item_mob COMMAND $<TARGET_FILE:proto_test>)
|
||||
if(DEFINED ENV{M2_ASSETS})
|
||||
set_tests_properties(proto.item_mob PROPERTIES ENVIRONMENT "M2_ASSETS=$ENV{M2_ASSETS}")
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
// Ported from ClientVS22/source/EterBase/cipher.cpp (Crypto++ 8.4.0).
|
||||
#include "classic_cipher.h"
|
||||
|
||||
#include <cryptopp/cryptlib.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <cryptopp/nbtheory.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/secblock.h>
|
||||
|
||||
#include <cryptopp/dh.h>
|
||||
#include <cryptopp/dh2.h>
|
||||
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/blowfish.h>
|
||||
#include <cryptopp/camellia.h>
|
||||
#include <cryptopp/cast.h>
|
||||
#include <cryptopp/des.h>
|
||||
#include <cryptopp/idea.h>
|
||||
#include <cryptopp/mars.h>
|
||||
#include <cryptopp/rc5.h>
|
||||
#include <cryptopp/rc6.h>
|
||||
#include <cryptopp/seed.h>
|
||||
#include <cryptopp/serpent.h>
|
||||
#include <cryptopp/shacal2.h>
|
||||
#include <cryptopp/tea.h>
|
||||
#include <cryptopp/twofish.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
using namespace CryptoPP;
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
namespace {
|
||||
|
||||
// Block cipher algorithm selector abstract base class.
|
||||
struct BlockCipherAlgorithm {
|
||||
enum {
|
||||
kDefault, // to give more chances to default algorithm
|
||||
kRC6,
|
||||
kMARS,
|
||||
kTwofish,
|
||||
kSerpent,
|
||||
kCAST256,
|
||||
kIDEA,
|
||||
k3DES, // DES-EDE2
|
||||
kCamellia,
|
||||
kSEED,
|
||||
kRC5,
|
||||
kBlowfish,
|
||||
kTEA,
|
||||
kSHACAL2,
|
||||
kMaxAlgorithms
|
||||
};
|
||||
|
||||
BlockCipherAlgorithm() = default;
|
||||
virtual ~BlockCipherAlgorithm() = default;
|
||||
|
||||
static BlockCipherAlgorithm *Pick(int hint);
|
||||
|
||||
virtual int GetBlockSize() const = 0;
|
||||
virtual int GetDefaultKeyLength() const = 0;
|
||||
|
||||
virtual SymmetricCipher *CreateEncoder(const CryptoPP::byte *key, size_t keylen,
|
||||
const CryptoPP::byte *iv) const = 0;
|
||||
virtual SymmetricCipher *CreateDecoder(const CryptoPP::byte *key, size_t keylen,
|
||||
const CryptoPP::byte *iv) const = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct BlockCipherDetail : public BlockCipherAlgorithm {
|
||||
int GetBlockSize() const override { return T::BLOCKSIZE; }
|
||||
int GetDefaultKeyLength() const override { return T::DEFAULT_KEYLENGTH; }
|
||||
|
||||
SymmetricCipher *CreateEncoder(const CryptoPP::byte *key, size_t keylen,
|
||||
const CryptoPP::byte *iv) const override {
|
||||
return new typename CTR_Mode<T>::Encryption(key, keylen, iv);
|
||||
}
|
||||
SymmetricCipher *CreateDecoder(const CryptoPP::byte *key, size_t keylen,
|
||||
const CryptoPP::byte *iv) const override {
|
||||
return new typename CTR_Mode<T>::Decryption(key, keylen, iv);
|
||||
}
|
||||
};
|
||||
|
||||
BlockCipherAlgorithm *BlockCipherAlgorithm::Pick(int hint) {
|
||||
BlockCipherAlgorithm *detail;
|
||||
int selector = hint % kMaxAlgorithms;
|
||||
switch (selector) {
|
||||
case kRC6: detail = new BlockCipherDetail<RC6>(); break;
|
||||
case kMARS: detail = new BlockCipherDetail<MARS>(); break;
|
||||
case kTwofish: detail = new BlockCipherDetail<Twofish>(); break;
|
||||
case kSerpent: detail = new BlockCipherDetail<Serpent>(); break;
|
||||
case kCAST256: detail = new BlockCipherDetail<CAST256>(); break;
|
||||
case kIDEA: detail = new BlockCipherDetail<IDEA>(); break;
|
||||
case k3DES: detail = new BlockCipherDetail<DES_EDE2>(); break;
|
||||
case kCamellia: detail = new BlockCipherDetail<Camellia>(); break;
|
||||
case kSEED: detail = new BlockCipherDetail<SEED>(); break;
|
||||
case kRC5: detail = new BlockCipherDetail<RC5>(); break;
|
||||
case kBlowfish: detail = new BlockCipherDetail<Blowfish>(); break;
|
||||
case kTEA: detail = new BlockCipherDetail<TEA>(); break;
|
||||
case kSHACAL2: detail = new BlockCipherDetail<SHACAL2>(); break;
|
||||
case kDefault:
|
||||
default: detail = new BlockCipherDetail<Twofish>(); break; // default algorithm
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Key agreement scheme abstract class.
|
||||
class KeyAgreement {
|
||||
public:
|
||||
KeyAgreement() = default;
|
||||
virtual ~KeyAgreement() = default;
|
||||
|
||||
virtual size_t Prepare(void *buffer, size_t *length) = 0;
|
||||
virtual bool Agree(size_t agreed_length, const void *buffer, size_t length) = 0;
|
||||
|
||||
const SecByteBlock &shared() const { return shared_; }
|
||||
|
||||
protected:
|
||||
SecByteBlock shared_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
// Crypto++ Unified Diffie-Hellman key agreement scheme.
|
||||
class DH2KeyAgreement : public KeyAgreement {
|
||||
public:
|
||||
DH2KeyAgreement() : dh_(), dh2_(dh_) {}
|
||||
|
||||
size_t Prepare(void *buffer, size_t *length) override;
|
||||
bool Agree(size_t agreed_length, const void *buffer, size_t length) override;
|
||||
|
||||
private:
|
||||
DH dh_;
|
||||
DH2 dh2_;
|
||||
SecByteBlock spriv_key_;
|
||||
SecByteBlock epriv_key_;
|
||||
};
|
||||
|
||||
size_t DH2KeyAgreement::Prepare(void *buffer, size_t *length) {
|
||||
// RFC 5114, 1024-bit MODP Group with 160-bit Prime Order Subgroup.
|
||||
Integer p("0xB10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
|
||||
"9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0"
|
||||
"13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
|
||||
"98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0"
|
||||
"A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
|
||||
"DF1FB2BC2E4A4371");
|
||||
Integer g("0xA4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
|
||||
"D6406CFF14266D31266FEA1E5C41564B777E690F5504F213"
|
||||
"160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
|
||||
"909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A"
|
||||
"D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
|
||||
"855E6EEB22B3B2E5");
|
||||
Integer q("0xF518AA8781A8DF278ABA4E7D64B7CB9D49462353");
|
||||
|
||||
AutoSeededRandomPool rnd;
|
||||
|
||||
dh_.AccessGroupParameters().Initialize(p, q, g);
|
||||
if (!dh_.GetGroupParameters().ValidateGroup(rnd, 3)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = dh_.GetGroupParameters().GetModulus();
|
||||
q = dh_.GetGroupParameters().GetSubgroupOrder();
|
||||
g = dh_.GetGroupParameters().GetGenerator();
|
||||
|
||||
Integer v = ModularExponentiation(g, q, p);
|
||||
if (v != Integer::One()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
spriv_key_.New(dh2_.StaticPrivateKeyLength());
|
||||
epriv_key_.New(dh2_.EphemeralPrivateKeyLength());
|
||||
SecByteBlock spub_key(dh2_.StaticPublicKeyLength());
|
||||
SecByteBlock epub_key(dh2_.EphemeralPublicKeyLength());
|
||||
|
||||
dh2_.GenerateStaticKeyPair(rnd, spriv_key_, spub_key);
|
||||
dh2_.GenerateEphemeralKeyPair(rnd, epriv_key_, epub_key);
|
||||
|
||||
const size_t spub_key_length = spub_key.size();
|
||||
const size_t epub_key_length = epub_key.size();
|
||||
const size_t data_length = spub_key_length + epub_key_length;
|
||||
if (*length < data_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*length = data_length;
|
||||
CryptoPP::byte *buf = (CryptoPP::byte *)buffer;
|
||||
std::memcpy(buf, spub_key.BytePtr(), spub_key_length);
|
||||
std::memcpy(buf + spub_key_length, epub_key.BytePtr(), epub_key_length);
|
||||
|
||||
return dh2_.AgreedValueLength();
|
||||
}
|
||||
|
||||
bool DH2KeyAgreement::Agree(size_t agreed_length, const void *buffer, size_t length) {
|
||||
if (agreed_length != dh2_.AgreedValueLength()) {
|
||||
return false;
|
||||
}
|
||||
const size_t spub_key_length = dh2_.StaticPublicKeyLength();
|
||||
const size_t epub_key_length = dh2_.EphemeralPublicKeyLength();
|
||||
if (length != (spub_key_length + epub_key_length)) {
|
||||
return false;
|
||||
}
|
||||
shared_.New(dh2_.AgreedValueLength());
|
||||
const CryptoPP::byte *buf = (const CryptoPP::byte *)buffer;
|
||||
if (!dh2_.Agree(shared_, spriv_key_, epriv_key_, buf, buf + spub_key_length)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// --------------------------------------------------------------------- ClassicCipher
|
||||
|
||||
ClassicCipher::ClassicCipher() = default;
|
||||
|
||||
ClassicCipher::~ClassicCipher() {
|
||||
clean_up();
|
||||
}
|
||||
|
||||
void ClassicCipher::clean_up() {
|
||||
delete encoder_;
|
||||
encoder_ = nullptr;
|
||||
delete decoder_;
|
||||
decoder_ = nullptr;
|
||||
delete key_agreement_;
|
||||
key_agreement_ = nullptr;
|
||||
activated_ = false;
|
||||
}
|
||||
|
||||
void ClassicCipher::encrypt(void *buffer, size_t length) {
|
||||
if (!activated_ || !encoder_) {
|
||||
return;
|
||||
}
|
||||
encoder_->ProcessData((CryptoPP::byte *)buffer, (const CryptoPP::byte *)buffer, length);
|
||||
}
|
||||
|
||||
void ClassicCipher::decrypt(void *buffer, size_t length) {
|
||||
if (!activated_ || !decoder_) {
|
||||
return;
|
||||
}
|
||||
decoder_->ProcessData((CryptoPP::byte *)buffer, (const CryptoPP::byte *)buffer, length);
|
||||
}
|
||||
|
||||
size_t ClassicCipher::prepare(void *buffer, size_t *length) {
|
||||
assert(key_agreement_ == nullptr);
|
||||
key_agreement_ = new DH2KeyAgreement();
|
||||
size_t agreed_length = key_agreement_->Prepare(buffer, length);
|
||||
if (agreed_length == 0) {
|
||||
delete key_agreement_;
|
||||
key_agreement_ = nullptr;
|
||||
}
|
||||
return agreed_length;
|
||||
}
|
||||
|
||||
bool ClassicCipher::activate(bool polarity, size_t agreed_length, const void *buffer,
|
||||
size_t length) {
|
||||
assert(!activated_);
|
||||
if (!key_agreement_) {
|
||||
return false;
|
||||
}
|
||||
bool result = false;
|
||||
if (key_agreement_->Agree(agreed_length, buffer, length)) {
|
||||
result = set_up(polarity);
|
||||
}
|
||||
// NOTE: unlike the upstream Cipher, we keep key_agreement_ alive until here
|
||||
// only; set_up() reads shared() before we free it below.
|
||||
delete key_agreement_;
|
||||
key_agreement_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ClassicCipher::set_up(bool polarity) {
|
||||
const SecByteBlock &shared = key_agreement_->shared();
|
||||
if (shared.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int hint_0 = shared.BytePtr()[*(shared.BytePtr()) % shared.size()];
|
||||
int hint_1 = shared.BytePtr()[*(shared.BytePtr() + 1) % shared.size()];
|
||||
std::unique_ptr<BlockCipherAlgorithm> algorithm_0(BlockCipherAlgorithm::Pick(hint_0));
|
||||
std::unique_ptr<BlockCipherAlgorithm> algorithm_1(BlockCipherAlgorithm::Pick(hint_1));
|
||||
|
||||
const size_t key_length_0 = algorithm_0->GetDefaultKeyLength();
|
||||
const size_t iv_length_0 = algorithm_0->GetBlockSize();
|
||||
if (shared.size() < key_length_0 || shared.size() < iv_length_0) {
|
||||
return false;
|
||||
}
|
||||
const size_t key_length_1 = algorithm_1->GetDefaultKeyLength();
|
||||
const size_t iv_length_1 = algorithm_1->GetBlockSize();
|
||||
if (shared.size() < key_length_1 || shared.size() < iv_length_1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SecByteBlock key_0(key_length_0), iv_0(iv_length_0);
|
||||
SecByteBlock key_1(key_length_1), iv_1(iv_length_1);
|
||||
|
||||
size_t offset;
|
||||
key_0.Assign(shared, key_length_0);
|
||||
offset = std::min(key_length_0, shared.size() - key_length_1);
|
||||
key_1.Assign(shared.BytePtr() + offset, key_length_1);
|
||||
|
||||
offset = shared.size() - iv_length_0;
|
||||
iv_0.Assign(shared.BytePtr() + offset, iv_length_0);
|
||||
offset = (offset < iv_length_1 ? 0 : offset - iv_length_1);
|
||||
iv_1.Assign(shared.BytePtr() + offset, iv_length_1);
|
||||
|
||||
if (polarity) {
|
||||
encoder_ = algorithm_1->CreateEncoder(key_1, key_1.size(), iv_1);
|
||||
decoder_ = algorithm_0->CreateDecoder(key_0, key_0.size(), iv_0);
|
||||
} else {
|
||||
encoder_ = algorithm_0->CreateEncoder(key_0, key_0.size(), iv_0);
|
||||
decoder_ = algorithm_1->CreateDecoder(key_1, key_1.size(), iv_1);
|
||||
}
|
||||
return encoder_ != nullptr && decoder_ != nullptr;
|
||||
}
|
||||
|
||||
} // namespace mtnet::classic
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
// ClassicCipher — 40250 _IMPROVED_PACKET_ENCRYPTION_ (docs/CLIENT-40250-PORT.md §3).
|
||||
// Ported near-verbatim from ClientVS22/source/EterBase/cipher.{h,cpp}
|
||||
// (Themida / stdafx / logging stripped). Needs Crypto++ 8.4.0 (mt3p::cryptopp).
|
||||
//
|
||||
// Prepare(buf,&len) -> generate this end's DH2 public blob (spub||epub),
|
||||
// returns the agreed-value length (0 = failure).
|
||||
// Activate(polarity, agreed_len, peer_blob, peer_len)
|
||||
// -> DH2 Agree() -> derive 2 hint-selected block ciphers in
|
||||
// CTR mode, wire encoder_/decoder_ per polarity.
|
||||
// **client polarity = true**, server = false.
|
||||
// Encrypt/Decrypt(buf,len) in place, no padding (CTR stream).
|
||||
// activated() flips true only after set_activated(true) (on GC_KEY_AGREEMENT_COMPLETED).
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
namespace CryptoPP {
|
||||
class SymmetricCipher;
|
||||
}
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
class KeyAgreement;
|
||||
|
||||
class ClassicCipher {
|
||||
public:
|
||||
ClassicCipher();
|
||||
~ClassicCipher();
|
||||
|
||||
void clean_up();
|
||||
|
||||
size_t prepare(void *buffer, size_t *length);
|
||||
bool activate(bool polarity, size_t agreed_length, const void *buffer, size_t length);
|
||||
|
||||
void encrypt(void *buffer, size_t length);
|
||||
void decrypt(void *buffer, size_t length);
|
||||
|
||||
bool activated() const { return activated_; }
|
||||
void set_activated(bool value) { activated_ = value; }
|
||||
bool key_ready() const { return encoder_ != nullptr && decoder_ != nullptr; }
|
||||
|
||||
private:
|
||||
bool set_up(bool polarity);
|
||||
|
||||
bool activated_ = false;
|
||||
CryptoPP::SymmetricCipher *encoder_ = nullptr;
|
||||
CryptoPP::SymmetricCipher *decoder_ = nullptr;
|
||||
KeyAgreement *key_agreement_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace mtnet::classic
|
||||
@@ -0,0 +1,297 @@
|
||||
#pragma once
|
||||
// ClassicMarkClient — the 40250 guild-mark side connection.
|
||||
//
|
||||
// The side socket shares the classic handshake framing with the game socket.
|
||||
// The stock mark client sends CG_MARK_LOGIN immediately after the first
|
||||
// GC_HANDSHAKE; depending on the server build, the mark side can then stay
|
||||
// plaintext or continue through the optional DH2/CTR exchange. Its mark
|
||||
// protocol has its own frames:
|
||||
// CG_MARK_* are raw fixed packets (one-byte header),
|
||||
// GC_MARK_IDXLIST/GC_MARK_BLOCK are [header][u32 whole-size] packets, and
|
||||
// GC_SYMBOL_DATA is a normal [header][u16 whole-size] dynamic packet.
|
||||
//
|
||||
// Keeping this separate from MarkClient is intentional: MarkClient implements
|
||||
// the m2dev fork's four-byte frame protocol and those bytes are not accepted by
|
||||
// a stock 40250 mark server.
|
||||
|
||||
#include "classic_stream.h"
|
||||
#include "mark_image.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
class ClassicMarkClient {
|
||||
public:
|
||||
enum class Mode { Download, DownloadSymbol, UploadMark, UploadSymbol };
|
||||
|
||||
ClassicMarkClient(uint32_t handle, uint32_t random_key) : m_handle(handle), m_random_key(random_key) {
|
||||
// GuildMarkDownloader does not use the normal game-side time-sync
|
||||
// response; every handshake is answered by its mark login packet.
|
||||
m_stream.set_time_sync_mode(false);
|
||||
m_stream.on_server_handshake = [this]() { on_server_handshake(); };
|
||||
m_stream.on_cipher_active = [this]() { on_cipher_active(); };
|
||||
m_stream.on_phase = [this](uint8_t phase) { on_phase(phase); };
|
||||
m_stream.on_packet = [this](uint8_t header, const uint8_t *body, uint32_t len) {
|
||||
return on_packet(header, body, len);
|
||||
};
|
||||
m_stream.on_raw_packet = [this](uint8_t header, const uint8_t *packet, size_t available) {
|
||||
return on_raw_packet(header, packet, available);
|
||||
};
|
||||
}
|
||||
|
||||
bool connect(const std::string &host, uint16_t port) {
|
||||
if (!m_valid) {
|
||||
return false;
|
||||
}
|
||||
return m_stream.connect(host, port);
|
||||
}
|
||||
void disconnect() { m_stream.disconnect(); }
|
||||
void process() { m_stream.process(); }
|
||||
|
||||
ClassicStream::State state() const { return m_stream.state(); }
|
||||
const std::string &last_error() const { return m_stream.last_error(); }
|
||||
void set_wire_trace(bool on) { m_stream.set_wire_trace(on); }
|
||||
|
||||
void set_upload_mark(uint32_t guild_id,
|
||||
const uint32_t px[GUILD_MARK_WIDTH * GUILD_MARK_HEIGHT]) {
|
||||
m_mode = Mode::UploadMark;
|
||||
m_up_gid = guild_id;
|
||||
std::memcpy(m_up_mark, px, sizeof(m_up_mark));
|
||||
}
|
||||
|
||||
void set_upload_symbol(uint32_t guild_id, std::vector<uint8_t> bytes) {
|
||||
m_mode = Mode::UploadSymbol;
|
||||
m_up_gid = guild_id;
|
||||
m_up_symbol = std::move(bytes);
|
||||
m_valid = m_up_symbol.size() + sizeof(CGGuildSymbolUpload) <= 0xFFFFu;
|
||||
}
|
||||
|
||||
void set_download_symbol(uint32_t guild_id, uint32_t crc = 0, uint32_t size = 0) {
|
||||
m_mode = Mode::DownloadSymbol;
|
||||
m_symbol_gid = guild_id;
|
||||
m_symbol_crc = crc;
|
||||
m_symbol_size = size;
|
||||
}
|
||||
|
||||
Mode mode() const { return m_mode; }
|
||||
bool complete() const { return m_complete; }
|
||||
bool upload_sent() const { return m_upload_sent; }
|
||||
bool upload_done() const { return m_upload_sent && m_stream.outgoing_pending() == 0; }
|
||||
const MarkImageSet &marks() const { return m_marks; }
|
||||
uint32_t symbol_guild_id() const { return m_symbol_gid; }
|
||||
const std::vector<uint8_t> &symbol_data() const { return m_symbol_data; }
|
||||
|
||||
// Socket-free access is useful for protocol tests and alternative transports.
|
||||
ClassicStream &stream() { return m_stream; }
|
||||
MarkImageSet &marks() { return m_marks; }
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kMaxPacket = 8u * 1024 * 1024;
|
||||
|
||||
static uint16_t read_u16(const uint8_t *p) {
|
||||
return static_cast<uint16_t>(p[0] | (static_cast<uint16_t>(p[1]) << 8));
|
||||
}
|
||||
static uint32_t read_u32(const uint8_t *p) {
|
||||
return static_cast<uint32_t>(p[0]) |
|
||||
(static_cast<uint32_t>(p[1]) << 8) |
|
||||
(static_cast<uint32_t>(p[2]) << 16) |
|
||||
(static_cast<uint32_t>(p[3]) << 24);
|
||||
}
|
||||
static ClassicStream::RawPacketResult raw(ClassicStream::RawPacketStatus status,
|
||||
size_t bytes = 0) {
|
||||
return {status, bytes};
|
||||
}
|
||||
|
||||
void on_server_handshake() {
|
||||
CGMarkLogin p{};
|
||||
p.header = HDR_CG_MARK_LOGIN;
|
||||
p.handle = m_handle;
|
||||
p.random_key = m_random_key;
|
||||
m_login_sent = m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
void on_cipher_active() {
|
||||
// The stock 40250 mark client logs in immediately after the first
|
||||
// GC_HANDSHAKE (while still in PHASE_HANDSHAKE). Do not send a second
|
||||
// login after key agreement: the server has already switched to its
|
||||
// login input processor and would reject the duplicate header.
|
||||
if (!m_login_sent) {
|
||||
on_server_handshake();
|
||||
}
|
||||
}
|
||||
|
||||
void on_phase(uint8_t phase) {
|
||||
if (phase == PHASE_CLOSE) {
|
||||
m_complete = true;
|
||||
return;
|
||||
}
|
||||
if (phase != PHASE_LOGIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mode == Mode::UploadMark) {
|
||||
m_upload_sent = send_upload_mark();
|
||||
m_complete = m_upload_sent;
|
||||
return;
|
||||
}
|
||||
if (m_mode == Mode::UploadSymbol) {
|
||||
m_upload_sent = send_upload_symbol();
|
||||
m_complete = m_upload_sent;
|
||||
return;
|
||||
}
|
||||
if (m_mode == Mode::DownloadSymbol) {
|
||||
CGSymbolCRC p{};
|
||||
p.header = HDR_CG_SYMBOL_CRC;
|
||||
p.guild_id = m_symbol_gid;
|
||||
p.crc = m_symbol_crc;
|
||||
p.size = m_symbol_size;
|
||||
m_symbol_crc_sent = m_stream.send_fixed(&p, sizeof(p));
|
||||
return;
|
||||
}
|
||||
if (!m_idx_requested) {
|
||||
CGMarkIDXList p{HDR_CG_MARK_IDXLIST};
|
||||
m_idx_requested = m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
}
|
||||
|
||||
ClassicStream::RawPacketResult on_raw_packet(uint8_t header, const uint8_t *packet,
|
||||
size_t available) {
|
||||
if (header == HDR_GC_MARK_IDXLIST) {
|
||||
return receive_idxlist(packet, available);
|
||||
}
|
||||
if (header == HDR_GC_MARK_BLOCK) {
|
||||
return receive_block(packet, available);
|
||||
}
|
||||
return raw(ClassicStream::RawPacketStatus::NotHandled);
|
||||
}
|
||||
|
||||
bool on_packet(uint8_t header, const uint8_t *body, uint32_t len) {
|
||||
// ClassicStream has already consumed [header][u16 size] here. The
|
||||
// remaining body starts with guild_id and then the raw symbol bytes.
|
||||
if (header == HDR_GC_SYMBOL_DATA && m_mode == Mode::DownloadSymbol) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
return false;
|
||||
}
|
||||
m_symbol_gid = read_u32(body);
|
||||
m_symbol_data.assign(body + sizeof(uint32_t), body + len);
|
||||
m_complete = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ClassicStream::RawPacketResult receive_idxlist(const uint8_t *packet, size_t available) {
|
||||
if (available < sizeof(GCMarkIDXList)) {
|
||||
return raw(ClassicStream::RawPacketStatus::NeedMore);
|
||||
}
|
||||
const uint32_t total = read_u32(packet + 1);
|
||||
const uint16_t count = read_u16(packet + 5);
|
||||
const size_t expected = sizeof(GCMarkIDXList) + static_cast<size_t>(count) * 4;
|
||||
if (total < expected || total > kMaxPacket) {
|
||||
return raw(ClassicStream::RawPacketStatus::Error);
|
||||
}
|
||||
if (available < total) {
|
||||
return raw(ClassicStream::RawPacketStatus::NeedMore);
|
||||
}
|
||||
|
||||
m_marks.clear();
|
||||
const size_t done = parse_mark_idxlist(packet + sizeof(GCMarkIDXList),
|
||||
total - sizeof(GCMarkIDXList), count, m_marks);
|
||||
if (done != count) {
|
||||
return raw(ClassicStream::RawPacketStatus::Error);
|
||||
}
|
||||
m_needed = m_marks.needed_images();
|
||||
m_next = 0;
|
||||
if (m_needed.empty()) {
|
||||
m_complete = true;
|
||||
} else {
|
||||
send_crclist(m_needed[0]);
|
||||
}
|
||||
return raw(ClassicStream::RawPacketStatus::Consumed, total);
|
||||
}
|
||||
|
||||
ClassicStream::RawPacketResult receive_block(const uint8_t *packet, size_t available) {
|
||||
if (available < sizeof(GCMarkBlock)) {
|
||||
return raw(ClassicStream::RawPacketStatus::NeedMore);
|
||||
}
|
||||
const uint32_t total = read_u32(packet + 1);
|
||||
const uint8_t img_idx = packet[5];
|
||||
const uint32_t count = read_u32(packet + 6);
|
||||
if (total < sizeof(GCMarkBlock) || total > kMaxPacket) {
|
||||
return raw(ClassicStream::RawPacketStatus::Error);
|
||||
}
|
||||
if (available < total) {
|
||||
return raw(ClassicStream::RawPacketStatus::NeedMore);
|
||||
}
|
||||
|
||||
parse_mark_block(packet + sizeof(GCMarkBlock), total - sizeof(GCMarkBlock), img_idx,
|
||||
count, m_marks);
|
||||
++m_next;
|
||||
if (m_next < m_needed.size()) {
|
||||
send_crclist(m_needed[m_next]);
|
||||
} else {
|
||||
m_complete = true;
|
||||
}
|
||||
return raw(ClassicStream::RawPacketStatus::Consumed, total);
|
||||
}
|
||||
|
||||
void send_crclist(int img_idx) {
|
||||
CGMarkCRCList p{};
|
||||
p.header = HDR_CG_MARK_CRCLIST;
|
||||
p.img_idx = static_cast<uint8_t>(img_idx);
|
||||
// All-zero CRCs ask the stock server for every block in the image.
|
||||
m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool send_upload_mark() {
|
||||
CGMarkUpload p{};
|
||||
p.header = HDR_CG_MARK_UPLOAD;
|
||||
p.gid = m_up_gid;
|
||||
std::memcpy(p.image, m_up_mark, sizeof(p.image));
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool send_upload_symbol() {
|
||||
if (m_up_symbol.empty() || !m_valid) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> packet(sizeof(CGGuildSymbolUpload) + m_up_symbol.size());
|
||||
CGGuildSymbolUpload head{};
|
||||
head.header = HDR_CG_GUILD_SYMBOL_UPLOAD;
|
||||
head.size = static_cast<uint16_t>(packet.size());
|
||||
head.guild_id = m_up_gid;
|
||||
std::memcpy(packet.data(), &head, sizeof(head));
|
||||
std::memcpy(packet.data() + sizeof(head), m_up_symbol.data(), m_up_symbol.size());
|
||||
return m_stream.send_dynamic(packet.data(), packet.size());
|
||||
}
|
||||
|
||||
ClassicStream m_stream;
|
||||
uint32_t m_handle = 0;
|
||||
uint32_t m_random_key = 0;
|
||||
bool m_valid = true;
|
||||
bool m_login_sent = false;
|
||||
bool m_idx_requested = false;
|
||||
bool m_complete = false;
|
||||
bool m_upload_sent = false;
|
||||
bool m_symbol_crc_sent = false;
|
||||
|
||||
Mode m_mode = Mode::Download;
|
||||
uint32_t m_up_gid = 0;
|
||||
uint32_t m_up_mark[GUILD_MARK_WIDTH * GUILD_MARK_HEIGHT] = {};
|
||||
std::vector<uint8_t> m_up_symbol;
|
||||
uint32_t m_symbol_gid = 0;
|
||||
uint32_t m_symbol_crc = 0;
|
||||
uint32_t m_symbol_size = 0;
|
||||
std::vector<uint8_t> m_symbol_data;
|
||||
|
||||
MarkImageSet m_marks;
|
||||
std::vector<int> m_needed;
|
||||
size_t m_next = 0;
|
||||
};
|
||||
|
||||
} // namespace mtnet::classic
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
// ClassicParser — 40250 GC packet bodies -> shared world model (EntityStore via
|
||||
// its protocol-neutral mut_* API) + login/char-list state.
|
||||
// Counterpart of EntityStore::apply() for MT_PROTOCOL=classic.
|
||||
// See docs/CLIENT-40250-PORT.md §4 / §5.
|
||||
//
|
||||
// `body` is the bytes AFTER the 1-byte header (for dynamic packets: after the
|
||||
// [header][uint16 size] prefix); `len` its length — i.e. exactly what
|
||||
// ClassicStream::on_packet delivers.
|
||||
|
||||
#include "wire_classic.h"
|
||||
|
||||
#include "../entity_store.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
struct CharSlot {
|
||||
uint32_t id = 0;
|
||||
std::string name;
|
||||
uint8_t job = 0;
|
||||
uint8_t level = 0;
|
||||
uint8_t st = 0, ht = 0, dx = 0, iq = 0;
|
||||
uint32_t play_minutes = 0;
|
||||
int32_t x = 0, y = 0;
|
||||
uint16_t main_part = 0, hair_part = 0;
|
||||
uint8_t skill_group = 0;
|
||||
// Server requires this character to be renamed before entering the game.
|
||||
bool change_name = false;
|
||||
uint32_t guild_id = 0;
|
||||
std::string guild_name;
|
||||
bool empty() const { return id == 0 && name.empty(); }
|
||||
};
|
||||
|
||||
class ClassicParser {
|
||||
public:
|
||||
struct CharEvent {
|
||||
enum Kind { CreateOk, CreateFail, DeleteOk, DeleteFail } kind;
|
||||
int slot = -1;
|
||||
int fail_type = 0;
|
||||
};
|
||||
struct NameEvent {
|
||||
uint32_t pid = 0;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
explicit ClassicParser(EntityStore &world) : m_world(world) {}
|
||||
|
||||
// one GC packet. returns false to abort the connection.
|
||||
bool on_gc(uint8_t header, const uint8_t *body, uint32_t len);
|
||||
|
||||
// --- login / select state ---
|
||||
const std::vector<CharSlot> &char_slots() const { return m_slots; }
|
||||
int slot_count() const { return m_slot_count; } // 3 or 4
|
||||
uint32_t handle() const { return m_handle; }
|
||||
uint32_t random_key() const { return m_random_key; }
|
||||
bool char_list_ready() const { return m_char_list_ready; }
|
||||
const std::string &login_failure() const { return m_login_failure; }
|
||||
uint32_t login_key() const { return m_login_key; } // GC_LOGIN_KEY (auth path)
|
||||
uint8_t empire() const { return m_empire; }
|
||||
std::vector<CharEvent> drain_char_events() {
|
||||
auto v = std::move(m_char_events);
|
||||
m_char_events.clear();
|
||||
return v;
|
||||
}
|
||||
std::vector<NameEvent> drain_name_events() {
|
||||
auto v = std::move(m_name_events);
|
||||
m_name_events.clear();
|
||||
return v;
|
||||
}
|
||||
int drain_guild_make_requests() {
|
||||
int count = m_guild_make_requests;
|
||||
m_guild_make_requests = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
static bool fill(T &t, uint8_t header, const uint8_t *body, uint32_t len) {
|
||||
if (len + 1u < sizeof(T)) {
|
||||
return false;
|
||||
}
|
||||
t.header = header;
|
||||
std::memcpy(reinterpret_cast<uint8_t *>(&t) + 1, body, sizeof(T) - 1);
|
||||
return true;
|
||||
}
|
||||
void parse_login_success(const SimplePlayer *players, const uint32_t *guild_id,
|
||||
const char (*guild_name)[GUILD_NAME_MAX_LEN + 1], int n, uint32_t handle,
|
||||
uint32_t rkey);
|
||||
|
||||
EntityStore &m_world;
|
||||
std::vector<CharSlot> m_slots;
|
||||
int m_slot_count = 0;
|
||||
uint32_t m_handle = 0;
|
||||
uint32_t m_random_key = 0;
|
||||
uint32_t m_login_key = 0;
|
||||
uint8_t m_empire = 0;
|
||||
std::vector<CharEvent> m_char_events;
|
||||
std::vector<NameEvent> m_name_events;
|
||||
int m_guild_make_requests = 0;
|
||||
bool m_char_list_ready = false;
|
||||
std::string m_login_failure;
|
||||
};
|
||||
|
||||
} // namespace mtnet::classic
|
||||
@@ -0,0 +1,947 @@
|
||||
#include "classic_session.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
ClassicSession::ClassicSession() : m_parser(m_world) {
|
||||
m_stream.on_phase = [this](uint8_t p) { on_phase(p); };
|
||||
m_stream.on_packet = [this](uint8_t h, const uint8_t *b, uint32_t l) {
|
||||
return on_packet(h, b, l);
|
||||
};
|
||||
m_stream.on_error = [this](const std::string &e) {
|
||||
m_last_error = e;
|
||||
set_stage(Stage::Failed);
|
||||
if (on_error) {
|
||||
on_error(e);
|
||||
}
|
||||
};
|
||||
m_stream.on_state_change = [this](ClassicStream::State s) {
|
||||
if (s == ClassicStream::State::Offline && m_stage != Stage::Failed &&
|
||||
m_stage != Stage::Offline) {
|
||||
m_last_error = m_stream.last_error().empty() ? "disconnected" : m_stream.last_error();
|
||||
set_stage(Stage::Failed);
|
||||
} else if (s == ClassicStream::State::Online && m_stage == Stage::Connecting) {
|
||||
// handshake packets flow next; stay in Connecting until GC_PHASE(LOGIN)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void ClassicSession::set_stage(Stage s) {
|
||||
if (m_stage == s) {
|
||||
return;
|
||||
}
|
||||
m_stage = s;
|
||||
if (on_stage_change) {
|
||||
on_stage_change(s);
|
||||
}
|
||||
}
|
||||
|
||||
void ClassicSession::start_offline(const std::string &id, const std::string &pw) {
|
||||
m_id = id;
|
||||
m_pw = pw;
|
||||
m_login_sent = false;
|
||||
m_entergame_sent = false;
|
||||
m_version_sent = false;
|
||||
m_last_error.clear();
|
||||
// During PHASE_HANDSHAKE even retry handshakes must be answered with
|
||||
// 0xFF. The normal game client switches to CG_TIME_SYNC only after the
|
||||
// server has advanced into the login/select side of the connection.
|
||||
m_stream.set_time_sync_mode(false);
|
||||
set_stage(Stage::Connecting);
|
||||
}
|
||||
|
||||
bool ClassicSession::connect(const std::string &game_host, uint16_t game_port,
|
||||
const std::string &id, const std::string &pw) {
|
||||
start_offline(id, pw);
|
||||
if (!m_stream.connect(game_host, game_port)) {
|
||||
m_last_error = m_stream.last_error();
|
||||
set_stage(Stage::Failed);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassicSession::disconnect() {
|
||||
m_stream.disconnect();
|
||||
set_stage(Stage::Offline);
|
||||
}
|
||||
|
||||
void ClassicSession::pump() {
|
||||
m_stream.process();
|
||||
// auto CG_ENTERGAME a beat into PHASE_LOADING (the m2dev client waits ~1.5s;
|
||||
// sending it too early makes the server drop us mid spawn-burst).
|
||||
if (m_stage == Stage::Loading && !m_entergame_sent && m_entergame_delay > 0 &&
|
||||
m_now - m_loading_since >= m_entergame_delay) {
|
||||
enter_game();
|
||||
}
|
||||
m_world.tick();
|
||||
}
|
||||
|
||||
void ClassicSession::send_login() {
|
||||
if (m_login_sent) {
|
||||
return;
|
||||
}
|
||||
// docs §2.2: the client turns sequence mode on around here; CG_LOGIN is a
|
||||
// bSeq packet and the server expects the trailing byte from packet #1.
|
||||
m_stream.set_sequence_mode(true);
|
||||
|
||||
CGLogin p{};
|
||||
p.header = HDR_CG_LOGIN;
|
||||
std::strncpy(p.login, m_id.c_str(), sizeof(p.login) - 1);
|
||||
std::strncpy(p.passwd, m_pw.c_str(), sizeof(p.passwd) - 1);
|
||||
m_stream.send_fixed(&p, sizeof(p));
|
||||
m_login_sent = true;
|
||||
}
|
||||
|
||||
bool ClassicSession::create_character(int slot, const std::string &name, int job, int shape,
|
||||
int con, int intel, int str, int dex) {
|
||||
if (m_stage != Stage::CharSelect || slot < 0 || slot >= m_parser.slot_count() ||
|
||||
name.empty() || name.size() > CHARACTER_NAME_MAX_LEN) {
|
||||
return false;
|
||||
}
|
||||
CGPlayerCreate p{};
|
||||
p.header = HDR_CG_CHARACTER_CREATE;
|
||||
p.index = static_cast<uint8_t>(slot);
|
||||
std::memcpy(p.name, name.data(), name.size());
|
||||
p.job = static_cast<uint16_t>(job);
|
||||
p.shape = static_cast<uint8_t>(shape);
|
||||
p.con = static_cast<uint8_t>(con);
|
||||
p.intel = static_cast<uint8_t>(intel);
|
||||
p.str = static_cast<uint8_t>(str);
|
||||
p.dex = static_cast<uint8_t>(dex);
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::delete_character(int slot, const std::string &private_code) {
|
||||
if (m_stage != Stage::CharSelect || slot < 0 || slot >= m_parser.slot_count() ||
|
||||
private_code.size() > PRIVATE_CODE_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
CGPlayerDelete p{};
|
||||
p.header = HDR_CG_CHARACTER_DELETE;
|
||||
p.index = static_cast<uint8_t>(slot);
|
||||
std::memcpy(p.private_code, private_code.data(), private_code.size());
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::change_name(int slot, const std::string &name) {
|
||||
if (m_stage != Stage::CharSelect || slot < 0 || slot >= m_parser.slot_count() ||
|
||||
name.empty() || name.size() > CHARACTER_NAME_MAX_LEN) {
|
||||
return false;
|
||||
}
|
||||
CGChangeName p{};
|
||||
p.header = HDR_CG_CHANGE_NAME;
|
||||
p.index = static_cast<uint8_t>(slot);
|
||||
std::memcpy(p.name, name.data(), name.size());
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_empire(uint8_t empire) {
|
||||
if (m_stage != Stage::CharSelect) {
|
||||
return false;
|
||||
}
|
||||
CGEmpire p{HDR_CG_EMPIRE, empire};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_client_version() {
|
||||
if (m_version_sent) {
|
||||
return true;
|
||||
}
|
||||
CGClientVersion p{};
|
||||
// The stock 40250 game client sends the version report immediately after
|
||||
// receiving the main-character packet and advances the normal CG sequence.
|
||||
p.header = HDR_CG_CLIENT_VERSION;
|
||||
std::strncpy(p.filename, "metin2.bin", sizeof(p.filename) - 1);
|
||||
std::strncpy(p.timestamp, "1215955205", sizeof(p.timestamp) - 1);
|
||||
if (!m_stream.send_fixed(&p, sizeof(p))) {
|
||||
return false;
|
||||
}
|
||||
m_version_sent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassicSession::on_phase(uint8_t phase) {
|
||||
if (phase == PHASE_LOGIN || phase == PHASE_SELECT || phase == PHASE_LOADING ||
|
||||
phase == PHASE_GAME || phase == PHASE_DEAD) {
|
||||
m_stream.set_time_sync_mode(true);
|
||||
}
|
||||
switch (phase) {
|
||||
case PHASE_LOGIN:
|
||||
set_stage(Stage::LoggingIn);
|
||||
send_login();
|
||||
break;
|
||||
case PHASE_SELECT:
|
||||
set_stage(Stage::CharSelect);
|
||||
if (m_parser.char_list_ready() && on_char_list) {
|
||||
on_char_list(m_parser.char_slots());
|
||||
}
|
||||
break;
|
||||
case PHASE_LOADING:
|
||||
set_stage(Stage::Loading);
|
||||
m_loading_since = m_now;
|
||||
m_entergame_sent = false;
|
||||
break;
|
||||
case PHASE_GAME:
|
||||
set_stage(Stage::InGame);
|
||||
if (on_entered_game) {
|
||||
on_entered_game();
|
||||
}
|
||||
break;
|
||||
case PHASE_CLOSE:
|
||||
m_last_error = m_last_error.empty() ? "server closed phase" : m_last_error;
|
||||
set_stage(Stage::Failed);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicSession::on_packet(uint8_t header, const uint8_t *body, uint32_t len) {
|
||||
if (!m_parser.on_gc(header, body, len)) {
|
||||
m_last_error = "parse error on GC header " + std::to_string(header);
|
||||
return false;
|
||||
}
|
||||
if ((header == HDR_GC_MAIN_CHARACTER || header == HDR_GC_MAIN_CHARACTER3_BGM ||
|
||||
header == HDR_GC_MAIN_CHARACTER4_BGM_VOL) && !send_client_version()) {
|
||||
m_last_error = "failed to send client version";
|
||||
return false;
|
||||
}
|
||||
if (!m_parser.login_failure().empty() && m_stage == Stage::LoggingIn) {
|
||||
m_last_error = "login failed: " + m_parser.login_failure();
|
||||
set_stage(Stage::Failed);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassicSession::select_char(int slot) {
|
||||
// Caller-side misuse: return false but don't poison m_last_error, which is
|
||||
// reserved for transport/protocol failures ("is the connection healthy").
|
||||
if (m_stage != Stage::CharSelect) {
|
||||
return false;
|
||||
}
|
||||
if (slot < 0 || slot >= m_parser.slot_count()) {
|
||||
return false;
|
||||
}
|
||||
CGPlayerSelect p{};
|
||||
p.header = HDR_CG_CHARACTER_SELECT;
|
||||
p.player_index = static_cast<uint8_t>(slot);
|
||||
m_stream.send_fixed(&p, sizeof(p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassicSession::enter_game() {
|
||||
if (m_entergame_sent) {
|
||||
return true;
|
||||
}
|
||||
CGEnterGame p{};
|
||||
p.header = HDR_CG_ENTERGAME;
|
||||
m_stream.send_fixed(&p, sizeof(p));
|
||||
m_entergame_sent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- in-game intents ---------------------------------------------------------
|
||||
|
||||
bool ClassicSession::send_move(uint8_t func, uint8_t arg, float rot_deg, int32_t x, int32_t y,
|
||||
uint32_t time_ms) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGMove p{};
|
||||
p.header = HDR_CG_MOVE;
|
||||
p.func = func;
|
||||
p.arg = arg;
|
||||
p.rot = static_cast<uint8_t>(rot_deg / 5.0f); // classic wire: bRot = deg/5
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
p.time = time_ms ? time_ms : m_now;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_attack(uint8_t type, uint32_t victim_vid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGAttack p{};
|
||||
p.header = HDR_CG_ATTACK;
|
||||
p.type = type;
|
||||
p.victim_vid = victim_vid;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_chat(uint8_t type, const std::string &text) {
|
||||
if (m_stage != Stage::InGame || text.empty() || text.size() >= 512) {
|
||||
return false;
|
||||
}
|
||||
// wire: [header][WORD length][BYTE type][text\0]; length = 4 + strlen + 1
|
||||
const uint16_t len = static_cast<uint16_t>(sizeof(CGChatHead) + text.size() + 1);
|
||||
std::vector<uint8_t> buf(len, 0);
|
||||
CGChatHead h{};
|
||||
h.header = HDR_CG_CHAT;
|
||||
h.length = len;
|
||||
h.type = type;
|
||||
std::memcpy(buf.data(), &h, sizeof(h));
|
||||
std::memcpy(buf.data() + sizeof(h), text.data(), text.size());
|
||||
// buf[len-1] stays 0 (the trailing NUL)
|
||||
return m_stream.send_dynamic(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_target(uint32_t vid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGTarget p{HDR_CG_TARGET, vid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_character_position(uint8_t position) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGPosition p{HDR_CG_CHARACTER_POSITION, position};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_click_npc(uint32_t vid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGOnClick p{HDR_CG_ON_CLICK, vid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_script_answer(uint8_t answer) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGScriptAnswer p{HDR_CG_SCRIPT_ANSWER, answer};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_item_use(uint8_t window, uint16_t cell) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGItemUse p{};
|
||||
p.header = HDR_CG_ITEM_USE;
|
||||
p.pos = {window, cell};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_item_move(uint8_t window, uint16_t cell, uint8_t to_window,
|
||||
uint16_t to_cell, uint8_t count) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGItemMove p{};
|
||||
p.header = HDR_CG_ITEM_MOVE;
|
||||
p.pos = {window, cell};
|
||||
p.change_pos = {to_window, to_cell};
|
||||
p.num = count;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_item_drop(uint8_t window, uint16_t cell, uint32_t gold, uint8_t count) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
if (count > 0) {
|
||||
CGItemDrop2 p{};
|
||||
p.header = HDR_CG_ITEM_DROP2;
|
||||
p.pos = {window, cell};
|
||||
p.gold = gold;
|
||||
p.count = count;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
CGItemDrop p{};
|
||||
p.header = HDR_CG_ITEM_DROP;
|
||||
p.pos = {window, cell};
|
||||
p.gold = gold;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_item_pickup(uint32_t ground_vid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGItemPickup p{HDR_CG_ITEM_PICKUP, ground_vid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_use_skill(uint32_t skill_vnum, uint32_t target_vid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
if (target_vid) {
|
||||
CGFlyTargeting ft{};
|
||||
ft.header = HDR_CG_FLY_TARGETING;
|
||||
ft.target_vid = target_vid;
|
||||
m_stream.send_fixed(&ft, sizeof(ft));
|
||||
}
|
||||
CGUseSkill p{};
|
||||
p.header = HDR_CG_USE_SKILL;
|
||||
p.vnum = skill_vnum;
|
||||
p.target_vid = target_vid;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_item_use_to_item(uint8_t window, uint16_t cell, uint8_t target_window,
|
||||
uint16_t target_cell) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGItemUseToItem p{};
|
||||
p.header = HDR_CG_ITEM_USE_TO_ITEM;
|
||||
p.pos = {window, cell};
|
||||
p.target_pos = {target_window, target_cell};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_item_give(uint32_t target_vid, uint8_t window, uint16_t cell,
|
||||
uint8_t count) {
|
||||
if (m_stage != Stage::InGame || target_vid == 0 || count == 0) {
|
||||
return false;
|
||||
}
|
||||
CGGiveItem p{};
|
||||
p.header = HDR_CG_ITEM_GIVE;
|
||||
p.target_vid = target_vid;
|
||||
p.pos = {window, cell};
|
||||
p.count = count;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_request_warp() {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGWarp p{HDR_CG_WARP};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_fishing(uint8_t rot) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGFishing p{HDR_CG_FISHING, rot};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_dungeon() {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGWarp p{HDR_CG_DUNGEON};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_sync_positions(const std::vector<SyncPosElement> &positions) {
|
||||
if (m_stage != Stage::InGame || positions.empty() || positions.size() > 16) {
|
||||
return false;
|
||||
}
|
||||
const size_t total = sizeof(DynHead) + positions.size() * sizeof(SyncPosElement);
|
||||
if (total > 0xffffu) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> buf(total, 0);
|
||||
DynHead head{HDR_CG_SYNC_POSITION, static_cast<uint16_t>(total)};
|
||||
std::memcpy(buf.data(), &head, sizeof(head));
|
||||
std::memcpy(buf.data() + sizeof(head), positions.data(),
|
||||
positions.size() * sizeof(SyncPosElement));
|
||||
return m_stream.send_fixed(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_whisper(const std::string &to, const std::string &text) {
|
||||
if (m_stage != Stage::InGame || to.empty() || to.size() > CHARACTER_NAME_MAX_LEN ||
|
||||
text.empty() || text.size() >= 512) {
|
||||
return false;
|
||||
}
|
||||
const size_t total = sizeof(CGWhisper) + text.size() + 1;
|
||||
if (total > 0xffffu) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> buf(total, 0);
|
||||
CGWhisper p{};
|
||||
p.header = HDR_CG_WHISPER;
|
||||
p.size = static_cast<uint16_t>(total);
|
||||
std::memcpy(p.name_to, to.data(), to.size());
|
||||
std::memcpy(buf.data(), &p, sizeof(p));
|
||||
std::memcpy(buf.data() + sizeof(p), text.data(), text.size());
|
||||
return m_stream.send_dynamic(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_shoot(uint8_t type) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGShoot p{HDR_CG_SHOOT, type};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_add_fly_targeting(uint32_t target_vid, int32_t x, int32_t y) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGFlyTargeting p{HDR_CG_ADD_FLY_TARGETING, target_vid, x, y};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_quickslot_add(uint8_t pos, uint8_t type, uint8_t ref) {
|
||||
if (m_stage != Stage::InGame || pos >= QUICKSLOT_MAX_NUM) {
|
||||
return false;
|
||||
}
|
||||
CGQuickSlotAdd p{HDR_CG_QUICKSLOT_ADD, pos, type, ref};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_quickslot_del(uint8_t pos) {
|
||||
if (m_stage != Stage::InGame || pos >= QUICKSLOT_MAX_NUM) {
|
||||
return false;
|
||||
}
|
||||
CGQuickSlotDel p{HDR_CG_QUICKSLOT_DEL, pos};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_quickslot_swap(uint8_t pos, uint8_t pos_to) {
|
||||
if (m_stage != Stage::InGame || pos >= QUICKSLOT_MAX_NUM || pos_to >= QUICKSLOT_MAX_NUM) {
|
||||
return false;
|
||||
}
|
||||
CGQuickSlotSwap p{HDR_CG_QUICKSLOT_SWAP, pos, pos_to};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_script_button(uint32_t index) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGScriptButton p{HDR_CG_SCRIPT_BUTTON, index};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_script_select_item(uint32_t selection) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGScriptSelectItem p{HDR_CG_SCRIPT_SELECT_ITEM, selection};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_quest_input(const std::string &text) {
|
||||
if (m_stage != Stage::InGame || text.size() > 64) {
|
||||
return false;
|
||||
}
|
||||
CGQuestInput p{};
|
||||
p.header = HDR_CG_QUEST_INPUT_STRING;
|
||||
std::memcpy(p.msg, text.data(), text.size());
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_quest_confirm(bool yes, uint32_t request_pid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGQuestConfirm p{HDR_CG_QUEST_CONFIRM, static_cast<uint8_t>(yes ? 1 : 0), request_pid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_quest_cancel() {
|
||||
// The classic protocol has no separate CG_QUEST_CANCEL header. The stock
|
||||
// client closes a script by answering 255 (the same sentinel exposed by
|
||||
// script_answer()) and the server routes it through ScriptAnswer.
|
||||
return send_script_answer(255);
|
||||
}
|
||||
|
||||
bool ClassicSession::send_party_invite(uint32_t vid) {
|
||||
if (m_stage != Stage::InGame || vid == 0) return false;
|
||||
CGPartyInvite p{HDR_CG_PARTY_INVITE, vid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_party_answer(uint32_t leader_vid, bool accept) {
|
||||
if (m_stage != Stage::InGame || leader_vid == 0) return false;
|
||||
CGPartyInviteAnswer p{HDR_CG_PARTY_INVITE_ANSWER, leader_vid,
|
||||
static_cast<uint8_t>(accept ? 1 : 0)};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_party_remove(uint32_t pid) {
|
||||
if (m_stage != Stage::InGame || pid == 0) return false;
|
||||
CGPartyRemove p{HDR_CG_PARTY_REMOVE, pid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_party_set_state(uint32_t pid, uint8_t role, bool on) {
|
||||
if (m_stage != Stage::InGame || pid == 0) return false;
|
||||
CGPartySetState p{HDR_CG_PARTY_SET_STATE, pid, role, static_cast<uint8_t>(on ? 1 : 0)};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_party_use_skill(uint8_t skill_index, uint32_t vid) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
CGPartyUseSkill p{HDR_CG_PARTY_USE_SKILL, skill_index, vid};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_party_parameter(uint8_t mode) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
CGPartyParameter p{HDR_CG_PARTY_PARAMETER, mode};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_shop_buy(uint8_t pos, uint8_t count) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
uint8_t buf[sizeof(CGShop) + sizeof(CGShopBuy)] = {};
|
||||
CGShop head{HDR_CG_SHOP, SHOP_CG_BUY};
|
||||
CGShopBuy body{count, pos};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &body, sizeof(body));
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_shop_sell(uint8_t pos, uint8_t count) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
if (count == 0) {
|
||||
uint8_t buf[sizeof(CGShop) + sizeof(CGShopSell)] = {};
|
||||
CGShop head{HDR_CG_SHOP, SHOP_CG_SELL};
|
||||
CGShopSell body{pos};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &body, sizeof(body));
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
uint8_t buf[sizeof(CGShop) + sizeof(CGShopSell2)] = {};
|
||||
CGShop head{HDR_CG_SHOP, SHOP_CG_SELL2};
|
||||
CGShopSell2 body{pos, count};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &body, sizeof(body));
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_shop_close() {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
CGShop p{HDR_CG_SHOP, SHOP_CG_END};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_private_shop(const std::string &sign,
|
||||
const std::vector<CGMyShopItem> &items) {
|
||||
if (m_stage != Stage::InGame || sign.size() > SHOP_TAB_NAME_MAX) return false;
|
||||
const size_t n = std::min(items.size(), static_cast<size_t>(39));
|
||||
const size_t total = sizeof(CGMyShopHead) + n * sizeof(CGMyShopItem);
|
||||
std::vector<uint8_t> buf(total, 0);
|
||||
CGMyShopHead head{};
|
||||
head.header = HDR_CG_MYSHOP;
|
||||
std::memcpy(head.sign, sign.data(), sign.size());
|
||||
head.count = static_cast<uint8_t>(n);
|
||||
std::memcpy(buf.data(), &head, sizeof(head));
|
||||
if (n > 0) {
|
||||
std::memcpy(buf.data() + sizeof(head), items.data(), n * sizeof(CGMyShopItem));
|
||||
}
|
||||
return m_stream.send_fixed(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_make(int result_index) {
|
||||
// 40250's command handler ignores a recipe index: an argument means
|
||||
// "make all" while no argument makes one recipe from the currently loaded
|
||||
// cube slots. Never send the index through as it would unexpectedly craft
|
||||
// repeatedly and consume every matching material.
|
||||
(void)result_index;
|
||||
return send_chat(0, "/cube make");
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_result_list(int npc_vnum) {
|
||||
(void)npc_vnum; // 40250 derives the active NPC from the selected cube context.
|
||||
return send_chat(0, "/cube r_info");
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_materials(int start_index, int count) {
|
||||
if (start_index < 0 || count < 1 || start_index > 255 || count > 255) return false;
|
||||
return send_chat(0, "/cube r_info " + std::to_string(start_index) + " " +
|
||||
std::to_string(count));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_open() {
|
||||
return send_chat(0, "/cube open");
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_close() {
|
||||
return send_chat(0, "/cube close");
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_list() {
|
||||
return send_chat(0, "/cube list");
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_add_item(int cube_index, int inventory_index) {
|
||||
if (cube_index < 0 || cube_index >= 24 || inventory_index < 0 || inventory_index > 255) {
|
||||
return false;
|
||||
}
|
||||
return send_chat(0, "/cube add " + std::to_string(cube_index) + " " +
|
||||
std::to_string(inventory_index));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_cube_delete_item(int cube_index) {
|
||||
if (cube_index < 0 || cube_index >= 24) return false;
|
||||
return send_chat(0, "/cube delete " + std::to_string(cube_index));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_sub(uint8_t subheader) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
CGGuild p{HDR_CG_GUILD, subheader};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_u32(uint8_t subheader, uint32_t value) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(value)] = {};
|
||||
CGGuild head{HDR_CG_GUILD, subheader};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &value, sizeof(value));
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_i32(uint8_t subheader, int32_t value) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(value)] = {};
|
||||
CGGuild head{HDR_CG_GUILD, subheader};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &value, sizeof(value));
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_grade_name(uint8_t grade, const std::string &name) {
|
||||
if (m_stage != Stage::InGame || grade == 0 || grade >= 16 || name.empty() ||
|
||||
name.size() > GUILD_GRADE_NAME_MAX_LEN) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + 1 + GUILD_GRADE_NAME_MAX_LEN + 1] = {};
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_CHANGE_GRADE_NAME};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
buf[sizeof(head)] = grade;
|
||||
std::memcpy(buf + sizeof(head) + 1, name.data(), name.size());
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_grade_authority(uint8_t grade, uint8_t authority) {
|
||||
if (m_stage != Stage::InGame || grade == 0 || grade >= 16) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + 2] = {};
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_CHANGE_GRADE_AUTHORITY};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
buf[sizeof(head)] = grade;
|
||||
buf[sizeof(head) + 1] = authority;
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_member_grade(uint32_t pid, uint8_t grade) {
|
||||
if (m_stage != Stage::InGame || pid == 0 || grade == 0 || grade >= 16) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(pid) + 1] = {};
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_CHANGE_MEMBER_GRADE};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &pid, sizeof(pid));
|
||||
buf[sizeof(head) + sizeof(pid)] = grade;
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_member_general(uint32_t pid, bool enabled) {
|
||||
if (m_stage != Stage::InGame || pid == 0) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(pid) + 1] = {};
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_CHANGE_MEMBER_GENERAL};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &pid, sizeof(pid));
|
||||
buf[sizeof(head) + sizeof(pid)] = enabled ? 1 : 0;
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_comment(const std::string &text) {
|
||||
if (m_stage != Stage::InGame || text.empty() ||
|
||||
text.size() + 1 > GUILD_COMMENT_MAX_LEN) return false;
|
||||
std::vector<uint8_t> buf(sizeof(CGGuild) + 1 + text.size() + 1, 0);
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_POST_COMMENT};
|
||||
std::memcpy(buf.data(), &head, sizeof(head));
|
||||
buf[sizeof(head)] = static_cast<uint8_t>(text.size() + 1);
|
||||
if (!text.empty()) {
|
||||
std::memcpy(buf.data() + sizeof(head) + 1, text.data(), text.size());
|
||||
}
|
||||
return m_stream.send_fixed(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_invite_answer(uint32_t guild_id, bool accept) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(guild_id) + 1] = {};
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_GUILD_INVITE_ANSWER};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &guild_id, sizeof(guild_id));
|
||||
buf[sizeof(head) + sizeof(guild_id)] = accept ? 1 : 0;
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_answer_make(const std::string &name) {
|
||||
if (m_stage != Stage::InGame || name.empty() || name.size() > GUILD_NAME_MAX_LEN) return false;
|
||||
CGAnswerMakeGuild p{};
|
||||
p.header = HDR_CG_ANSWER_MAKE_GUILD;
|
||||
std::memcpy(p.guild_name, name.data(), name.size());
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_guild_skill(uint32_t skill_vnum, uint32_t target_pid) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(CGGuildUseSkill)] = {};
|
||||
CGGuild head{HDR_CG_GUILD, GUILD_CG_USE_SKILL};
|
||||
CGGuildUseSkill body{skill_vnum, target_pid};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &body, sizeof(body));
|
||||
return m_stream.send_fixed(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_refine(uint8_t pos, uint8_t type) {
|
||||
if (m_stage != Stage::InGame) return false;
|
||||
CGRefine p{HDR_CG_REFINE, pos, type};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_dragon_soul_refine(uint8_t subheader, const ItemPos *grid, size_t count) {
|
||||
if (m_stage != Stage::InGame || !grid || count > DS_REFINE_GRID_MAX_NUM) return false;
|
||||
CGDragonSoulRefine p{};
|
||||
p.header = HDR_CG_DRAGON_SOUL_REFINE;
|
||||
p.subheader = subheader;
|
||||
std::memcpy(p.grid, grid, count * sizeof(ItemPos));
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool valid_character_name(const std::string &name) {
|
||||
return !name.empty() && name.size() < CHARACTER_NAME_MAX_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicSession::send_friend_add(const std::string &name) {
|
||||
if (m_stage != Stage::InGame || !valid_character_name(name)) {
|
||||
return false;
|
||||
}
|
||||
// Messenger's extra payload is not length-prefixed: the server consumes a
|
||||
// fixed CHARACTER_NAME_MAX_LEN byte buffer after the two-byte base header.
|
||||
std::vector<uint8_t> buf(sizeof(CGMessenger) + CHARACTER_NAME_MAX_LEN, 0);
|
||||
CGMessenger p{HDR_CG_MESSENGER, MESSENGER_CG_ADD_BY_NAME};
|
||||
std::memcpy(buf.data(), &p, sizeof(p));
|
||||
std::memcpy(buf.data() + sizeof(p), name.data(), name.size());
|
||||
return m_stream.send_fixed(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_friend_remove(const std::string &name) {
|
||||
if (m_stage != Stage::InGame || !valid_character_name(name)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> buf(sizeof(CGMessenger) + CHARACTER_NAME_MAX_LEN, 0);
|
||||
CGMessenger p{HDR_CG_MESSENGER, MESSENGER_CG_REMOVE};
|
||||
std::memcpy(buf.data(), &p, sizeof(p));
|
||||
std::memcpy(buf.data() + sizeof(p), name.data(), name.size());
|
||||
return m_stream.send_fixed(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool ClassicSession::send_exchange_start(uint32_t partner_vid) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGExchange p{};
|
||||
p.header = HDR_CG_EXCHANGE;
|
||||
p.subheader = EXCHANGE_CG_START;
|
||||
p.arg1 = partner_vid;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_exchange_item_add(uint8_t inv_window, uint16_t inv_cell,
|
||||
uint8_t display_pos) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGExchange p{};
|
||||
p.header = HDR_CG_EXCHANGE;
|
||||
p.subheader = EXCHANGE_CG_ITEM_ADD;
|
||||
p.arg2 = display_pos;
|
||||
p.pos = {inv_window, inv_cell};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_exchange_gold(uint32_t gold) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGExchange p{};
|
||||
p.header = HDR_CG_EXCHANGE;
|
||||
p.subheader = EXCHANGE_CG_GOLD_ADD;
|
||||
p.arg1 = gold;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_exchange_accept() {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGExchange p{};
|
||||
p.header = HDR_CG_EXCHANGE;
|
||||
p.subheader = EXCHANGE_CG_ACCEPT;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_exchange_cancel() {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGExchange p{};
|
||||
p.header = HDR_CG_EXCHANGE;
|
||||
p.subheader = EXCHANGE_CG_CANCEL;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_safebox_checkin(uint8_t safe_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGSafeboxCheckin p{};
|
||||
p.header = HDR_CG_SAFEBOX_CHECKIN;
|
||||
p.safe_pos = safe_pos;
|
||||
p.item_pos = {inv_window, inv_cell};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_safebox_checkout(uint8_t safe_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGSafeboxCheckout p{};
|
||||
p.header = HDR_CG_SAFEBOX_CHECKOUT;
|
||||
p.safe_pos = safe_pos;
|
||||
p.item_pos = {inv_window, inv_cell};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_safebox_move(uint16_t from_cell, uint16_t to_cell, uint8_t count) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGItemMove p{};
|
||||
p.header = HDR_CG_SAFEBOX_ITEM_MOVE;
|
||||
// The stock 40250 client sends inventory-window positions; the active
|
||||
// safebox context tells the server these positions refer to the safebox.
|
||||
p.pos = {WINDOW_INVENTORY, from_cell};
|
||||
p.change_pos = {WINDOW_INVENTORY, to_cell};
|
||||
p.num = count;
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
bool ClassicSession::send_mall_checkout(uint8_t mall_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) {
|
||||
if (m_stage != Stage::InGame) {
|
||||
return false;
|
||||
}
|
||||
CGMallCheckout p{};
|
||||
p.header = HDR_CG_MALL_CHECKOUT;
|
||||
p.mall_pos = mall_pos;
|
||||
p.item_pos = {inv_window, inv_cell};
|
||||
return m_stream.send_fixed(&p, sizeof(p));
|
||||
}
|
||||
|
||||
} // namespace mtnet::classic
|
||||
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
// ClassicSession — MT_PROTOCOL=classic: drives ClassicStream through
|
||||
// HANDSHAKE -> LOGIN -> SELECT -> LOADING -> GAME and parses GC packets into a
|
||||
// shared EntityStore. See docs/CLIENT-40250-PORT.md §4 / §8.
|
||||
//
|
||||
// The stock 40250 server uses _IMPROVED_PACKET_ENCRYPTION_; ClassicStream
|
||||
// negotiates that DH2 + CTR session before the first login packet.
|
||||
|
||||
#include "classic_parser.h"
|
||||
#include "classic_stream.h"
|
||||
#include "wire_classic.h"
|
||||
|
||||
#include "../entity_store.h"
|
||||
#include "../i_net_session.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
class ClassicSession : public mtnet::INetSession {
|
||||
public:
|
||||
ClassicSession();
|
||||
|
||||
// --- INetSession ---
|
||||
bool connect(const std::string &game_host, uint16_t game_port, const std::string &id,
|
||||
const std::string &pw) override;
|
||||
void disconnect() override;
|
||||
bool select_char(int slot) override;
|
||||
bool enter_game() override;
|
||||
bool create_character(int slot, const std::string &name, int job, int shape,
|
||||
int con, int intel, int str, int dex);
|
||||
bool delete_character(int slot, const std::string &private_code);
|
||||
bool change_name(int slot, const std::string &name);
|
||||
bool send_empire(uint8_t empire);
|
||||
void pump() override;
|
||||
Stage stage() const override { return m_stage; }
|
||||
const std::string &last_error() const override { return m_last_error; }
|
||||
EntityStore &world() override { return m_world; }
|
||||
|
||||
bool send_move(uint8_t func, uint8_t arg, float rot_deg, int32_t x, int32_t y,
|
||||
uint32_t time_ms = 0) override;
|
||||
bool send_attack(uint8_t type, uint32_t victim_vid) override;
|
||||
bool send_chat(uint8_t type, const std::string &text) override;
|
||||
bool send_target(uint32_t vid) override;
|
||||
bool send_character_position(uint8_t position) override;
|
||||
bool send_click_npc(uint32_t vid) override;
|
||||
bool send_script_answer(uint8_t answer) override;
|
||||
bool send_item_use(uint8_t window, uint16_t cell) override;
|
||||
bool send_item_move(uint8_t window, uint16_t cell, uint8_t to_window, uint16_t to_cell,
|
||||
uint8_t count) override;
|
||||
bool send_item_drop(uint8_t window, uint16_t cell, uint32_t gold, uint8_t count) override;
|
||||
bool send_item_pickup(uint32_t ground_vid) override;
|
||||
bool send_use_skill(uint32_t skill_vnum, uint32_t target_vid) override;
|
||||
bool send_item_use_to_item(uint8_t window, uint16_t cell, uint8_t target_window,
|
||||
uint16_t target_cell);
|
||||
bool send_item_give(uint32_t target_vid, uint8_t window, uint16_t cell, uint8_t count);
|
||||
bool send_request_warp();
|
||||
bool send_fishing(uint8_t rot);
|
||||
bool send_dungeon();
|
||||
bool send_sync_positions(const std::vector<SyncPosElement> &positions);
|
||||
bool send_whisper(const std::string &to, const std::string &text);
|
||||
bool send_shoot(uint8_t type);
|
||||
bool send_add_fly_targeting(uint32_t target_vid, int32_t x, int32_t y);
|
||||
bool send_quickslot_add(uint8_t pos, uint8_t type, uint8_t ref);
|
||||
bool send_quickslot_del(uint8_t pos);
|
||||
bool send_quickslot_swap(uint8_t pos, uint8_t pos_to);
|
||||
bool send_script_button(uint32_t index);
|
||||
bool send_script_select_item(uint32_t selection);
|
||||
bool send_quest_input(const std::string &text);
|
||||
bool send_quest_confirm(bool yes, uint32_t request_pid);
|
||||
bool send_quest_cancel();
|
||||
bool send_party_invite(uint32_t vid);
|
||||
bool send_party_answer(uint32_t leader_vid, bool accept);
|
||||
bool send_party_remove(uint32_t pid);
|
||||
bool send_party_set_state(uint32_t pid, uint8_t role, bool on);
|
||||
bool send_party_use_skill(uint8_t skill_index, uint32_t vid);
|
||||
bool send_party_parameter(uint8_t mode);
|
||||
bool send_shop_buy(uint8_t pos, uint8_t count);
|
||||
bool send_shop_sell(uint8_t pos, uint8_t count);
|
||||
bool send_shop_close();
|
||||
bool send_private_shop(const std::string &sign, const std::vector<CGMyShopItem> &items);
|
||||
bool send_cube_make(int result_index);
|
||||
bool send_cube_result_list(int npc_vnum);
|
||||
bool send_cube_materials(int start_index, int count);
|
||||
bool send_cube_open();
|
||||
bool send_cube_close();
|
||||
bool send_cube_list();
|
||||
bool send_cube_add_item(int cube_index, int inventory_index);
|
||||
bool send_cube_delete_item(int cube_index);
|
||||
bool send_guild_sub(uint8_t subheader);
|
||||
bool send_guild_u32(uint8_t subheader, uint32_t value);
|
||||
bool send_guild_i32(uint8_t subheader, int32_t value);
|
||||
bool send_guild_grade_name(uint8_t grade, const std::string &name);
|
||||
bool send_guild_grade_authority(uint8_t grade, uint8_t authority);
|
||||
bool send_guild_member_grade(uint32_t pid, uint8_t grade);
|
||||
bool send_guild_member_general(uint32_t pid, bool enabled);
|
||||
bool send_guild_comment(const std::string &text);
|
||||
bool send_guild_invite_answer(uint32_t guild_id, bool accept);
|
||||
bool send_guild_answer_make(const std::string &name);
|
||||
bool send_guild_skill(uint32_t skill_vnum, uint32_t target_pid);
|
||||
bool send_refine(uint8_t pos, uint8_t type);
|
||||
bool send_dragon_soul_refine(uint8_t subheader, const ItemPos *grid, size_t count);
|
||||
bool send_friend_add(const std::string &name) override;
|
||||
bool send_friend_remove(const std::string &name) override;
|
||||
bool send_exchange_start(uint32_t partner_vid) override;
|
||||
bool send_exchange_item_add(uint8_t inv_window, uint16_t inv_cell,
|
||||
uint8_t display_pos) override;
|
||||
bool send_exchange_gold(uint32_t gold) override;
|
||||
bool send_exchange_accept() override;
|
||||
bool send_exchange_cancel() override;
|
||||
bool send_safebox_checkin(uint8_t safe_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) override;
|
||||
bool send_safebox_checkout(uint8_t safe_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) override;
|
||||
bool send_safebox_move(uint16_t from_cell, uint16_t to_cell, uint8_t count) override;
|
||||
bool send_mall_checkout(uint8_t mall_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) override;
|
||||
|
||||
// --- callbacks ---
|
||||
std::function<void(const std::vector<CharSlot> &)> on_char_list;
|
||||
std::function<void()> on_entered_game;
|
||||
std::function<void(Stage)> on_stage_change;
|
||||
std::function<void(const std::string &)> on_error;
|
||||
|
||||
// Set up creds + state WITHOUT opening a socket — feed the stream by hand via
|
||||
// stream().feed()/take_outgoing(). Used by the offline flow test and any
|
||||
// non-TCP transport. connect() == start_offline() + m_stream.connect().
|
||||
void start_offline(const std::string &id, const std::string &pw);
|
||||
|
||||
// --- accessors ---
|
||||
const std::vector<CharSlot> &char_slots() const { return m_parser.char_slots(); }
|
||||
ClassicStream &stream() { return m_stream; } // for tests / trace
|
||||
ClassicParser &parser() { return m_parser; }
|
||||
uint32_t now_ms() const { return m_now; }
|
||||
void set_now(uint32_t ms) {
|
||||
m_now = ms;
|
||||
m_world.set_now(ms);
|
||||
}
|
||||
// auto-send CG_ENTERGAME this many ms after entering PHASE_LOADING (0 = never,
|
||||
// caller drives enter_game()). Default mirrors the m2dev client's ~1.5s.
|
||||
void set_auto_entergame_delay(uint32_t ms) { m_entergame_delay = ms; }
|
||||
void set_wire_trace(bool on) { m_stream.set_wire_trace(on); }
|
||||
|
||||
private:
|
||||
void set_stage(Stage s);
|
||||
void on_phase(uint8_t phase);
|
||||
bool on_packet(uint8_t header, const uint8_t *body, uint32_t len);
|
||||
void send_login();
|
||||
bool send_client_version();
|
||||
|
||||
EntityStore m_world;
|
||||
ClassicStream m_stream;
|
||||
ClassicParser m_parser;
|
||||
|
||||
Stage m_stage = Stage::Offline;
|
||||
std::string m_last_error;
|
||||
std::string m_id, m_pw;
|
||||
uint32_t m_now = 0;
|
||||
uint32_t m_loading_since = 0;
|
||||
uint32_t m_entergame_delay = 1500;
|
||||
bool m_entergame_sent = false;
|
||||
bool m_login_sent = false;
|
||||
bool m_version_sent = false;
|
||||
};
|
||||
|
||||
} // namespace mtnet::classic
|
||||
@@ -0,0 +1,499 @@
|
||||
#include "classic_stream.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
namespace {
|
||||
constexpr size_t RECV_CHUNK = 32 * 1024;
|
||||
|
||||
uint32_t now_ms() {
|
||||
using namespace std::chrono;
|
||||
return static_cast<uint32_t>(
|
||||
duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
void set_nonblocking(int fd) {
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ClassicStream::~ClassicStream() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void ClassicStream::set_state(State s) {
|
||||
if (m_state == s) {
|
||||
return;
|
||||
}
|
||||
m_state = s;
|
||||
if (on_state_change) {
|
||||
on_state_change(s);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------- socket
|
||||
bool ClassicStream::connect(const std::string &host, uint16_t port) {
|
||||
disconnect();
|
||||
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
addrinfo *res = nullptr;
|
||||
char portbuf[8];
|
||||
std::snprintf(portbuf, sizeof(portbuf), "%u", port);
|
||||
if (getaddrinfo(host.c_str(), portbuf, &hints, &res) != 0 || !res) {
|
||||
m_last_error = "getaddrinfo(" + host + ") failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sock = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (m_sock < 0) {
|
||||
m_last_error = std::string("socket: ") + std::strerror(errno);
|
||||
freeaddrinfo(res);
|
||||
return false;
|
||||
}
|
||||
set_nonblocking(m_sock);
|
||||
int one = 1;
|
||||
setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||
|
||||
int rc = ::connect(m_sock, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
if (rc == 0) {
|
||||
set_state(State::Online);
|
||||
} else if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
|
||||
set_state(State::Connecting);
|
||||
} else {
|
||||
m_last_error = std::string("connect: ") + std::strerror(errno);
|
||||
::close(m_sock);
|
||||
m_sock = -1;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassicStream::disconnect() {
|
||||
if (m_sock >= 0) {
|
||||
::close(m_sock);
|
||||
m_sock = -1;
|
||||
}
|
||||
m_recv.clear();
|
||||
m_send.clear();
|
||||
m_seq_idx = 0;
|
||||
m_seq_on = false;
|
||||
m_handshake_seen = false;
|
||||
m_cipher.clean_up();
|
||||
set_state(State::Offline);
|
||||
}
|
||||
|
||||
void ClassicStream::decrypt_appended(size_t n) {
|
||||
if (n == 0 || !m_cipher.activated()) {
|
||||
return;
|
||||
}
|
||||
// the n bytes just written sit at the tail of the unread region
|
||||
m_cipher.decrypt(m_recv.mutable_unread() + (m_recv.readable() - n), n);
|
||||
}
|
||||
|
||||
bool ClassicStream::recv_into_buffer() {
|
||||
uint8_t *dst = m_recv.reserve_write(RECV_CHUNK);
|
||||
ssize_t n = ::recv(m_sock, dst, RECV_CHUNK, 0);
|
||||
if (n > 0) {
|
||||
m_recv.commit_write(static_cast<size_t>(n), RECV_CHUNK);
|
||||
decrypt_appended(static_cast<size_t>(n));
|
||||
return true;
|
||||
}
|
||||
m_recv.commit_write(0, RECV_CHUNK);
|
||||
if (n == 0) {
|
||||
m_last_error = "peer closed";
|
||||
return false;
|
||||
}
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return true;
|
||||
}
|
||||
m_last_error = std::string("recv: ") + std::strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClassicStream::flush_send() {
|
||||
while (m_send.readable() > 0) {
|
||||
ssize_t n = ::send(m_sock, m_send.read_ptr(), m_send.readable(), 0);
|
||||
if (n > 0) {
|
||||
m_send.discard(static_cast<size_t>(n));
|
||||
continue;
|
||||
}
|
||||
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
break;
|
||||
}
|
||||
m_last_error = std::string("send: ") + std::strerror(errno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassicStream::process() {
|
||||
if (m_sock < 0) {
|
||||
return;
|
||||
}
|
||||
if (m_state == State::Connecting) {
|
||||
int err = 0;
|
||||
socklen_t len = sizeof(err);
|
||||
if (getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err != 0) {
|
||||
m_last_error = "connect failed";
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
set_state(State::Online);
|
||||
}
|
||||
if (!recv_into_buffer()) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
dispatch();
|
||||
if (m_sock >= 0 && !flush_send()) {
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------- framing core
|
||||
void ClassicStream::feed(const void *data, size_t n) {
|
||||
m_recv.write(data, n);
|
||||
decrypt_appended(n);
|
||||
dispatch();
|
||||
}
|
||||
|
||||
void ClassicStream::emit_bytes(const void *p, size_t n) {
|
||||
if (!m_cipher.activated()) {
|
||||
m_send.write(p, n);
|
||||
return;
|
||||
}
|
||||
// CTR stream: encrypt in call order; the encoder keeps its counter across calls.
|
||||
std::string tmp(static_cast<const char *>(p), n);
|
||||
m_cipher.encrypt(tmp.data(), n);
|
||||
m_send.write(tmp.data(), n);
|
||||
}
|
||||
|
||||
size_t ClassicStream::take_outgoing(void *dst, size_t cap) {
|
||||
size_t n = m_send.readable() < cap ? m_send.readable() : cap;
|
||||
if (n) {
|
||||
std::memcpy(dst, m_send.read_ptr(), n);
|
||||
m_send.discard(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void ClassicStream::append_sequence_if_needed(uint8_t header) {
|
||||
if (!m_seq_on || !is_sequence_cg(header)) {
|
||||
return;
|
||||
}
|
||||
uint8_t seq = SEQUENCE_TABLE[m_seq_idx];
|
||||
m_seq_idx = (m_seq_idx + 1) % SEQUENCE_TABLE_SIZE;
|
||||
emit_bytes(&seq, 1);
|
||||
}
|
||||
|
||||
bool ClassicStream::send_fixed(const void *struct_bytes, size_t n) {
|
||||
if (n == 0) {
|
||||
return false;
|
||||
}
|
||||
uint8_t header = *static_cast<const uint8_t *>(struct_bytes);
|
||||
emit_bytes(struct_bytes, n);
|
||||
append_sequence_if_needed(header);
|
||||
if (m_trace) {
|
||||
std::fprintf(stderr, "[classic] send hdr=%u n=%zu seq=%d\n", header, n,
|
||||
(m_seq_on && is_sequence_cg(header)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassicStream::send_dynamic(const void *bytes, size_t n) {
|
||||
if (n < sizeof(DynHead)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t header = *static_cast<const uint8_t *>(bytes);
|
||||
emit_bytes(bytes, n);
|
||||
append_sequence_if_needed(header);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------- dispatch
|
||||
bool ClassicStream::handle_control(uint8_t header, bool &consumed) {
|
||||
consumed = false;
|
||||
switch (header) {
|
||||
case HDR_HANDSHAKE: {
|
||||
Handshake hs{};
|
||||
if (!m_recv.peek(&hs, sizeof(hs))) {
|
||||
return true; // need more
|
||||
}
|
||||
m_recv.discard(sizeof(hs));
|
||||
consumed = true;
|
||||
// mirror EterLib/PythonNetworkStreamPhaseHandshake.cpp + desc.cpp:
|
||||
m_server_time_base = hs.time + static_cast<uint32_t>(hs.delta);
|
||||
m_client_time_base = now_ms();
|
||||
hs.time = hs.time + 2u * static_cast<uint32_t>(hs.delta);
|
||||
hs.delta = 0;
|
||||
const bool initial = !m_handshake_seen;
|
||||
m_handshake_seen = true;
|
||||
if (!initial && m_time_sync_mode) {
|
||||
// PythonNetworkStreamPhaseHandShake.cpp uses 0xFF only for the
|
||||
// first handshake. Select/Game phase resyncs are sent as the
|
||||
// same struct with header 0xFC and the next sequence byte.
|
||||
hs.header = HDR_CG_TIME_SYNC;
|
||||
send_fixed(&hs, sizeof(hs));
|
||||
} else {
|
||||
// Initial handshake (and the mark side's repeated login
|
||||
// handshake) stays 0xFF and has no sequence byte.
|
||||
emit_bytes(&hs, sizeof(hs));
|
||||
}
|
||||
if (on_server_handshake) {
|
||||
on_server_handshake();
|
||||
}
|
||||
if (m_trace) {
|
||||
std::fprintf(stderr, "[classic] handshake echo time=%u\n", hs.time);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case HDR_GC_TIME_SYNC: { // 0xFC — 1-byte "handshake ok" blank (re-sync path)
|
||||
Blank b{};
|
||||
if (!m_recv.peek(&b, sizeof(b))) {
|
||||
return true;
|
||||
}
|
||||
m_recv.discard(sizeof(b));
|
||||
consumed = true;
|
||||
if (on_handshake_ok) {
|
||||
on_handshake_ok();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case HDR_GC_PHASE: {
|
||||
Phase_ p{};
|
||||
if (!m_recv.peek(&p, sizeof(p))) {
|
||||
return true;
|
||||
}
|
||||
m_recv.discard(sizeof(p));
|
||||
consumed = true;
|
||||
if (m_trace) {
|
||||
std::fprintf(stderr, "[classic] phase -> %u\n", p.phase);
|
||||
}
|
||||
if (on_phase) {
|
||||
on_phase(p.phase);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case HDR_GC_PING: {
|
||||
Blank b{};
|
||||
if (!m_recv.peek(&b, sizeof(b))) {
|
||||
return true;
|
||||
}
|
||||
m_recv.discard(sizeof(b));
|
||||
consumed = true;
|
||||
uint8_t pong = HDR_CG_PONG; // 0xFE
|
||||
send_fixed(&pong, 1); // CG_PONG is bSeq=true (sizeof(BYTE)+seq)
|
||||
return true;
|
||||
}
|
||||
case HDR_GC_BINDUDP: {
|
||||
BindUDP u{};
|
||||
if (!m_recv.peek(&u, sizeof(u))) {
|
||||
return true;
|
||||
}
|
||||
m_recv.discard(sizeof(u));
|
||||
consumed = true;
|
||||
return true; // UDP not used
|
||||
}
|
||||
case HDR_KEY_AGREEMENT: { // 0xFB — GC_KEY_AGREEMENT (server's DH2 blob)
|
||||
if (m_recv.readable() < sizeof(KeyAgreement)) {
|
||||
return true; // need the whole 261-byte packet (consumed stays false)
|
||||
}
|
||||
consumed = true;
|
||||
return handle_key_agreement();
|
||||
}
|
||||
case HDR_GC_KEY_AGREEMENT_COMPLETED: { // 0xFA
|
||||
KeyAgreementCompleted kac{};
|
||||
if (!m_recv.peek(&kac, sizeof(kac))) {
|
||||
return true;
|
||||
}
|
||||
m_recv.discard(sizeof(kac));
|
||||
consumed = true;
|
||||
m_cipher.set_activated(true);
|
||||
// the server may have pipelined encrypted bytes right behind this;
|
||||
// decrypt whatever's already buffered before we dispatch it.
|
||||
if (m_recv.readable() > 0) {
|
||||
m_cipher.decrypt(m_recv.mutable_unread(), m_recv.readable());
|
||||
}
|
||||
if (m_trace) {
|
||||
std::fprintf(stderr, "[classic] cipher activated\n");
|
||||
}
|
||||
if (on_cipher_active) {
|
||||
on_cipher_active();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return true; // not a control header
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicStream::handle_key_agreement() {
|
||||
KeyAgreement pkt{};
|
||||
if (!m_recv.peek(&pkt, sizeof(pkt))) {
|
||||
return true; // need the whole 261-byte packet
|
||||
}
|
||||
m_recv.discard(sizeof(pkt));
|
||||
|
||||
// generate our own DH2 public blob
|
||||
KeyAgreement out{};
|
||||
out.header = HDR_KEY_AGREEMENT; // CG_KEY_AGREEMENT (same 0xFB)
|
||||
size_t data_len = sizeof(out.data);
|
||||
size_t agreed = m_cipher.prepare(out.data, &data_len);
|
||||
if (agreed == 0) {
|
||||
m_last_error = "classic cipher: DH2 Prepare failed";
|
||||
if (on_error) {
|
||||
on_error(m_last_error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
out.agreed_length = static_cast<uint16_t>(agreed);
|
||||
out.data_length = static_cast<uint16_t>(data_len);
|
||||
|
||||
// derive keys from the peer's blob (does NOT flip activated_ yet)
|
||||
if (!m_cipher.activate(m_polarity, pkt.agreed_length, pkt.data, pkt.data_length)) {
|
||||
m_last_error = "classic cipher: DH2 Agree/SetUp failed";
|
||||
if (on_error) {
|
||||
on_error(m_last_error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// reply with our blob — still plaintext (peer activates on its own COMPLETED)
|
||||
emit_bytes(&out, sizeof(out));
|
||||
if (m_trace) {
|
||||
std::fprintf(stderr, "[classic] key agreement: replied, keys ready\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassicStream::dispatch() {
|
||||
for (;;) {
|
||||
if (m_recv.readable() == 0) {
|
||||
return;
|
||||
}
|
||||
uint8_t header = *m_recv.read_ptr();
|
||||
|
||||
// zero padding between packets (cipher block alignment on the m2dev side;
|
||||
// harmless to skip here too).
|
||||
if (header == 0) {
|
||||
m_recv.discard(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool consumed = false;
|
||||
if (!handle_control(header, consumed)) {
|
||||
if (on_error && m_last_error.empty()) {
|
||||
on_error("dispatch aborted");
|
||||
}
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
if (consumed) {
|
||||
continue;
|
||||
}
|
||||
// control handler said "need more bytes" for a control header it owns?
|
||||
if (header == HDR_HANDSHAKE || header == HDR_GC_TIME_SYNC || header == HDR_GC_PHASE ||
|
||||
header == HDR_GC_PING || header == HDR_GC_BINDUDP || header == HDR_KEY_AGREEMENT ||
|
||||
header == HDR_GC_KEY_AGREEMENT_COMPLETED) {
|
||||
return; // wait for the rest of this control packet
|
||||
}
|
||||
|
||||
// Some side connections share the classic cipher/control handshake but
|
||||
// have their own packet framing. Give that connection a chance before
|
||||
// the normal GC static/dynamic table is consulted.
|
||||
if (on_raw_packet) {
|
||||
RawPacketResult raw = on_raw_packet(header, m_recv.read_ptr(), m_recv.readable());
|
||||
switch (raw.status) {
|
||||
case RawPacketStatus::NeedMore:
|
||||
return;
|
||||
case RawPacketStatus::Consumed:
|
||||
if (raw.bytes == 0 || raw.bytes > m_recv.readable()) {
|
||||
m_last_error = "raw packet consumed invalid byte count";
|
||||
if (on_error) {
|
||||
on_error(m_last_error);
|
||||
}
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
m_recv.discard(raw.bytes);
|
||||
continue;
|
||||
case RawPacketStatus::Error:
|
||||
m_last_error = "raw packet framing error";
|
||||
if (on_error) {
|
||||
on_error(m_last_error);
|
||||
}
|
||||
disconnect();
|
||||
return;
|
||||
case RawPacketStatus::NotHandled:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --- dynamic-size GC packet ---
|
||||
if (is_dynamic_gc(header)) {
|
||||
DynHead dh{};
|
||||
if (!m_recv.peek(&dh, sizeof(dh))) {
|
||||
return;
|
||||
}
|
||||
if (dh.size < sizeof(DynHead)) {
|
||||
m_last_error = "dynamic packet size underflow";
|
||||
if (on_error) {
|
||||
on_error(m_last_error);
|
||||
}
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
if (m_recv.readable() < dh.size) {
|
||||
return; // whole packet not here yet
|
||||
}
|
||||
m_recv.discard(sizeof(DynHead));
|
||||
uint32_t body_len = dh.size - sizeof(DynHead);
|
||||
const uint8_t *body = m_recv.read_ptr();
|
||||
bool ok = !on_packet || on_packet(header, body, body_len);
|
||||
m_recv.discard(body_len);
|
||||
if (!ok) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- static-size GC packet --- (packet_size_gc = FULL size incl. header)
|
||||
int total = packet_size_gc(header);
|
||||
if (total <= 0) {
|
||||
m_last_error = "unknown/unsupported GC header " + std::to_string(header);
|
||||
if (on_error) {
|
||||
on_error(m_last_error);
|
||||
}
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
if (m_recv.readable() < static_cast<size_t>(total)) {
|
||||
return;
|
||||
}
|
||||
const uint8_t *p = m_recv.read_ptr();
|
||||
bool ok = !on_packet || on_packet(header, p + 1, static_cast<uint32_t>(total - 1));
|
||||
m_recv.discard(static_cast<size_t>(total));
|
||||
if (!ok) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mtnet::classic
|
||||
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
// ClassicStream — 40250 "classic" Metin2 framing over non-blocking TCP.
|
||||
// The `MT_PROTOCOL=classic` counterpart of mtnet::NetStream (m2dev fork).
|
||||
// See docs/CLIENT-40250-PORT.md §2 (framing) / §4 (handshake / phase).
|
||||
//
|
||||
// 1-byte header -> static size table (wire_classic.h) OR dynamic `uint16 size`.
|
||||
// CG game packets append a trailing sequence byte once sequence mode is on.
|
||||
// Control packets (HANDSHAKE / TIME_SYNC / PHASE / PING / BINDUDP) are handled
|
||||
// here; everything else goes to on_packet(header, body, len).
|
||||
//
|
||||
// Encryption (Crypto++ DH2 + CTR, docs §3) is negotiated in the stream before
|
||||
// phase/login packets are dispatched.
|
||||
//
|
||||
// Testability: feed()/take_outgoing() drive the framing synchronously with no
|
||||
// socket; connect()/process() add the real POSIX I/O on top.
|
||||
|
||||
#include "classic_cipher.h"
|
||||
#include "sequence_table.h"
|
||||
#include "wire_classic.h"
|
||||
|
||||
#include "../byte_buffer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace mtnet::classic {
|
||||
|
||||
class ClassicStream {
|
||||
public:
|
||||
enum class State { Offline, Connecting, Online };
|
||||
enum class RawPacketStatus { NotHandled, NeedMore, Consumed, Error };
|
||||
struct RawPacketResult {
|
||||
RawPacketStatus status = RawPacketStatus::NotHandled;
|
||||
size_t bytes = 0;
|
||||
};
|
||||
|
||||
ClassicStream() = default;
|
||||
~ClassicStream();
|
||||
|
||||
// --- callbacks (set before connect/feed) ---
|
||||
std::function<void(State)> on_state_change;
|
||||
// Fired after the initial GC_HANDSHAKE has been consumed and echoed. The
|
||||
// 40250 guild-mark side sends CG_MARK_LOGIN at this point, before the
|
||||
// normal game-side key agreement/phase transition.
|
||||
std::function<void()> on_server_handshake;
|
||||
std::function<void(uint8_t phase)> on_phase; // GC_PHASE
|
||||
std::function<void()> on_handshake_ok; // time sync converged
|
||||
std::function<void()> on_cipher_active; // GC_KEY_AGREEMENT_COMPLETED
|
||||
// A non-control packet is fully buffered. `body` covers the bytes AFTER the
|
||||
// header byte (for dynamic packets: after the header+size prefix), `len` its
|
||||
// length. Return false to abort the connection.
|
||||
std::function<bool(uint8_t header, const uint8_t *body, uint32_t len)> on_packet;
|
||||
// Optional protocol-specific framing hook. The 40250 mark side uses a
|
||||
// one-byte header followed by a u32 whole-packet size for GC_MARK_* packets,
|
||||
// instead of the normal static/dynamic game packet map.
|
||||
// Return the exact number of bytes consumed when status == Consumed.
|
||||
std::function<RawPacketResult(uint8_t header, const uint8_t *packet, size_t available)>
|
||||
on_raw_packet;
|
||||
std::function<void(const std::string &)> on_error;
|
||||
|
||||
// --- real socket lifecycle ---
|
||||
bool connect(const std::string &host, uint16_t port);
|
||||
void disconnect();
|
||||
void process(); // one recv() + dispatch + one send() flush
|
||||
State state() const { return m_state; }
|
||||
bool is_online() const { return m_state == State::Online; }
|
||||
const std::string &last_error() const { return m_last_error; }
|
||||
|
||||
// --- socket-free framing (tests / alternative transports) ---
|
||||
void feed(const void *data, size_t n); // append inbound bytes, run dispatch
|
||||
size_t take_outgoing(void *dst, size_t cap);
|
||||
size_t outgoing_pending() const { return m_send.readable(); }
|
||||
|
||||
// --- sending ---
|
||||
// `struct_bytes[0]` must already be the header byte; frames the body and
|
||||
// appends the sequence byte when sequence mode is on and the header is bSeq.
|
||||
bool send_fixed(const void *struct_bytes, size_t n);
|
||||
// Caller supplies the full dynamic packet incl. [header][uint16 size][...].
|
||||
bool send_dynamic(const void *bytes, size_t n);
|
||||
|
||||
// docs §2.2: turn on once the client would call net.SetPacketSequenceMode()
|
||||
// (LOGIN phase onward). Off during pure HANDSHAKE.
|
||||
void set_sequence_mode(bool on) { m_seq_on = on; }
|
||||
bool sequence_mode() const { return m_seq_on; }
|
||||
uint32_t sequence_index() const { return m_seq_idx; }
|
||||
|
||||
// The normal game connection changes later GC_HANDSHAKE packets into
|
||||
// CG_TIME_SYNC. The guild-mark connection is a special case: the stock
|
||||
// downloader keeps answering with CG_MARK_LOGIN, so it leaves this off.
|
||||
void set_time_sync_mode(bool on) { m_time_sync_mode = on; }
|
||||
|
||||
void set_wire_trace(bool on) { m_trace = on; }
|
||||
|
||||
// _IMPROVED_PACKET_ENCRYPTION_ (docs §3). Client polarity = true.
|
||||
bool cipher_active() const { return m_cipher.activated(); }
|
||||
void set_polarity(bool client) { m_polarity = client; }
|
||||
|
||||
private:
|
||||
void set_state(State s);
|
||||
bool recv_into_buffer();
|
||||
bool flush_send();
|
||||
void dispatch(); // consume complete packets from m_recv
|
||||
bool handle_control(uint8_t header, bool &consumed);
|
||||
// append + (if the cipher is live) encrypt bytes into the send buffer.
|
||||
void emit_bytes(const void *p, size_t n);
|
||||
void append_sequence_if_needed(uint8_t header);
|
||||
bool handle_key_agreement();
|
||||
void decrypt_appended(size_t n); // decrypt the last n bytes just written to m_recv
|
||||
|
||||
int m_sock = -1;
|
||||
State m_state = State::Offline;
|
||||
std::string m_last_error;
|
||||
|
||||
ByteBuffer m_recv;
|
||||
ByteBuffer m_send;
|
||||
|
||||
ClassicCipher m_cipher;
|
||||
bool m_polarity = true; // client
|
||||
|
||||
bool m_seq_on = false;
|
||||
uint32_t m_seq_idx = 0;
|
||||
bool m_handshake_seen = false;
|
||||
bool m_time_sync_mode = false;
|
||||
bool m_trace = false;
|
||||
|
||||
// server clock offset from the last handshake exchange (informational).
|
||||
uint32_t m_server_time_base = 0;
|
||||
uint32_t m_client_time_base = 0;
|
||||
};
|
||||
|
||||
} // namespace mtnet::classic
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@ struct Entity {
|
||||
uint16_t race = 0;
|
||||
uint8_t ch_type = 0; // CHRTYPE: 0 PC, 1 NPC, 2 MONSTER, 3 STONE, 4 WARP, ...
|
||||
std::string name;
|
||||
std::string shop_sign; // GC_SHOP_SIGN; empty when the private shop is closed
|
||||
uint16_t parts[CHR_EQUIPPART_NUM] = {0, 0, 0, 0};
|
||||
bool is_main = false;
|
||||
|
||||
@@ -230,6 +231,7 @@ struct PartyMember {
|
||||
struct Friend {
|
||||
std::string name;
|
||||
bool online = false;
|
||||
bool mobile = false; // 40250 GC_MESSENGER_MOBILE / connected-state bit
|
||||
};
|
||||
|
||||
// --- P8 NPC shop (GC_SHOP) ----------------------------------------------------
|
||||
@@ -269,11 +271,21 @@ struct GuildMember {
|
||||
uint32_t pid = 0;
|
||||
uint8_t grade = 0;
|
||||
bool is_general = false;
|
||||
bool online = false;
|
||||
uint8_t job = 0;
|
||||
uint8_t level = 0;
|
||||
uint32_t offer = 0;
|
||||
std::string name;
|
||||
};
|
||||
struct GuildComment {
|
||||
uint32_t id = 0;
|
||||
std::string name;
|
||||
std::string content;
|
||||
};
|
||||
struct GuildInvite {
|
||||
uint32_t guild_id = 0;
|
||||
std::string guild_name;
|
||||
};
|
||||
struct GuildGrade {
|
||||
std::string name;
|
||||
uint8_t auth = 0;
|
||||
@@ -333,6 +345,37 @@ struct RefineCue {
|
||||
uint8_t material_count = 0;
|
||||
};
|
||||
|
||||
// One stateful line from the 40250 GC_CHAT/COMMAND bus. The original client
|
||||
// routes these through CPythonNetworkStream::ServerCommand(); keeping them as
|
||||
// typed events lets the Godot bridge expose the same behaviour without making
|
||||
// the parser depend on UI code.
|
||||
struct ServerCommandEvent {
|
||||
enum Kind {
|
||||
PartyRequestDenied,
|
||||
SafeboxPasswordRequired,
|
||||
SafeboxWrongPassword,
|
||||
MallPasswordRequired,
|
||||
RefineSucceeded,
|
||||
RefineFailed,
|
||||
PrivateShopOpenRequested,
|
||||
MyShopPrice,
|
||||
BlockModeChanged,
|
||||
ObserverModeChanged,
|
||||
ObserverCountChanged,
|
||||
StoneDetected,
|
||||
StaminaStarted,
|
||||
StaminaStopped,
|
||||
MobileFlagChanged,
|
||||
MobileAuthRequired,
|
||||
ComboChanged,
|
||||
GiftAvailable,
|
||||
};
|
||||
Kind kind = PartyRequestDenied;
|
||||
uint32_t value = 0; // item vnum / price, depending on kind
|
||||
uint32_t value2 = 0;
|
||||
float value3 = 0.0f; // StoneDetect angle, in degrees after legacy conversion
|
||||
};
|
||||
|
||||
// An item lying in the world (GC_ITEM_GROUND_ADD).
|
||||
struct GroundItem {
|
||||
uint32_t vid = 0;
|
||||
@@ -381,6 +424,8 @@ struct PlayerPoints {
|
||||
int32_t max_hp() const { return v[POINT_MAX_HP]; }
|
||||
int32_t sp() const { return v[POINT_SP]; }
|
||||
int32_t max_sp() const { return v[POINT_MAX_SP]; }
|
||||
int32_t stamina() const { return v[POINT_STAMINA]; }
|
||||
int32_t max_stamina() const { return v[POINT_MAX_STAMINA]; }
|
||||
int32_t level() const { return v[POINT_LEVEL]; }
|
||||
int32_t exp() const { return v[POINT_EXP]; }
|
||||
int32_t next_exp() const { return v[POINT_NEXT_EXP]; }
|
||||
@@ -408,8 +453,158 @@ public:
|
||||
|
||||
// Feed one complete game-phase packet. `body` points at the packet start
|
||||
// (header/length included); `len` == that length. Unknown headers ignored.
|
||||
// This is the m2dev-fork parser; the 40250 "classic" backend calls the
|
||||
// protocol-neutral mutation API below instead (docs/CLIENT-40250-PORT.md §6).
|
||||
void apply(uint16_t header, const void *body, uint16_t len);
|
||||
|
||||
// --- protocol-neutral mutation API ---------------------------------------
|
||||
// Both the m2dev apply() path and mtnet::classic::ClassicParser drive the
|
||||
// same world model through these. Positions are server cm; `rot256` is the
|
||||
// legacy 0..255 heading; `func` mirrors FUNC_*.
|
||||
void mut_spawn(uint32_t vid, uint16_t race, uint8_t ch_type, const std::string &name,
|
||||
float x, float y, float z, float angle, uint16_t moving_speed = 0,
|
||||
uint8_t attack_speed = 0);
|
||||
void mut_spawn_main(uint32_t vid, uint16_t race, const std::string &name, float x, float y,
|
||||
float z);
|
||||
void mut_despawn(uint32_t vid);
|
||||
// angle_deg is the already-decoded compass heading (m2dev: rot*360/256;
|
||||
// classic: rot*5 — the two wire encodings differ, so decode at the parser).
|
||||
void mut_move(uint32_t vid, float angle_deg, uint8_t func, float tx, float ty,
|
||||
uint32_t dur_ms);
|
||||
// Full stat block (index = POINT_*). `n` <= 256.
|
||||
void mut_set_points(const int32_t *pts, int n);
|
||||
// One stat delta (GC_CHARACTER_POINT_CHANGE equivalent).
|
||||
void mut_set_point(uint8_t type, int32_t value, uint32_t vid);
|
||||
|
||||
// --- items --- window: 1=inventory, 2=equipment, 5=dragon-soul, 7=belt.
|
||||
// `sockets` is ITEM_SOCKET_SLOT_MAX_NUM ints; `attrs` is
|
||||
// ITEM_ATTRIBUTE_SLOT_MAX_NUM ItemAttr (pass nullptr to leave them zero).
|
||||
void mut_item_set(uint8_t window, uint16_t cell, uint32_t vnum, uint8_t count, uint32_t flags,
|
||||
uint32_t anti_flags, const int32_t *sockets, const ItemAttr *attrs);
|
||||
void mut_item_del(uint8_t window, uint16_t cell);
|
||||
void mut_item_update(uint8_t window, uint16_t cell, uint8_t count, const int32_t *sockets,
|
||||
const ItemAttr *attrs);
|
||||
void mut_ground_add(uint32_t vid, uint32_t vnum, float x, float y, float z);
|
||||
void mut_ground_del(uint32_t vid);
|
||||
void mut_ground_owner(uint32_t vid, const std::string &owner);
|
||||
void mut_item_used(uint32_t vnum);
|
||||
void mut_item_picked(uint32_t vnum, uint8_t count, const std::string &from);
|
||||
|
||||
// --- entity state (all no-op if vid unknown) ---
|
||||
// `parts` is CHR_EQUIPPART_NUM u16 (nullptr = leave unchanged).
|
||||
void mut_char_update(uint32_t vid, const uint16_t *parts, uint8_t moving_speed,
|
||||
uint8_t attack_speed, int32_t guild_id, int16_t alignment, uint8_t pk_mode,
|
||||
uint32_t mount_vnum);
|
||||
void mut_set_position(uint32_t vid, uint8_t position);
|
||||
// snap an entity to (x,y) with no interpolation (GC_SYNC_POSITION).
|
||||
void mut_snap_position(uint32_t vid, float x, float y);
|
||||
void mut_change_speed(uint32_t vid, uint16_t moving_speed);
|
||||
void mut_walk_mode(uint32_t vid, uint8_t mode);
|
||||
void mut_stun(uint32_t vid);
|
||||
void mut_dead(uint32_t vid);
|
||||
void mut_motion(uint32_t vid, uint32_t victim_vid, uint16_t motion);
|
||||
void mut_target(uint32_t vid, uint8_t hp_pct);
|
||||
void mut_damage(uint32_t vid, uint8_t flag, int32_t amount);
|
||||
void mut_mount(uint32_t vid, uint32_t mount_vnum);
|
||||
void mut_fly(uint8_t type, uint32_t start_vid, uint32_t end_vid);
|
||||
void mut_fly_target(uint32_t shooter_vid, uint32_t target_vid, int32_t x, int32_t y,
|
||||
bool append);
|
||||
void mut_dig(uint32_t vid, uint32_t target_vid, uint8_t count);
|
||||
void mut_special_effect(uint32_t vid, int32_t special);
|
||||
void mut_specific_effect(uint32_t vid, const std::string &file);
|
||||
void mut_pvp(uint32_t src_vid, uint32_t dst_vid, uint8_t mode);
|
||||
void mut_duel_start();
|
||||
void mut_duel_start(const uint32_t *opponents, int count);
|
||||
void mut_view_equipment(uint32_t vid, const Item *items, int n);
|
||||
// GC_CHAR_ADDITIONAL_INFO — fills a spawned entity's name/parts/level/guild.
|
||||
void mut_char_info(uint32_t vid, const std::string &name, const uint16_t *parts,
|
||||
uint8_t empire, int32_t guild_id, int32_t level, int16_t alignment, uint8_t pk_mode,
|
||||
uint32_t mount_vnum);
|
||||
// GC_SHOP_SIGN — the sign belongs to the entity that owns the private shop.
|
||||
void mut_shop_sign(uint32_t vid, const std::string &sign);
|
||||
|
||||
// --- local-player skills / quickslots / affects ---
|
||||
// `level` / `master` are SKILL_MAX_NUM bytes (master may be nullptr).
|
||||
void mut_skill_levels(const uint8_t *level, const uint8_t *master, int n);
|
||||
void mut_skill_group(uint8_t group);
|
||||
void mut_skill_cooldown_end(uint8_t skill);
|
||||
void mut_quickslot_set(uint8_t pos, uint8_t type, uint8_t position);
|
||||
void mut_quickslot_del(uint8_t pos);
|
||||
void mut_quickslot_swap(uint8_t pos, uint8_t pos_to);
|
||||
void mut_affect_add(uint32_t type, uint8_t point_idx, int32_t value, uint32_t flag,
|
||||
int32_t duration);
|
||||
void mut_affect_remove(uint32_t type);
|
||||
|
||||
// --- chat --- (GC_CHAT with type==CHAT_TYPE_COMMAND routes to apply_server_command)
|
||||
void mut_chat(uint8_t type, uint32_t vid, const std::string &text);
|
||||
void mut_whisper(uint8_t sub, const std::string &from, const std::string &text);
|
||||
|
||||
// --- NPC shop (GC_SHOP) --- a single-shelf shop is one unnamed tab.
|
||||
void mut_shop_open(uint32_t vid, const std::vector<ShopTab> &tabs);
|
||||
void mut_shop_update_item(uint8_t pos, uint32_t vnum, uint32_t price, uint8_t count);
|
||||
void mut_shop_update_price(int32_t price);
|
||||
void mut_shop_close();
|
||||
void mut_shop_error(const std::string &code);
|
||||
|
||||
// --- quest / NPC dialog ---
|
||||
void mut_script(uint8_t skin, const std::string &text); // GC_SCRIPT
|
||||
void mut_quest_confirm(const std::string &msg, int32_t timeout, uint32_t request_pid);
|
||||
void mut_quest_info(uint16_t index, uint8_t flag, bool begin, const std::string &title,
|
||||
const std::string &clock_name, int32_t clock_value, const std::string &counter_name,
|
||||
int32_t counter_value, const std::string &icon);
|
||||
|
||||
// --- P9 world systems ---
|
||||
void mut_warp(int32_t x, int32_t y, int32_t addr, uint16_t port);
|
||||
void mut_server_time(int64_t time);
|
||||
void mut_channel(uint8_t channel);
|
||||
void mut_npc_marks(const std::vector<NPCMark> &marks);
|
||||
void mut_land_areas(const std::vector<LandArea> &areas);
|
||||
void mut_marker_create(int32_t id, const std::string &name, uint32_t vid, uint8_t type);
|
||||
void mut_marker_update(int32_t id, int32_t x, int32_t y);
|
||||
void mut_marker_delete(int32_t id);
|
||||
void mut_observer(ObserverEvent::Kind kind, uint32_t vid, int32_t x, int32_t y);
|
||||
void mut_lover(const std::string &name, uint8_t love_point);
|
||||
void mut_love_point(uint8_t love_point);
|
||||
void mut_fishing(uint8_t subheader, uint32_t info, uint8_t dir);
|
||||
void mut_dungeon(uint8_t subheader, int32_t x, int32_t y, bool has_destination);
|
||||
|
||||
// --- party (GC_PARTY_*) --- `affects` is PARTY_AFFECT_SLOT_MAX_NUM int16 (may be nullptr).
|
||||
void mut_party_invite(uint32_t leader);
|
||||
void mut_party_add(uint32_t pid, const std::string &name);
|
||||
void mut_party_update(uint32_t pid, uint8_t role, uint8_t hp_pct, const int16_t *affects);
|
||||
void mut_party_remove(uint32_t pid);
|
||||
void mut_party_link(uint32_t pid, uint32_t vid);
|
||||
void mut_party_unlink(uint32_t pid);
|
||||
void mut_party_parameter(uint8_t distribute_mode);
|
||||
|
||||
// --- messenger / exchange / storage -------------------------------------
|
||||
void mut_friend_set(const std::string &name, bool online);
|
||||
void mut_friend_mobile(const std::string &name, bool mobile);
|
||||
void mut_friend_remove(const std::string &name);
|
||||
void mut_friend_clear();
|
||||
// Server command `messenger_auth <name>` asks the local player to approve
|
||||
// or deny a pending friend request.
|
||||
void mut_friend_invite(const std::string &name);
|
||||
void mut_exchange_start(uint32_t partner_vid);
|
||||
void mut_exchange_item(bool self, uint8_t slot, uint32_t vnum, uint8_t count);
|
||||
void mut_exchange_item_del(bool self, uint8_t slot);
|
||||
void mut_exchange_gold(bool self, int64_t gold);
|
||||
void mut_exchange_accept(bool self, bool accepted);
|
||||
void mut_exchange_end();
|
||||
void mut_exchange_notice(); // server-side ALREADY / LESS_GOLD notice
|
||||
void mut_safebox_open(int size);
|
||||
void mut_safebox_money(int64_t money);
|
||||
void mut_safebox_set(uint16_t cell, uint32_t vnum, uint8_t count, uint32_t flags,
|
||||
uint32_t anti_flags, const int32_t *sockets, const ItemAttr *attrs);
|
||||
void mut_safebox_del(uint16_t cell);
|
||||
void mut_safebox_wrong_password();
|
||||
void mut_safebox_close();
|
||||
void mut_mall_open(int size);
|
||||
void mut_mall_set(uint16_t cell, uint32_t vnum, uint8_t count, uint32_t flags,
|
||||
uint32_t anti_flags, const int32_t *sockets, const ItemAttr *attrs);
|
||||
void mut_mall_del(uint16_t cell);
|
||||
void mut_mall_close();
|
||||
|
||||
// Advance interpolation of moving entities to m_now.
|
||||
void tick();
|
||||
|
||||
@@ -522,10 +717,16 @@ public:
|
||||
return v;
|
||||
}
|
||||
bool friends_dirty() { bool d = m_friends_dirty; m_friends_dirty = false; return d; }
|
||||
std::vector<std::string> drain_friend_invites() {
|
||||
auto v = std::move(m_friend_invites);
|
||||
m_friend_invites.clear();
|
||||
return v;
|
||||
}
|
||||
|
||||
// --- P8 NPC shop ---
|
||||
bool shop_open() const { return m_shop_open; }
|
||||
uint32_t shop_vid() const { return m_shop_vid; }
|
||||
int32_t shop_selling_price() const { return m_shop_selling_price; }
|
||||
const std::vector<ShopEntry> &shop_items() const { return m_shop_items; }
|
||||
// SHOP_GC_START_EX shelves; empty for a plain SHOP_GC_START shop (use shop_items()).
|
||||
const std::vector<ShopTab> &shop_tabs() const { return m_shop_tabs; }
|
||||
@@ -588,6 +789,29 @@ public:
|
||||
CubeResultEntry cube_last_success() const { return m_cube_last_success; }
|
||||
|
||||
// --- guild ---
|
||||
void mut_guild_info(uint16_t member_count, uint16_t max_member_count, uint32_t id,
|
||||
uint32_t master_pid, uint32_t exp, uint8_t level, const std::string &name,
|
||||
uint32_t gold, bool has_land);
|
||||
void mut_guild_clear_members();
|
||||
void mut_guild_member(uint32_t pid, uint8_t grade, bool is_general, uint8_t job,
|
||||
uint8_t level, uint32_t offer, const std::string &name);
|
||||
void mut_guild_member_online(uint32_t pid, bool online);
|
||||
void mut_guild_remove_member(uint32_t pid);
|
||||
void mut_guild_grade(uint8_t grade, const std::string &name, uint8_t auth);
|
||||
void mut_guild_grade_name(uint8_t grade, const std::string &name);
|
||||
void mut_guild_grade_auth(uint8_t grade, uint8_t auth);
|
||||
void mut_guild_member_grade(uint32_t pid, uint8_t grade);
|
||||
void mut_guild_member_general(uint32_t pid, bool is_general);
|
||||
void mut_guild_exp(uint8_t level, uint32_t exp);
|
||||
void mut_guild_money(uint32_t gold);
|
||||
void mut_guild_skill(uint8_t skill_point, const uint8_t *levels, uint16_t guild_point,
|
||||
uint16_t max_guild_point);
|
||||
void mut_guild_war(uint32_t self_id, uint32_t opponent_id, uint8_t type, uint8_t state);
|
||||
void mut_guild_war_pairs(const std::vector<GuildWarPair> &pairs, bool remove);
|
||||
void mut_guild_war_score(uint32_t gain_guild_id, uint32_t opponent_guild_id, int32_t point);
|
||||
void mut_guild_name(uint32_t id, const std::string &name);
|
||||
void mut_guild_comments(const std::vector<GuildComment> &comments);
|
||||
void mut_guild_invite(uint32_t guild_id, const std::string &guild_name);
|
||||
const GuildState &guild() const { return m_guild; }
|
||||
std::vector<GuildMember> guild_members() const {
|
||||
std::vector<GuildMember> v;
|
||||
@@ -602,6 +826,17 @@ public:
|
||||
return (i >= 0 && i < 16) ? m_guild_grades[i] : kEmpty;
|
||||
}
|
||||
bool guild_dirty() { bool d = m_guild_dirty; m_guild_dirty = false; return d; }
|
||||
const std::vector<GuildComment> &guild_comments() const { return m_guild_comments; }
|
||||
bool guild_comments_dirty() {
|
||||
bool d = m_guild_comments_dirty;
|
||||
m_guild_comments_dirty = false;
|
||||
return d;
|
||||
}
|
||||
std::vector<GuildInvite> drain_guild_invites() {
|
||||
auto v = std::move(m_guild_invites);
|
||||
m_guild_invites.clear();
|
||||
return v;
|
||||
}
|
||||
|
||||
// --- guild war / guild skill ---
|
||||
const GuildSkillState &guild_skill() const { return m_guild_skill; }
|
||||
@@ -637,6 +872,10 @@ public:
|
||||
m_ds_cues.clear();
|
||||
return v;
|
||||
}
|
||||
void mut_refine(uint8_t type, uint8_t pos, uint32_t src_vnum, uint32_t result_vnum,
|
||||
uint8_t material_count, int32_t cost, int32_t prob,
|
||||
const RefineCue::Mat *materials);
|
||||
void mut_dragon_soul_refine(uint8_t sub_type, uint8_t window, uint16_t cell);
|
||||
const Item &dragon_soul_slot(int cell) const;
|
||||
|
||||
// --- P9 world systems ---
|
||||
@@ -703,12 +942,21 @@ public:
|
||||
return v;
|
||||
}
|
||||
std::vector<PvpRelation> pvp_relations() const;
|
||||
const std::vector<uint32_t> &duel_opponents() const { return m_duel_opponents; }
|
||||
bool duel_cannot_attack() const { return m_duel_cannot_attack; }
|
||||
bool take_duel_started() {
|
||||
bool started = m_duel_started;
|
||||
m_duel_started = false;
|
||||
return started;
|
||||
}
|
||||
const LoverInfo &lover() const { return m_lover; }
|
||||
bool observer_mode() const { return m_observer_mode; }
|
||||
int observer_count() const { return m_observer_count; }
|
||||
bool mobile_flag() const { return m_mobile_flag; }
|
||||
bool combo_skill_flag() const { return m_combo_skill_flag; }
|
||||
bool stamina_consuming() const { return m_stamina_consuming; }
|
||||
uint32_t stamina_per_sec() const { return m_stamina_per_sec; }
|
||||
uint32_t current_stamina() const { return m_current_stamina; }
|
||||
bool take_lover_dirty() {
|
||||
bool dirty = m_lover_dirty;
|
||||
m_lover_dirty = false;
|
||||
@@ -725,6 +973,11 @@ public:
|
||||
m_chat.clear();
|
||||
return v;
|
||||
}
|
||||
std::vector<ServerCommandEvent> drain_server_commands() {
|
||||
auto v = std::move(m_server_commands);
|
||||
m_server_commands.clear();
|
||||
return v;
|
||||
}
|
||||
// vids whose hp/sp/level/dead/stunned changed since last drain (deduped).
|
||||
std::vector<uint32_t> drain_vitals() {
|
||||
auto v = std::move(m_vitals);
|
||||
@@ -855,9 +1108,20 @@ private:
|
||||
std::vector<ItemEvent> m_item_events;
|
||||
std::unordered_map<uint64_t, PvpRelation> m_pvp;
|
||||
std::vector<PvpRelation> m_pvp_changes;
|
||||
std::vector<uint32_t> m_duel_opponents;
|
||||
bool m_duel_started = false;
|
||||
bool m_duel_cannot_attack = false;
|
||||
LoverInfo m_lover;
|
||||
bool m_lover_dirty = false;
|
||||
bool m_observer_mode = false;
|
||||
int m_observer_count = 0;
|
||||
bool m_mobile_flag = false;
|
||||
bool m_combo_skill_flag = false;
|
||||
bool m_stamina_consuming = false;
|
||||
uint32_t m_stamina_per_sec = 0;
|
||||
uint32_t m_current_stamina = 0;
|
||||
float m_stamina_current_f = 0.0f;
|
||||
uint32_t m_stamina_last_ms = 0;
|
||||
|
||||
// P8 social / shop / storage
|
||||
std::unordered_map<uint32_t, PartyMember> m_party;
|
||||
@@ -866,11 +1130,13 @@ private:
|
||||
std::vector<uint32_t> m_party_invites;
|
||||
std::unordered_map<std::string, Friend> m_friends;
|
||||
bool m_friends_dirty = false;
|
||||
std::vector<std::string> m_friend_invites;
|
||||
bool m_shop_open = false;
|
||||
uint32_t m_shop_vid = 0;
|
||||
std::vector<ShopEntry> m_shop_items; // == m_shop_tabs[0].items when tabs present
|
||||
std::vector<ShopTab> m_shop_tabs;
|
||||
bool m_shop_dirty = false;
|
||||
int32_t m_shop_selling_price = 0;
|
||||
std::vector<std::string> m_shop_errors;
|
||||
ExchangeState m_exchange;
|
||||
bool m_exchange_dirty = false;
|
||||
@@ -888,6 +1154,7 @@ private:
|
||||
CubeState m_cube;
|
||||
std::vector<CubeEvent> m_cube_events;
|
||||
CubeResultEntry m_cube_last_success;
|
||||
std::vector<ServerCommandEvent> m_server_commands;
|
||||
void apply_server_command(const std::string &line);
|
||||
|
||||
// P9 world systems
|
||||
@@ -905,8 +1172,11 @@ private:
|
||||
// guild / refine
|
||||
GuildState m_guild;
|
||||
std::unordered_map<uint32_t, GuildMember> m_guild_members;
|
||||
std::vector<GuildComment> m_guild_comments;
|
||||
std::vector<GuildInvite> m_guild_invites;
|
||||
GuildGrade m_guild_grades[16];
|
||||
bool m_guild_dirty = false;
|
||||
bool m_guild_comments_dirty = false;
|
||||
GuildSkillState m_guild_skill;
|
||||
bool m_guild_skill_dirty = false;
|
||||
GuildWarStatus m_guild_war;
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
uint32_t play_minutes = 0;
|
||||
int st = 0, ht = 0, dx = 0, iq = 0;
|
||||
int main_part = 0, hair_part = 0;
|
||||
bool change_name = false;
|
||||
};
|
||||
|
||||
static void fill_slot_stats(CharSlot &c, const SimplePlayerInfo &s) {
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
c.iq = s.iq;
|
||||
c.main_part = s.main_part;
|
||||
c.hair_part = s.hair_part;
|
||||
c.change_name = s.change_name != 0;
|
||||
}
|
||||
|
||||
// guild-mark side connection auth (from GC_LOGIN_SUCCESS3/4).
|
||||
@@ -582,6 +584,70 @@ public:
|
||||
std::memcpy(buf + sizeof(head), &arg, 4);
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
bool send_guild_sub_i32(uint8_t subheader, int32_t arg) {
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(arg)];
|
||||
CGGuild head{CG_GUILD, (uint16_t)sizeof(buf), subheader};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &arg, sizeof(arg));
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
bool send_guild_grade_name(uint8_t grade, const std::string &name) {
|
||||
if (grade == 0 || grade >= 16 || name.empty() || name.size() > GUILD_GRADE_NAME_MAX_LEN) {
|
||||
return false;
|
||||
}
|
||||
uint8_t buf[sizeof(CGGuild) + 1 + GUILD_GRADE_NAME_MAX_LEN + 1] = {};
|
||||
CGGuild head{CG_GUILD, (uint16_t)sizeof(buf), GUILD_CG_CHANGE_GRADE_NAME};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
buf[sizeof(head)] = grade;
|
||||
std::memcpy(buf + sizeof(head) + 1, name.data(), name.size());
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
bool send_guild_grade_authority(uint8_t grade, uint8_t authority) {
|
||||
if (grade == 0 || grade >= 16) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + 2];
|
||||
CGGuild head{CG_GUILD, (uint16_t)sizeof(buf), GUILD_CG_CHANGE_GRADE_AUTHORITY};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
buf[sizeof(head)] = grade;
|
||||
buf[sizeof(head) + 1] = authority;
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
bool send_guild_member_grade(uint32_t pid, uint8_t grade) {
|
||||
if (pid == 0 || grade == 0 || grade >= 16) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(pid) + 1];
|
||||
CGGuild head{CG_GUILD, (uint16_t)sizeof(buf), GUILD_CG_CHANGE_MEMBER_GRADE};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &pid, sizeof(pid));
|
||||
buf[sizeof(head) + sizeof(pid)] = grade;
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
bool send_guild_member_general(uint32_t pid, bool enabled) {
|
||||
if (pid == 0) return false;
|
||||
uint8_t buf[sizeof(CGGuild) + sizeof(pid) + 1];
|
||||
CGGuild head{CG_GUILD, (uint16_t)sizeof(buf), GUILD_CG_CHANGE_MEMBER_GENERAL};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &pid, sizeof(pid));
|
||||
buf[sizeof(head) + sizeof(pid)] = enabled ? 1 : 0;
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
bool send_guild_comment(const std::string &text) {
|
||||
if (text.empty() || text.size() + 1 > 50) return false;
|
||||
std::vector<uint8_t> buf(sizeof(CGGuild) + 1 + text.size() + 1, 0);
|
||||
CGGuild head{CG_GUILD, (uint16_t)buf.size(), GUILD_CG_POST_COMMENT};
|
||||
std::memcpy(buf.data(), &head, sizeof(head));
|
||||
buf[sizeof(head)] = static_cast<uint8_t>(text.size() + 1);
|
||||
if (!text.empty()) {
|
||||
std::memcpy(buf.data() + sizeof(head) + 1, text.data(), text.size());
|
||||
}
|
||||
return send_packet(buf.data(), buf.size());
|
||||
}
|
||||
bool send_guild_invite_answer(uint32_t guild_id, bool accept) {
|
||||
uint8_t buf[sizeof(CGGuild) + 4 + 1];
|
||||
CGGuild head{CG_GUILD, (uint16_t)sizeof(buf), GUILD_CG_INVITE_ANSWER};
|
||||
std::memcpy(buf, &head, sizeof(head));
|
||||
std::memcpy(buf + sizeof(head), &guild_id, sizeof(guild_id));
|
||||
buf[sizeof(head) + sizeof(guild_id)] = accept ? 1 : 0;
|
||||
return send_packet(buf, sizeof(buf));
|
||||
}
|
||||
// GUILD_CG_USE_SKILL: sub-header + {u32 skill_vnum, u32 target_vid}.
|
||||
bool send_guild_use_skill(uint32_t skill_vnum, uint32_t target_vid) {
|
||||
uint8_t buf[sizeof(CGGuild) + 8];
|
||||
@@ -744,6 +810,7 @@ protected:
|
||||
for (auto &slot : m_chars) {
|
||||
if (slot.id == p.pid) {
|
||||
slot.name = name;
|
||||
slot.change_name = false;
|
||||
}
|
||||
}
|
||||
m_name_events.push_back({p.pid, std::move(name)});
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
// INetSession — the seam M2Client will talk to so the m2dev-fork stack and the
|
||||
// 40250 "classic" stack are swappable via MT_PROTOCOL (docs/CLIENT-40250-PORT.md
|
||||
// §6). Lifecycle + pump only for now; the per-intent send_* surface is added as
|
||||
// the classic backend grows (both backends already share EntityStore).
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace mtnet {
|
||||
|
||||
class EntityStore;
|
||||
|
||||
class INetSession {
|
||||
public:
|
||||
virtual ~INetSession() = default;
|
||||
|
||||
// Coarse lifecycle state for the host node's state machine.
|
||||
enum class Stage { Offline, Connecting, LoggingIn, CharSelect, Loading, InGame, Failed };
|
||||
|
||||
// auth_host/port may equal game_host/port when there is no separate auth server.
|
||||
virtual bool connect(const std::string &game_host, uint16_t game_port,
|
||||
const std::string &id, const std::string &pw) = 0;
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
// pick a character-list slot (0-based); sends the select packet.
|
||||
virtual bool select_char(int slot) = 0;
|
||||
// explicit CG_ENTERGAME once loading is done (backends may also send it themselves).
|
||||
virtual bool enter_game() = 0;
|
||||
|
||||
// pump the socket once per frame: I/O + parse into the shared EntityStore.
|
||||
virtual void pump() = 0;
|
||||
|
||||
virtual Stage stage() const = 0;
|
||||
virtual bool in_game() const { return stage() == Stage::InGame; }
|
||||
virtual const std::string &last_error() const = 0;
|
||||
|
||||
// The world model both backends write into.
|
||||
virtual EntityStore &world() = 0;
|
||||
|
||||
// --- in-game intents (only valid once in_game()) -----------------------
|
||||
// x/y are server cm; rot_deg is a compass heading; time_ms from the frame
|
||||
// clock (0 = let the backend fill it). window: 1=inventory, 2=equipment.
|
||||
virtual bool send_move(uint8_t func, uint8_t arg, float rot_deg, int32_t x, int32_t y,
|
||||
uint32_t time_ms = 0) = 0;
|
||||
virtual bool send_attack(uint8_t type, uint32_t victim_vid) = 0;
|
||||
virtual bool send_chat(uint8_t type, const std::string &text) = 0;
|
||||
virtual bool send_target(uint32_t vid) = 0;
|
||||
virtual bool send_character_position(uint8_t position) = 0;
|
||||
virtual bool send_click_npc(uint32_t vid) = 0;
|
||||
virtual bool send_script_answer(uint8_t answer) = 0;
|
||||
virtual bool send_item_use(uint8_t window, uint16_t cell) = 0;
|
||||
virtual bool send_item_move(uint8_t window, uint16_t cell, uint8_t to_window, uint16_t to_cell,
|
||||
uint8_t count) = 0;
|
||||
virtual bool send_item_drop(uint8_t window, uint16_t cell, uint32_t gold, uint8_t count) = 0;
|
||||
virtual bool send_item_pickup(uint32_t ground_vid) = 0;
|
||||
// sends CG_FLY_TARGETING first when target_vid != 0, matching the legacy order.
|
||||
virtual bool send_use_skill(uint32_t skill_vnum, uint32_t target_vid) = 0;
|
||||
|
||||
// --- P8 social / exchange / storage intents -----------------------------
|
||||
virtual bool send_friend_add(const std::string &name) = 0;
|
||||
virtual bool send_friend_remove(const std::string &name) = 0;
|
||||
virtual bool send_exchange_start(uint32_t partner_vid) = 0;
|
||||
virtual bool send_exchange_item_add(uint8_t inv_window, uint16_t inv_cell,
|
||||
uint8_t display_pos) = 0;
|
||||
virtual bool send_exchange_gold(uint32_t gold) = 0;
|
||||
virtual bool send_exchange_accept() = 0;
|
||||
virtual bool send_exchange_cancel() = 0;
|
||||
virtual bool send_safebox_checkin(uint8_t safe_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) = 0;
|
||||
virtual bool send_safebox_checkout(uint8_t safe_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) = 0;
|
||||
virtual bool send_safebox_move(uint16_t from_cell, uint16_t to_cell, uint8_t count) = 0;
|
||||
virtual bool send_mall_checkout(uint8_t mall_pos, uint8_t inv_window,
|
||||
uint16_t inv_cell) = 0;
|
||||
};
|
||||
|
||||
} // namespace mtnet
|
||||
+1434
-146
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,11 @@ class AuthClient;
|
||||
class GameClient;
|
||||
class MarkClient;
|
||||
class MarkImageSet;
|
||||
class EntityStore;
|
||||
namespace classic {
|
||||
class ClassicSession;
|
||||
class ClassicMarkClient;
|
||||
}
|
||||
} // namespace mtnet
|
||||
|
||||
namespace mtgodot {
|
||||
@@ -61,6 +66,9 @@ public:
|
||||
bool change_name(int slot, const godot::String &name);
|
||||
// GC_EMPIRE (0 = server still wants an empire pick before select).
|
||||
int get_empire() const;
|
||||
// CG_EMPIRE: choose one of the three empires when the account has no empire.
|
||||
// Both network backends expose the same intent for the character-select UI.
|
||||
bool select_empire(int empire);
|
||||
// 3 or 4 depending on which GC_LOGIN_SUCCESS the server sent.
|
||||
int get_slot_count() const;
|
||||
|
||||
@@ -120,6 +128,7 @@ public:
|
||||
// --- P8 messenger / friends ---
|
||||
bool add_friend(const godot::String &name);
|
||||
bool remove_friend(const godot::String &name);
|
||||
bool friend_answer(const godot::String &name, bool accept);
|
||||
godot::Array get_friends() const; // [{name, online}]
|
||||
godot::Dictionary get_lover() const; // {valid, name, love_point}
|
||||
|
||||
@@ -147,12 +156,14 @@ public:
|
||||
int get_safebox_size() const;
|
||||
int get_safebox_gold() const;
|
||||
godot::Array get_safebox_items() const; // [{cell, vnum, count}]
|
||||
bool safebox_password(const godot::String &password);
|
||||
|
||||
// --- item-mall (창고몰) ---
|
||||
bool is_mall_open() const;
|
||||
int get_mall_size() const;
|
||||
godot::Array get_mall_items() const; // [{cell, vnum, count}]
|
||||
bool mall_checkout(int mall_pos, int inv_window, int inv_cell);
|
||||
bool mall_password(const godot::String &password);
|
||||
|
||||
// --- private (PC) shop ---
|
||||
// items: Array of {vnum, count, inv_cell, price, display_pos}
|
||||
@@ -164,6 +175,11 @@ public:
|
||||
bool cube_make(int result_index);
|
||||
bool cube_request_result_list(int npc_vnum);
|
||||
bool cube_request_materials(int start_index, int count);
|
||||
bool cube_open();
|
||||
bool cube_close();
|
||||
bool cube_list();
|
||||
bool cube_add_item(int cube_index, int inventory_index);
|
||||
bool cube_delete_item(int cube_index);
|
||||
|
||||
// --- guild ---
|
||||
godot::Dictionary get_guild() const; // {in_guild, id, name, level, exp, gold, ...}
|
||||
@@ -172,7 +188,17 @@ public:
|
||||
bool guild_add_member(int vid);
|
||||
bool guild_remove_member(int pid);
|
||||
bool guild_offer(int amount);
|
||||
bool guild_charge_gsp(int amount);
|
||||
bool guild_change_grade_name(int grade, const godot::String &name);
|
||||
bool guild_change_grade_authority(int grade, int authority);
|
||||
bool guild_change_member_grade(int pid, int grade);
|
||||
bool guild_change_member_general(int pid, bool enabled);
|
||||
bool guild_post_comment(const godot::String &text);
|
||||
bool guild_delete_comment(int comment_id);
|
||||
bool guild_refresh_comments();
|
||||
bool guild_answer_invite(int guild_id, bool accept);
|
||||
bool guild_answer_make(const godot::String &name);
|
||||
godot::Array get_guild_comments() const; // [{id, name, content}]
|
||||
|
||||
// --- guild war / guild skill ---
|
||||
godot::Dictionary get_guild_skill() const; // {valid, skill_point, guild_point, max_guild_point, levels[12]}
|
||||
@@ -223,6 +249,11 @@ public:
|
||||
godot::Array get_land_areas() const; // [{id, guild_id, rect}] in server cm
|
||||
godot::Array get_observers() const; // [{vid, pos}] in Godot metres
|
||||
godot::Array get_world_markers() const; // [{id, name, vid, type, pos}]
|
||||
bool is_observer_mode() const;
|
||||
int get_observer_count() const;
|
||||
bool has_mobile_flag() const;
|
||||
bool combo_skill_enabled() const;
|
||||
godot::Dictionary get_stamina_state() const; // {consuming, per_sec, current}
|
||||
|
||||
// items. window: 1=inventory, 2=equipment (mtnet::WINDOW_*).
|
||||
bool move_item(int from_window, int from_cell, int to_window, int to_cell, int count);
|
||||
@@ -239,11 +270,13 @@ public:
|
||||
godot::Dictionary get_item(int window, int cell) const;
|
||||
godot::Array get_ground_items() const; // {vid, vnum, pos}
|
||||
godot::Array get_pvp_relations() const; // [{src_vid, dst_vid, mode}]
|
||||
godot::Dictionary get_duel() const; // {active, cannot_attack, opponents}
|
||||
|
||||
// --- networked world snapshot (positions already Godot-space, metres) ---
|
||||
godot::Dictionary get_entity(int vid) const;
|
||||
godot::Array get_entities() const;
|
||||
int get_main_vid() const;
|
||||
int get_main_pid() const;
|
||||
godot::Dictionary get_points() const; // local player stat block
|
||||
godot::Dictionary get_target() const; // {vid, hp_percent} of selected target
|
||||
godot::Array get_affects() const; // active buffs/debuffs on the local player
|
||||
@@ -287,7 +320,17 @@ private:
|
||||
|
||||
std::unique_ptr<mtnet::AuthClient> auth;
|
||||
std::unique_ptr<mtnet::GameClient> game;
|
||||
// MT_PROTOCOL=classic (40250): replaces auth+game for the flows it supports
|
||||
// (connect/login/select/enter-game/world/intents). Null otherwise.
|
||||
std::unique_ptr<mtnet::classic::ClassicSession> classic_sess;
|
||||
bool classic_list_emitted = false;
|
||||
int classic_last_stage = -1;
|
||||
int classic_last_empire = -1;
|
||||
void pump_classic();
|
||||
// the EntityStore of whichever backend is active (classic_sess or game).
|
||||
const mtnet::EntityStore *active_world() const;
|
||||
std::unique_ptr<mtnet::MarkClient> mark;
|
||||
std::unique_ptr<mtnet::classic::ClassicMarkClient> classic_mark;
|
||||
std::unique_ptr<mtnet::MarkImageSet> mark_store; // survives after `mark` is torn down
|
||||
godot::String mark_host;
|
||||
int mark_port = 0;
|
||||
@@ -295,11 +338,14 @@ private:
|
||||
uint32_t symbol_guild_id = 0;
|
||||
std::vector<uint8_t> symbol_data;
|
||||
void pump_mark();
|
||||
void pump_classic_mark();
|
||||
|
||||
int last_auth_state = -1;
|
||||
int last_game_state = -1;
|
||||
int last_game_phase = -1;
|
||||
int last_game_empire = -1;
|
||||
bool char_list_emitted = false;
|
||||
uint32_t selected_pid = 0;
|
||||
godot::Array build_char_list() const;
|
||||
// CG_ENTERGAME is sent ~1.5s into PHASE_LOADING (not immediately) — sending
|
||||
// it before the server finishes the spawn burst makes it drop us ~10s later.
|
||||
|
||||
@@ -326,6 +326,8 @@ enum : uint8_t {
|
||||
POINT_MAX_HP = 6,
|
||||
POINT_SP = 7,
|
||||
POINT_MAX_SP = 8,
|
||||
POINT_STAMINA = 9,
|
||||
POINT_MAX_STAMINA = 10,
|
||||
POINT_GOLD = 11,
|
||||
POINT_ATT_SPEED = 17,
|
||||
POINT_ENERGY = 128,
|
||||
@@ -457,9 +459,13 @@ enum : uint8_t { // GuildSub::CG
|
||||
GUILD_CG_CHANGE_GRADE_AUTHORITY = 3,
|
||||
GUILD_CG_OFFER = 4,
|
||||
GUILD_CG_POST_COMMENT = 5,
|
||||
GUILD_CG_DELETE_COMMENT = 6,
|
||||
GUILD_CG_REFRESH_COMMENT = 7,
|
||||
GUILD_CG_CHANGE_MEMBER_GRADE = 8,
|
||||
GUILD_CG_USE_SKILL = 9,
|
||||
GUILD_CG_CHANGE_MEMBER_GENERAL = 10,
|
||||
GUILD_CG_INVITE_ANSWER = 11,
|
||||
GUILD_CG_CHARGE_GSP = 12,
|
||||
GUILD_CG_DEPOSIT_MONEY = 13,
|
||||
GUILD_CG_WITHDRAW_MONEY = 14,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// net_classic_cipher_test — 40250 _IMPROVED_PACKET_ENCRYPTION_ end to end,
|
||||
// no socket: a client-polarity ClassicCipher and a server-polarity one run the
|
||||
// DH2 key agreement against each other, then encrypt/decrypt must round-trip
|
||||
// symmetrically (client.encrypt -> server.decrypt and vice versa).
|
||||
#include "../src/net/classic/classic_cipher.h"
|
||||
#include "../src/net/classic/wire_classic.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace mtnet::classic;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(c, msg) \
|
||||
do { \
|
||||
if (!(c)) { \
|
||||
std::fprintf(stderr, "FAIL: %s\n", msg); \
|
||||
++g_fail; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int main() {
|
||||
ClassicCipher client; // polarity = true (EterLib/NetStream.cpp:3004)
|
||||
ClassicCipher server; // polarity = false (desc.cpp FinishHandshake)
|
||||
|
||||
// Each side generates its DH2 public blob (spub || epub).
|
||||
uint8_t cbuf[KEY_AGREEMENT_MAX_DATA_LEN];
|
||||
uint8_t sbuf[KEY_AGREEMENT_MAX_DATA_LEN];
|
||||
size_t clen = sizeof(cbuf);
|
||||
size_t slen = sizeof(sbuf);
|
||||
size_t cagreed = client.prepare(cbuf, &clen);
|
||||
size_t sagreed = server.prepare(sbuf, &slen);
|
||||
|
||||
CHECK(cagreed != 0, "client prepare -> non-zero agreed length");
|
||||
CHECK(sagreed != 0, "server prepare -> non-zero agreed length");
|
||||
CHECK(cagreed == sagreed, "both sides same agreed-value length (same DH2 params)");
|
||||
CHECK(clen > 0 && clen <= KEY_AGREEMENT_MAX_DATA_LEN, "client blob length sane");
|
||||
CHECK(clen == slen, "blob lengths match");
|
||||
|
||||
// Activate: each consumes the PEER's (agreed_length, blob).
|
||||
bool ca = client.activate(true, sagreed, sbuf, slen);
|
||||
bool sa = server.activate(false, cagreed, cbuf, clen);
|
||||
CHECK(ca, "client activate");
|
||||
CHECK(sa, "server activate");
|
||||
CHECK(client.key_ready() && server.key_ready(), "both have encoder+decoder");
|
||||
|
||||
// The stream turns real only after GC_KEY_AGREEMENT_COMPLETED.
|
||||
CHECK(!client.activated(), "cipher not 'activated' until set_activated");
|
||||
client.set_activated(true);
|
||||
server.set_activated(true);
|
||||
|
||||
// --- round trip: client -> server ---
|
||||
{
|
||||
std::string plain = "the quick brown fox CG_MOVE \x07\x01\x00\x00 jumps";
|
||||
std::vector<uint8_t> buf(plain.begin(), plain.end());
|
||||
std::vector<uint8_t> orig = buf;
|
||||
client.encrypt(buf.data(), buf.size());
|
||||
CHECK(buf != orig, "ciphertext differs from plaintext");
|
||||
server.decrypt(buf.data(), buf.size());
|
||||
CHECK(buf == orig, "server recovers client's plaintext");
|
||||
}
|
||||
|
||||
// --- round trip: server -> client, and CTR keystream continuity ---
|
||||
{
|
||||
std::string a = "GC_MAIN_CHARACTER hello world 0123456789";
|
||||
std::string b = "second chunk keeps the CTR counter advancing";
|
||||
std::vector<uint8_t> ba(a.begin(), a.end()), bb(b.begin(), b.end());
|
||||
std::vector<uint8_t> oa = ba, ob = bb;
|
||||
server.encrypt(ba.data(), ba.size());
|
||||
server.encrypt(bb.data(), bb.size());
|
||||
client.decrypt(ba.data(), ba.size());
|
||||
client.decrypt(bb.data(), bb.size());
|
||||
CHECK(ba == oa && bb == ob, "client recovers server's two chunks (CTR continuity)");
|
||||
}
|
||||
|
||||
// --- a fresh pair must derive a *different* key (ephemeral) ---
|
||||
{
|
||||
ClassicCipher c2, s2;
|
||||
uint8_t x[256], y[256];
|
||||
size_t xl = sizeof(x), yl = sizeof(y);
|
||||
size_t xa = c2.prepare(x, &xl);
|
||||
size_t ya = s2.prepare(y, &yl);
|
||||
c2.activate(true, ya, y, yl);
|
||||
s2.activate(false, xa, x, xl);
|
||||
c2.set_activated(true);
|
||||
s2.set_activated(true);
|
||||
|
||||
std::string msg = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // 32 identical bytes
|
||||
std::vector<uint8_t> p1(msg.begin(), msg.end());
|
||||
std::vector<uint8_t> p2 = p1;
|
||||
client.encrypt(p1.data(), p1.size()); // from the first session (already advanced)
|
||||
c2.encrypt(p2.data(), p2.size()); // fresh session
|
||||
CHECK(p1 != p2, "different sessions produce different ciphertext");
|
||||
}
|
||||
|
||||
if (g_fail) {
|
||||
std::fprintf(stderr, "%d check(s) failed\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
std::puts("net_classic_cipher_test OK");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// net_classic_encstream_test — ClassicStream with _IMPROVED_PACKET_ENCRYPTION_.
|
||||
// A server-side ClassicCipher plays the peer: HANDSHAKE -> GC_KEY_AGREEMENT ->
|
||||
// (stream replies CG_KEY_AGREEMENT) -> GC_KEY_AGREEMENT_COMPLETED. After that
|
||||
// every byte both ways is CTR-encrypted; the test verifies the stream decrypts
|
||||
// an inbound GC_PHASE and that an outbound CG_MOVE decrypts cleanly server-side.
|
||||
#include "../src/net/classic/classic_cipher.h"
|
||||
#include "../src/net/classic/classic_stream.h"
|
||||
#include "../src/net/classic/wire_classic.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace mtnet::classic;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(c, msg) \
|
||||
do { \
|
||||
if (!(c)) { \
|
||||
std::fprintf(stderr, "FAIL: %s\n", msg); \
|
||||
++g_fail; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
template <class T>
|
||||
static std::vector<uint8_t> raw(const T &s) {
|
||||
std::vector<uint8_t> b(sizeof(T));
|
||||
std::memcpy(b.data(), &s, sizeof(T));
|
||||
return b;
|
||||
}
|
||||
static std::vector<uint8_t> drain(ClassicStream &s) {
|
||||
std::vector<uint8_t> out;
|
||||
uint8_t buf[2048];
|
||||
for (size_t n; (n = s.take_outgoing(buf, sizeof(buf)));) {
|
||||
out.insert(out.end(), buf, buf + n);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int main() {
|
||||
ClassicStream cs;
|
||||
cs.set_polarity(true); // client
|
||||
int phase_seen = -1;
|
||||
std::string err;
|
||||
bool cipher_on = false;
|
||||
cs.on_phase = [&](uint8_t p) { phase_seen = p; };
|
||||
cs.on_error = [&](const std::string &e) { err = e; };
|
||||
cs.on_cipher_active = [&]() { cipher_on = true; };
|
||||
|
||||
ClassicCipher server; // polarity = false
|
||||
|
||||
// --- handshake (plaintext) ---
|
||||
{
|
||||
Handshake hs{};
|
||||
hs.header = HDR_HANDSHAKE;
|
||||
hs.time = 1000;
|
||||
hs.delta = 5;
|
||||
cs.feed(raw(hs).data(), sizeof(hs));
|
||||
CHECK(drain(cs).size() == sizeof(Handshake), "handshake echo (plaintext)");
|
||||
}
|
||||
|
||||
// --- GC_KEY_AGREEMENT: server sends its DH2 blob ---
|
||||
{
|
||||
KeyAgreement srv_pkt{};
|
||||
srv_pkt.header = HDR_KEY_AGREEMENT;
|
||||
size_t dl = sizeof(srv_pkt.data);
|
||||
size_t agreed = server.prepare(srv_pkt.data, &dl);
|
||||
CHECK(agreed != 0, "server DH2 prepare");
|
||||
srv_pkt.agreed_length = (uint16_t)agreed;
|
||||
srv_pkt.data_length = (uint16_t)dl;
|
||||
|
||||
cs.feed(raw(srv_pkt).data(), sizeof(srv_pkt));
|
||||
|
||||
// stream must have replied with CG_KEY_AGREEMENT (plaintext, its own blob)
|
||||
auto reply = drain(cs);
|
||||
CHECK(reply.size() == sizeof(KeyAgreement), "CG_KEY_AGREEMENT reply is 261 bytes");
|
||||
CHECK(reply[0] == HDR_KEY_AGREEMENT, "reply header 0xFB");
|
||||
KeyAgreement cli_pkt{};
|
||||
std::memcpy(&cli_pkt, reply.data(), sizeof(cli_pkt));
|
||||
CHECK(err.empty(), "no error during key agreement");
|
||||
|
||||
// server consumes the client's blob
|
||||
CHECK(server.activate(false, cli_pkt.agreed_length, cli_pkt.data, cli_pkt.data_length),
|
||||
"server activate with client blob");
|
||||
CHECK(!cs.cipher_active(), "stream cipher not active until COMPLETED");
|
||||
}
|
||||
|
||||
// --- GC_KEY_AGREEMENT_COMPLETED (plaintext) -> both sides go live ---
|
||||
{
|
||||
KeyAgreementCompleted done{};
|
||||
done.header = HDR_GC_KEY_AGREEMENT_COMPLETED;
|
||||
cs.feed(raw(done).data(), sizeof(done));
|
||||
server.set_activated(true);
|
||||
CHECK(cs.cipher_active() && cipher_on, "stream cipher active after COMPLETED");
|
||||
}
|
||||
|
||||
// --- server -> client: an ENCRYPTED GC_PHASE(GAME) must decrypt + dispatch ---
|
||||
{
|
||||
auto pkt = raw(Phase_{HDR_GC_PHASE, PHASE_GAME});
|
||||
server.encrypt(pkt.data(), pkt.size());
|
||||
CHECK(pkt[0] != HDR_GC_PHASE, "GC_PHASE really is ciphertext on the wire");
|
||||
cs.feed(pkt.data(), pkt.size());
|
||||
CHECK(phase_seen == PHASE_GAME, "stream decrypted + dispatched GC_PHASE(GAME)");
|
||||
}
|
||||
|
||||
// --- client -> server: an outbound CG_MOVE must be ciphertext the server reads ---
|
||||
{
|
||||
cs.set_sequence_mode(true);
|
||||
CGMove mv{};
|
||||
mv.header = HDR_CG_MOVE;
|
||||
mv.func = 1;
|
||||
mv.x = 12345;
|
||||
mv.y = 67890;
|
||||
cs.send_fixed(&mv, sizeof(mv));
|
||||
auto wire = drain(cs);
|
||||
CHECK(wire.size() == sizeof(CGMove) + 1, "CG_MOVE + seq byte on the wire");
|
||||
CHECK(wire[0] != HDR_CG_MOVE, "CG_MOVE header is encrypted on the wire");
|
||||
|
||||
server.decrypt(wire.data(), wire.size());
|
||||
CGMove got{};
|
||||
std::memcpy(&got, wire.data(), sizeof(got));
|
||||
CHECK(got.header == HDR_CG_MOVE && got.func == 1 && got.x == 12345 && got.y == 67890,
|
||||
"server decrypts CG_MOVE body");
|
||||
CHECK(wire[sizeof(CGMove)] == SEQUENCE_TABLE[0], "seq byte decrypts to table[0]");
|
||||
}
|
||||
|
||||
// --- pipelined case: COMPLETED + encrypted packet in one feed() ---
|
||||
{
|
||||
ClassicStream cs2;
|
||||
cs2.set_polarity(true);
|
||||
int seen2 = -1;
|
||||
cs2.on_phase = [&](uint8_t p) { seen2 = p; };
|
||||
ClassicCipher srv2;
|
||||
|
||||
Handshake hs{};
|
||||
hs.header = HDR_HANDSHAKE;
|
||||
cs2.feed(raw(hs).data(), sizeof(hs));
|
||||
drain(cs2);
|
||||
|
||||
KeyAgreement sp{};
|
||||
sp.header = HDR_KEY_AGREEMENT;
|
||||
size_t dl = sizeof(sp.data);
|
||||
sp.agreed_length = (uint16_t)srv2.prepare(sp.data, &dl);
|
||||
sp.data_length = (uint16_t)dl;
|
||||
cs2.feed(raw(sp).data(), sizeof(sp));
|
||||
auto rep = drain(cs2);
|
||||
KeyAgreement cp{};
|
||||
std::memcpy(&cp, rep.data(), sizeof(cp));
|
||||
srv2.activate(false, cp.agreed_length, cp.data, cp.data_length);
|
||||
srv2.set_activated(true);
|
||||
|
||||
// one buffer: [COMPLETED plaintext][encrypted GC_PHASE(SELECT)]
|
||||
std::vector<uint8_t> blob = raw(KeyAgreementCompleted{HDR_GC_KEY_AGREEMENT_COMPLETED, {}});
|
||||
auto enc = raw(Phase_{HDR_GC_PHASE, PHASE_SELECT});
|
||||
srv2.encrypt(enc.data(), enc.size());
|
||||
blob.insert(blob.end(), enc.begin(), enc.end());
|
||||
cs2.feed(blob.data(), blob.size());
|
||||
CHECK(seen2 == PHASE_SELECT, "pipelined COMPLETED+encrypted packet handled");
|
||||
}
|
||||
|
||||
if (g_fail) {
|
||||
std::fprintf(stderr, "%d check(s) failed\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
std::puts("net_classic_encstream_test OK");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "classic/classic_mark_client.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
using namespace mtnet::classic;
|
||||
|
||||
static std::vector<uint8_t> take_all(ClassicStream &stream) {
|
||||
std::vector<uint8_t> out(stream.outgoing_pending());
|
||||
if (!out.empty()) {
|
||||
assert(stream.take_outgoing(out.data(), out.size()) == out.size());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void put_u16(std::vector<uint8_t> &p, size_t off, uint16_t v) {
|
||||
p[off] = static_cast<uint8_t>(v);
|
||||
p[off + 1] = static_cast<uint8_t>(v >> 8);
|
||||
}
|
||||
|
||||
static void put_u32(std::vector<uint8_t> &p, size_t off, uint32_t v) {
|
||||
p[off] = static_cast<uint8_t>(v);
|
||||
p[off + 1] = static_cast<uint8_t>(v >> 8);
|
||||
p[off + 2] = static_cast<uint8_t>(v >> 16);
|
||||
p[off + 3] = static_cast<uint8_t>(v >> 24);
|
||||
}
|
||||
|
||||
static void test_mark_download() {
|
||||
ClassicMarkClient client(0x11223344u, 0x55667788u);
|
||||
Handshake handshake{};
|
||||
handshake.header = HDR_HANDSHAKE;
|
||||
handshake.handshake = 7;
|
||||
handshake.time = 100;
|
||||
client.stream().feed(&handshake, sizeof(handshake));
|
||||
std::vector<uint8_t> login = take_all(client.stream());
|
||||
assert(login.size() == sizeof(Handshake) + sizeof(CGMarkLogin));
|
||||
assert(login[sizeof(Handshake)] == HDR_CG_MARK_LOGIN);
|
||||
uint32_t handle = 0;
|
||||
std::memcpy(&handle, login.data() + sizeof(Handshake) + 1, sizeof(handle));
|
||||
assert(handle == 0x11223344u);
|
||||
uint32_t random_key = 0;
|
||||
std::memcpy(&random_key, login.data() + sizeof(Handshake) + 5, sizeof(random_key));
|
||||
assert(random_key == 0x55667788u);
|
||||
|
||||
Handshake resync = handshake;
|
||||
resync.time = 200;
|
||||
resync.delta = 3;
|
||||
client.stream().feed(&resync, sizeof(resync));
|
||||
std::vector<uint8_t> repeated_login = take_all(client.stream());
|
||||
assert(repeated_login.size() == sizeof(Handshake) + sizeof(CGMarkLogin));
|
||||
assert(repeated_login[0] == HDR_HANDSHAKE);
|
||||
assert(repeated_login[sizeof(Handshake)] == HDR_CG_MARK_LOGIN);
|
||||
|
||||
std::vector<uint8_t> phase{HDR_GC_PHASE, PHASE_LOGIN};
|
||||
client.stream().feed(phase.data(), phase.size());
|
||||
std::vector<uint8_t> request = take_all(client.stream());
|
||||
assert(request.size() == sizeof(CGMarkIDXList));
|
||||
assert(request[0] == HDR_CG_MARK_IDXLIST);
|
||||
|
||||
// One guild maps to image zero, position one. Feed the whole-size frame in
|
||||
// two chunks to exercise the custom raw framing path.
|
||||
std::vector<uint8_t> idx(sizeof(GCMarkIDXList) + 4);
|
||||
idx[0] = HDR_GC_MARK_IDXLIST;
|
||||
put_u32(idx, 1, static_cast<uint32_t>(idx.size()));
|
||||
put_u16(idx, 5, 1);
|
||||
put_u16(idx, 7, 42);
|
||||
put_u16(idx, 9, 1);
|
||||
client.stream().feed(idx.data(), 6);
|
||||
assert(client.stream().outgoing_pending() == 0);
|
||||
client.stream().feed(idx.data() + 6, idx.size() - 6);
|
||||
request = take_all(client.stream());
|
||||
assert(request.size() == sizeof(CGMarkCRCList));
|
||||
assert(request[0] == HDR_CG_MARK_CRCLIST && request[1] == 0);
|
||||
assert(client.marks().has_mark(42));
|
||||
|
||||
// A valid zero-block image response advances the image state and completes
|
||||
// the download without requiring LZO data in this framing test.
|
||||
std::vector<uint8_t> block(sizeof(GCMarkBlock));
|
||||
block[0] = HDR_GC_MARK_BLOCK;
|
||||
put_u32(block, 1, static_cast<uint32_t>(block.size()));
|
||||
block[5] = 0;
|
||||
put_u32(block, 6, 0);
|
||||
client.stream().feed(block.data(), block.size());
|
||||
assert(client.complete());
|
||||
}
|
||||
|
||||
static void test_symbol_download() {
|
||||
ClassicMarkClient client(0x11u, 0x22u);
|
||||
client.set_download_symbol(42);
|
||||
std::vector<uint8_t> phase{HDR_GC_PHASE, PHASE_LOGIN};
|
||||
client.stream().feed(phase.data(), phase.size());
|
||||
std::vector<uint8_t> request = take_all(client.stream());
|
||||
assert(request.size() == sizeof(CGSymbolCRC));
|
||||
assert(request[0] == HDR_CG_SYMBOL_CRC);
|
||||
uint32_t gid = 0;
|
||||
std::memcpy(&gid, request.data() + 1, sizeof(gid));
|
||||
assert(gid == 42);
|
||||
|
||||
const std::vector<uint8_t> payload{0xAA, 0xBB, 0xCC};
|
||||
std::vector<uint8_t> symbol(sizeof(GCSymbolData) + payload.size());
|
||||
symbol[0] = HDR_GC_SYMBOL_DATA;
|
||||
put_u16(symbol, 1, static_cast<uint16_t>(symbol.size()));
|
||||
put_u32(symbol, 3, 42);
|
||||
std::memcpy(symbol.data() + sizeof(GCSymbolData), payload.data(), payload.size());
|
||||
client.stream().feed(symbol.data(), symbol.size());
|
||||
assert(client.complete());
|
||||
assert(client.symbol_guild_id() == 42);
|
||||
assert(client.symbol_data() == payload);
|
||||
}
|
||||
|
||||
static void test_upload_layouts() {
|
||||
uint32_t px[GUILD_MARK_WIDTH * GUILD_MARK_HEIGHT] = {};
|
||||
px[0] = 0x44332211u;
|
||||
ClassicMarkClient mark(1, 2);
|
||||
mark.set_upload_mark(42, px);
|
||||
std::vector<uint8_t> phase{HDR_GC_PHASE, PHASE_LOGIN};
|
||||
mark.stream().feed(phase.data(), phase.size());
|
||||
std::vector<uint8_t> packet = take_all(mark.stream());
|
||||
assert(packet.size() == sizeof(CGMarkUpload));
|
||||
assert(packet[0] == HDR_CG_MARK_UPLOAD);
|
||||
uint32_t gid = 0;
|
||||
std::memcpy(&gid, packet.data() + 1, sizeof(gid));
|
||||
assert(gid == 42);
|
||||
assert(packet[5] == 0x11 && packet[6] == 0x22 && packet[7] == 0x33 && packet[8] == 0x44);
|
||||
|
||||
ClassicMarkClient symbol(3, 4);
|
||||
symbol.set_upload_symbol(99, std::vector<uint8_t>{1, 2, 3});
|
||||
symbol.stream().feed(phase.data(), phase.size());
|
||||
packet = take_all(symbol.stream());
|
||||
assert(packet.size() == sizeof(CGGuildSymbolUpload) + 3);
|
||||
assert(packet[0] == HDR_CG_GUILD_SYMBOL_UPLOAD);
|
||||
assert(packet[1] == packet.size() && packet[2] == 0);
|
||||
std::memcpy(&gid, packet.data() + 3, sizeof(gid));
|
||||
assert(gid == 99);
|
||||
assert(packet[7] == 1 && packet[8] == 2 && packet[9] == 3);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_mark_download();
|
||||
test_symbol_download();
|
||||
test_upload_layouts();
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,241 @@
|
||||
// net_classic_stream_test — 40250 "classic" framing layer, socket-free.
|
||||
// Drives ClassicStream via feed()/take_outgoing() with synthetic packets.
|
||||
#include "../src/net/classic/classic_stream.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace mtnet::classic;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(c, msg) \
|
||||
do { \
|
||||
if (!(c)) { \
|
||||
std::fprintf(stderr, "FAIL: %s\n", msg); \
|
||||
++g_fail; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
template <class T>
|
||||
static std::vector<uint8_t> raw(const T &s) {
|
||||
std::vector<uint8_t> b(sizeof(T));
|
||||
std::memcpy(b.data(), &s, sizeof(T));
|
||||
return b;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> drain(ClassicStream &s) {
|
||||
std::vector<uint8_t> out;
|
||||
uint8_t buf[4096];
|
||||
for (;;) {
|
||||
size_t n = s.take_outgoing(buf, sizeof(buf));
|
||||
if (!n) {
|
||||
break;
|
||||
}
|
||||
out.insert(out.end(), buf, buf + n);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// ---------------------------------------------------------- handshake echo
|
||||
{
|
||||
ClassicStream s;
|
||||
Handshake hs{};
|
||||
hs.header = HDR_HANDSHAKE;
|
||||
hs.handshake = 0xDEADBEEF;
|
||||
hs.time = 1000;
|
||||
hs.delta = 50;
|
||||
auto b = raw(hs);
|
||||
s.feed(b.data(), b.size());
|
||||
|
||||
auto out = drain(s);
|
||||
CHECK(out.size() == sizeof(Handshake), "handshake echo is 13 bytes (no seq)");
|
||||
Handshake echo{};
|
||||
std::memcpy(&echo, out.data(), sizeof(echo));
|
||||
CHECK(echo.header == HDR_HANDSHAKE, "echo keeps header 0xFF");
|
||||
CHECK(echo.handshake == 0xDEADBEEF, "echo keeps handshake id");
|
||||
CHECK(echo.time == 1000u + 2u * 50u, "echo time = time + 2*delta");
|
||||
CHECK(echo.delta == 0, "echo delta = 0");
|
||||
|
||||
Handshake resync{};
|
||||
resync.header = HDR_HANDSHAKE;
|
||||
resync.handshake = 0xDEADBEEF;
|
||||
resync.time = 2000;
|
||||
resync.delta = 10;
|
||||
s.set_sequence_mode(true);
|
||||
s.set_time_sync_mode(true);
|
||||
b = raw(resync);
|
||||
s.feed(b.data(), b.size());
|
||||
out = drain(s);
|
||||
CHECK(out.size() == sizeof(Handshake) + 1, "later handshake uses time-sync + seq");
|
||||
Handshake sync_echo{};
|
||||
std::memcpy(&sync_echo, out.data(), sizeof(sync_echo));
|
||||
CHECK(sync_echo.header == HDR_CG_TIME_SYNC, "later handshake header is CG_TIME_SYNC");
|
||||
CHECK(sync_echo.time == 2000u + 2u * 10u && sync_echo.delta == 0,
|
||||
"later handshake time fields are converged");
|
||||
CHECK(out[sizeof(Handshake)] == SEQUENCE_TABLE[0], "time-sync uses next sequence byte");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- GC_PHASE
|
||||
{
|
||||
ClassicStream s;
|
||||
int seen = -1;
|
||||
s.on_phase = [&](uint8_t p) { seen = p; };
|
||||
Phase_ p{HDR_GC_PHASE, PHASE_LOGIN};
|
||||
auto b = raw(p);
|
||||
s.feed(b.data(), b.size());
|
||||
CHECK(seen == PHASE_LOGIN, "GC_PHASE delivers phase 2");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- GC_PING -> CG_PONG
|
||||
{
|
||||
ClassicStream s;
|
||||
uint8_t ping = HDR_GC_PING; // 44
|
||||
s.feed(&ping, 1);
|
||||
auto out = drain(s);
|
||||
CHECK(out.size() == 1 && out[0] == HDR_CG_PONG, "PING -> bare CG_PONG (seq off)");
|
||||
|
||||
s.set_sequence_mode(true);
|
||||
s.feed(&ping, 1);
|
||||
out = drain(s);
|
||||
CHECK(out.size() == 2 && out[0] == HDR_CG_PONG && out[1] == SEQUENCE_TABLE[0],
|
||||
"PING -> CG_PONG + seq[0] (seq on)");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- static GC packet
|
||||
{
|
||||
ClassicStream s;
|
||||
uint8_t got_hdr = 0;
|
||||
uint32_t got_len = 0;
|
||||
std::vector<uint8_t> got_body;
|
||||
s.on_packet = [&](uint8_t h, const uint8_t *body, uint32_t len) {
|
||||
got_hdr = h;
|
||||
got_len = len;
|
||||
got_body.assign(body, body + len);
|
||||
return true;
|
||||
};
|
||||
GCMainCharacter mc{};
|
||||
mc.header = HDR_GC_MAIN_CHARACTER;
|
||||
mc.vid = 0x0A0B0C0D;
|
||||
mc.race = 4;
|
||||
std::strcpy(mc.name, "Hero");
|
||||
mc.x = 123456;
|
||||
mc.y = 654321;
|
||||
mc.z = 7;
|
||||
mc.empire = 2;
|
||||
mc.skill_group = 1;
|
||||
auto b = raw(mc);
|
||||
s.feed(b.data(), b.size());
|
||||
CHECK(got_hdr == HDR_GC_MAIN_CHARACTER, "static: header 113 delivered");
|
||||
CHECK(got_len == sizeof(GCMainCharacter) - 1, "static: body len = sizeof-1");
|
||||
// body starts right after the header byte -> first 4 bytes are the vid
|
||||
uint32_t vid;
|
||||
std::memcpy(&vid, got_body.data(), 4);
|
||||
CHECK(vid == 0x0A0B0C0D, "static: body begins at vid");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- dynamic GC packet
|
||||
{
|
||||
ClassicStream s;
|
||||
uint8_t got_hdr = 0;
|
||||
uint32_t got_len = 0;
|
||||
std::string got_tail;
|
||||
s.on_packet = [&](uint8_t h, const uint8_t *body, uint32_t len) {
|
||||
got_hdr = h;
|
||||
got_len = len;
|
||||
got_tail.assign(reinterpret_cast<const char *>(body), len);
|
||||
return true;
|
||||
};
|
||||
// GC_CHAT: [header][u16 size][type][u32 vid][empire] + "hi"
|
||||
const char *msg = "hi";
|
||||
GCChatHead ch{};
|
||||
ch.header = HDR_GC_CHAT;
|
||||
ch.type = 1;
|
||||
ch.vid = 42;
|
||||
ch.empire = 3;
|
||||
ch.size = static_cast<uint16_t>(sizeof(GCChatHead) + std::strlen(msg));
|
||||
std::vector<uint8_t> pkt = raw(ch);
|
||||
pkt.insert(pkt.end(), msg, msg + std::strlen(msg));
|
||||
s.feed(pkt.data(), pkt.size());
|
||||
CHECK(got_hdr == HDR_GC_CHAT, "dynamic: GC_CHAT delivered");
|
||||
// body = everything after [header][u16 size] => (type,vid,empire,"hi")
|
||||
CHECK(got_len == ch.size - sizeof(DynHead), "dynamic: body len = size - 3");
|
||||
CHECK(got_tail.size() >= 2 && got_tail.substr(got_tail.size() - 2) == "hi",
|
||||
"dynamic: text tail present");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- send seq sequencing
|
||||
{
|
||||
ClassicStream s;
|
||||
s.set_sequence_mode(true);
|
||||
CGMove mv{};
|
||||
mv.header = HDR_CG_MOVE;
|
||||
mv.func = 1;
|
||||
mv.x = 10;
|
||||
mv.y = 20;
|
||||
s.send_fixed(raw(mv).data(), sizeof(mv));
|
||||
s.send_fixed(raw(mv).data(), sizeof(mv));
|
||||
auto out = drain(s);
|
||||
CHECK(out.size() == 2 * (sizeof(CGMove) + 1), "two CG_MOVE + 2 seq bytes");
|
||||
CHECK(out[sizeof(CGMove)] == SEQUENCE_TABLE[0], "first seq = table[0]");
|
||||
CHECK(out[2 * sizeof(CGMove) + 1] == SEQUENCE_TABLE[1], "second seq = table[1]");
|
||||
CHECK(s.sequence_index() == 2, "seq index advanced to 2");
|
||||
}
|
||||
|
||||
// seq off => no trailing byte
|
||||
{
|
||||
ClassicStream s;
|
||||
CGMove mv{};
|
||||
mv.header = HDR_CG_MOVE;
|
||||
s.send_fixed(raw(mv).data(), sizeof(mv));
|
||||
CHECK(drain(s).size() == sizeof(CGMove), "seq off: no trailing byte");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- partial feed
|
||||
{
|
||||
ClassicStream s;
|
||||
int hits = 0;
|
||||
s.on_packet = [&](uint8_t, const uint8_t *, uint32_t) {
|
||||
++hits;
|
||||
return true;
|
||||
};
|
||||
GCCharacterDel del{HDR_GC_CHARACTER_DEL, 99};
|
||||
auto b = raw(del);
|
||||
s.feed(b.data(), 2); // header + 1 byte of vid
|
||||
CHECK(hits == 0, "partial: nothing delivered yet");
|
||||
s.feed(b.data() + 2, b.size() - 2);
|
||||
CHECK(hits == 1, "partial: delivered once the rest arrives");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- unknown header
|
||||
{
|
||||
ClassicStream s;
|
||||
std::string err;
|
||||
s.on_error = [&](const std::string &e) { err = e; };
|
||||
uint8_t bad = 200; // ROULETTE — not in the size table yet
|
||||
s.feed(&bad, 1);
|
||||
CHECK(!err.empty(), "unknown header -> on_error");
|
||||
CHECK(s.state() == ClassicStream::State::Offline, "unknown header -> disconnect");
|
||||
}
|
||||
|
||||
// ---- GC_KEY_AGREEMENT: a partial packet just waits (no error, no crash) ----
|
||||
// (the full DH2 exchange lives in net_classic_encstream_test.)
|
||||
{
|
||||
ClassicStream s;
|
||||
std::string err;
|
||||
s.on_error = [&](const std::string &e) { err = e; };
|
||||
uint8_t ka[100] = {HDR_KEY_AGREEMENT}; // < sizeof(KeyAgreement) == 261
|
||||
s.feed(ka, sizeof(ka));
|
||||
s.feed(ka, sizeof(ka)); // still short (200 < 261)
|
||||
CHECK(err.empty(), "partial GC_KEY_AGREEMENT -> buffered, no error");
|
||||
}
|
||||
|
||||
if (g_fail) {
|
||||
std::fprintf(stderr, "%d check(s) failed\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
std::puts("net_classic_stream_test OK");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// net_classic_wire_test — 40250 "classic" backend wire layer.
|
||||
// The struct sizes are already guarded by static_assert in wire_classic.h (so a
|
||||
// mismatch is a compile error); this test locks down the size-table / sequence /
|
||||
// dynamic-flag lookups and a couple of round-trip byte layouts.
|
||||
#include "../src/net/classic/wire_classic.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
using namespace mtnet::classic;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(c, msg) \
|
||||
do { \
|
||||
if (!(c)) { \
|
||||
std::fprintf(stderr, "FAIL: %s\n", msg); \
|
||||
++g_fail; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int main() {
|
||||
// --- header values (spot check vs server packet.h) ---
|
||||
CHECK(HDR_CG_MOVE == 7, "CG_MOVE == 7");
|
||||
CHECK(HDR_GC_MAIN_CHARACTER == 113, "GC_MAIN_CHARACTER == 113");
|
||||
CHECK(HDR_GC_LOGIN_SUCCESS_NEWSLOT == 32, "GC_LOGIN_SUCCESS_NEWSLOT == 32");
|
||||
CHECK(HDR_HANDSHAKE == 0xFF, "HANDSHAKE == 0xFF");
|
||||
CHECK(HDR_GC_TIME_SYNC == 0xFC && HDR_CG_TIME_SYNC == 0xFC, "TIME_SYNC == 0xFC");
|
||||
CHECK(HDR_KEY_AGREEMENT == 0xFB, "KEY_AGREEMENT == 0xFB");
|
||||
CHECK(PHASE_GAME == 5 && PHASE_SELECT == 3, "EPhase ordinals");
|
||||
|
||||
// --- struct sizes (redundant with static_assert, but explicit) ---
|
||||
CHECK(sizeof(Handshake) == 13, "sizeof Handshake");
|
||||
CHECK(sizeof(CGText) == 1, "sizeof CG_TEXT base");
|
||||
CHECK(sizeof(SimplePlayer) == 63, "sizeof SimplePlayer");
|
||||
CHECK(sizeof(GCMainCharacter) == 46, "sizeof GCMainCharacter");
|
||||
CHECK(sizeof(GCMainCharacter3BGM) == 71 && sizeof(GCMainCharacter4BGM) == 75,
|
||||
"sizeof main-character BGM variants");
|
||||
CHECK(sizeof(GCCharacterAdd2) == 80 && sizeof(GCCharacterUpdate) == 35,
|
||||
"sizeof character add/update variants");
|
||||
CHECK(sizeof(GCFishing) == 7, "sizeof GC_FISHING");
|
||||
CHECK(sizeof(GCWarpClassic) == 15, "sizeof classic GC_WARP");
|
||||
CHECK(sizeof(GCAuthSuccess) == 6 && sizeof(GCAuthSuccessOpenID) == 37,
|
||||
"sizeof auth success packets");
|
||||
CHECK(sizeof(GCPasspodFailed) == 130 && sizeof(GCXTrapRequest) == 129 && sizeof(GCHSRequest) == 403,
|
||||
"sizeof optional security packets");
|
||||
CHECK(sizeof(GCLoginSuccess) == 329, "sizeof GCLoginSuccess (NEWSLOT, 4 players)");
|
||||
CHECK(sizeof(GCPoints) == 1 + 255 * 4, "sizeof GCPoints");
|
||||
CHECK(sizeof(GCPointChange) == 17, "sizeof GCPointChange (int header quirk)");
|
||||
CHECK(sizeof(CGMove) == 16, "sizeof CGMove");
|
||||
CHECK(sizeof(GCMove) == 24, "sizeof GCMove");
|
||||
CHECK(sizeof(CGMessenger) == 2 && sizeof(CGExchange) == 10, "sizeof P8 CG packets");
|
||||
CHECK(sizeof(GCExchange) == 47, "sizeof GC_EXCHANGE");
|
||||
CHECK(sizeof(GCMessengerHead) == 4 && sizeof(GCSafeboxMoneyChange) == 5,
|
||||
"sizeof P8 dynamic/storage packets");
|
||||
|
||||
// --- size-table lookup (body size, no trailing seq byte) ---
|
||||
CHECK(packet_size_gc(HDR_GC_PHASE) == 2, "gc size PHASE");
|
||||
CHECK(packet_size_gc(HDR_GC_MAIN_CHARACTER) == 46, "gc size MAIN_CHARACTER");
|
||||
CHECK(packet_size_gc(HDR_GC_WARP) == sizeof(GCWarpClassic), "gc size WARP");
|
||||
CHECK(packet_size_gc(HDR_GC_MAIN_CHARACTER3_BGM) == 71 &&
|
||||
packet_size_gc(HDR_GC_MAIN_CHARACTER4_BGM_VOL) == 75, "gc size MAIN_CHARACTER BGM");
|
||||
CHECK(packet_size_gc(HDR_GC_CHARACTER_ADD2) == 80 &&
|
||||
packet_size_gc(HDR_GC_CHARACTER_UPDATE2) == 35, "gc size character variants");
|
||||
CHECK(packet_size_gc(HDR_GC_FISHING) == 7, "gc size FISHING");
|
||||
CHECK(packet_size_gc(HDR_GC_AUTH_SUCCESS) == 6 &&
|
||||
packet_size_gc(HDR_GC_XTRAP_CS1_REQUEST) == 129, "gc size auth/security");
|
||||
CHECK(packet_size_gc(HDR_GC_LOGIN_SUCCESS_NEWSLOT) == 329, "gc size LOGIN_SUCCESS_NEWSLOT");
|
||||
CHECK(packet_size_gc(HDR_GC_CHARACTER_POINTS) == 1021, "gc size CHARACTER_POINTS");
|
||||
CHECK(packet_size_gc(HDR_GC_EXCHANGE) == 47, "gc size EXCHANGE");
|
||||
CHECK(packet_size_gc(HDR_GC_SAFEBOX_MONEY_CHANGE) == 5, "gc size SAFEBOX_MONEY_CHANGE");
|
||||
CHECK(packet_size_gc(HDR_GC_SAFEBOX_SET) == 51, "gc size SAFEBOX_SET");
|
||||
CHECK(packet_size_gc(HDR_GC_MALL_OPEN) == 2, "gc size MALL_OPEN");
|
||||
CHECK(packet_size_gc(HDR_GC_CHAT) == 0, "gc size CHAT == 0 (dynamic)");
|
||||
CHECK(packet_size_cg(HDR_CG_LOGIN) == 49, "cg size LOGIN");
|
||||
CHECK(packet_size_cg(HDR_CG_TEXT) == sizeof(CGText), "cg size TEXT base");
|
||||
CHECK(packet_size_cg(HDR_CG_MOVE) == 16, "cg size MOVE");
|
||||
CHECK(packet_size_cg(HDR_CG_ENTERGAME) == 1, "cg size ENTERGAME");
|
||||
CHECK(packet_size_cg(HDR_CG_EXCHANGE) == 10, "cg size EXCHANGE");
|
||||
CHECK(packet_size_cg(HDR_CG_MESSENGER) == 2, "cg size MESSENGER base");
|
||||
CHECK(packet_size_cg(HDR_CG_SAFEBOX_ITEM_MOVE) == 8, "cg size SAFEBOX_ITEM_MOVE");
|
||||
|
||||
// --- sequence-byte flags (docs §2.2) ---
|
||||
CHECK(is_sequence_cg(HDR_CG_MOVE), "CG_MOVE has sequence");
|
||||
CHECK(is_sequence_cg(HDR_CG_LOGIN), "CG_LOGIN has sequence");
|
||||
CHECK(is_sequence_cg(HDR_CG_TIME_SYNC), "CG_TIME_SYNC has sequence");
|
||||
CHECK(!is_sequence_cg(HDR_HANDSHAKE), "HANDSHAKE no sequence");
|
||||
CHECK(!is_sequence_cg(HDR_KEY_AGREEMENT), "KEY_AGREEMENT no sequence");
|
||||
CHECK(!is_sequence_cg(HDR_CG_MARK_UPLOAD), "MARK_UPLOAD no sequence");
|
||||
CHECK(!is_sequence_cg(HDR_CG_DRAGON_SOUL_REFINE), "DRAGON_SOUL_REFINE no sequence");
|
||||
CHECK(!is_sequence_cg(HDR_CG_STATE_CHECKER), "STATE_CHECKER no sequence");
|
||||
|
||||
// --- dynamic-size flags (docs §2.2) ---
|
||||
CHECK(is_dynamic_gc(HDR_GC_CHAT), "GC_CHAT dynamic");
|
||||
CHECK(is_dynamic_gc(HDR_GC_SHOP), "GC_SHOP dynamic");
|
||||
CHECK(is_dynamic_gc(HDR_GC_GUILD), "GC_GUILD dynamic");
|
||||
CHECK(is_dynamic_gc(HDR_GC_QUEST_INFO), "GC_QUEST_INFO dynamic");
|
||||
CHECK(!is_dynamic_gc(HDR_GC_MALL_SET), "GC_MALL_SET NOT dynamic");
|
||||
CHECK(!is_dynamic_gc(HDR_GC_SAFEBOX_SET), "GC_SAFEBOX_SET NOT dynamic");
|
||||
CHECK(!is_dynamic_gc(HDR_GC_PARTY_ADD), "GC_PARTY_ADD NOT dynamic");
|
||||
// client map registers GC_WHISPER STATIC, but its wSize sits where
|
||||
// TDynamicSizePacketHeader.size does — we frame it via the dynamic path.
|
||||
CHECK(is_dynamic_gc(HDR_GC_WHISPER), "GC_WHISPER framed as dynamic (wSize prefix)");
|
||||
CHECK(is_dynamic_cg(HDR_CG_CHAT) && is_dynamic_cg(HDR_CG_WHISPER), "CG chat/whisper dynamic");
|
||||
CHECK(!is_dynamic_cg(HDR_CG_SHOP), "CG_SHOP NOT dynamic");
|
||||
|
||||
// --- byte-layout round trip: GC_MAIN_CHARACTER ---
|
||||
{
|
||||
GCMainCharacter mc{};
|
||||
mc.header = HDR_GC_MAIN_CHARACTER;
|
||||
mc.vid = 0x11223344;
|
||||
mc.race = 4;
|
||||
std::strcpy(mc.name, "Hero");
|
||||
mc.x = 100000;
|
||||
mc.y = 200000;
|
||||
mc.z = 12;
|
||||
mc.empire = 2;
|
||||
mc.skill_group = 1;
|
||||
|
||||
uint8_t buf[sizeof(mc)];
|
||||
std::memcpy(buf, &mc, sizeof(mc));
|
||||
CHECK(buf[0] == 113, "wire byte 0 == header");
|
||||
// header(1) vid(4) race(2) name(25) -> x at offset 32
|
||||
int32_t x_wire;
|
||||
std::memcpy(&x_wire, buf + 1 + 4 + 2 + 25, 4);
|
||||
CHECK(x_wire == 100000, "wire x at offset 32");
|
||||
|
||||
GCMainCharacter rt{};
|
||||
std::memcpy(&rt, buf, sizeof(rt));
|
||||
CHECK(rt.vid == 0x11223344 && rt.z == 12 && rt.empire == 2, "round-trip fields");
|
||||
CHECK(std::strcmp(rt.name, "Hero") == 0, "round-trip name");
|
||||
}
|
||||
|
||||
// --- GCPointChange: real header byte is buf[0] despite `int32_t header` ---
|
||||
{
|
||||
GCPointChange pc{};
|
||||
pc.header = HDR_GC_CHARACTER_POINT_CHANGE; // 17
|
||||
pc.vid = 7;
|
||||
pc.type = 5;
|
||||
pc.amount = -3;
|
||||
pc.value = 42;
|
||||
uint8_t buf[sizeof(pc)];
|
||||
std::memcpy(buf, &pc, sizeof(pc));
|
||||
CHECK(buf[0] == 17, "point_change wire byte 0 == 17");
|
||||
CHECK(sizeof(pc) == 17, "point_change wire size 17 (4B int header + 4+1+4+4)");
|
||||
}
|
||||
|
||||
if (g_fail) {
|
||||
std::fprintf(stderr, "%d check(s) failed\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
std::puts("net_classic_wire_test OK");
|
||||
return 0;
|
||||
}
|
||||
@@ -825,6 +825,20 @@ int main() {
|
||||
if (f.name == "Bob") bob_now = f.online;
|
||||
}
|
||||
CHECK(bob_now, "messenger: LOGIN flips Bob online");
|
||||
|
||||
const char *invite = "messenger_auth Alice";
|
||||
std::vector<uint8_t> invite_buf(sizeof(GCChat) + std::strlen(invite) + 1);
|
||||
GCChat invite_head{};
|
||||
invite_head.header = GC_CHAT;
|
||||
invite_head.length = (uint16_t)invite_buf.size();
|
||||
invite_head.type = CHAT_TYPE_COMMAND;
|
||||
std::memcpy(invite_buf.data(), &invite_head, sizeof(invite_head));
|
||||
std::memcpy(invite_buf.data() + sizeof(invite_head), invite, std::strlen(invite) + 1);
|
||||
es.apply(GC_CHAT, invite_buf.data(), (uint16_t)invite_buf.size());
|
||||
auto invites = es.drain_friend_invites();
|
||||
CHECK(invites.size() == 1 && invites[0] == "Alice",
|
||||
"messenger: invitation command surfaced");
|
||||
CHECK(es.drain_friend_invites().empty(), "messenger: invitation queue drained");
|
||||
}
|
||||
|
||||
// --- P8 NPC shop (GC_SHOP START / END + error) ---
|
||||
@@ -1414,6 +1428,119 @@ int main() {
|
||||
"cube: COMMAND lines never enter the chat log");
|
||||
}
|
||||
|
||||
// --- 40250 command-bus callbacks outside cube -------------------------
|
||||
{
|
||||
auto cmd = [&](const std::string &text) {
|
||||
std::vector<uint8_t> buf(sizeof(GCChat) + text.size() + 1);
|
||||
GCChat h{};
|
||||
h.header = GC_CHAT;
|
||||
h.length = (uint16_t)buf.size();
|
||||
h.type = CHAT_TYPE_COMMAND;
|
||||
std::memcpy(buf.data(), &h, sizeof(h));
|
||||
std::memcpy(buf.data() + sizeof(h), text.c_str(), text.size() + 1);
|
||||
es.apply(GC_CHAT, buf.data(), (uint16_t)buf.size());
|
||||
};
|
||||
cmd("PartyRequestDenied");
|
||||
cmd("ShowMeSafeboxPassword");
|
||||
cmd("ShowMeMallPassword");
|
||||
cmd("RefineSuceeded");
|
||||
cmd("RefineFailed");
|
||||
cmd("OpenPrivateShop");
|
||||
cmd("MyShopPriceList 11209 250000");
|
||||
cmd("setblockmode 37");
|
||||
cmd("ObserverMode 1");
|
||||
cmd("ObserverCount 3");
|
||||
cmd("StoneDetect 9001 2 90");
|
||||
cmd("StartStaminaConsume 10 77");
|
||||
cmd("StopStaminaConsume 55");
|
||||
cmd("sms");
|
||||
cmd("combo 1");
|
||||
cmd("mobile_auth");
|
||||
cmd("gift");
|
||||
cmd("kiss 1 2");
|
||||
cmd("slap 1 2");
|
||||
cmd("dance6 1");
|
||||
cmd("joy 2");
|
||||
auto events = es.drain_server_commands();
|
||||
int denied = 0, safe = 0, mall = 0, ok = 0, fail = 0, open = 0, price = 0, block = 0;
|
||||
int observer = 0, observer_count = 0, stone = 0, stamina_start = 0, stamina_stop = 0;
|
||||
int mobile = 0, mobile_auth = 0, combo = 0, gift = 0;
|
||||
for (const auto &ev : events) {
|
||||
switch (ev.kind) {
|
||||
case ServerCommandEvent::PartyRequestDenied: ++denied; break;
|
||||
case ServerCommandEvent::SafeboxPasswordRequired: ++safe; break;
|
||||
case ServerCommandEvent::SafeboxWrongPassword: break;
|
||||
case ServerCommandEvent::MallPasswordRequired: ++mall; break;
|
||||
case ServerCommandEvent::RefineSucceeded: ++ok; break;
|
||||
case ServerCommandEvent::RefineFailed: ++fail; break;
|
||||
case ServerCommandEvent::PrivateShopOpenRequested: ++open; break;
|
||||
case ServerCommandEvent::MyShopPrice:
|
||||
price += ev.value == 11209 && ev.value2 == 250000;
|
||||
break;
|
||||
case ServerCommandEvent::BlockModeChanged:
|
||||
block += ev.value == 37;
|
||||
break;
|
||||
case ServerCommandEvent::ObserverModeChanged:
|
||||
observer += ev.value == 1 && es.observer_mode();
|
||||
break;
|
||||
case ServerCommandEvent::ObserverCountChanged:
|
||||
observer_count += ev.value == 3 && es.observer_count() == 3;
|
||||
break;
|
||||
case ServerCommandEvent::StoneDetected:
|
||||
stone += ev.value == 9001 && ev.value2 == 2 && ev.value3 == 90.0f;
|
||||
break;
|
||||
case ServerCommandEvent::StaminaStarted:
|
||||
stamina_start += ev.value == 10 && ev.value2 == 77;
|
||||
break;
|
||||
case ServerCommandEvent::StaminaStopped:
|
||||
stamina_stop += ev.value == 0 && ev.value2 == 55;
|
||||
break;
|
||||
case ServerCommandEvent::MobileFlagChanged:
|
||||
mobile += ev.value == 1 && es.mobile_flag();
|
||||
break;
|
||||
case ServerCommandEvent::MobileAuthRequired:
|
||||
++mobile_auth;
|
||||
break;
|
||||
case ServerCommandEvent::ComboChanged:
|
||||
combo += ev.value == 1 && es.combo_skill_flag();
|
||||
break;
|
||||
case ServerCommandEvent::GiftAvailable:
|
||||
++gift;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK(denied == 1 && safe == 1 && mall == 1 && ok == 1 && fail == 1 && open == 1
|
||||
&& price == 1 && block == 1 && observer == 1 && observer_count == 1
|
||||
&& stone == 1 && stamina_start == 1 && stamina_stop == 1 && mobile == 1
|
||||
&& mobile_auth == 1 && combo == 1 && gift == 1 && !es.stamina_consuming()
|
||||
&& es.current_stamina() == 55,
|
||||
"40250 command bus: callbacks surfaced");
|
||||
auto motions = es.drain_motions();
|
||||
CHECK(motions.size() == 6 && motions[0].vid == 1 && motions[0].victim_vid == 2
|
||||
&& motions[0].motion == 308 && motions[1].vid == 2 && motions[1].motion == 308
|
||||
&& motions[2].motion == 320 && motions[3].motion == 316
|
||||
&& motions[4].motion == 330 && motions[5].motion == 349,
|
||||
"40250 command bus: emotions feed the GC_MOTION stream");
|
||||
es.set_now(1000);
|
||||
cmd("StartStaminaConsume 10 77");
|
||||
es.drain_server_commands();
|
||||
es.set_now(1500);
|
||||
es.tick();
|
||||
CHECK(es.stamina_consuming() && es.current_stamina() == 72
|
||||
&& es.points().stamina() == 72 && es.take_points_dirty(),
|
||||
"40250 command bus: stamina drains at server-command rate");
|
||||
es.mut_safebox_wrong_password();
|
||||
bool wrong_password = false;
|
||||
for (const auto &ev : es.drain_server_commands()) {
|
||||
wrong_password = wrong_password || ev.kind == ServerCommandEvent::SafeboxWrongPassword;
|
||||
}
|
||||
CHECK(wrong_password, "40250 command bus: wrong safebox password surfaced");
|
||||
cmd("CloseSafebox");
|
||||
cmd("CloseMall");
|
||||
CHECK(!es.safebox_open() && !es.mall_open(),
|
||||
"40250 command bus: storage close callbacks update state");
|
||||
}
|
||||
|
||||
// --- despawn ---
|
||||
// --- guild land list + minimap observers ---
|
||||
{
|
||||
|
||||
@@ -47,16 +47,26 @@ public:
|
||||
|
||||
uint16_t listen_ephemeral() {
|
||||
m_listen = ::socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (m_listen < 0) {
|
||||
return 0;
|
||||
}
|
||||
int one = 1;
|
||||
setsockopt(m_listen, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
sockaddr_in a{};
|
||||
a.sin_family = AF_INET;
|
||||
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
a.sin_port = 0;
|
||||
bind(m_listen, (sockaddr *)&a, sizeof(a));
|
||||
::listen(m_listen, 1);
|
||||
if (::bind(m_listen, (sockaddr *)&a, sizeof(a)) != 0 || ::listen(m_listen, 1) != 0) {
|
||||
::close(m_listen);
|
||||
m_listen = -1;
|
||||
return 0;
|
||||
}
|
||||
socklen_t l = sizeof(a);
|
||||
getsockname(m_listen, (sockaddr *)&a, &l);
|
||||
if (::getsockname(m_listen, (sockaddr *)&a, &l) != 0) {
|
||||
::close(m_listen);
|
||||
m_listen = -1;
|
||||
return 0;
|
||||
}
|
||||
int fl = fcntl(m_listen, F_GETFL, 0);
|
||||
fcntl(m_listen, F_SETFL, fl | O_NONBLOCK);
|
||||
return ntohs(a.sin_port);
|
||||
@@ -694,6 +704,10 @@ int main() {
|
||||
{
|
||||
MockServer srv(MockServer::Mode::Auth);
|
||||
uint16_t port = srv.listen_ephemeral();
|
||||
if (port == 0) {
|
||||
std::fprintf(stdout, "SKIP: localhost socket bind unavailable in this environment\n");
|
||||
return 0;
|
||||
}
|
||||
AuthClient auth("admin", "123456789");
|
||||
CHECK(auth.connect("127.0.0.1", port), "auth connect");
|
||||
run(srv, auth, 5000, [&] { return auth.done(); });
|
||||
|
||||
Vendored
+3
@@ -34,6 +34,9 @@ set(ZSTD_MULTITHREAD_SUPPORT OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(zstd/build/cmake zstd-build EXCLUDE_FROM_ALL)
|
||||
add_library(mt3p::zstd ALIAS libzstd_static)
|
||||
|
||||
# --- Crypto++ (MT_PROTOCOL=classic cipher) ----------------------------------
|
||||
add_subdirectory(cryptopp EXCLUDE_FROM_ALL)
|
||||
|
||||
# --- miniLZO ------------------------------------------------------------------
|
||||
# One translation unit, an amalgamation generated from the LZO sources. Its
|
||||
# public API (<lzo/lzo1x.h> via the shim header) is a strict subset of full LZO.
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// 3way.cpp - modified by Wei Dai from Joan Daemen's 3way.c
|
||||
// The original code and all modifications are in the public domain.
|
||||
|
||||
#include "pch.h"
|
||||
#include "3way.h"
|
||||
#include "misc.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
#if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
|
||||
void ThreeWay_TestInstantiations()
|
||||
{
|
||||
ThreeWay::Encryption x1;
|
||||
ThreeWay::Decryption x2;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const word32 START_E = 0x0b0b; // round constant of first encryption round
|
||||
const word32 START_D = 0xb1b1; // round constant of first decryption round
|
||||
}
|
||||
|
||||
static inline word32 reverseBits(word32 a)
|
||||
{
|
||||
a = ((a & 0xAAAAAAAA) >> 1) | ((a & 0x55555555) << 1);
|
||||
a = ((a & 0xCCCCCCCC) >> 2) | ((a & 0x33333333) << 2);
|
||||
return ((a & 0xF0F0F0F0) >> 4) | ((a & 0x0F0F0F0F) << 4);
|
||||
}
|
||||
|
||||
#define mu(a0, a1, a2) \
|
||||
{ \
|
||||
a1 = reverseBits(a1); \
|
||||
word32 t = reverseBits(a0); \
|
||||
a0 = reverseBits(a2); \
|
||||
a2 = t; \
|
||||
}
|
||||
|
||||
#define pi_gamma_pi(a0, a1, a2) \
|
||||
{ \
|
||||
word32 b0, b2; \
|
||||
b2 = rotlConstant<1>(a2); \
|
||||
b0 = rotlConstant<22>(a0); \
|
||||
a0 = rotlConstant<1>(b0 ^ (a1|(~b2))); \
|
||||
a2 = rotlConstant<22>(b2 ^ (b0|(~a1))); \
|
||||
a1 ^= (b2|(~b0)); \
|
||||
}
|
||||
|
||||
// thanks to Paulo Barreto for this optimized theta()
|
||||
#define theta(a0, a1, a2) \
|
||||
{ \
|
||||
word32 b0, b1, c; \
|
||||
c = a0 ^ a1 ^ a2; \
|
||||
c = rotlConstant<16>(c) ^ rotlConstant<8>(c); \
|
||||
b0 = (a0 << 24) ^ (a2 >> 8) ^ (a1 << 8) ^ (a0 >> 24); \
|
||||
b1 = (a1 << 24) ^ (a0 >> 8) ^ (a2 << 8) ^ (a1 >> 24); \
|
||||
a0 ^= c ^ b0; \
|
||||
a1 ^= c ^ b1; \
|
||||
a2 ^= c ^ (b0 >> 16) ^ (b1 << 16); \
|
||||
}
|
||||
|
||||
#define rho(a0, a1, a2) \
|
||||
{ \
|
||||
theta(a0, a1, a2); \
|
||||
pi_gamma_pi(a0, a1, a2); \
|
||||
}
|
||||
|
||||
void ThreeWay::Base::UncheckedSetKey(const byte *uk, unsigned int length, const NameValuePairs ¶ms)
|
||||
{
|
||||
AssertValidKeyLength(length);
|
||||
|
||||
m_rounds = GetRoundsAndThrowIfInvalid(params, this);
|
||||
|
||||
for (unsigned int i=0; i<3; i++)
|
||||
m_k[i] = (word32)uk[4*i+3] | ((word32)uk[4*i+2]<<8) | ((word32)uk[4*i+1]<<16) | ((word32)uk[4*i]<<24);
|
||||
|
||||
if (!IsForwardTransformation())
|
||||
{
|
||||
theta(m_k[0], m_k[1], m_k[2]);
|
||||
mu(m_k[0], m_k[1], m_k[2]);
|
||||
m_k[0] = ByteReverse(m_k[0]);
|
||||
m_k[1] = ByteReverse(m_k[1]);
|
||||
m_k[2] = ByteReverse(m_k[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreeWay::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
typedef BlockGetAndPut<word32, BigEndian> Block;
|
||||
|
||||
word32 a0, a1, a2;
|
||||
Block::Get(inBlock)(a0)(a1)(a2);
|
||||
|
||||
word32 rc = START_E;
|
||||
|
||||
for(unsigned i=0; i<m_rounds; i++)
|
||||
{
|
||||
a0 ^= m_k[0] ^ (rc<<16);
|
||||
a1 ^= m_k[1];
|
||||
a2 ^= m_k[2] ^ rc;
|
||||
rho(a0, a1, a2);
|
||||
|
||||
rc <<= 1;
|
||||
if (rc&0x10000) rc ^= 0x11011;
|
||||
}
|
||||
a0 ^= m_k[0] ^ (rc<<16);
|
||||
a1 ^= m_k[1];
|
||||
a2 ^= m_k[2] ^ rc;
|
||||
theta(a0, a1, a2);
|
||||
|
||||
Block::Put(xorBlock, outBlock)(a0)(a1)(a2);
|
||||
}
|
||||
|
||||
void ThreeWay::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
typedef BlockGetAndPut<word32, LittleEndian> Block;
|
||||
|
||||
word32 a0, a1, a2;
|
||||
Block::Get(inBlock)(a0)(a1)(a2);
|
||||
|
||||
word32 rc = START_D;
|
||||
|
||||
mu(a0, a1, a2);
|
||||
for(unsigned i=0; i<m_rounds; i++)
|
||||
{
|
||||
a0 ^= m_k[0] ^ (rc<<16);
|
||||
a1 ^= m_k[1];
|
||||
a2 ^= m_k[2] ^ rc;
|
||||
rho(a0, a1, a2);
|
||||
|
||||
rc <<= 1;
|
||||
if (rc&0x10000) rc ^= 0x11011;
|
||||
}
|
||||
a0 ^= m_k[0] ^ (rc<<16);
|
||||
a1 ^= m_k[1];
|
||||
a2 ^= m_k[2] ^ rc;
|
||||
theta(a0, a1, a2);
|
||||
mu(a0, a1, a2);
|
||||
|
||||
Block::Put(xorBlock, outBlock)(a0)(a1)(a2);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// 3way.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file 3way.h
|
||||
/// \brief Classes for the 3-Way block cipher
|
||||
|
||||
#ifndef CRYPTOPP_THREEWAY_H
|
||||
#define CRYPTOPP_THREEWAY_H
|
||||
|
||||
#include "config.h"
|
||||
#include "seckey.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief ThreeWay block cipher information
|
||||
struct ThreeWay_Info : public FixedBlockSize<12>, public FixedKeyLength<12>, public VariableRounds<11>
|
||||
{
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "3-Way";}
|
||||
};
|
||||
|
||||
/// \brief ThreeWay block cipher
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/3-Way">3-Way</a>
|
||||
class ThreeWay : public ThreeWay_Info, public BlockCipherDocumentation
|
||||
{
|
||||
/// \brief Class specific implementation and overrides used to operate the cipher.
|
||||
/// \details Implementations and overrides in \p Base apply to both \p ENCRYPTION and \p DECRYPTION directions
|
||||
class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl<ThreeWay_Info>
|
||||
{
|
||||
public:
|
||||
void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms);
|
||||
|
||||
protected:
|
||||
unsigned int m_rounds;
|
||||
FixedSizeSecBlock<word32, 3> m_k;
|
||||
};
|
||||
|
||||
/// \brief Class specific methods used to operate the cipher in the forward direction.
|
||||
/// \details Implementations and overrides in \p Enc apply to \p ENCRYPTION.
|
||||
class CRYPTOPP_NO_VTABLE Enc : public Base
|
||||
{
|
||||
public:
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
};
|
||||
|
||||
/// \brief Class specific methods used to operate the cipher in the reverse direction.
|
||||
/// \details Implementations and overrides in \p Dec apply to \p DECRYPTION.
|
||||
class CRYPTOPP_NO_VTABLE Dec : public Base
|
||||
{
|
||||
public:
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BlockCipherFinal<ENCRYPTION, Enc> Encryption;
|
||||
typedef BlockCipherFinal<DECRYPTION, Dec> Decryption;
|
||||
};
|
||||
|
||||
typedef ThreeWay::Encryption ThreeWayEncryption;
|
||||
typedef ThreeWay::Decryption ThreeWayDecryption;
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
# Crypto++ 8.4.0 — vendored from the 40250 client tree
|
||||
# (ClientVS22/extern/include/cryptopp, CRYPTOPP_VERSION 840). Needed for the
|
||||
# MT_PROTOCOL=classic backend's _IMPROVED_PACKET_ENCRYPTION_ cipher:
|
||||
# DH2 key agreement + hint-selected block ciphers (Twofish default) in CTR mode.
|
||||
# The server links this exact version (Server/.../extern/cryptopp_8_4_0.tar.gz).
|
||||
#
|
||||
# No upstream CMakeLists — we glob the library TUs and drop the test / bench /
|
||||
# adhoc / FIPS-selftest ones. ASM is disabled everywhere (CRYPTOPP_DISABLE_ASM)
|
||||
# so the same pure-C++/intrinsics build works on the Android NDK and iOS SDK;
|
||||
# the classic cipher doesn't need AES-NI / SHA-ext.
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
file(GLOB CRYPTOPP_SRC CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
# TUs that are the cryptest program / benchmarks / dev scratch, not the library.
|
||||
set(_cp_exclude
|
||||
adhoc bench1 bench2 bench3 datatest dlltest fipsalgt fipstest pch
|
||||
regtest1 regtest2 regtest3 regtest4 test
|
||||
validat0 validat1 validat2 validat3 validat4 validat5
|
||||
validat6 validat7 validat8 validat9 validat10)
|
||||
foreach(_n ${_cp_exclude})
|
||||
list(REMOVE_ITEM CRYPTOPP_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${_n}.cpp")
|
||||
endforeach()
|
||||
|
||||
# Android: cpu.cpp does `#include "cpu-features.h"` (unconditionally on __ANDROID__)
|
||||
# and calls android_getCpuFeatures() etc. — those live in the NDK's cpufeatures
|
||||
# helper, which isn't on the sysroot include path by default.
|
||||
if(ANDROID)
|
||||
set(_ndk "${CMAKE_ANDROID_NDK}")
|
||||
if(NOT _ndk)
|
||||
set(_ndk "$ENV{ANDROID_NDK_HOME}")
|
||||
endif()
|
||||
set(_cpufeat "${_ndk}/sources/android/cpufeatures")
|
||||
if(EXISTS "${_cpufeat}/cpu-features.c")
|
||||
list(APPEND CRYPTOPP_SRC "${_cpufeat}/cpu-features.c")
|
||||
else()
|
||||
message(FATAL_ERROR "cryptopp/Android: cpu-features.c not found under ${_cpufeat}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(cryptopp STATIC ${CRYPTOPP_SRC})
|
||||
target_include_directories(cryptopp SYSTEM PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
if(ANDROID)
|
||||
target_include_directories(cryptopp SYSTEM PRIVATE "${_cpufeat}")
|
||||
endif()
|
||||
target_compile_features(cryptopp PUBLIC cxx_std_17)
|
||||
target_compile_definitions(cryptopp PUBLIC
|
||||
CRYPTOPP_DISABLE_ASM
|
||||
CRYPTOPP_DISABLE_SSSE3
|
||||
CRYPTOPP_DISABLE_AESNI)
|
||||
# Upstream is not warning-clean under -Wall; this is vendored 3rd-party code.
|
||||
target_compile_options(cryptopp PRIVATE -w)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(cryptopp PRIVATE -fno-strict-aliasing)
|
||||
endif()
|
||||
|
||||
# Consumers include <cryptopp/sha.h> etc. — parent dir is on the include path
|
||||
# above (third_party/), so the "cryptopp/" prefix resolves.
|
||||
add_library(mt3p::cryptopp ALIAS cryptopp)
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
Compilation Copyright (c) 1995-2019 by Wei Dai. All rights reserved.
|
||||
This copyright applies only to this software distribution package
|
||||
as a compilation, and does not imply a copyright on any particular
|
||||
file in the package.
|
||||
|
||||
All individual files in this compilation are placed in the public domain by
|
||||
Wei Dai and other contributors.
|
||||
|
||||
I would like to thank the following authors for placing their works into
|
||||
the public domain:
|
||||
|
||||
Joan Daemen - 3way.cpp
|
||||
Leonard Janke - cast.cpp, seal.cpp
|
||||
Steve Reid - cast.cpp
|
||||
Phil Karn - des.cpp
|
||||
Andrew M. Kuchling - md2.cpp, md4.cpp
|
||||
Colin Plumb - md5.cpp
|
||||
Seal Woods - rc6.cpp
|
||||
Chris Morgan - rijndael.cpp
|
||||
Paulo Baretto - rijndael.cpp, skipjack.cpp, square.cpp
|
||||
Richard De Moliner - safer.cpp
|
||||
Matthew Skala - twofish.cpp
|
||||
Kevin Springle - camellia.cpp, shacal2.cpp, ttmac.cpp, whrlpool.cpp, ripemd.cpp
|
||||
Ronny Van Keer - sha3.cpp
|
||||
Aumasson, Neves, Wilcox-O'Hearn and Winnerlein - blake2.cpp, blake2b_simd.cpp, blake2s_simd.cpp
|
||||
Aaram Yun - aria.cpp, aria_simd.cpp
|
||||
Han Lulu, Markku-Juhani O. Saarinen - sm4.cpp sm4_simd.cpp
|
||||
Daniel J. Bernstein, Jack Lloyd - chacha.cpp, chacha_simd.cpp, chacha_avx.cpp
|
||||
Andrew Moon - ed25519, x25519, donna_32.cpp, donna_64.cpp, donna_sse.cpp
|
||||
|
||||
The Crypto++ Library uses portions of Andy Polyakov's CRYPTOGAMS for Poly1305
|
||||
scalar multiplication, aes_armv4.S, sha1_armv4.S and sha256_armv4.S. CRYPTOGAMS
|
||||
is dual licensed with a permissive BSD-style license. The CRYPTOGAMS license is
|
||||
reproduced below.
|
||||
|
||||
The Crypto++ Library uses portions of Jack Lloyd's Botan for ChaCha SSE2 and
|
||||
AVX. Botan placed the code in public domain for Crypto++ to use.
|
||||
|
||||
The Crypto++ Library (as a compilation) is currently licensed under the Boost
|
||||
Software License 1.0 (http://www.boost.org/users/license.html).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
CRYPTOGAMS License
|
||||
|
||||
Copyright (c) 2006-2017, CRYPTOGAMS by <appro@openssl.org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain copyright notices,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
* Neither the name of the CRYPTOGAMS nor the names of its copyright
|
||||
holder and contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
Crypto++: free C++ Class Library of Cryptographic Schemes
|
||||
Version 8.4 - TBD
|
||||
|
||||
Crypto++ Library is a free C++ class library of cryptographic schemes.
|
||||
Currently the library contains the following algorithms:
|
||||
|
||||
algorithm type name
|
||||
|
||||
authenticated encryption schemes GCM, CCM, EAX, ChaCha20Poly1305 and
|
||||
XChaCha20Poly1305
|
||||
|
||||
high speed stream ciphers ChaCha (8/12/20), ChaCha (IETF), Panama, Salsa20,
|
||||
Sosemanuk, XSalsa20, XChaCha20
|
||||
|
||||
AES and AES candidates AES (Rijndael), RC6, MARS, Twofish, Serpent,
|
||||
CAST-256
|
||||
|
||||
ARIA, Blowfish, Camellia, CHAM, HIGHT, IDEA,
|
||||
Kalyna (128/256/512), LEA, SEED, RC5, SHACAL-2,
|
||||
other block ciphers SIMON (64/128), Skipjack, SPECK (64/128),
|
||||
Simeck, SM4, Threefish (256/512/1024),
|
||||
Triple-DES (DES-EDE2 and DES-EDE3), TEA, XTEA
|
||||
|
||||
block cipher modes of operation ECB, CBC, CBC ciphertext stealing (CTS),
|
||||
CFB, OFB, counter mode (CTR), XTS
|
||||
|
||||
message authentication codes BLAKE2s, BLAKE2b, CMAC, CBC-MAC, DMAC, GMAC, HMAC,
|
||||
Poly1305, Poly1305 (IETF), SipHash, Two-Track-MAC,
|
||||
VMAC
|
||||
|
||||
BLAKE2s, BLAKE2b, Keccack (F1600), SHA-1,
|
||||
hash functions SHA-2 (224/256/384/512), SHA-3 (224/256/384/512),
|
||||
SHAKE (128/256), SipHash, SM3, Tiger,
|
||||
RIPEMD (128/160/256/320), WHIRLPOOL
|
||||
|
||||
RSA, DSA, Determinsitic DSA, ElGamal,
|
||||
public-key cryptography Nyberg-Rueppel (NR), Rabin-Williams (RW), LUC,
|
||||
LUCELG, EC-based German Digital Signature (ECGDSA),
|
||||
DLIES (variants of DHAES), ESIGN
|
||||
|
||||
padding schemes for public-key PKCS#1 v2.0, OAEP, PSS, PSSR, IEEE P1363
|
||||
systems EMSA2 and EMSA5
|
||||
|
||||
Diffie-Hellman (DH), Unified Diffie-Hellman (DH2),
|
||||
key agreement schemes Menezes-Qu-Vanstone (MQV), Hashed MQV (HMQV),
|
||||
Fully Hashed MQV (FHMQV), LUCDIF, XTR-DH
|
||||
|
||||
elliptic curve cryptography ECDSA, Determinsitic ECDSA, ed25519, ECNR, ECIES,
|
||||
ECDH, ECMQV, x25519
|
||||
|
||||
insecure or obsolescent MD2, MD4, MD5, Panama Hash, DES, ARC4, SEAL
|
||||
algorithms retained for backwards 3.0, WAKE-OFB, DESX (DES-XEX3), RC2,
|
||||
compatibility and historical SAFER, 3-WAY, GOST, SHARK, CAST-128, Square
|
||||
value
|
||||
|
||||
Other features include:
|
||||
|
||||
* pseudo random number generators (PRNG): ANSI X9.17 appendix C, RandomPool,
|
||||
DARN, VIA Padlock, RDRAND, RDSEED, NIST Hash and HMAC DRBGs
|
||||
* password based key derivation functions: PBKDF1 and PBKDF2 from PKCS #5,
|
||||
PBKDF from PKCS #12 appendix B, HKDF from RFC 5869, Scrypt from RFC 7914
|
||||
* Shamir's secret sharing scheme and Rabin's information dispersal algorithm
|
||||
(IDA)
|
||||
* fast multi-precision integer (bignum) and polynomial operations
|
||||
* finite field arithmetics, including GF(p) and GF(2^n)
|
||||
* prime number generation and verification
|
||||
* useful non-cryptographic algorithms
|
||||
+ DEFLATE (RFC 1951) compression/decompression with gzip (RFC 1952) and
|
||||
zlib (RFC 1950) format support
|
||||
+ Hex, base-32, base-64, URL safe base-64 encoding and decoding
|
||||
+ 32-bit CRC, CRC-C and Adler32 checksum
|
||||
* class wrappers for these platform and operating system features (optional):
|
||||
+ high resolution timers on Windows, Unix, and Mac OS
|
||||
+ /dev/random, /dev/urandom, /dev/srandom
|
||||
+ Microsoft's CryptGenRandom or BCryptGenRandom on Windows
|
||||
* A high level interface for most of the above, using a filter/pipeline
|
||||
metaphor
|
||||
* benchmarks and validation testing
|
||||
* x86, x64 (x86-64), x32 (ILP32), ARM-32, Aarch32, Aarch64 and Power8 in-core code
|
||||
for the commonly used algorithms
|
||||
+ run-time CPU feature detection and code selection
|
||||
+ supports GCC-style and MSVC-style inline assembly, and MASM for x64
|
||||
+ x86, x64 (x86-64), x32 provides MMX, SSE2, and SSE4 implementations
|
||||
+ ARM-32, Aarch32 and Aarch64 provides NEON, ASIMD and ARMv8 implementations
|
||||
+ Power8 provides in-core AES using NX Crypto Acceleration
|
||||
|
||||
The Crypto++ library was orginally written by Wei Dai. The library is now
|
||||
maintained by several team members and the community. You are welcome to use it
|
||||
for any purpose without paying anyone, but see License.txt for the fine print.
|
||||
|
||||
The following compilers are supported for this release. Please visit
|
||||
http://www.cryptopp.com the most up to date build instructions and porting notes.
|
||||
|
||||
* Visual Studio 2003 - 2019
|
||||
* GCC 3.3 - 10.1
|
||||
* Apple Clang 4.3 - 9.3
|
||||
* LLVM Clang 2.9 - 10.0
|
||||
* C++ Builder 2015
|
||||
* Intel C++ Compiler 9 - 16.0
|
||||
* Sun Studio 12u1 - 12.6
|
||||
* IBM XL C/C++ 10.0 - 13.3
|
||||
|
||||
*** Important Usage Notes ***
|
||||
|
||||
1. If a constructor for A takes a pointer to an object B (except primitive
|
||||
types such as int and char), then A owns B and will delete B at A's
|
||||
destruction. If a constructor for A takes a reference to an object B,
|
||||
then the caller retains ownership of B and should not destroy it until
|
||||
A no longer needs it.
|
||||
|
||||
2. Crypto++ is thread safe at the class level. This means you can use
|
||||
Crypto++ safely in a multithreaded application, but you must provide
|
||||
synchronization when multiple threads access a common Crypto++ object.
|
||||
|
||||
*** MSVC-Specific Information ***
|
||||
|
||||
To compile Crypto++ with MSVC, open "cryptest.sln" (for MSVC 2003 - 2015)
|
||||
and build one or more of the following projects:
|
||||
|
||||
cryptest Non-DLL-Import Configuration - This builds the full static library
|
||||
along with a full test driver.
|
||||
cryptest DLL-Import Configuration - This builds a static library containing
|
||||
only algorithms not in the DLL, along with a full test driver that uses
|
||||
both the DLL and the static library.
|
||||
cryptdll - This builds the DLL. Please note that if you wish to use Crypto++
|
||||
as a FIPS validated module, you must use a pre-built DLL that has undergone
|
||||
the FIPS validation process instead of building your own.
|
||||
dlltest - This builds a sample application that only uses the DLL.
|
||||
|
||||
The DLL used to provide FIPS validated cryptography. The library was moved
|
||||
to the CMVP's <A HREF=
|
||||
"http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140val-historical.htm">
|
||||
Historical Validation List</A>. The library and the DLL are no longer considered
|
||||
validated. You should no longer use the DLL.
|
||||
|
||||
To use the Crypto++ DLL in your application, #include "dll.h" before including
|
||||
any other Crypto++ header files, and place the DLL in the same directory as
|
||||
your .exe file. dll.h includes the line #pragma comment(lib, "cryptopp")
|
||||
so you don't have to explicitly list the import library in your project
|
||||
settings. To use a static library form of Crypto++, make the "cryptlib"
|
||||
project a dependency of your application project, or specify it as
|
||||
an additional library to link with in your project settings.
|
||||
In either case you should check the compiler options to
|
||||
make sure that the library and your application are using the same C++
|
||||
run-time libraries and calling conventions.
|
||||
|
||||
*** DLL Memory Management ***
|
||||
|
||||
Because it's possible for the Crypto++ DLL to delete objects allocated
|
||||
by the calling application, they must use the same C++ memory heap. Three
|
||||
methods are provided to achieve this.
|
||||
1. The calling application can tell Crypto++ what heap to use. This method
|
||||
is required when the calling application uses a non-standard heap.
|
||||
2. Crypto++ can tell the calling application what heap to use. This method
|
||||
is required when the calling application uses a statically linked C++ Run
|
||||
Time Library. (Method 1 does not work in this case because the Crypto++ DLL
|
||||
is initialized before the calling application's heap is initialized.)
|
||||
3. Crypto++ can automatically use the heap provided by the calling application's
|
||||
dynamically linked C++ Run Time Library. The calling application must
|
||||
make sure that the dynamically linked C++ Run Time Library is initialized
|
||||
before Crypto++ is loaded. (At this time it is not clear if it is possible
|
||||
to control the order in which DLLs are initialized on Windows 9x machines,
|
||||
so it might be best to avoid using this method.)
|
||||
|
||||
When Crypto++ attaches to a new process, it searches all modules loaded
|
||||
into the process space for exported functions "GetNewAndDeleteForCryptoPP"
|
||||
and "SetNewAndDeleteFromCryptoPP". If one of these functions is found,
|
||||
Crypto++ uses methods 1 or 2, respectively, by calling the function.
|
||||
Otherwise, method 3 is used.
|
||||
|
||||
*** Linux and Unix-like Specific Information ***
|
||||
|
||||
A makefile is included for you to compile Crypto++ with GCC and compatibles.
|
||||
Make sure you are using GNU Make and GNU ld. The make process will produce
|
||||
two files, libcryptopp.a and cryptest.exe. Run "cryptest.exe v" for the
|
||||
validation suite and "cryptest.exe tv all" for additional test vectors.
|
||||
|
||||
The makefile uses '-DNDEBUG -g2 -O2' CXXFLAGS by default. If you use an
|
||||
alternate build system, like Autotools or CMake, then ensure the build system
|
||||
includes '-DNDEBUG' for production or release builds. The Crypto++ library uses
|
||||
asserts for debugging and diagnostics during development; it does not
|
||||
rely on them to crash a program at runtime.
|
||||
|
||||
If an assert triggers in production software, then unprotected sensitive
|
||||
information could be egressed from the program to the filesystem or the
|
||||
platform's error reporting program, like Apport on Ubuntu or CrashReporter
|
||||
on Apple.
|
||||
|
||||
The makefile orders object files to help remediate problems associated with
|
||||
C++ static initialization order. The library does not use custom linker scripts.
|
||||
If you use an alternate build system, like Autotools or CMake, and collect source
|
||||
files into a list, then ensure these three are at the head of the list: 'cryptlib.cpp
|
||||
cpu.cpp integer.cpp <other sources>'. They should be linked in the same order:
|
||||
'cryptlib.o cpu.o integer.o <other objects>'.
|
||||
|
||||
If your linker supports initialization attributes, like init_priority, then you can
|
||||
define CRYPTOPP_INIT_PRIORITY to control object initialization order. Set it to a
|
||||
value like 250. User programs can use CRYPTOPP_USER_PRIORITY to avoid conflicts with
|
||||
library values. Initialization attributes are more reliable than object file ordering,
|
||||
but its not ubiquitously supported by linkers.
|
||||
|
||||
The makefile links to the static version of the Crypto++ library to avoid binary
|
||||
planting and other LD_PRELOAD tricks. You should use the static version of the
|
||||
library in your programs to help avoid unwanted redirections.
|
||||
|
||||
*** Side Channel Attacks ***
|
||||
|
||||
Crypto++ attempts to resist side channel attacks using various remediations.
|
||||
The remdiations are applied as a best effort but are probably incomplete. They
|
||||
are incomplete due to cpu speculation bugs like Spectre, Meltdown, Foreshadow.
|
||||
The attacks target both cpu caches and internal buffers. Intel generally refers
|
||||
to internal buffer attacks as "Microarchitectural Data Sampling" (MDS).
|
||||
|
||||
The library uses hardware instructions when possible for block ciphers, hashes
|
||||
and other operations. The hardware acceleration remediates some timing
|
||||
attacks. The library also uses cache-aware algoirthms and access patterns
|
||||
to minimize leakage cache evictions.
|
||||
|
||||
Elliptic curves over binary fields are believed to leak information. The task is a
|
||||
work in progress. We don't believe binary fields are used in production, so we feel it
|
||||
is a low risk at the moment.
|
||||
|
||||
Crypto++ does not enagage Specter remediations at this time. The GCC options
|
||||
for Specter are -mfunction-return=thunk and -mindirect-branch=thunk, and the
|
||||
library uses them during testing. If you want the Specter workarounds then add
|
||||
the GCC options to your CXXFLAGS when building the library.
|
||||
|
||||
To help resist attacks you should disable hyperthreading on cpus. If you
|
||||
suspect or find an information leak then please report it.
|
||||
|
||||
*** Documentation and Support ***
|
||||
|
||||
Crypto++ is documented through inline comments in header files, which are
|
||||
processed through Doxygen to produce an HTML reference manual. You can find
|
||||
a link to the manual from http://www.cryptopp.com. Also at that site is
|
||||
the Crypto++ FAQ, which you should browse through before attempting to
|
||||
use this library, because it will likely answer many of questions that
|
||||
may come up. Finally, the site provide the wiki which has many topics
|
||||
and code examples.
|
||||
|
||||
If you run into any problems, please try the Crypto++ mailing list.
|
||||
The subscription information and the list archive are available on
|
||||
http://www.cryptopp.com.
|
||||
|
||||
*** Source Code and Contributing ***
|
||||
|
||||
The source code and its planned changes are available at the following locations.
|
||||
|
||||
* The Crypto++ GitHub repository allows you to view the latest (unreleased)
|
||||
Crypto++ source code via the Linux kernel's git beginning around June 2015.
|
||||
Its also serves as an incubator to nuture and grow the library.
|
||||
* The former Crypto++ SourceForge repository allows you to view the Crypto++
|
||||
source code via Apache's subversion until about July 2015. At that time,
|
||||
SourceForge had infrastructure problems and a cutover to GutHub was performed.
|
||||
* The Roadmap on the wiki provides the general direction the library is heading.
|
||||
It includes planned features and releases, and even some wishlist items.
|
||||
|
||||
Contributions of all types are welcomed. Contributions include the following.
|
||||
|
||||
* Bug finding and fixes
|
||||
* Features and enhancements
|
||||
* Test scripts and test cases
|
||||
* Branch and release testing
|
||||
* Documentation and updates
|
||||
|
||||
If you think you have found a bug in the library, then you should discuss it on the
|
||||
Users mailing list. Discussing it will help bring the issue to the attention of folks
|
||||
who can help resolve the issue. If you want to contribute a bug fix to the library,
|
||||
then make a Pull Request or make a Diff available somewhere. Also see Bug Reports on
|
||||
the wiki.
|
||||
|
||||
Features and enhancements are welcomend additions to the library. This category tends
|
||||
to be time consuming because algorithms and their test cases need to be reviewed and
|
||||
merged. Please be mindful of the test cases, and attempt to procure them from an
|
||||
independent source.
|
||||
|
||||
The library cherishes test scripts and test cases. They ensure the library is fit and
|
||||
they help uncover issues with the library before users experience them. If you have
|
||||
some time, then write some test cases, especially the ones that are intended to break
|
||||
things.
|
||||
|
||||
Branch and release testing is your chance to ensure Master (and planned merges) meets
|
||||
your expectations and perform as expected. If you have a few spare cycles, then please
|
||||
test Master on your favorite platform. We need more testing on MinGW, Windows Phone,
|
||||
Windows Store, Solaris 10 (and below), and modern iOS and OS X (including TV and
|
||||
Watch builds).
|
||||
|
||||
Documentation and updates includes both the inline source code annotations using
|
||||
Doxygen, and the online information provided in the wiki. The wiki is more verbose and
|
||||
usually provides more contextual information than the API reference. Besides testing,
|
||||
documentation is one of the highest returns on investment.
|
||||
|
||||
*** History ***
|
||||
|
||||
The items in this section comprise the most recent history. Please see History.txt
|
||||
for the record back to Crypto++ 1.0.
|
||||
|
||||
8.4.0 - January 2, 2021
|
||||
- minor release, recompile of programs required
|
||||
- expanded community input and support
|
||||
* 67 unique contributors as of this release
|
||||
- fix SIGILL on POWER8 when compiling with GCC 10
|
||||
- fix potential out-of-bounds write in FixedSizeAllocatorWithCleanup
|
||||
- fix compile on AIX POWER7 with IBM XLC 12.01
|
||||
- fix compile on Solaris with SunCC 12.6
|
||||
- revert changes for constant-time elliptic curve algorithms
|
||||
- fix makefile clean and distclean recipes
|
||||
|
||||
8.3.0 - December 20, 2020
|
||||
- minor release, recompile of programs required
|
||||
- expanded community input and support
|
||||
* 66 unique contributors as of this release
|
||||
- fix use of macro CRYPTOPP_ALIGN_DATA
|
||||
- fix potential out-of-bounds read in ECDSA
|
||||
- fix std::bad_alloc when using ByteQueue in pipeline
|
||||
- fix missing CRYPTOPP_CXX17_EXCEPTIONS with Clang
|
||||
- fix potential out-of-bounds read in GCM mode
|
||||
- add configure.sh when preprocessor macros fail
|
||||
- fix potential out-of-bounds read in SipHash
|
||||
- fix compile error on POWER9 due to vec_xl_be
|
||||
- fix K233 curve on POWER8
|
||||
- add Cirrus CI testing
|
||||
- fix broken encryption for some 64-bit ciphers
|
||||
- fix Android cpu-features.c using C++ compiler
|
||||
- disable RDRAND and RDSEED for some AMD processors
|
||||
- fix BLAKE2 hash calculation using Salt and Personalization
|
||||
- refresh Android and iOS build scripts
|
||||
- add XTS mode
|
||||
- fix circular dependency between misc.h and secblock.h
|
||||
- add Certificate interface
|
||||
- fix recursion in AES::Encryption without AESNI
|
||||
- add missing OID for ElGamal encryption
|
||||
- fix missing override in KeyDerivationFunction-derived classes
|
||||
- fix RDSEED assemble under MSVC
|
||||
- fix elliptic curve timing leaks (CVE-2019-14318)
|
||||
- add link-library variable to Makefiles
|
||||
- fix SIZE_MAX definition in misc.h
|
||||
- add GetWord64 and PutWord64 to BufferedTransformation
|
||||
- use HKDF in AutoSeededX917RNG::Reseed
|
||||
- fix Asan finding in VMAC on i686 in inline asm
|
||||
- fix undeclared identifier _mm_roti_epi64 on Gentoo
|
||||
- fix ECIES and GetSymmetricKeyLength
|
||||
- fix possible divide by zero in PKCS5_PBKDF2_HMAC
|
||||
- refine ASN.1 encoders and decoders
|
||||
- disable BMI2 code paths in Integer class
|
||||
- fix use of CRYPTOPP_CLANG_VERSION
|
||||
- add NEON SHA1, SHA256 and SHA512 from Cryptogams
|
||||
- add ARM SHA1, SHA256 and SHA512 from Cryptogams
|
||||
- make config.h more autoconf friendly
|
||||
- handle Clang triplet armv8l-unknown-linux-gnueabihf
|
||||
- fix reference binding to misaligned address in xed25519
|
||||
- clear asserts in TestDataNameValuePairs
|
||||
|
||||
8.2.0 - April 28, 2019
|
||||
- minor release, no recompile of programs required
|
||||
- expanded community input and support
|
||||
* 56 unique contributors as of this release
|
||||
- use PowerPC unaligned loads and stores with Power8
|
||||
- add SKIPJACK test vectors
|
||||
- fix SHAKE-128 and SHAKE-256 compile
|
||||
- removed IS_NEON from Makefile
|
||||
- fix Aarch64 build on Fedora 29
|
||||
- fix missing GF2NT_233_Multiply_Reduce_CLMUL in FIPS DLL
|
||||
- add missing BLAKE2 constructors
|
||||
- fix missing BlockSize() in BLAKE2 classes
|
||||
|
||||
8.1.0 - February 22, 2019
|
||||
- minor release, no recompile of programs required
|
||||
- expanded community input and support
|
||||
* 56 unique contributors as of this release
|
||||
- fix OS X PowerPC builds with Clang
|
||||
- add Microsoft ARM64 support
|
||||
- fix iPhone Simulator build due to missing symbols
|
||||
- add CRYPTOPP_BUGGY_SIMD_LOAD_AND_STORE
|
||||
- add carryless multiplies for NIST b233 and k233 curves
|
||||
- fix OpenMP build due to use of OpenMP 4 with down-level compilers
|
||||
- add SignStream and VerifyStream for ed25519 and large files
|
||||
- fix missing AlgorithmProvider in PanamaHash
|
||||
- add SHAKE-128 and SHAKE-256
|
||||
- fix AVX2 build due to _mm256_broadcastsi128_si256
|
||||
- add IETF ChaCha, XChaCha, ChaChaPoly1305 and XChaChaPoly1305
|
||||
|
||||
8.0.0 - December 28, 2018
|
||||
- major release, recompile of programs required
|
||||
- expanded community input and support
|
||||
* 54 unique contributors as of this release
|
||||
- add x25519 key exchange and ed25519 signature scheme
|
||||
- add limited Asymmetric Key Package support from RFC 5958
|
||||
- add Power9 DARN random number generator support
|
||||
- add CHAM, HC-128, HC-256, Hight, LEA, Rabbit, Simeck
|
||||
- fix FixedSizeAllocatorWithCleanup may be unaligned on some platforms
|
||||
- cutover to GNU Make-based cpu feature tests
|
||||
- rename files with dashes to underscores
|
||||
- fix LegacyDecryptor and LegacyDecryptorWithMAC use wrong MAC
|
||||
- fix incorrect AES/CBC decryption on Windows
|
||||
- avoid Singleton<T> when possible, avoid std::call_once completely
|
||||
- fix SPARC alignment problems due to GetAlignmentOf<T>() on word64
|
||||
- add ARM AES asm implementation from Cryptogams
|
||||
- remove CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS support
|
||||
|
||||
June 2015 - Changing of the guard. Wei Dai turned the library over to the
|
||||
community. The first community release was Crypto++ 5.6.3. Wei is
|
||||
no longer involved with the daily operations of the project. Wei
|
||||
still provides guidance when we have questions.
|
||||
|
||||
Originally written by Wei Dai, maintained by the Crypto++ Project
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
#include "config.h"
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4100 4189 4996)
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_GCC_DIAGNOSTIC_AVAILABLE
|
||||
# pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
|
||||
USING_NAMESPACE(CryptoPP)
|
||||
USING_NAMESPACE(std)
|
||||
|
||||
#ifndef CRYPTOPP_UNUSED
|
||||
# define CRYPTOPP_UNUSED(x) (void(x))
|
||||
#endif
|
||||
|
||||
// Used for testing the compiler and linker in cryptest.sh
|
||||
#if defined(CRYPTOPP_ADHOC_MAIN) || defined(ADHOC_MAIN)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
CRYPTOPP_UNUSED(argc), CRYPTOPP_UNUSED(argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Classic use of adhoc to setup calling convention
|
||||
#else
|
||||
|
||||
extern int (*AdhocTest)(int argc, char *argv[]);
|
||||
|
||||
int MyAdhocTest(int argc, char *argv[])
|
||||
{
|
||||
CRYPTOPP_UNUSED(argc), CRYPTOPP_UNUSED(argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s_i = (AdhocTest = &MyAdhocTest, 0);
|
||||
|
||||
#endif
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// adler32.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
#include "adler32.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void Adler32::Update(const byte *input, size_t length)
|
||||
{
|
||||
const unsigned long BASE = 65521;
|
||||
|
||||
unsigned long s1 = m_s1;
|
||||
unsigned long s2 = m_s2;
|
||||
|
||||
if (length % 8 != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
s1 += *input++;
|
||||
s2 += s1;
|
||||
length--;
|
||||
} while (length % 8 != 0);
|
||||
|
||||
if (s1 >= BASE)
|
||||
s1 -= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
s1 += input[0]; s2 += s1;
|
||||
s1 += input[1]; s2 += s1;
|
||||
s1 += input[2]; s2 += s1;
|
||||
s1 += input[3]; s2 += s1;
|
||||
s1 += input[4]; s2 += s1;
|
||||
s1 += input[5]; s2 += s1;
|
||||
s1 += input[6]; s2 += s1;
|
||||
s1 += input[7]; s2 += s1;
|
||||
|
||||
length -= 8;
|
||||
input += 8;
|
||||
|
||||
if (s1 >= BASE)
|
||||
s1 -= BASE;
|
||||
if (length % 0x8000 == 0)
|
||||
s2 %= BASE;
|
||||
}
|
||||
|
||||
CRYPTOPP_ASSERT(s1 < BASE);
|
||||
CRYPTOPP_ASSERT(s2 < BASE);
|
||||
|
||||
m_s1 = (word16)s1;
|
||||
m_s2 = (word16)s2;
|
||||
}
|
||||
|
||||
void Adler32::TruncatedFinal(byte *hash, size_t size)
|
||||
{
|
||||
ThrowIfInvalidTruncatedSize(size);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
default:
|
||||
hash[3] = byte(m_s1);
|
||||
// fall through
|
||||
case 3:
|
||||
hash[2] = byte(m_s1 >> 8);
|
||||
// fall through
|
||||
case 2:
|
||||
hash[1] = byte(m_s2);
|
||||
// fall through
|
||||
case 1:
|
||||
hash[0] = byte(m_s2 >> 8);
|
||||
// fall through
|
||||
case 0:
|
||||
;
|
||||
// fall through
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// adler32.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file adler32.h
|
||||
/// \brief Class file for ADLER-32 checksum calculations
|
||||
|
||||
#ifndef CRYPTOPP_ADLER32_H
|
||||
#define CRYPTOPP_ADLER32_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// ADLER-32 checksum calculations
|
||||
class Adler32 : public HashTransformation
|
||||
{
|
||||
public:
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = 4);
|
||||
Adler32() {Reset();}
|
||||
void Update(const byte *input, size_t length);
|
||||
void TruncatedFinal(byte *hash, size_t size);
|
||||
unsigned int DigestSize() const {return DIGESTSIZE;}
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "Adler32";}
|
||||
std::string AlgorithmName() const {return StaticAlgorithmName();}
|
||||
|
||||
private:
|
||||
void Reset() {m_s1 = 1; m_s2 = 0;}
|
||||
|
||||
word16 m_s1, m_s2;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+1281
File diff suppressed because it is too large
Load Diff
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
// aes.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file
|
||||
/// \brief Class file for the AES cipher (Rijndael)
|
||||
/// \details AES is a typdef for Rijndael classes. All key sizes are supported.
|
||||
/// The library only provides Rijndael with 128-bit blocks, and not 192-bit or 256-bit blocks
|
||||
/// \since Rijndael since Crypto++ 3.1, Intel AES-NI since Crypto++ 5.6.1, ARMv8 AES since Crypto++ 6.0,
|
||||
/// Power8 AES since Crypto++ 6.0
|
||||
|
||||
#ifndef CRYPTOPP_AES_H
|
||||
#define CRYPTOPP_AES_H
|
||||
|
||||
#include "rijndael.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief AES block cipher (Rijndael)
|
||||
/// \details AES is a typdef for Rijndael classes. All key sizes are supported.
|
||||
/// The library only provides Rijndael with 128-bit blocks, and not 192-bit or 256-bit blocks
|
||||
/// \sa <a href="http://www.cryptolounge.org/wiki/AES">AES</a> winner, announced on 10/2/2000
|
||||
/// \since Rijndael since Crypto++ 3.1, Intel AES-NI since Crypto++ 5.6.1, ARMv8 AES since Crypto++ 6.0,
|
||||
/// Power8 AES since Crypto++ 6.0
|
||||
DOCUMENTED_TYPEDEF(Rijndael, AES);
|
||||
|
||||
typedef RijndaelEncryption AESEncryption;
|
||||
typedef RijndaelDecryption AESDecryption;
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+1215
File diff suppressed because it is too large
Load Diff
+30
@@ -0,0 +1,30 @@
|
||||
/* Header file for use with Cryptogam's ARMv4 AES. */
|
||||
/* Also see http://www.openssl.org/~appro/cryptogams/ and */
|
||||
/* https://wiki.openssl.org/index.php?title=Cryptogams_AES */
|
||||
|
||||
#ifndef CRYPTOGAMS_AES_ARMV4_H
|
||||
#define CRYPTOGAMS_AES_ARMV4_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//#define AES_MAXNR 14
|
||||
//typedef struct AES_KEY_st {
|
||||
// unsigned int rd_key[4 * (AES_MAXNR + 1)];
|
||||
// int rounds;
|
||||
//} AES_KEY;
|
||||
|
||||
// Instead of AES_KEY we use a 'word32 rkey[4*15+4]'. It has space for
|
||||
// both the AES_MAXNR round keys and the number of rounds in the tail.
|
||||
|
||||
int cryptogams_AES_set_encrypt_key(const unsigned char *userKey, const int bits, unsigned int *rkey);
|
||||
int cryptogams_AES_set_decrypt_key(const unsigned char *userKey, const int bits, unsigned int *rkey);
|
||||
void cryptogams_AES_encrypt_block(const unsigned char *in, unsigned char *out, const unsigned int *rkey);
|
||||
void cryptogams_AES_decrypt_block(const unsigned char *in, unsigned char *out, const unsigned int *rkey);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CRYPTOGAMS_AES_ARMV4_H */
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
// algebra.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#ifndef CRYPTOPP_ALGEBRA_CPP // SunCC workaround: compiler could cause this file to be included twice
|
||||
#define CRYPTOPP_ALGEBRA_CPP
|
||||
|
||||
#include "algebra.h"
|
||||
#include "integer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
template <class T> const T& AbstractGroup<T>::Double(const Element &a) const
|
||||
{
|
||||
return this->Add(a, a);
|
||||
}
|
||||
|
||||
template <class T> const T& AbstractGroup<T>::Subtract(const Element &a, const Element &b) const
|
||||
{
|
||||
// make copy of a in case Inverse() overwrites it
|
||||
Element a1(a);
|
||||
return this->Add(a1, Inverse(b));
|
||||
}
|
||||
|
||||
template <class T> T& AbstractGroup<T>::Accumulate(Element &a, const Element &b) const
|
||||
{
|
||||
return a = this->Add(a, b);
|
||||
}
|
||||
|
||||
template <class T> T& AbstractGroup<T>::Reduce(Element &a, const Element &b) const
|
||||
{
|
||||
return a = this->Subtract(a, b);
|
||||
}
|
||||
|
||||
template <class T> const T& AbstractRing<T>::Square(const Element &a) const
|
||||
{
|
||||
return this->Multiply(a, a);
|
||||
}
|
||||
|
||||
template <class T> const T& AbstractRing<T>::Divide(const Element &a, const Element &b) const
|
||||
{
|
||||
// make copy of a in case MultiplicativeInverse() overwrites it
|
||||
Element a1(a);
|
||||
return this->Multiply(a1, this->MultiplicativeInverse(b));
|
||||
}
|
||||
|
||||
template <class T> const T& AbstractEuclideanDomain<T>::Mod(const Element &a, const Element &b) const
|
||||
{
|
||||
Element q;
|
||||
this->DivisionAlgorithm(result, q, a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T> const T& AbstractEuclideanDomain<T>::Gcd(const Element &a, const Element &b) const
|
||||
{
|
||||
Element g[3]={b, a};
|
||||
unsigned int i0=0, i1=1, i2=2;
|
||||
|
||||
while (!this->Equal(g[i1], this->Identity()))
|
||||
{
|
||||
g[i2] = this->Mod(g[i0], g[i1]);
|
||||
unsigned int t = i0; i0 = i1; i1 = i2; i2 = t;
|
||||
}
|
||||
|
||||
return result = g[i0];
|
||||
}
|
||||
|
||||
template <class T> const typename QuotientRing<T>::Element& QuotientRing<T>::MultiplicativeInverse(const Element &a) const
|
||||
{
|
||||
Element g[3]={m_modulus, a};
|
||||
Element v[3]={m_domain.Identity(), m_domain.MultiplicativeIdentity()};
|
||||
Element y;
|
||||
unsigned int i0=0, i1=1, i2=2;
|
||||
|
||||
while (!this->Equal(g[i1], this->Identity()))
|
||||
{
|
||||
// y = g[i0] / g[i1];
|
||||
// g[i2] = g[i0] % g[i1];
|
||||
m_domain.DivisionAlgorithm(g[i2], y, g[i0], g[i1]);
|
||||
// v[i2] = v[i0] - (v[i1] * y);
|
||||
v[i2] = m_domain.Subtract(v[i0], m_domain.Multiply(v[i1], y));
|
||||
unsigned int t = i0; i0 = i1; i1 = i2; i2 = t;
|
||||
}
|
||||
|
||||
return m_domain.IsUnit(g[i0]) ? m_domain.Divide(v[i0], g[i0]) : m_domain.Identity();
|
||||
}
|
||||
|
||||
template <class T> T AbstractGroup<T>::ScalarMultiply(const Element &base, const Integer &exponent) const
|
||||
{
|
||||
Element result;
|
||||
this->SimultaneousMultiply(&result, base, &exponent, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T> T AbstractGroup<T>::CascadeScalarMultiply(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const
|
||||
{
|
||||
const unsigned expLen = STDMAX(e1.BitCount(), e2.BitCount());
|
||||
if (expLen==0)
|
||||
return this->Identity();
|
||||
|
||||
const unsigned w = (expLen <= 46 ? 1 : (expLen <= 260 ? 2 : 3));
|
||||
const unsigned tableSize = 1<<w;
|
||||
std::vector<Element> powerTable(tableSize << w);
|
||||
|
||||
powerTable[1] = x;
|
||||
powerTable[tableSize] = y;
|
||||
if (w==1)
|
||||
powerTable[3] = this->Add(x,y);
|
||||
else
|
||||
{
|
||||
powerTable[2] = this->Double(x);
|
||||
powerTable[2*tableSize] = this->Double(y);
|
||||
|
||||
unsigned i, j;
|
||||
|
||||
for (i=3; i<tableSize; i+=2)
|
||||
powerTable[i] = Add(powerTable[i-2], powerTable[2]);
|
||||
for (i=1; i<tableSize; i+=2)
|
||||
for (j=i+tableSize; j<(tableSize<<w); j+=tableSize)
|
||||
powerTable[j] = Add(powerTable[j-tableSize], y);
|
||||
|
||||
for (i=3*tableSize; i<(tableSize<<w); i+=2*tableSize)
|
||||
powerTable[i] = Add(powerTable[i-2*tableSize], powerTable[2*tableSize]);
|
||||
for (i=tableSize; i<(tableSize<<w); i+=2*tableSize)
|
||||
for (j=i+2; j<i+tableSize; j+=2)
|
||||
powerTable[j] = Add(powerTable[j-1], x);
|
||||
}
|
||||
|
||||
Element result;
|
||||
unsigned power1 = 0, power2 = 0, prevPosition = expLen-1;
|
||||
bool firstTime = true;
|
||||
|
||||
for (int i = expLen-1; i>=0; i--)
|
||||
{
|
||||
power1 = 2*power1 + e1.GetBit(i);
|
||||
power2 = 2*power2 + e2.GetBit(i);
|
||||
|
||||
if (i==0 || 2*power1 >= tableSize || 2*power2 >= tableSize)
|
||||
{
|
||||
unsigned squaresBefore = prevPosition-i;
|
||||
unsigned squaresAfter = 0;
|
||||
prevPosition = i;
|
||||
while ((power1 || power2) && power1%2 == 0 && power2%2==0)
|
||||
{
|
||||
power1 /= 2;
|
||||
power2 /= 2;
|
||||
squaresBefore--;
|
||||
squaresAfter++;
|
||||
}
|
||||
if (firstTime)
|
||||
{
|
||||
result = powerTable[(power2<<w) + power1];
|
||||
firstTime = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (squaresBefore--)
|
||||
result = this->Double(result);
|
||||
if (power1 || power2)
|
||||
Accumulate(result, powerTable[(power2<<w) + power1]);
|
||||
}
|
||||
while (squaresAfter--)
|
||||
result = this->Double(result);
|
||||
power1 = power2 = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Element, class Iterator> Element GeneralCascadeMultiplication(const AbstractGroup<Element> &group, Iterator begin, Iterator end)
|
||||
{
|
||||
if (end-begin == 1)
|
||||
return group.ScalarMultiply(begin->base, begin->exponent);
|
||||
else if (end-begin == 2)
|
||||
return group.CascadeScalarMultiply(begin->base, begin->exponent, (begin+1)->base, (begin+1)->exponent);
|
||||
else
|
||||
{
|
||||
Integer q, t;
|
||||
Iterator last = end;
|
||||
--last;
|
||||
|
||||
std::make_heap(begin, end);
|
||||
std::pop_heap(begin, end);
|
||||
|
||||
while (!!begin->exponent)
|
||||
{
|
||||
// last->exponent is largest exponent, begin->exponent is next largest
|
||||
t = last->exponent;
|
||||
Integer::Divide(last->exponent, q, t, begin->exponent);
|
||||
|
||||
if (q == Integer::One())
|
||||
group.Accumulate(begin->base, last->base); // avoid overhead of ScalarMultiply()
|
||||
else
|
||||
group.Accumulate(begin->base, group.ScalarMultiply(last->base, q));
|
||||
|
||||
std::push_heap(begin, end);
|
||||
std::pop_heap(begin, end);
|
||||
}
|
||||
|
||||
return group.ScalarMultiply(last->base, last->exponent);
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowSlider
|
||||
{
|
||||
WindowSlider(const Integer &expIn, bool fastNegate, unsigned int windowSizeIn=0)
|
||||
: exp(expIn), windowModulus(Integer::One()), windowSize(windowSizeIn), windowBegin(0), expWindow(0)
|
||||
, fastNegate(fastNegate), negateNext(false), firstTime(true), finished(false)
|
||||
{
|
||||
if (windowSize == 0)
|
||||
{
|
||||
unsigned int expLen = exp.BitCount();
|
||||
windowSize = expLen <= 17 ? 1 : (expLen <= 24 ? 2 : (expLen <= 70 ? 3 : (expLen <= 197 ? 4 : (expLen <= 539 ? 5 : (expLen <= 1434 ? 6 : 7)))));
|
||||
}
|
||||
windowModulus <<= windowSize;
|
||||
}
|
||||
|
||||
void FindNextWindow()
|
||||
{
|
||||
unsigned int expLen = exp.WordCount() * WORD_BITS;
|
||||
unsigned int skipCount = firstTime ? 0 : windowSize;
|
||||
firstTime = false;
|
||||
while (!exp.GetBit(skipCount))
|
||||
{
|
||||
if (skipCount >= expLen)
|
||||
{
|
||||
finished = true;
|
||||
return;
|
||||
}
|
||||
skipCount++;
|
||||
}
|
||||
|
||||
exp >>= skipCount;
|
||||
windowBegin += skipCount;
|
||||
expWindow = word32(exp % (word(1) << windowSize));
|
||||
|
||||
if (fastNegate && exp.GetBit(windowSize))
|
||||
{
|
||||
negateNext = true;
|
||||
expWindow = (word32(1) << windowSize) - expWindow;
|
||||
exp += windowModulus;
|
||||
}
|
||||
else
|
||||
negateNext = false;
|
||||
}
|
||||
|
||||
Integer exp, windowModulus;
|
||||
unsigned int windowSize, windowBegin;
|
||||
word32 expWindow;
|
||||
bool fastNegate, negateNext, firstTime, finished;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void AbstractGroup<T>::SimultaneousMultiply(T *results, const T &base, const Integer *expBegin, unsigned int expCount) const
|
||||
{
|
||||
std::vector<std::vector<Element> > buckets(expCount);
|
||||
std::vector<WindowSlider> exponents;
|
||||
exponents.reserve(expCount);
|
||||
unsigned int i;
|
||||
|
||||
for (i=0; expBegin && i<expCount; i++)
|
||||
{
|
||||
CRYPTOPP_ASSERT(expBegin->NotNegative());
|
||||
exponents.push_back(WindowSlider(*expBegin++, InversionIsFast(), 0));
|
||||
exponents[i].FindNextWindow();
|
||||
buckets[i].resize(((size_t) 1) << (exponents[i].windowSize-1), Identity());
|
||||
}
|
||||
|
||||
unsigned int expBitPosition = 0;
|
||||
Element g = base;
|
||||
bool notDone = true;
|
||||
|
||||
while (notDone)
|
||||
{
|
||||
notDone = false;
|
||||
for (i=0; i<expCount; i++)
|
||||
{
|
||||
if (!exponents[i].finished && expBitPosition == exponents[i].windowBegin)
|
||||
{
|
||||
Element &bucket = buckets[i][exponents[i].expWindow/2];
|
||||
if (exponents[i].negateNext)
|
||||
Accumulate(bucket, Inverse(g));
|
||||
else
|
||||
Accumulate(bucket, g);
|
||||
exponents[i].FindNextWindow();
|
||||
}
|
||||
notDone = notDone || !exponents[i].finished;
|
||||
}
|
||||
|
||||
if (notDone)
|
||||
{
|
||||
g = Double(g);
|
||||
expBitPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<expCount; i++)
|
||||
{
|
||||
Element &r = *results++;
|
||||
r = buckets[i][buckets[i].size()-1];
|
||||
if (buckets[i].size() > 1)
|
||||
{
|
||||
for (int j = (int)buckets[i].size()-2; j >= 1; j--)
|
||||
{
|
||||
Accumulate(buckets[i][j], buckets[i][j+1]);
|
||||
Accumulate(r, buckets[i][j]);
|
||||
}
|
||||
Accumulate(buckets[i][0], buckets[i][1]);
|
||||
r = Add(Double(r), buckets[i][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> T AbstractRing<T>::Exponentiate(const Element &base, const Integer &exponent) const
|
||||
{
|
||||
Element result;
|
||||
SimultaneousExponentiate(&result, base, &exponent, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T> T AbstractRing<T>::CascadeExponentiate(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const
|
||||
{
|
||||
return MultiplicativeGroup().AbstractGroup<T>::CascadeScalarMultiply(x, e1, y, e2);
|
||||
}
|
||||
|
||||
template <class Element, class Iterator> Element GeneralCascadeExponentiation(const AbstractRing<Element> &ring, Iterator begin, Iterator end)
|
||||
{
|
||||
return GeneralCascadeMultiplication<Element>(ring.MultiplicativeGroup(), begin, end);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void AbstractRing<T>::SimultaneousExponentiate(T *results, const T &base, const Integer *exponents, unsigned int expCount) const
|
||||
{
|
||||
MultiplicativeGroup().AbstractGroup<T>::SimultaneousMultiply(results, base, exponents, expCount);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
// algebra.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file algebra.h
|
||||
/// \brief Classes for performing mathematics over different fields
|
||||
|
||||
#ifndef CRYPTOPP_ALGEBRA_H
|
||||
#define CRYPTOPP_ALGEBRA_H
|
||||
|
||||
#include "config.h"
|
||||
#include "integer.h"
|
||||
#include "misc.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
class Integer;
|
||||
|
||||
/// \brief Abstract group
|
||||
/// \tparam T element class or type
|
||||
/// \details <tt>const Element&</tt> returned by member functions are references
|
||||
/// to internal data members. Since each object may have only
|
||||
/// one such data member for holding results, the following code
|
||||
/// will produce incorrect results:
|
||||
/// <pre> abcd = group.Add(group.Add(a,b), group.Add(c,d));</pre>
|
||||
/// But this should be fine:
|
||||
/// <pre> abcd = group.Add(a, group.Add(b, group.Add(c,d));</pre>
|
||||
template <class T> class CRYPTOPP_NO_VTABLE AbstractGroup
|
||||
{
|
||||
public:
|
||||
typedef T Element;
|
||||
|
||||
virtual ~AbstractGroup() {}
|
||||
|
||||
/// \brief Compare two elements for equality
|
||||
/// \param a first element
|
||||
/// \param b second element
|
||||
/// \return true if the elements are equal, false otherwise
|
||||
/// \details Equal() tests the elements for equality using <tt>a==b</tt>
|
||||
virtual bool Equal(const Element &a, const Element &b) const =0;
|
||||
|
||||
/// \brief Provides the Identity element
|
||||
/// \return the Identity element
|
||||
virtual const Element& Identity() const =0;
|
||||
|
||||
/// \brief Adds elements in the group
|
||||
/// \param a first element
|
||||
/// \param b second element
|
||||
/// \return the sum of <tt>a</tt> and <tt>b</tt>
|
||||
virtual const Element& Add(const Element &a, const Element &b) const =0;
|
||||
|
||||
/// \brief Inverts the element in the group
|
||||
/// \param a first element
|
||||
/// \return the inverse of the element
|
||||
virtual const Element& Inverse(const Element &a) const =0;
|
||||
|
||||
/// \brief Determine if inversion is fast
|
||||
/// \return true if inversion is fast, false otherwise
|
||||
virtual bool InversionIsFast() const {return false;}
|
||||
|
||||
/// \brief Doubles an element in the group
|
||||
/// \param a the element
|
||||
/// \return the element doubled
|
||||
virtual const Element& Double(const Element &a) const;
|
||||
|
||||
/// \brief Subtracts elements in the group
|
||||
/// \param a first element
|
||||
/// \param b second element
|
||||
/// \return the difference of <tt>a</tt> and <tt>b</tt>. The element <tt>a</tt> must provide a Subtract member function.
|
||||
virtual const Element& Subtract(const Element &a, const Element &b) const;
|
||||
|
||||
/// \brief TODO
|
||||
/// \param a first element
|
||||
/// \param b second element
|
||||
/// \return TODO
|
||||
virtual Element& Accumulate(Element &a, const Element &b) const;
|
||||
|
||||
/// \brief Reduces an element in the congruence class
|
||||
/// \param a element to reduce
|
||||
/// \param b the congruence class
|
||||
/// \return the reduced element
|
||||
virtual Element& Reduce(Element &a, const Element &b) const;
|
||||
|
||||
/// \brief Performs a scalar multiplication
|
||||
/// \param a multiplicand
|
||||
/// \param e multiplier
|
||||
/// \return the product
|
||||
virtual Element ScalarMultiply(const Element &a, const Integer &e) const;
|
||||
|
||||
/// \brief TODO
|
||||
/// \param x first multiplicand
|
||||
/// \param e1 the first multiplier
|
||||
/// \param y second multiplicand
|
||||
/// \param e2 the second multiplier
|
||||
/// \return TODO
|
||||
virtual Element CascadeScalarMultiply(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const;
|
||||
|
||||
/// \brief Multiplies a base to multiple exponents in a group
|
||||
/// \param results an array of Elements
|
||||
/// \param base the base to raise to the exponents
|
||||
/// \param exponents an array of exponents
|
||||
/// \param exponentsCount the number of exponents in the array
|
||||
/// \details SimultaneousMultiply() multiplies the base to each exponent in the exponents array and stores the
|
||||
/// result at the respective position in the results array.
|
||||
/// \details SimultaneousMultiply() must be implemented in a derived class.
|
||||
/// \pre <tt>COUNTOF(results) == exponentsCount</tt>
|
||||
/// \pre <tt>COUNTOF(exponents) == exponentsCount</tt>
|
||||
virtual void SimultaneousMultiply(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const;
|
||||
};
|
||||
|
||||
/// \brief Abstract ring
|
||||
/// \tparam T element class or type
|
||||
/// \details <tt>const Element&</tt> returned by member functions are references
|
||||
/// to internal data members. Since each object may have only
|
||||
/// one such data member for holding results, the following code
|
||||
/// will produce incorrect results:
|
||||
/// <pre> abcd = group.Add(group.Add(a,b), group.Add(c,d));</pre>
|
||||
/// But this should be fine:
|
||||
/// <pre> abcd = group.Add(a, group.Add(b, group.Add(c,d));</pre>
|
||||
template <class T> class CRYPTOPP_NO_VTABLE AbstractRing : public AbstractGroup<T>
|
||||
{
|
||||
public:
|
||||
typedef T Element;
|
||||
|
||||
/// \brief Construct an AbstractRing
|
||||
AbstractRing() {m_mg.m_pRing = this;}
|
||||
|
||||
/// \brief Copy construct an AbstractRing
|
||||
/// \param source other AbstractRing
|
||||
AbstractRing(const AbstractRing &source)
|
||||
{CRYPTOPP_UNUSED(source); m_mg.m_pRing = this;}
|
||||
|
||||
/// \brief Assign an AbstractRing
|
||||
/// \param source other AbstractRing
|
||||
AbstractRing& operator=(const AbstractRing &source)
|
||||
{CRYPTOPP_UNUSED(source); return *this;}
|
||||
|
||||
/// \brief Determines whether an element is a unit in the group
|
||||
/// \param a the element
|
||||
/// \return true if the element is a unit after reduction, false otherwise.
|
||||
virtual bool IsUnit(const Element &a) const =0;
|
||||
|
||||
/// \brief Retrieves the multiplicative identity
|
||||
/// \return the multiplicative identity
|
||||
virtual const Element& MultiplicativeIdentity() const =0;
|
||||
|
||||
/// \brief Multiplies elements in the group
|
||||
/// \param a the multiplicand
|
||||
/// \param b the multiplier
|
||||
/// \return the product of a and b
|
||||
virtual const Element& Multiply(const Element &a, const Element &b) const =0;
|
||||
|
||||
/// \brief Calculate the multiplicative inverse of an element in the group
|
||||
/// \param a the element
|
||||
virtual const Element& MultiplicativeInverse(const Element &a) const =0;
|
||||
|
||||
/// \brief Square an element in the group
|
||||
/// \param a the element
|
||||
/// \return the element squared
|
||||
virtual const Element& Square(const Element &a) const;
|
||||
|
||||
/// \brief Divides elements in the group
|
||||
/// \param a the dividend
|
||||
/// \param b the divisor
|
||||
/// \return the quotient
|
||||
virtual const Element& Divide(const Element &a, const Element &b) const;
|
||||
|
||||
/// \brief Raises a base to an exponent in the group
|
||||
/// \param a the base
|
||||
/// \param e the exponent
|
||||
/// \return the exponentiation
|
||||
virtual Element Exponentiate(const Element &a, const Integer &e) const;
|
||||
|
||||
/// \brief TODO
|
||||
/// \param x first element
|
||||
/// \param e1 first exponent
|
||||
/// \param y second element
|
||||
/// \param e2 second exponent
|
||||
/// \return TODO
|
||||
virtual Element CascadeExponentiate(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const;
|
||||
|
||||
/// \brief Exponentiates a base to multiple exponents in the Ring
|
||||
/// \param results an array of Elements
|
||||
/// \param base the base to raise to the exponents
|
||||
/// \param exponents an array of exponents
|
||||
/// \param exponentsCount the number of exponents in the array
|
||||
/// \details SimultaneousExponentiate() raises the base to each exponent in the exponents array and stores the
|
||||
/// result at the respective position in the results array.
|
||||
/// \details SimultaneousExponentiate() must be implemented in a derived class.
|
||||
/// \pre <tt>COUNTOF(results) == exponentsCount</tt>
|
||||
/// \pre <tt>COUNTOF(exponents) == exponentsCount</tt>
|
||||
virtual void SimultaneousExponentiate(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const;
|
||||
|
||||
/// \brief Retrieves the multiplicative group
|
||||
/// \return the multiplicative group
|
||||
virtual const AbstractGroup<T>& MultiplicativeGroup() const
|
||||
{return m_mg;}
|
||||
|
||||
private:
|
||||
class MultiplicativeGroupT : public AbstractGroup<T>
|
||||
{
|
||||
public:
|
||||
const AbstractRing<T>& GetRing() const
|
||||
{return *m_pRing;}
|
||||
|
||||
bool Equal(const Element &a, const Element &b) const
|
||||
{return GetRing().Equal(a, b);}
|
||||
|
||||
const Element& Identity() const
|
||||
{return GetRing().MultiplicativeIdentity();}
|
||||
|
||||
const Element& Add(const Element &a, const Element &b) const
|
||||
{return GetRing().Multiply(a, b);}
|
||||
|
||||
Element& Accumulate(Element &a, const Element &b) const
|
||||
{return a = GetRing().Multiply(a, b);}
|
||||
|
||||
const Element& Inverse(const Element &a) const
|
||||
{return GetRing().MultiplicativeInverse(a);}
|
||||
|
||||
const Element& Subtract(const Element &a, const Element &b) const
|
||||
{return GetRing().Divide(a, b);}
|
||||
|
||||
Element& Reduce(Element &a, const Element &b) const
|
||||
{return a = GetRing().Divide(a, b);}
|
||||
|
||||
const Element& Double(const Element &a) const
|
||||
{return GetRing().Square(a);}
|
||||
|
||||
Element ScalarMultiply(const Element &a, const Integer &e) const
|
||||
{return GetRing().Exponentiate(a, e);}
|
||||
|
||||
Element CascadeScalarMultiply(const Element &x, const Integer &e1, const Element &y, const Integer &e2) const
|
||||
{return GetRing().CascadeExponentiate(x, e1, y, e2);}
|
||||
|
||||
void SimultaneousMultiply(Element *results, const Element &base, const Integer *exponents, unsigned int exponentsCount) const
|
||||
{GetRing().SimultaneousExponentiate(results, base, exponents, exponentsCount);}
|
||||
|
||||
const AbstractRing<T> *m_pRing;
|
||||
};
|
||||
|
||||
MultiplicativeGroupT m_mg;
|
||||
};
|
||||
|
||||
// ********************************************************
|
||||
|
||||
/// \brief Base and exponent
|
||||
/// \tparam T base class or type
|
||||
/// \tparam E exponent class or type
|
||||
template <class T, class E = Integer>
|
||||
struct BaseAndExponent
|
||||
{
|
||||
public:
|
||||
BaseAndExponent() {}
|
||||
BaseAndExponent(const T &base, const E &exponent) : base(base), exponent(exponent) {}
|
||||
bool operator<(const BaseAndExponent<T, E> &rhs) const {return exponent < rhs.exponent;}
|
||||
T base;
|
||||
E exponent;
|
||||
};
|
||||
|
||||
// VC60 workaround: incomplete member template support
|
||||
template <class Element, class Iterator>
|
||||
Element GeneralCascadeMultiplication(const AbstractGroup<Element> &group, Iterator begin, Iterator end);
|
||||
template <class Element, class Iterator>
|
||||
Element GeneralCascadeExponentiation(const AbstractRing<Element> &ring, Iterator begin, Iterator end);
|
||||
|
||||
// ********************************************************
|
||||
|
||||
/// \brief Abstract Euclidean domain
|
||||
/// \tparam T element class or type
|
||||
/// \details <tt>const Element&</tt> returned by member functions are references
|
||||
/// to internal data members. Since each object may have only
|
||||
/// one such data member for holding results, the following code
|
||||
/// will produce incorrect results:
|
||||
/// <pre> abcd = group.Add(group.Add(a,b), group.Add(c,d));</pre>
|
||||
/// But this should be fine:
|
||||
/// <pre> abcd = group.Add(a, group.Add(b, group.Add(c,d));</pre>
|
||||
template <class T> class CRYPTOPP_NO_VTABLE AbstractEuclideanDomain : public AbstractRing<T>
|
||||
{
|
||||
public:
|
||||
typedef T Element;
|
||||
|
||||
/// \brief Performs the division algorithm on two elements in the ring
|
||||
/// \param r the remainder
|
||||
/// \param q the quotient
|
||||
/// \param a the dividend
|
||||
/// \param d the divisor
|
||||
virtual void DivisionAlgorithm(Element &r, Element &q, const Element &a, const Element &d) const =0;
|
||||
|
||||
/// \brief Performs a modular reduction in the ring
|
||||
/// \param a the element
|
||||
/// \param b the modulus
|
||||
/// \return the result of <tt>a%b</tt>.
|
||||
virtual const Element& Mod(const Element &a, const Element &b) const =0;
|
||||
|
||||
/// \brief Calculates the greatest common denominator in the ring
|
||||
/// \param a the first element
|
||||
/// \param b the second element
|
||||
/// \return the the greatest common denominator of a and b.
|
||||
virtual const Element& Gcd(const Element &a, const Element &b) const;
|
||||
|
||||
protected:
|
||||
mutable Element result;
|
||||
};
|
||||
|
||||
// ********************************************************
|
||||
|
||||
/// \brief Euclidean domain
|
||||
/// \tparam T element class or type
|
||||
/// \details <tt>const Element&</tt> returned by member functions are references
|
||||
/// to internal data members. Since each object may have only
|
||||
/// one such data member for holding results, the following code
|
||||
/// will produce incorrect results:
|
||||
/// <pre> abcd = group.Add(group.Add(a,b), group.Add(c,d));</pre>
|
||||
/// But this should be fine:
|
||||
/// <pre> abcd = group.Add(a, group.Add(b, group.Add(c,d));</pre>
|
||||
template <class T> class EuclideanDomainOf : public AbstractEuclideanDomain<T>
|
||||
{
|
||||
public:
|
||||
typedef T Element;
|
||||
|
||||
EuclideanDomainOf() {}
|
||||
|
||||
bool Equal(const Element &a, const Element &b) const
|
||||
{return a==b;}
|
||||
|
||||
const Element& Identity() const
|
||||
{return Element::Zero();}
|
||||
|
||||
const Element& Add(const Element &a, const Element &b) const
|
||||
{return result = a+b;}
|
||||
|
||||
Element& Accumulate(Element &a, const Element &b) const
|
||||
{return a+=b;}
|
||||
|
||||
const Element& Inverse(const Element &a) const
|
||||
{return result = -a;}
|
||||
|
||||
const Element& Subtract(const Element &a, const Element &b) const
|
||||
{return result = a-b;}
|
||||
|
||||
Element& Reduce(Element &a, const Element &b) const
|
||||
{return a-=b;}
|
||||
|
||||
const Element& Double(const Element &a) const
|
||||
{return result = a.Doubled();}
|
||||
|
||||
const Element& MultiplicativeIdentity() const
|
||||
{return Element::One();}
|
||||
|
||||
const Element& Multiply(const Element &a, const Element &b) const
|
||||
{return result = a*b;}
|
||||
|
||||
const Element& Square(const Element &a) const
|
||||
{return result = a.Squared();}
|
||||
|
||||
bool IsUnit(const Element &a) const
|
||||
{return a.IsUnit();}
|
||||
|
||||
const Element& MultiplicativeInverse(const Element &a) const
|
||||
{return result = a.MultiplicativeInverse();}
|
||||
|
||||
const Element& Divide(const Element &a, const Element &b) const
|
||||
{return result = a/b;}
|
||||
|
||||
const Element& Mod(const Element &a, const Element &b) const
|
||||
{return result = a%b;}
|
||||
|
||||
void DivisionAlgorithm(Element &r, Element &q, const Element &a, const Element &d) const
|
||||
{Element::Divide(r, q, a, d);}
|
||||
|
||||
bool operator==(const EuclideanDomainOf<T> &rhs) const
|
||||
{CRYPTOPP_UNUSED(rhs); return true;}
|
||||
|
||||
private:
|
||||
mutable Element result;
|
||||
};
|
||||
|
||||
/// \brief Quotient ring
|
||||
/// \tparam T element class or type
|
||||
/// \details <tt>const Element&</tt> returned by member functions are references
|
||||
/// to internal data members. Since each object may have only
|
||||
/// one such data member for holding results, the following code
|
||||
/// will produce incorrect results:
|
||||
/// <pre> abcd = group.Add(group.Add(a,b), group.Add(c,d));</pre>
|
||||
/// But this should be fine:
|
||||
/// <pre> abcd = group.Add(a, group.Add(b, group.Add(c,d));</pre>
|
||||
template <class T> class QuotientRing : public AbstractRing<typename T::Element>
|
||||
{
|
||||
public:
|
||||
typedef T EuclideanDomain;
|
||||
typedef typename T::Element Element;
|
||||
|
||||
QuotientRing(const EuclideanDomain &domain, const Element &modulus)
|
||||
: m_domain(domain), m_modulus(modulus) {}
|
||||
|
||||
const EuclideanDomain & GetDomain() const
|
||||
{return m_domain;}
|
||||
|
||||
const Element& GetModulus() const
|
||||
{return m_modulus;}
|
||||
|
||||
bool Equal(const Element &a, const Element &b) const
|
||||
{return m_domain.Equal(m_domain.Mod(m_domain.Subtract(a, b), m_modulus), m_domain.Identity());}
|
||||
|
||||
const Element& Identity() const
|
||||
{return m_domain.Identity();}
|
||||
|
||||
const Element& Add(const Element &a, const Element &b) const
|
||||
{return m_domain.Add(a, b);}
|
||||
|
||||
Element& Accumulate(Element &a, const Element &b) const
|
||||
{return m_domain.Accumulate(a, b);}
|
||||
|
||||
const Element& Inverse(const Element &a) const
|
||||
{return m_domain.Inverse(a);}
|
||||
|
||||
const Element& Subtract(const Element &a, const Element &b) const
|
||||
{return m_domain.Subtract(a, b);}
|
||||
|
||||
Element& Reduce(Element &a, const Element &b) const
|
||||
{return m_domain.Reduce(a, b);}
|
||||
|
||||
const Element& Double(const Element &a) const
|
||||
{return m_domain.Double(a);}
|
||||
|
||||
bool IsUnit(const Element &a) const
|
||||
{return m_domain.IsUnit(m_domain.Gcd(a, m_modulus));}
|
||||
|
||||
const Element& MultiplicativeIdentity() const
|
||||
{return m_domain.MultiplicativeIdentity();}
|
||||
|
||||
const Element& Multiply(const Element &a, const Element &b) const
|
||||
{return m_domain.Mod(m_domain.Multiply(a, b), m_modulus);}
|
||||
|
||||
const Element& Square(const Element &a) const
|
||||
{return m_domain.Mod(m_domain.Square(a), m_modulus);}
|
||||
|
||||
const Element& MultiplicativeInverse(const Element &a) const;
|
||||
|
||||
bool operator==(const QuotientRing<T> &rhs) const
|
||||
{return m_domain == rhs.m_domain && m_modulus == rhs.m_modulus;}
|
||||
|
||||
protected:
|
||||
EuclideanDomain m_domain;
|
||||
Element m_modulus;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#ifdef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES
|
||||
#include "algebra.cpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// algparam.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "algparam.h"
|
||||
#include "integer.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
bool CombinedNameValuePairs::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const
|
||||
{
|
||||
if (strcmp(name, "ValueNames") == 0)
|
||||
return m_pairs1.GetVoidValue(name, valueType, pValue) && m_pairs2.GetVoidValue(name, valueType, pValue);
|
||||
else
|
||||
return m_pairs1.GetVoidValue(name, valueType, pValue) || m_pairs2.GetVoidValue(name, valueType, pValue);
|
||||
}
|
||||
|
||||
void AlgorithmParametersBase::operator=(const AlgorithmParametersBase &rhs)
|
||||
{
|
||||
CRYPTOPP_UNUSED(rhs);
|
||||
CRYPTOPP_ASSERT(false);
|
||||
}
|
||||
|
||||
bool AlgorithmParametersBase::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const
|
||||
{
|
||||
if (strcmp(name, "ValueNames") == 0)
|
||||
{
|
||||
NameValuePairs::ThrowIfTypeMismatch(name, typeid(std::string), valueType);
|
||||
if (m_next.get())
|
||||
m_next->GetVoidValue(name, valueType, pValue);
|
||||
(*reinterpret_cast<std::string *>(pValue) += m_name) += ";";
|
||||
return true;
|
||||
}
|
||||
else if (strcmp(name, m_name) == 0)
|
||||
{
|
||||
AssignValue(name, valueType, pValue);
|
||||
m_used = true;
|
||||
return true;
|
||||
}
|
||||
else if (m_next.get())
|
||||
return m_next->GetVoidValue(name, valueType, pValue);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
AlgorithmParameters::AlgorithmParameters()
|
||||
: m_defaultThrowIfNotUsed(true)
|
||||
{
|
||||
}
|
||||
|
||||
AlgorithmParameters::AlgorithmParameters(const AlgorithmParameters &x)
|
||||
: m_defaultThrowIfNotUsed(x.m_defaultThrowIfNotUsed)
|
||||
{
|
||||
m_next.reset(const_cast<AlgorithmParameters &>(x).m_next.release());
|
||||
}
|
||||
|
||||
AlgorithmParameters & AlgorithmParameters::operator=(const AlgorithmParameters &x)
|
||||
{
|
||||
m_next.reset(const_cast<AlgorithmParameters &>(x).m_next.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool AlgorithmParameters::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const
|
||||
{
|
||||
if (m_next.get())
|
||||
return m_next->GetVoidValue(name, valueType, pValue);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+520
@@ -0,0 +1,520 @@
|
||||
// algparam.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file algparam.h
|
||||
/// \brief Classes for working with NameValuePairs
|
||||
|
||||
#ifndef CRYPTOPP_ALGPARAM_H
|
||||
#define CRYPTOPP_ALGPARAM_H
|
||||
|
||||
#include "config.h"
|
||||
#include "cryptlib.h"
|
||||
|
||||
#include "smartptr.h"
|
||||
#include "secblock.h"
|
||||
#include "integer.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <exception>
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Used to pass byte array input as part of a NameValuePairs object
|
||||
class ConstByteArrayParameter
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a ConstByteArrayParameter
|
||||
/// \param data a C-String
|
||||
/// \param deepCopy flag indicating whether the data should be copied
|
||||
/// \details The deepCopy option is used when the NameValuePairs object can't
|
||||
/// keep a copy of the data available
|
||||
ConstByteArrayParameter(const char *data = NULLPTR, bool deepCopy = false)
|
||||
: m_deepCopy(false), m_data(NULLPTR), m_size(0)
|
||||
{
|
||||
Assign(reinterpret_cast<const byte *>(data), data ? strlen(data) : 0, deepCopy);
|
||||
}
|
||||
|
||||
/// \brief Construct a ConstByteArrayParameter
|
||||
/// \param data a memory buffer
|
||||
/// \param size the length of the memory buffer
|
||||
/// \param deepCopy flag indicating whether the data should be copied
|
||||
/// \details The deepCopy option is used when the NameValuePairs object can't
|
||||
/// keep a copy of the data available
|
||||
ConstByteArrayParameter(const byte *data, size_t size, bool deepCopy = false)
|
||||
: m_deepCopy(false), m_data(NULLPTR), m_size(0)
|
||||
{
|
||||
Assign(data, size, deepCopy);
|
||||
}
|
||||
|
||||
/// \brief Construct a ConstByteArrayParameter
|
||||
/// \tparam T a std::basic_string<char> or std::vector<byte> class
|
||||
/// \param string a std::basic_string<char> or std::vector<byte> object
|
||||
/// \param deepCopy flag indicating whether the data should be copied
|
||||
/// \details The deepCopy option is used when the NameValuePairs object can't
|
||||
/// keep a copy of the data available
|
||||
template <class T> ConstByteArrayParameter(const T &string, bool deepCopy = false)
|
||||
: m_deepCopy(false), m_data(NULLPTR), m_size(0)
|
||||
{
|
||||
CRYPTOPP_COMPILE_ASSERT(sizeof(typename T::value_type) == 1);
|
||||
Assign(reinterpret_cast<const byte *>(&string[0]), string.size(), deepCopy);
|
||||
}
|
||||
|
||||
/// \brief Assign contents from a memory buffer
|
||||
/// \param data a memory buffer
|
||||
/// \param size the length of the memory buffer
|
||||
/// \param deepCopy flag indicating whether the data should be copied
|
||||
/// \details The deepCopy option is used when the NameValuePairs object can't
|
||||
/// keep a copy of the data available
|
||||
void Assign(const byte *data, size_t size, bool deepCopy)
|
||||
{
|
||||
// This fires, which means: no data with a size, or data with no size.
|
||||
// CRYPTOPP_ASSERT((data && size) || !(data || size));
|
||||
if (deepCopy)
|
||||
m_block.Assign(data, size);
|
||||
else
|
||||
{
|
||||
m_data = data;
|
||||
m_size = size;
|
||||
}
|
||||
m_deepCopy = deepCopy;
|
||||
}
|
||||
|
||||
/// \brief Pointer to the first byte in the memory block
|
||||
const byte *begin() const {return m_deepCopy ? m_block.begin() : m_data;}
|
||||
/// \brief Pointer beyond the last byte in the memory block
|
||||
const byte *end() const {return m_deepCopy ? m_block.end() : m_data + m_size;}
|
||||
/// \brief Length of the memory block
|
||||
size_t size() const {return m_deepCopy ? m_block.size() : m_size;}
|
||||
|
||||
private:
|
||||
bool m_deepCopy;
|
||||
const byte *m_data;
|
||||
size_t m_size;
|
||||
SecByteBlock m_block;
|
||||
};
|
||||
|
||||
/// \brief Used to pass byte array input as part of a NameValuePairs object
|
||||
class ByteArrayParameter
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a ByteArrayParameter
|
||||
/// \param data a memory buffer
|
||||
/// \param size the length of the memory buffer
|
||||
ByteArrayParameter(byte *data = NULLPTR, unsigned int size = 0)
|
||||
: m_data(data), m_size(size) {}
|
||||
|
||||
/// \brief Construct a ByteArrayParameter
|
||||
/// \param block a SecByteBlock
|
||||
ByteArrayParameter(SecByteBlock &block)
|
||||
: m_data(block.begin()), m_size(block.size()) {}
|
||||
|
||||
/// \brief Pointer to the first byte in the memory block
|
||||
byte *begin() const {return m_data;}
|
||||
/// \brief Pointer beyond the last byte in the memory block
|
||||
byte *end() const {return m_data + m_size;}
|
||||
/// \brief Length of the memory block
|
||||
size_t size() const {return m_size;}
|
||||
|
||||
private:
|
||||
byte *m_data;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
/// \brief Combines two sets of NameValuePairs
|
||||
/// \details CombinedNameValuePairs allows you to provide two sets of of NameValuePairs.
|
||||
/// If a name is not found in the first set, then the second set is searched for the
|
||||
/// name and value pair. The second set of NameValuePairs often provides default values.
|
||||
class CRYPTOPP_DLL CombinedNameValuePairs : public NameValuePairs
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a CombinedNameValuePairs
|
||||
/// \param pairs1 reference to the first set of NameValuePairs
|
||||
/// \param pairs2 reference to the second set of NameValuePairs
|
||||
CombinedNameValuePairs(const NameValuePairs &pairs1, const NameValuePairs &pairs2)
|
||||
: m_pairs1(pairs1), m_pairs2(pairs2) {}
|
||||
|
||||
bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const;
|
||||
|
||||
private:
|
||||
const NameValuePairs &m_pairs1, &m_pairs2;
|
||||
};
|
||||
|
||||
#ifndef CRYPTOPP_DOXYGEN_PROCESSING
|
||||
template <class T, class BASE>
|
||||
class GetValueHelperClass
|
||||
{
|
||||
public:
|
||||
GetValueHelperClass(const T *pObject, const char *name, const std::type_info &valueType, void *pValue, const NameValuePairs *searchFirst)
|
||||
: m_pObject(pObject), m_name(name), m_valueType(&valueType), m_pValue(pValue), m_found(false), m_getValueNames(false)
|
||||
{
|
||||
if (strcmp(m_name, "ValueNames") == 0)
|
||||
{
|
||||
m_found = m_getValueNames = true;
|
||||
NameValuePairs::ThrowIfTypeMismatch(m_name, typeid(std::string), *m_valueType);
|
||||
if (searchFirst)
|
||||
searchFirst->GetVoidValue(m_name, valueType, pValue);
|
||||
if (typeid(T) != typeid(BASE))
|
||||
pObject->BASE::GetVoidValue(m_name, valueType, pValue);
|
||||
((*reinterpret_cast<std::string *>(m_pValue) += "ThisPointer:") += typeid(T).name()) += ';';
|
||||
}
|
||||
|
||||
if (!m_found && strncmp(m_name, "ThisPointer:", 12) == 0 && strcmp(m_name+12, typeid(T).name()) == 0)
|
||||
{
|
||||
NameValuePairs::ThrowIfTypeMismatch(m_name, typeid(T *), *m_valueType);
|
||||
*reinterpret_cast<const T **>(pValue) = pObject;
|
||||
m_found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_found && searchFirst)
|
||||
m_found = searchFirst->GetVoidValue(m_name, valueType, pValue);
|
||||
|
||||
if (!m_found && typeid(T) != typeid(BASE))
|
||||
m_found = pObject->BASE::GetVoidValue(m_name, valueType, pValue);
|
||||
}
|
||||
|
||||
operator bool() const {return m_found;}
|
||||
|
||||
template <class R>
|
||||
GetValueHelperClass<T,BASE> & operator()(const char *name, const R & (T::*pm)() const)
|
||||
{
|
||||
if (m_getValueNames)
|
||||
(*reinterpret_cast<std::string *>(m_pValue) += name) += ";";
|
||||
if (!m_found && strcmp(name, m_name) == 0)
|
||||
{
|
||||
NameValuePairs::ThrowIfTypeMismatch(name, typeid(R), *m_valueType);
|
||||
*reinterpret_cast<R *>(m_pValue) = (m_pObject->*pm)();
|
||||
m_found = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GetValueHelperClass<T,BASE> &Assignable()
|
||||
{
|
||||
#ifndef __INTEL_COMPILER // ICL 9.1 workaround: Intel compiler copies the vTable pointer for some reason
|
||||
if (m_getValueNames)
|
||||
((*reinterpret_cast<std::string *>(m_pValue) += "ThisObject:") += typeid(T).name()) += ';';
|
||||
if (!m_found && strncmp(m_name, "ThisObject:", 11) == 0 && strcmp(m_name+11, typeid(T).name()) == 0)
|
||||
{
|
||||
NameValuePairs::ThrowIfTypeMismatch(m_name, typeid(T), *m_valueType);
|
||||
*reinterpret_cast<T *>(m_pValue) = *m_pObject;
|
||||
m_found = true;
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const T *m_pObject;
|
||||
const char *m_name;
|
||||
const std::type_info *m_valueType;
|
||||
void *m_pValue;
|
||||
bool m_found, m_getValueNames;
|
||||
};
|
||||
|
||||
template <class BASE, class T>
|
||||
GetValueHelperClass<T, BASE> GetValueHelper(const T *pObject, const char *name, const std::type_info &valueType, void *pValue, const NameValuePairs *searchFirst=NULLPTR)
|
||||
{
|
||||
return GetValueHelperClass<T, BASE>(pObject, name, valueType, pValue, searchFirst);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
GetValueHelperClass<T, T> GetValueHelper(const T *pObject, const char *name, const std::type_info &valueType, void *pValue, const NameValuePairs *searchFirst=NULLPTR)
|
||||
{
|
||||
return GetValueHelperClass<T, T>(pObject, name, valueType, pValue, searchFirst);
|
||||
}
|
||||
|
||||
// ********************************************************
|
||||
|
||||
template <class T, class BASE>
|
||||
class AssignFromHelperClass
|
||||
{
|
||||
public:
|
||||
AssignFromHelperClass(T *pObject, const NameValuePairs &source)
|
||||
: m_pObject(pObject), m_source(source), m_done(false)
|
||||
{
|
||||
if (source.GetThisObject(*pObject))
|
||||
m_done = true;
|
||||
else if (typeid(BASE) != typeid(T))
|
||||
pObject->BASE::AssignFrom(source);
|
||||
}
|
||||
|
||||
template <class R>
|
||||
AssignFromHelperClass & operator()(const char *name, void (T::*pm)(const R&))
|
||||
{
|
||||
if (!m_done)
|
||||
{
|
||||
R value;
|
||||
if (!m_source.GetValue(name, value))
|
||||
throw InvalidArgument(std::string(typeid(T).name()) + ": Missing required parameter '" + name + "'");
|
||||
(m_pObject->*pm)(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class R, class S>
|
||||
AssignFromHelperClass & operator()(const char *name1, const char *name2, void (T::*pm)(const R&, const S&))
|
||||
{
|
||||
if (!m_done)
|
||||
{
|
||||
R value1;
|
||||
if (!m_source.GetValue(name1, value1))
|
||||
throw InvalidArgument(std::string(typeid(T).name()) + ": Missing required parameter '" + name1 + "'");
|
||||
S value2;
|
||||
if (!m_source.GetValue(name2, value2))
|
||||
throw InvalidArgument(std::string(typeid(T).name()) + ": Missing required parameter '" + name2 + "'");
|
||||
(m_pObject->*pm)(value1, value2);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_pObject;
|
||||
const NameValuePairs &m_source;
|
||||
bool m_done;
|
||||
};
|
||||
|
||||
template <class BASE, class T>
|
||||
AssignFromHelperClass<T, BASE> AssignFromHelper(T *pObject, const NameValuePairs &source)
|
||||
{
|
||||
return AssignFromHelperClass<T, BASE>(pObject, source);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
AssignFromHelperClass<T, T> AssignFromHelper(T *pObject, const NameValuePairs &source)
|
||||
{
|
||||
return AssignFromHelperClass<T, T>(pObject, source);
|
||||
}
|
||||
|
||||
#endif // CRYPTOPP_DOXYGEN_PROCESSING
|
||||
|
||||
// ********************************************************
|
||||
|
||||
#ifndef CRYPTOPP_NO_ASSIGN_TO_INTEGER
|
||||
// Allow the linker to discard Integer code if not needed.
|
||||
// Also see http://github.com/weidai11/cryptopp/issues/389.
|
||||
CRYPTOPP_DLL bool AssignIntToInteger(const std::type_info &valueType, void *pInteger, const void *pInt);
|
||||
#endif
|
||||
|
||||
CRYPTOPP_DLL const std::type_info & CRYPTOPP_API IntegerTypeId();
|
||||
|
||||
/// \brief Base class for AlgorithmParameters
|
||||
class CRYPTOPP_DLL AlgorithmParametersBase
|
||||
{
|
||||
public:
|
||||
/// \brief Exception thrown when an AlgorithmParameter is unused
|
||||
class ParameterNotUsed : public Exception
|
||||
{
|
||||
public:
|
||||
ParameterNotUsed(const char *name) : Exception(OTHER_ERROR, std::string("AlgorithmParametersBase: parameter \"") + name + "\" not used") {}
|
||||
};
|
||||
|
||||
virtual ~AlgorithmParametersBase() CRYPTOPP_THROW
|
||||
{
|
||||
|
||||
#if defined(CRYPTOPP_CXX17_UNCAUGHT_EXCEPTIONS)
|
||||
if (std::uncaught_exceptions() == 0)
|
||||
#elif defined(CRYPTOPP_CXX98_UNCAUGHT_EXCEPTION)
|
||||
if (std::uncaught_exception() == false)
|
||||
#else
|
||||
try
|
||||
#endif
|
||||
{
|
||||
if (m_throwIfNotUsed && !m_used)
|
||||
throw ParameterNotUsed(m_name);
|
||||
}
|
||||
#if !defined(CRYPTOPP_CXX98_UNCAUGHT_EXCEPTION)
|
||||
# if !defined(CRYPTOPP_CXX17_UNCAUGHT_EXCEPTIONS)
|
||||
catch(const Exception&)
|
||||
{
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// this is actually a move, not a copy
|
||||
AlgorithmParametersBase(const AlgorithmParametersBase &x)
|
||||
: m_name(x.m_name), m_throwIfNotUsed(x.m_throwIfNotUsed), m_used(x.m_used)
|
||||
{
|
||||
m_next.reset(const_cast<AlgorithmParametersBase &>(x).m_next.release());
|
||||
x.m_used = true;
|
||||
}
|
||||
|
||||
/// \brief Construct a AlgorithmParametersBase
|
||||
/// \param name the parameter name
|
||||
/// \param throwIfNotUsed flags indicating whether an exception should be thrown
|
||||
/// \details If throwIfNotUsed is true, then a ParameterNotUsed exception
|
||||
/// will be thrown in the destructor if the parameter is not not retrieved.
|
||||
AlgorithmParametersBase(const char *name, bool throwIfNotUsed)
|
||||
: m_name(name), m_throwIfNotUsed(throwIfNotUsed), m_used(false) {}
|
||||
|
||||
bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const;
|
||||
|
||||
protected:
|
||||
friend class AlgorithmParameters;
|
||||
void operator=(const AlgorithmParametersBase& rhs); // assignment not allowed, declare this for VC60
|
||||
|
||||
virtual void AssignValue(const char *name, const std::type_info &valueType, void *pValue) const =0;
|
||||
virtual void MoveInto(void *p) const =0; // not really const
|
||||
|
||||
const char *m_name;
|
||||
bool m_throwIfNotUsed;
|
||||
mutable bool m_used;
|
||||
member_ptr<AlgorithmParametersBase> m_next;
|
||||
};
|
||||
|
||||
/// \brief Template base class for AlgorithmParameters
|
||||
/// \tparam T the class or type
|
||||
template <class T>
|
||||
class AlgorithmParametersTemplate : public AlgorithmParametersBase
|
||||
{
|
||||
public:
|
||||
/// \brief Construct an AlgorithmParametersTemplate
|
||||
/// \param name the name of the value
|
||||
/// \param value a reference to the value
|
||||
/// \param throwIfNotUsed flags indicating whether an exception should be thrown
|
||||
/// \details If throwIfNotUsed is true, then a ParameterNotUsed exception
|
||||
/// will be thrown in the destructor if the parameter is not not retrieved.
|
||||
AlgorithmParametersTemplate(const char *name, const T &value, bool throwIfNotUsed)
|
||||
: AlgorithmParametersBase(name, throwIfNotUsed), m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
void AssignValue(const char *name, const std::type_info &valueType, void *pValue) const
|
||||
{
|
||||
#ifndef CRYPTOPP_NO_ASSIGN_TO_INTEGER
|
||||
// Special case for retrieving an Integer parameter when an int was passed in
|
||||
if (!(typeid(T) == typeid(int) && AssignIntToInteger(valueType, pValue, &m_value)))
|
||||
#endif
|
||||
{
|
||||
NameValuePairs::ThrowIfTypeMismatch(name, typeid(T), valueType);
|
||||
*reinterpret_cast<T *>(pValue) = m_value;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG_NEW) && (_MSC_VER >= 1300)
|
||||
# pragma push_macro("new")
|
||||
# undef new
|
||||
#endif
|
||||
|
||||
void MoveInto(void *buffer) const
|
||||
{
|
||||
AlgorithmParametersTemplate<T>* p = new(buffer) AlgorithmParametersTemplate<T>(*this);
|
||||
CRYPTOPP_UNUSED(p); // silence warning
|
||||
}
|
||||
|
||||
#if defined(DEBUG_NEW) && (_MSC_VER >= 1300)
|
||||
# pragma pop_macro("new")
|
||||
#endif
|
||||
|
||||
protected:
|
||||
T m_value;
|
||||
};
|
||||
|
||||
CRYPTOPP_DLL_TEMPLATE_CLASS AlgorithmParametersTemplate<bool>;
|
||||
CRYPTOPP_DLL_TEMPLATE_CLASS AlgorithmParametersTemplate<int>;
|
||||
CRYPTOPP_DLL_TEMPLATE_CLASS AlgorithmParametersTemplate<ConstByteArrayParameter>;
|
||||
|
||||
/// \brief An object that implements NameValuePairs
|
||||
/// \note A NameValuePairs object containing an arbitrary number of name value pairs may be constructed by
|
||||
/// repeatedly using operator() on the object returned by MakeParameters, for example:
|
||||
/// <pre>
|
||||
/// AlgorithmParameters parameters = MakeParameters(name1, value1)(name2, value2)(name3, value3);
|
||||
/// </pre>
|
||||
class CRYPTOPP_DLL AlgorithmParameters : public NameValuePairs
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a AlgorithmParameters
|
||||
/// \note A NameValuePairs object containing an arbitrary number of name value pairs may be constructed by
|
||||
/// repeatedly using operator() on the object returned by MakeParameters, for example:
|
||||
/// <pre>
|
||||
/// AlgorithmParameters parameters = MakeParameters(name1, value1)(name2, value2)(name3, value3);
|
||||
/// </pre>
|
||||
AlgorithmParameters();
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
/// \brief Construct a AlgorithmParameters
|
||||
/// \tparam T the class or type
|
||||
/// \param name the name of the object or value to retrieve
|
||||
/// \param value reference to a variable that receives the value
|
||||
/// \param throwIfNotUsed if true, the object will throw an exception if the value is not accessed
|
||||
/// \note throwIfNotUsed is ignored if using a compiler that does not support std::uncaught_exception(),
|
||||
/// such as MSVC 7.0 and earlier.
|
||||
/// \note A NameValuePairs object containing an arbitrary number of name value pairs may be constructed by
|
||||
/// repeatedly using operator() on the object returned by MakeParameters, for example:
|
||||
/// <pre>
|
||||
/// AlgorithmParameters parameters = MakeParameters(name1, value1)(name2, value2)(name3, value3);
|
||||
/// </pre>
|
||||
template <class T>
|
||||
AlgorithmParameters(const char *name, const T &value, bool throwIfNotUsed=true)
|
||||
: m_next(new AlgorithmParametersTemplate<T>(name, value, throwIfNotUsed))
|
||||
, m_defaultThrowIfNotUsed(throwIfNotUsed)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
AlgorithmParameters(const AlgorithmParameters &x);
|
||||
|
||||
AlgorithmParameters & operator=(const AlgorithmParameters &x);
|
||||
|
||||
/// \tparam T the class or type
|
||||
/// \param name the name of the object or value to retrieve
|
||||
/// \param value reference to a variable that receives the value
|
||||
/// \param throwIfNotUsed if true, the object will throw an exception if the value is not accessed
|
||||
template <class T>
|
||||
AlgorithmParameters & operator()(const char *name, const T &value, bool throwIfNotUsed)
|
||||
{
|
||||
member_ptr<AlgorithmParametersBase> p(new AlgorithmParametersTemplate<T>(name, value, throwIfNotUsed));
|
||||
p->m_next.reset(m_next.release());
|
||||
m_next.reset(p.release());
|
||||
m_defaultThrowIfNotUsed = throwIfNotUsed;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \brief Appends a NameValuePair to a collection of NameValuePairs
|
||||
/// \tparam T the class or type
|
||||
/// \param name the name of the object or value to retrieve
|
||||
/// \param value reference to a variable that receives the value
|
||||
template <class T>
|
||||
AlgorithmParameters & operator()(const char *name, const T &value)
|
||||
{
|
||||
return operator()(name, value, m_defaultThrowIfNotUsed);
|
||||
}
|
||||
|
||||
bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const;
|
||||
|
||||
protected:
|
||||
member_ptr<AlgorithmParametersBase> m_next;
|
||||
bool m_defaultThrowIfNotUsed;
|
||||
};
|
||||
|
||||
/// \brief Create an object that implements NameValuePairs
|
||||
/// \tparam T the class or type
|
||||
/// \param name the name of the object or value to retrieve
|
||||
/// \param value reference to a variable that receives the value
|
||||
/// \param throwIfNotUsed if true, the object will throw an exception if the value is not accessed
|
||||
/// \note throwIfNotUsed is ignored if using a compiler that does not support std::uncaught_exception(),
|
||||
/// such as MSVC 7.0 and earlier.
|
||||
/// \note A NameValuePairs object containing an arbitrary number of name value pairs may be constructed by
|
||||
/// repeatedly using \p operator() on the object returned by \p MakeParameters, for example:
|
||||
/// <pre>
|
||||
/// AlgorithmParameters parameters = MakeParameters(name1, value1)(name2, value2)(name3, value3);
|
||||
/// </pre>
|
||||
#ifdef __BORLANDC__
|
||||
typedef AlgorithmParameters MakeParameters;
|
||||
#else
|
||||
template <class T>
|
||||
AlgorithmParameters MakeParameters(const char *name, const T &value, bool throwIfNotUsed = true)
|
||||
{
|
||||
return AlgorithmParameters()(name, value, throwIfNotUsed);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CRYPTOPP_GET_FUNCTION_ENTRY(name) (Name::name(), &ThisClass::Get##name)
|
||||
#define CRYPTOPP_SET_FUNCTION_ENTRY(name) (Name::name(), &ThisClass::Set##name)
|
||||
#define CRYPTOPP_SET_FUNCTION_ENTRY2(name1, name2) (Name::name1(), Name::name2(), &ThisClass::Set##name1##And##name2)
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// allocate.cpp - written and placed in the public domain by Jeffrey Walton
|
||||
|
||||
// The functions in allocate.h and allocate.cpp were originally in misc.h
|
||||
// and misc.cpp. They were extracted in September 2019 to sidestep a circular
|
||||
// dependency with misc.h and secblock.h.
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "allocate.h"
|
||||
#include "stdcpp.h"
|
||||
#include "misc.h"
|
||||
#include "trap.h"
|
||||
|
||||
// for memalign
|
||||
#if defined(CRYPTOPP_MEMALIGN_AVAILABLE) || defined(CRYPTOPP_MM_MALLOC_AVAILABLE) || defined(QNX)
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
// for posix_memalign
|
||||
#if defined(CRYPTOPP_POSIX_MEMALIGN_AVAILABLE)
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void CallNewHandler()
|
||||
{
|
||||
using std::new_handler;
|
||||
using std::set_new_handler;
|
||||
|
||||
new_handler newHandler = set_new_handler(NULLPTR);
|
||||
if (newHandler)
|
||||
set_new_handler(newHandler);
|
||||
|
||||
if (newHandler)
|
||||
newHandler();
|
||||
else
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
void * AlignedAllocate(size_t size)
|
||||
{
|
||||
byte *p;
|
||||
#if defined(CRYPTOPP_MM_MALLOC_AVAILABLE)
|
||||
while ((p = (byte *)_mm_malloc(size, 16)) == NULLPTR)
|
||||
#elif defined(CRYPTOPP_MEMALIGN_AVAILABLE)
|
||||
while ((p = (byte *)memalign(16, size)) == NULLPTR)
|
||||
#elif defined(CRYPTOPP_MALLOC_ALIGNMENT_IS_16)
|
||||
while ((p = (byte *)malloc(size)) == NULLPTR)
|
||||
#elif defined(CRYPTOPP_POSIX_MEMALIGN_AVAILABLE)
|
||||
while (posix_memalign(reinterpret_cast<void**>(&p), 16, size) != 0)
|
||||
#else
|
||||
while ((p = (byte *)malloc(size + 16)) == NULLPTR)
|
||||
#endif
|
||||
CallNewHandler();
|
||||
|
||||
#ifdef CRYPTOPP_NO_ALIGNED_ALLOC
|
||||
size_t adjustment = 16-((size_t)p%16);
|
||||
CRYPTOPP_ASSERT(adjustment > 0);
|
||||
p += adjustment;
|
||||
p[-1] = (byte)adjustment;
|
||||
#endif
|
||||
|
||||
// If this assert fires then there are problems that need
|
||||
// to be fixed. Please open a bug report.
|
||||
CRYPTOPP_ASSERT(IsAlignedOn(p, 16));
|
||||
return p;
|
||||
}
|
||||
|
||||
void AlignedDeallocate(void *p)
|
||||
{
|
||||
// Guard pointer due to crash on AIX when CRYPTOPP_NO_ALIGNED_ALLOC
|
||||
// is in effect. The guard was previously in place in SecBlock,
|
||||
// but it was removed at f4d68353ca7c as part of GH #875.
|
||||
CRYPTOPP_ASSERT(p);
|
||||
|
||||
if (p != NULLPTR)
|
||||
{
|
||||
#ifdef CRYPTOPP_MM_MALLOC_AVAILABLE
|
||||
_mm_free(p);
|
||||
#elif defined(CRYPTOPP_NO_ALIGNED_ALLOC)
|
||||
p = (byte *)p - ((byte *)p)[-1];
|
||||
free(p);
|
||||
#else
|
||||
free(p);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void * UnalignedAllocate(size_t size)
|
||||
{
|
||||
void *p;
|
||||
while ((p = malloc(size)) == NULLPTR)
|
||||
CallNewHandler();
|
||||
return p;
|
||||
}
|
||||
|
||||
void UnalignedDeallocate(void *p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif // CRYPTOPP_IMPORTS
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
// allocate.h - written and placed in the public domain by Jeffrey Walton
|
||||
|
||||
// The functions in allocate.h and allocate.cpp were originally in misc.h
|
||||
// and misc.cpp. They were extracted in September 2019 to sidestep a circular
|
||||
// dependency with misc.h and secblock.h.
|
||||
|
||||
/// \file allocate.h
|
||||
/// \brief Functions for allocating aligned buffers
|
||||
|
||||
#ifndef CRYPTOPP_ALLOCATE_H
|
||||
#define CRYPTOPP_ALLOCATE_H
|
||||
|
||||
#include "config.h"
|
||||
#include "cryptlib.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Attempts to reclaim unused memory
|
||||
/// \throw bad_alloc
|
||||
/// \details In the normal course of running a program, a request for memory
|
||||
/// normally succeeds. If a call to AlignedAllocate or UnalignedAllocate fails,
|
||||
/// then CallNewHandler is called in n effort to recover. Internally,
|
||||
/// CallNewHandler calls set_new_handler(nullptr) in an effort to free memory.
|
||||
/// There is no guarantee CallNewHandler will be able to obtain more memory so
|
||||
/// an allocation succeeds. If the call to set_new_handler fails, then CallNewHandler
|
||||
/// throws a bad_alloc exception.
|
||||
/// \throw bad_alloc on failure
|
||||
/// \since Crypto++ 5.0
|
||||
/// \sa AlignedAllocate, AlignedDeallocate, UnalignedAllocate, UnalignedDeallocate
|
||||
CRYPTOPP_DLL void CRYPTOPP_API CallNewHandler();
|
||||
|
||||
/// \brief Allocates a buffer on 16-byte boundary
|
||||
/// \param size the size of the buffer
|
||||
/// \details AlignedAllocate is primarily used when the data will be
|
||||
/// processed by SSE, NEON, ARMv8 or PowerPC instructions. The assembly
|
||||
/// language routines rely on the alignment. If the alignment is not
|
||||
/// respected, then a SIGBUS could be generated on Unix and Linux, and an
|
||||
/// EXCEPTION_DATATYPE_MISALIGNMENT could be generated on Windows.
|
||||
/// \details Formerly, AlignedAllocate and AlignedDeallocate were only
|
||||
/// available on certain platforms when CRYTPOPP_DISABLE_ASM was not in
|
||||
/// effect. However, Android and iOS debug simulator builds got into a
|
||||
/// state where the aligned allocator was not available and caused link
|
||||
/// failures.
|
||||
/// \since AlignedAllocate for SIMD since Crypto++ 1.0, AlignedAllocate
|
||||
/// for all builds since Crypto++ 8.1
|
||||
/// \sa AlignedDeallocate, UnalignedAllocate, UnalignedDeallocate, CallNewHandler,
|
||||
/// <A HREF="http://github.com/weidai11/cryptopp/issues/779">Issue 779</A>
|
||||
CRYPTOPP_DLL void* CRYPTOPP_API AlignedAllocate(size_t size);
|
||||
|
||||
/// \brief Frees a buffer allocated with AlignedAllocate
|
||||
/// \param ptr the buffer to free
|
||||
/// \since AlignedDeallocate for SIMD since Crypto++ 1.0, AlignedAllocate
|
||||
/// for all builds since Crypto++ 8.1
|
||||
/// \sa AlignedAllocate, UnalignedAllocate, UnalignedDeallocate, CallNewHandler,
|
||||
/// <A HREF="http://github.com/weidai11/cryptopp/issues/779">Issue 779</A>
|
||||
CRYPTOPP_DLL void CRYPTOPP_API AlignedDeallocate(void *ptr);
|
||||
|
||||
/// \brief Allocates a buffer
|
||||
/// \param size the size of the buffer
|
||||
/// \since Crypto++ 1.0
|
||||
/// \sa AlignedAllocate, AlignedDeallocate, UnalignedDeallocate, CallNewHandler,
|
||||
/// <A HREF="http://github.com/weidai11/cryptopp/issues/779">Issue 779</A>
|
||||
CRYPTOPP_DLL void * CRYPTOPP_API UnalignedAllocate(size_t size);
|
||||
|
||||
/// \brief Frees a buffer allocated with UnalignedAllocate
|
||||
/// \param ptr the buffer to free
|
||||
/// \since Crypto++ 1.0
|
||||
/// \sa AlignedAllocate, AlignedDeallocate, UnalignedAllocate, CallNewHandler,
|
||||
/// <A HREF="http://github.com/weidai11/cryptopp/issues/779">Issue 779</A>
|
||||
CRYPTOPP_DLL void CRYPTOPP_API UnalignedDeallocate(void *ptr);
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif // CRYPTOPP_ALLOCATE_H
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
// arc4.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
// The ARC4 algorithm was first revealed in an anonymous email to the
|
||||
// cypherpunks mailing list. This file originally contained some
|
||||
// code copied from this email. The code has since been rewritten in order
|
||||
// to clarify the copyright status of this file. It should now be
|
||||
// completely in the public domain.
|
||||
|
||||
#include "pch.h"
|
||||
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
|
||||
#include "arc4.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
namespace Weak1 {
|
||||
|
||||
#if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
|
||||
void ARC4_TestInstantiations()
|
||||
{
|
||||
ARC4 x;
|
||||
}
|
||||
#endif
|
||||
|
||||
ARC4_Base::~ARC4_Base()
|
||||
{
|
||||
m_x = m_y = 0;
|
||||
}
|
||||
|
||||
void ARC4_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms)
|
||||
{
|
||||
AssertValidKeyLength(length);
|
||||
|
||||
m_x = 1;
|
||||
m_y = 0;
|
||||
|
||||
unsigned int i;
|
||||
for (i=0; i<256; i++)
|
||||
m_state[i] = byte(i);
|
||||
|
||||
unsigned int keyIndex = 0, stateIndex = 0;
|
||||
for (i=0; i<256; i++)
|
||||
{
|
||||
unsigned int a = m_state[i];
|
||||
stateIndex += key[keyIndex] + a;
|
||||
stateIndex &= 0xff;
|
||||
m_state[i] = m_state[stateIndex];
|
||||
m_state[stateIndex] = byte(a);
|
||||
if (++keyIndex >= length)
|
||||
keyIndex = 0;
|
||||
}
|
||||
|
||||
int discardBytes = params.GetIntValueWithDefault("DiscardBytes", GetDefaultDiscardBytes());
|
||||
DiscardBytes(discardBytes);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline unsigned int MakeByte(T &x, T &y, byte *s)
|
||||
{
|
||||
unsigned int a = s[x];
|
||||
y = byte((y+a) & 0xff);
|
||||
unsigned int b = s[y];
|
||||
s[x] = byte(b);
|
||||
s[y] = byte(a);
|
||||
x = byte((x+1) & 0xff);
|
||||
return s[(a+b) & 0xff];
|
||||
}
|
||||
|
||||
void ARC4_Base::GenerateBlock(byte *output, size_t size)
|
||||
{
|
||||
while (size--)
|
||||
*output++ = static_cast<byte>(MakeByte(m_x, m_y, m_state));
|
||||
}
|
||||
|
||||
void ARC4_Base::ProcessData(byte *outString, const byte *inString, size_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
byte *const s = m_state;
|
||||
unsigned int x = m_x;
|
||||
unsigned int y = m_y;
|
||||
|
||||
if (inString == outString)
|
||||
{
|
||||
do
|
||||
{
|
||||
*outString++ ^= MakeByte(x, y, s);
|
||||
} while (--length);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
*outString++ = *inString++ ^ byte(MakeByte(x, y, s));
|
||||
}
|
||||
while(--length);
|
||||
}
|
||||
|
||||
m_x = byte(x);
|
||||
m_y = byte(y);
|
||||
}
|
||||
|
||||
void ARC4_Base::DiscardBytes(size_t n)
|
||||
{
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
byte *const s = m_state;
|
||||
unsigned int x = m_x;
|
||||
unsigned int y = m_y;
|
||||
|
||||
do
|
||||
{
|
||||
MakeByte(x, y, s);
|
||||
}
|
||||
while(--n);
|
||||
|
||||
m_x = byte(x);
|
||||
m_y = byte(y);
|
||||
}
|
||||
|
||||
}
|
||||
NAMESPACE_END
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// arc4.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file arc4.h
|
||||
/// \brief Classes for ARC4 cipher
|
||||
/// \since Crypto++ 3.1
|
||||
|
||||
#ifndef CRYPTOPP_ARC4_H
|
||||
#define CRYPTOPP_ARC4_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "strciphr.h"
|
||||
#include "secblock.h"
|
||||
#include "smartptr.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
namespace Weak1 {
|
||||
|
||||
/// \brief ARC4 base class
|
||||
/// \details Implementations and overrides in \p Base apply to both \p ENCRYPTION and \p DECRYPTION directions
|
||||
/// \since Crypto++ 3.1
|
||||
class CRYPTOPP_NO_VTABLE ARC4_Base : public VariableKeyLength<16, 1, 256>, public RandomNumberGenerator, public SymmetricCipher, public SymmetricCipherDocumentation
|
||||
{
|
||||
public:
|
||||
~ARC4_Base();
|
||||
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "ARC4";}
|
||||
|
||||
void GenerateBlock(byte *output, size_t size);
|
||||
void DiscardBytes(size_t n);
|
||||
|
||||
void ProcessData(byte *outString, const byte *inString, size_t length);
|
||||
|
||||
bool IsRandomAccess() const {return false;}
|
||||
bool IsSelfInverting() const {return true;}
|
||||
bool IsForwardTransformation() const {return true;}
|
||||
|
||||
typedef SymmetricCipherFinal<ARC4_Base> Encryption;
|
||||
typedef SymmetricCipherFinal<ARC4_Base> Decryption;
|
||||
|
||||
protected:
|
||||
void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms);
|
||||
virtual unsigned int GetDefaultDiscardBytes() const {return 0;}
|
||||
|
||||
FixedSizeSecBlock<byte, 256> m_state;
|
||||
byte m_x, m_y;
|
||||
};
|
||||
|
||||
/// \brief Alleged RC4
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/RC4">Alleged RC4</a>
|
||||
/// \since Crypto++ 3.1
|
||||
DOCUMENTED_TYPEDEF(SymmetricCipherFinal<ARC4_Base>, ARC4);
|
||||
|
||||
/// \brief MARC4 base class
|
||||
/// \details Implementations and overrides in \p Base apply to both \p ENCRYPTION and \p DECRYPTION directions
|
||||
/// \details MARC4 discards the first 256 bytes of keystream, which may be weaker than the rest
|
||||
/// \since Crypto++ 3.1
|
||||
class CRYPTOPP_NO_VTABLE MARC4_Base : public ARC4_Base
|
||||
{
|
||||
public:
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "MARC4";}
|
||||
|
||||
typedef SymmetricCipherFinal<MARC4_Base> Encryption;
|
||||
typedef SymmetricCipherFinal<MARC4_Base> Decryption;
|
||||
|
||||
protected:
|
||||
unsigned int GetDefaultDiscardBytes() const {return 256;}
|
||||
};
|
||||
|
||||
/// \brief Modified Alleged RC4
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/RC4">Alleged RC4</a>
|
||||
/// \since Crypto++ 3.1
|
||||
DOCUMENTED_TYPEDEF(SymmetricCipherFinal<MARC4_Base>, MARC4);
|
||||
|
||||
}
|
||||
#if CRYPTOPP_ENABLE_NAMESPACE_WEAK >= 1
|
||||
namespace Weak {using namespace Weak1;} // import Weak1 into CryptoPP::Weak
|
||||
#else
|
||||
using namespace Weak1; // import Weak1 into CryptoPP with warning
|
||||
#ifdef __GNUC__
|
||||
#warning "You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning."
|
||||
#else
|
||||
#pragma message("You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning.")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// argnames.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file argnames.h
|
||||
/// \brief Standard names for retrieving values by name when working with \p NameValuePairs
|
||||
|
||||
#ifndef CRYPTOPP_ARGNAMES_H
|
||||
#define CRYPTOPP_ARGNAMES_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
DOCUMENTED_NAMESPACE_BEGIN(Name)
|
||||
|
||||
#define CRYPTOPP_DEFINE_NAME_STRING(name) inline const char *name() {return #name;}
|
||||
|
||||
CRYPTOPP_DEFINE_NAME_STRING(ValueNames) ///< string, a list of value names with a semicolon (';') after each name
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Version) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Seed) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Key) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(IV) ///< ConstByteArrayParameter, also accepts const byte * for backwards compatibility
|
||||
CRYPTOPP_DEFINE_NAME_STRING(StolenIV) ///< byte *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Nonce) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Rounds) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(FeedbackSize) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(WordSize) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(BlockSize) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(EffectiveKeyLength) ///< int, in bits
|
||||
CRYPTOPP_DEFINE_NAME_STRING(KeySize) ///< int, in bits
|
||||
CRYPTOPP_DEFINE_NAME_STRING(ModulusSize) ///< int, in bits
|
||||
CRYPTOPP_DEFINE_NAME_STRING(SubgroupOrderSize) ///< int, in bits
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PrivateExponentSize)///< int, in bits
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Modulus) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PublicExponent) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PrivateExponent) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PublicElement) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(SubgroupOrder) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Cofactor) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(SubgroupGenerator) ///< Integer, ECP::Point, or EC2N::Point
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Curve) ///< ECP or EC2N
|
||||
CRYPTOPP_DEFINE_NAME_STRING(GroupOID) ///< OID
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PointerToPrimeSelector) ///< const PrimeSelector *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Prime1) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Prime2) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(ModPrime1PrivateExponent) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(ModPrime2PrivateExponent) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(MultiplicativeInverseOfPrime2ModPrime1) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(QuadraticResidueModPrime1) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(QuadraticResidueModPrime2) ///< Integer
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PutMessage) ///< bool
|
||||
CRYPTOPP_DEFINE_NAME_STRING(TruncatedDigestSize) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(BlockPaddingScheme) ///< StreamTransformationFilter::BlockPaddingScheme
|
||||
CRYPTOPP_DEFINE_NAME_STRING(HashVerificationFilterFlags) ///< word32
|
||||
CRYPTOPP_DEFINE_NAME_STRING(AuthenticatedDecryptionFilterFlags) ///< word32
|
||||
CRYPTOPP_DEFINE_NAME_STRING(SignatureVerificationFilterFlags) ///< word32
|
||||
CRYPTOPP_DEFINE_NAME_STRING(InputBuffer) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(OutputBuffer) ///< ByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(InputFileName) ///< const char *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(InputFileNameWide) ///< const wchar_t *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(InputStreamPointer) ///< std::istream *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(InputBinaryMode) ///< bool
|
||||
CRYPTOPP_DEFINE_NAME_STRING(OutputFileName) ///< const char *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(OutputFileNameWide) ///< const wchar_t *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(OutputStreamPointer) ///< std::ostream *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(OutputBinaryMode) ///< bool
|
||||
CRYPTOPP_DEFINE_NAME_STRING(EncodingParameters) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(KeyDerivationParameters) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Separator) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Terminator) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Uppercase) ///< bool
|
||||
CRYPTOPP_DEFINE_NAME_STRING(GroupSize) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Pad) ///< bool
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PaddingByte) ///< byte
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Log2Base) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(EncodingLookupArray) ///< const byte *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(DecodingLookupArray) ///< const byte *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(InsertLineBreaks) ///< bool
|
||||
CRYPTOPP_DEFINE_NAME_STRING(MaxLineLength) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(DigestSize) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(L1KeyLength) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(TableSize) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Blinding) ///< bool, timing attack mitigations, ON by default
|
||||
CRYPTOPP_DEFINE_NAME_STRING(DerivedKey) ///< ByteArrayParameter, key derivation, derived key
|
||||
CRYPTOPP_DEFINE_NAME_STRING(DerivedKeyLength) ///< int, key derivation, derived key length in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Personalization) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(PersonalizationSize) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Salt) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Tweak) ///< ConstByteArrayParameter
|
||||
CRYPTOPP_DEFINE_NAME_STRING(SaltSize) ///< int, in bytes
|
||||
CRYPTOPP_DEFINE_NAME_STRING(TreeMode) ///< byte
|
||||
CRYPTOPP_DEFINE_NAME_STRING(FileName) ///< const char *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(FileTime) ///< int
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Comment) ///< const char *
|
||||
CRYPTOPP_DEFINE_NAME_STRING(Identity) ///< ConstByteArrayParameter
|
||||
DOCUMENTED_NAMESPACE_END
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
// aria.cpp - written and placed in the public domain by Jeffrey Walton
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "aria.h"
|
||||
#include "misc.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#if CRYPTOPP_SSE2_INTRIN_AVAILABLE
|
||||
# define CRYPTOPP_ENABLE_ARIA_SSE2_INTRINSICS 1
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_SSSE3_AVAILABLE
|
||||
# define CRYPTOPP_ENABLE_ARIA_SSSE3_INTRINSICS 1
|
||||
#endif
|
||||
|
||||
// GCC cast warning. Note: this is used on round key table,
|
||||
// which is word32 and naturally aligned.
|
||||
#define UINT32_CAST(x) ((word32 *)(void *)(x))
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(ARIATab)
|
||||
|
||||
extern const word32 S1[256];
|
||||
extern const word32 S2[256];
|
||||
extern const word32 X1[256];
|
||||
extern const word32 X2[256];
|
||||
extern const word32 KRK[3][4];
|
||||
|
||||
NAMESPACE_END
|
||||
NAMESPACE_END
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
using CryptoPP::ARIATab::S1;
|
||||
using CryptoPP::ARIATab::S2;
|
||||
using CryptoPP::ARIATab::X1;
|
||||
using CryptoPP::ARIATab::X2;
|
||||
using CryptoPP::ARIATab::KRK;
|
||||
|
||||
inline byte ARIA_BRF(const word32 x, const int y) {
|
||||
return static_cast<byte>(GETBYTE(x, y));
|
||||
}
|
||||
|
||||
// Key XOR Layer
|
||||
#define ARIA_KXL { \
|
||||
typedef BlockGetAndPut<word32, NativeByteOrder, true, true> NativeBlock; \
|
||||
NativeBlock::Put(rk, t)(t[0])(t[1])(t[2])(t[3]); \
|
||||
}
|
||||
|
||||
// S-Box Layer 1 + M
|
||||
#define SBL1_M(T0,T1,T2,T3) { \
|
||||
T0=S1[ARIA_BRF(T0,3)]^S2[ARIA_BRF(T0,2)]^X1[ARIA_BRF(T0,1)]^X2[ARIA_BRF(T0,0)]; \
|
||||
T1=S1[ARIA_BRF(T1,3)]^S2[ARIA_BRF(T1,2)]^X1[ARIA_BRF(T1,1)]^X2[ARIA_BRF(T1,0)]; \
|
||||
T2=S1[ARIA_BRF(T2,3)]^S2[ARIA_BRF(T2,2)]^X1[ARIA_BRF(T2,1)]^X2[ARIA_BRF(T2,0)]; \
|
||||
T3=S1[ARIA_BRF(T3,3)]^S2[ARIA_BRF(T3,2)]^X1[ARIA_BRF(T3,1)]^X2[ARIA_BRF(T3,0)]; \
|
||||
}
|
||||
|
||||
// S-Box Layer 2 + M
|
||||
#define SBL2_M(T0,T1,T2,T3) { \
|
||||
T0=X1[ARIA_BRF(T0,3)]^X2[ARIA_BRF(T0,2)]^S1[ARIA_BRF(T0,1)]^S2[ARIA_BRF(T0,0)]; \
|
||||
T1=X1[ARIA_BRF(T1,3)]^X2[ARIA_BRF(T1,2)]^S1[ARIA_BRF(T1,1)]^S2[ARIA_BRF(T1,0)]; \
|
||||
T2=X1[ARIA_BRF(T2,3)]^X2[ARIA_BRF(T2,2)]^S1[ARIA_BRF(T2,1)]^S2[ARIA_BRF(T2,0)]; \
|
||||
T3=X1[ARIA_BRF(T3,3)]^X2[ARIA_BRF(T3,2)]^S1[ARIA_BRF(T3,1)]^S2[ARIA_BRF(T3,0)]; \
|
||||
}
|
||||
|
||||
#define ARIA_P(T0,T1,T2,T3) { \
|
||||
(T1) = (((T1)<< 8)&0xff00ff00) ^ (((T1)>> 8)&0x00ff00ff); \
|
||||
(T2) = rotrConstant<16>(T2); \
|
||||
(T3) = ByteReverse((T3)); \
|
||||
}
|
||||
|
||||
#define ARIA_M(X,Y) { \
|
||||
Y=(X)<<8 ^ (X)>>8 ^ (X)<<16 ^ (X)>>16 ^ (X)<<24 ^ (X)>>24; \
|
||||
}
|
||||
|
||||
#define ARIA_MM(T0,T1,T2,T3) { \
|
||||
(T1)^=(T2); (T2)^=(T3); (T0)^=(T1); \
|
||||
(T3)^=(T1); (T2)^=(T0); (T1)^=(T2); \
|
||||
}
|
||||
|
||||
#define ARIA_FO {SBL1_M(t[0],t[1],t[2],t[3]) ARIA_MM(t[0],t[1],t[2],t[3]) ARIA_P(t[0],t[1],t[2],t[3]) ARIA_MM(t[0],t[1],t[2],t[3])}
|
||||
#define ARIA_FE {SBL2_M(t[0],t[1],t[2],t[3]) ARIA_MM(t[0],t[1],t[2],t[3]) ARIA_P(t[2],t[3],t[0],t[1]) ARIA_MM(t[0],t[1],t[2],t[3])}
|
||||
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
extern void ARIA_UncheckedSetKey_Schedule_NEON(byte* rk, word32* ws, unsigned int keylen);
|
||||
extern void ARIA_ProcessAndXorBlock_NEON(const byte* xorBlock, byte* outblock, const byte *rk, word32 *t);
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_SSSE3_AVAILABLE)
|
||||
extern void ARIA_ProcessAndXorBlock_SSSE3(const byte* xorBlock, byte* outBlock, const byte *rk, word32 *t);
|
||||
#endif
|
||||
|
||||
// n-bit right shift of Y XORed to X
|
||||
template <unsigned int N>
|
||||
inline void ARIA_GSRK(const word32 X[4], const word32 Y[4], byte RK[16])
|
||||
{
|
||||
// MSVC is not generating a "rotate immediate". Constify to help it along.
|
||||
static const unsigned int Q = 4-(N/32);
|
||||
static const unsigned int R = N % 32;
|
||||
UINT32_CAST(RK)[0] = (X[0]) ^ ((Y[(Q )%4])>>R) ^ ((Y[(Q+3)%4])<<(32-R));
|
||||
UINT32_CAST(RK)[1] = (X[1]) ^ ((Y[(Q+1)%4])>>R) ^ ((Y[(Q )%4])<<(32-R));
|
||||
UINT32_CAST(RK)[2] = (X[2]) ^ ((Y[(Q+2)%4])>>R) ^ ((Y[(Q+1)%4])<<(32-R));
|
||||
UINT32_CAST(RK)[3] = (X[3]) ^ ((Y[(Q+3)%4])>>R) ^ ((Y[(Q+2)%4])<<(32-R));
|
||||
}
|
||||
|
||||
void ARIA::Base::UncheckedSetKey(const byte *key, unsigned int keylen, const NameValuePairs ¶ms)
|
||||
{
|
||||
CRYPTOPP_UNUSED(params);
|
||||
|
||||
m_rk.New(16*17); // round keys
|
||||
m_w.New(4*7); // w0, w1, w2, w3, t and u
|
||||
|
||||
byte *rk = m_rk.data();
|
||||
int Q, q, R, r;
|
||||
|
||||
switch (keylen)
|
||||
{
|
||||
case 16:
|
||||
R = r = m_rounds = 12;
|
||||
Q = q = 0;
|
||||
break;
|
||||
case 32:
|
||||
R = r = m_rounds = 16;
|
||||
Q = q = 2;
|
||||
break;
|
||||
case 24:
|
||||
R = r = m_rounds = 14;
|
||||
Q = q = 1;
|
||||
break;
|
||||
default:
|
||||
Q = q = R = r = m_rounds = 0;
|
||||
CRYPTOPP_ASSERT(0);
|
||||
}
|
||||
|
||||
// w0 has room for 32 bytes. w1-w3 each has room for 16 bytes. t and u are 16 byte temp areas.
|
||||
word32 *w0 = m_w.data(), *w1 = m_w.data()+8, *w2 = m_w.data()+12, *w3 = m_w.data()+16, *t = m_w.data()+20;
|
||||
|
||||
GetBlock<word32, BigEndian, false>block(key);
|
||||
block(w0[0])(w0[1])(w0[2])(w0[3]);
|
||||
|
||||
t[0]=w0[0]^KRK[q][0]; t[1]=w0[1]^KRK[q][1];
|
||||
t[2]=w0[2]^KRK[q][2]; t[3]=w0[3]^KRK[q][3];
|
||||
|
||||
ARIA_FO;
|
||||
|
||||
if (keylen == 32)
|
||||
{
|
||||
block(w1[0])(w1[1])(w1[2])(w1[3]);
|
||||
}
|
||||
else if (keylen == 24)
|
||||
{
|
||||
block(w1[0])(w1[1]); w1[2] = w1[3] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
w1[0]=w1[1]=w1[2]=w1[3]=0;
|
||||
}
|
||||
|
||||
w1[0]^=t[0]; w1[1]^=t[1]; w1[2]^=t[2]; w1[3]^=t[3];
|
||||
::memcpy(t, w1, 16);
|
||||
|
||||
q = (q==2) ? 0 : (q+1);
|
||||
t[0]^=KRK[q][0]; t[1]^=KRK[q][1]; t[2]^=KRK[q][2]; t[3]^=KRK[q][3];
|
||||
|
||||
ARIA_FE;
|
||||
|
||||
t[0]^=w0[0]; t[1]^=w0[1]; t[2]^=w0[2]; t[3]^=w0[3];
|
||||
::memcpy(w2, t, 16);
|
||||
|
||||
q = (q==2) ? 0 : (q+1);
|
||||
t[0]^=KRK[q][0]; t[1]^=KRK[q][1]; t[2]^=KRK[q][2]; t[3]^=KRK[q][3];
|
||||
|
||||
ARIA_FO;
|
||||
|
||||
w3[0]=t[0]^w1[0]; w3[1]=t[1]^w1[1]; w3[2]=t[2]^w1[2]; w3[3]=t[3]^w1[3];
|
||||
|
||||
#if CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
if (HasNEON())
|
||||
{
|
||||
ARIA_UncheckedSetKey_Schedule_NEON(rk, m_w, keylen);
|
||||
}
|
||||
else
|
||||
#endif // CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
{
|
||||
ARIA_GSRK<19>(w0, w1, rk + 0);
|
||||
ARIA_GSRK<19>(w1, w2, rk + 16);
|
||||
ARIA_GSRK<19>(w2, w3, rk + 32);
|
||||
ARIA_GSRK<19>(w3, w0, rk + 48);
|
||||
ARIA_GSRK<31>(w0, w1, rk + 64);
|
||||
ARIA_GSRK<31>(w1, w2, rk + 80);
|
||||
ARIA_GSRK<31>(w2, w3, rk + 96);
|
||||
ARIA_GSRK<31>(w3, w0, rk + 112);
|
||||
ARIA_GSRK<67>(w0, w1, rk + 128);
|
||||
ARIA_GSRK<67>(w1, w2, rk + 144);
|
||||
ARIA_GSRK<67>(w2, w3, rk + 160);
|
||||
ARIA_GSRK<67>(w3, w0, rk + 176);
|
||||
ARIA_GSRK<97>(w0, w1, rk + 192);
|
||||
|
||||
if (keylen > 16)
|
||||
{
|
||||
ARIA_GSRK<97>(w1, w2, rk + 208);
|
||||
ARIA_GSRK<97>(w2, w3, rk + 224);
|
||||
|
||||
if (keylen > 24)
|
||||
{
|
||||
ARIA_GSRK< 97>(w3, w0, rk + 240);
|
||||
ARIA_GSRK<109>(w0, w1, rk + 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decryption operation
|
||||
if (!IsForwardTransformation())
|
||||
{
|
||||
word32 *a, *z, *s;
|
||||
rk = m_rk.data();
|
||||
r = R; q = Q;
|
||||
|
||||
a=UINT32_CAST(rk); s=m_w.data()+24; z=a+r*4;
|
||||
::memcpy(t, a, 16); ::memcpy(a, z, 16); ::memcpy(z, t, 16);
|
||||
|
||||
a+=4; z-=4;
|
||||
for (; a<z; a+=4, z-=4)
|
||||
{
|
||||
ARIA_M(a[0],t[0]); ARIA_M(a[1],t[1]); ARIA_M(a[2],t[2]); ARIA_M(a[3],t[3]);
|
||||
ARIA_MM(t[0],t[1],t[2],t[3]); ARIA_P(t[0],t[1],t[2],t[3]); ARIA_MM(t[0],t[1],t[2],t[3]);
|
||||
::memcpy(s, t, 16);
|
||||
|
||||
ARIA_M(z[0],t[0]); ARIA_M(z[1],t[1]); ARIA_M(z[2],t[2]); ARIA_M(z[3],t[3]);
|
||||
ARIA_MM(t[0],t[1],t[2],t[3]); ARIA_P(t[0],t[1],t[2],t[3]); ARIA_MM(t[0],t[1],t[2],t[3]);
|
||||
::memcpy(a, t, 16); ::memcpy(z, s, 16);
|
||||
}
|
||||
|
||||
ARIA_M(a[0],t[0]); ARIA_M(a[1],t[1]); ARIA_M(a[2],t[2]); ARIA_M(a[3],t[3]);
|
||||
ARIA_MM(t[0],t[1],t[2],t[3]); ARIA_P(t[0],t[1],t[2],t[3]); ARIA_MM(t[0],t[1],t[2],t[3]);
|
||||
::memcpy(z, t, 16);
|
||||
}
|
||||
|
||||
// Silence warnings
|
||||
CRYPTOPP_UNUSED(Q); CRYPTOPP_UNUSED(R);
|
||||
CRYPTOPP_UNUSED(q); CRYPTOPP_UNUSED(r);
|
||||
}
|
||||
|
||||
void ARIA::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
const byte *rk = reinterpret_cast<const byte*>(m_rk.data());
|
||||
word32 *t = const_cast<word32*>(m_w.data()+20);
|
||||
|
||||
// Timing attack countermeasure. See comments in Rijndael for more details.
|
||||
// We used Yun's 32-bit implementation, so we use words rather than bytes.
|
||||
const int cacheLineSize = GetCacheLineSize();
|
||||
unsigned int i;
|
||||
volatile word32 _u = 0;
|
||||
word32 u = _u;
|
||||
|
||||
for (i=0; i<COUNTOF(S1); i+=cacheLineSize/(sizeof(S1[0])))
|
||||
u |= *(S1+i);
|
||||
t[0] |= u;
|
||||
|
||||
GetBlock<word32, BigEndian>block(inBlock);
|
||||
block(t[0])(t[1])(t[2])(t[3]);
|
||||
|
||||
if (m_rounds > 12) {
|
||||
ARIA_KXL; rk+= 16; ARIA_FO;
|
||||
ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
}
|
||||
|
||||
if (m_rounds > 14) {
|
||||
ARIA_KXL; rk+= 16; ARIA_FO;
|
||||
ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
}
|
||||
|
||||
ARIA_KXL; rk+= 16; ARIA_FO; ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
ARIA_KXL; rk+= 16; ARIA_FO; ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
ARIA_KXL; rk+= 16; ARIA_FO; ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
ARIA_KXL; rk+= 16; ARIA_FO; ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
ARIA_KXL; rk+= 16; ARIA_FO; ARIA_KXL; rk+= 16; ARIA_FE;
|
||||
ARIA_KXL; rk+= 16; ARIA_FO; ARIA_KXL; rk+= 16;
|
||||
|
||||
#if CRYPTOPP_ENABLE_ARIA_SSSE3_INTRINSICS
|
||||
if (HasSSSE3())
|
||||
{
|
||||
ARIA_ProcessAndXorBlock_SSSE3(xorBlock, outBlock, rk, t);
|
||||
return;
|
||||
}
|
||||
else
|
||||
#endif // CRYPTOPP_ENABLE_ARIA_SSSE3_INTRINSICS
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
{
|
||||
ARIA_ProcessAndXorBlock_NEON(xorBlock, outBlock, rk, t);
|
||||
return;
|
||||
}
|
||||
else
|
||||
#endif // CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
#if (CRYPTOPP_LITTLE_ENDIAN)
|
||||
{
|
||||
outBlock[ 0] = (byte)(X1[ARIA_BRF(t[0],3)] ) ^ rk[ 3];
|
||||
outBlock[ 1] = (byte)(X2[ARIA_BRF(t[0],2)]>>8) ^ rk[ 2];
|
||||
outBlock[ 2] = (byte)(S1[ARIA_BRF(t[0],1)] ) ^ rk[ 1];
|
||||
outBlock[ 3] = (byte)(S2[ARIA_BRF(t[0],0)] ) ^ rk[ 0];
|
||||
outBlock[ 4] = (byte)(X1[ARIA_BRF(t[1],3)] ) ^ rk[ 7];
|
||||
outBlock[ 5] = (byte)(X2[ARIA_BRF(t[1],2)]>>8) ^ rk[ 6];
|
||||
outBlock[ 6] = (byte)(S1[ARIA_BRF(t[1],1)] ) ^ rk[ 5];
|
||||
outBlock[ 7] = (byte)(S2[ARIA_BRF(t[1],0)] ) ^ rk[ 4];
|
||||
outBlock[ 8] = (byte)(X1[ARIA_BRF(t[2],3)] ) ^ rk[11];
|
||||
outBlock[ 9] = (byte)(X2[ARIA_BRF(t[2],2)]>>8) ^ rk[10];
|
||||
outBlock[10] = (byte)(S1[ARIA_BRF(t[2],1)] ) ^ rk[ 9];
|
||||
outBlock[11] = (byte)(S2[ARIA_BRF(t[2],0)] ) ^ rk[ 8];
|
||||
outBlock[12] = (byte)(X1[ARIA_BRF(t[3],3)] ) ^ rk[15];
|
||||
outBlock[13] = (byte)(X2[ARIA_BRF(t[3],2)]>>8) ^ rk[14];
|
||||
outBlock[14] = (byte)(S1[ARIA_BRF(t[3],1)] ) ^ rk[13];
|
||||
outBlock[15] = (byte)(S2[ARIA_BRF(t[3],0)] ) ^ rk[12];
|
||||
}
|
||||
#else
|
||||
{
|
||||
outBlock[ 0] = (byte)(X1[ARIA_BRF(t[0],3)] ) ^ rk[ 0];
|
||||
outBlock[ 1] = (byte)(X2[ARIA_BRF(t[0],2)]>>8) ^ rk[ 1];
|
||||
outBlock[ 2] = (byte)(S1[ARIA_BRF(t[0],1)] ) ^ rk[ 2];
|
||||
outBlock[ 3] = (byte)(S2[ARIA_BRF(t[0],0)] ) ^ rk[ 3];
|
||||
outBlock[ 4] = (byte)(X1[ARIA_BRF(t[1],3)] ) ^ rk[ 4];
|
||||
outBlock[ 5] = (byte)(X2[ARIA_BRF(t[1],2)]>>8) ^ rk[ 5];
|
||||
outBlock[ 6] = (byte)(S1[ARIA_BRF(t[1],1)] ) ^ rk[ 6];
|
||||
outBlock[ 7] = (byte)(S2[ARIA_BRF(t[1],0)] ) ^ rk[ 7];
|
||||
outBlock[ 8] = (byte)(X1[ARIA_BRF(t[2],3)] ) ^ rk[ 8];
|
||||
outBlock[ 9] = (byte)(X2[ARIA_BRF(t[2],2)]>>8) ^ rk[ 9];
|
||||
outBlock[10] = (byte)(S1[ARIA_BRF(t[2],1)] ) ^ rk[10];
|
||||
outBlock[11] = (byte)(S2[ARIA_BRF(t[2],0)] ) ^ rk[11];
|
||||
outBlock[12] = (byte)(X1[ARIA_BRF(t[3],3)] ) ^ rk[12];
|
||||
outBlock[13] = (byte)(X2[ARIA_BRF(t[3],2)]>>8) ^ rk[13];
|
||||
outBlock[14] = (byte)(S1[ARIA_BRF(t[3],1)] ) ^ rk[14];
|
||||
outBlock[15] = (byte)(S2[ARIA_BRF(t[3],0)] ) ^ rk[15];
|
||||
}
|
||||
#endif // CRYPTOPP_LITTLE_ENDIAN
|
||||
|
||||
if (xorBlock != NULLPTR)
|
||||
for (unsigned int n=0; n<ARIA::BLOCKSIZE; ++n)
|
||||
outBlock[n] ^= xorBlock[n];
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// aria.h - written and placed in the public domain by Jeffrey Walton
|
||||
|
||||
/// \file aria.h
|
||||
/// \brief Classes for the ARIA block cipher
|
||||
/// \details The Crypto++ ARIA implementation is based on the 32-bit implementation by Aaram Yun
|
||||
/// from the National Security Research Institute, KOREA. Aaram Yun's implementation is based on
|
||||
/// the 8-bit implementation by Jin Hong. The source files are available in ARIA.zip from the Korea
|
||||
/// Internet & Security Agency website.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc5794">RFC 5794, A Description of the ARIA Encryption Algorithm</A>,
|
||||
/// <A HREF="http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002">Korea
|
||||
/// Internet & Security Agency homepage</A>
|
||||
|
||||
#ifndef CRYPTOPP_ARIA_H
|
||||
#define CRYPTOPP_ARIA_H
|
||||
|
||||
#include "config.h"
|
||||
#include "seckey.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief ARIA block cipher information
|
||||
/// \since Crypto++ 6.0
|
||||
struct ARIA_Info : public FixedBlockSize<16>, public VariableKeyLength<16, 16, 32, 8>
|
||||
{
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "ARIA";}
|
||||
};
|
||||
|
||||
/// \brief ARIA block cipher
|
||||
/// \details The Crypto++ ARIA implementation is based on the 32-bit implementation by Aaram Yun
|
||||
/// from the National Security Research Institute, KOREA. Aaram Yun's implementation is based on
|
||||
/// the 8-bit implementation by Jin Hong. The source files are available in ARIA.zip from the Korea
|
||||
/// Internet & Security Agency website.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc5794">RFC 5794, A Description of the ARIA Encryption Algorithm</A>,
|
||||
/// <A HREF="http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002">Korea
|
||||
/// Internet & Security Agency homepage</A>
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/ARIA">ARIA</a>
|
||||
/// \since Crypto++ 6.0
|
||||
class ARIA : public ARIA_Info, public BlockCipherDocumentation
|
||||
{
|
||||
public:
|
||||
class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl<ARIA_Info>
|
||||
{
|
||||
public:
|
||||
Base() : m_rounds(0) {}
|
||||
|
||||
protected:
|
||||
void UncheckedSetKey(const byte *key, unsigned int keylen, const NameValuePairs ¶ms);
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
|
||||
private:
|
||||
// Reference implementation allocates a table of 17 round keys.
|
||||
typedef SecBlock<byte, AllocatorWithCleanup<byte, true> > AlignedByteBlock;
|
||||
typedef SecBlock<word32, AllocatorWithCleanup<word32, true> > AlignedWordBlock;
|
||||
|
||||
AlignedByteBlock m_rk; // round keys
|
||||
AlignedWordBlock m_w; // w0, w1, w2, w3, t and u
|
||||
unsigned int m_rounds;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BlockCipherFinal<ENCRYPTION, Base> Encryption;
|
||||
typedef BlockCipherFinal<DECRYPTION, Base> Decryption;
|
||||
};
|
||||
|
||||
typedef ARIA::Encryption ARIAEncryption;
|
||||
typedef ARIA::Decryption ARIADecryption;
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
// aria_simd.cpp - written and placed in the public domain by
|
||||
// Jeffrey Walton, Uri Blumenthal and Marcel Raad.
|
||||
//
|
||||
// This source file uses intrinsics to gain access to ARMv7a and
|
||||
// ARMv8a NEON instructions. A separate source file is needed
|
||||
// because additional CXXFLAGS are required to enable the
|
||||
// appropriate instructions sets in some build configurations.
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if (CRYPTOPP_SSSE3_AVAILABLE)
|
||||
# include <tmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ARM_NEON_HEADER)
|
||||
# include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ARM_ACLE_HEADER)
|
||||
# include <stdint.h>
|
||||
# include <arm_acle.h>
|
||||
#endif
|
||||
|
||||
// Clang intrinsic casts, http://bugs.llvm.org/show_bug.cgi?id=20670
|
||||
#define M128_CAST(x) ((__m128i *)(void *)(x))
|
||||
#define CONST_M128_CAST(x) ((const __m128i *)(const void *)(x))
|
||||
|
||||
// Squash MS LNK4221 and libtool warnings
|
||||
extern const char ARIA_SIMD_FNAME[] = __FILE__;
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(ARIATab)
|
||||
|
||||
extern const word32 S1[256];
|
||||
extern const word32 S2[256];
|
||||
extern const word32 X1[256];
|
||||
extern const word32 X2[256];
|
||||
extern const word32 KRK[3][4];
|
||||
|
||||
NAMESPACE_END
|
||||
NAMESPACE_END
|
||||
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
using CryptoPP::byte;
|
||||
using CryptoPP::word32;
|
||||
|
||||
inline byte ARIA_BRF(const word32 x, const int y) {
|
||||
return static_cast<byte>(GETBYTE(x, y));
|
||||
}
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
using CryptoPP::ARIATab::S1;
|
||||
using CryptoPP::ARIATab::S2;
|
||||
using CryptoPP::ARIATab::X1;
|
||||
using CryptoPP::ARIATab::X2;
|
||||
using CryptoPP::ARIATab::KRK;
|
||||
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
|
||||
template <unsigned int N>
|
||||
inline void ARIA_GSRK_NEON(const uint32x4_t X, const uint32x4_t Y, byte RK[16])
|
||||
{
|
||||
enum { Q1 = (4-(N/32)) % 4,
|
||||
Q2 = (3-(N/32)) % 4,
|
||||
R = N % 32
|
||||
};
|
||||
|
||||
vst1q_u8(RK, vreinterpretq_u8_u32(
|
||||
veorq_u32(X, veorq_u32(
|
||||
vshrq_n_u32(vextq_u32(Y, Y, Q1), R),
|
||||
vshlq_n_u32(vextq_u32(Y, Y, Q2), 32-R)))));
|
||||
}
|
||||
|
||||
void ARIA_UncheckedSetKey_Schedule_NEON(byte* rk, word32* ws, unsigned int keylen)
|
||||
{
|
||||
const uint32x4_t w0 = vld1q_u32(ws+ 0);
|
||||
const uint32x4_t w1 = vld1q_u32(ws+ 8);
|
||||
const uint32x4_t w2 = vld1q_u32(ws+12);
|
||||
const uint32x4_t w3 = vld1q_u32(ws+16);
|
||||
|
||||
ARIA_GSRK_NEON<19>(w0, w1, rk + 0);
|
||||
ARIA_GSRK_NEON<19>(w1, w2, rk + 16);
|
||||
ARIA_GSRK_NEON<19>(w2, w3, rk + 32);
|
||||
ARIA_GSRK_NEON<19>(w3, w0, rk + 48);
|
||||
ARIA_GSRK_NEON<31>(w0, w1, rk + 64);
|
||||
ARIA_GSRK_NEON<31>(w1, w2, rk + 80);
|
||||
ARIA_GSRK_NEON<31>(w2, w3, rk + 96);
|
||||
ARIA_GSRK_NEON<31>(w3, w0, rk + 112);
|
||||
ARIA_GSRK_NEON<67>(w0, w1, rk + 128);
|
||||
ARIA_GSRK_NEON<67>(w1, w2, rk + 144);
|
||||
ARIA_GSRK_NEON<67>(w2, w3, rk + 160);
|
||||
ARIA_GSRK_NEON<67>(w3, w0, rk + 176);
|
||||
ARIA_GSRK_NEON<97>(w0, w1, rk + 192);
|
||||
|
||||
if (keylen > 16)
|
||||
{
|
||||
ARIA_GSRK_NEON<97>(w1, w2, rk + 208);
|
||||
ARIA_GSRK_NEON<97>(w2, w3, rk + 224);
|
||||
|
||||
if (keylen > 24)
|
||||
{
|
||||
ARIA_GSRK_NEON< 97>(w3, w0, rk + 240);
|
||||
ARIA_GSRK_NEON<109>(w0, w1, rk + 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARIA_ProcessAndXorBlock_NEON(const byte* xorBlock, byte* outBlock, const byte *rk, word32 *t)
|
||||
{
|
||||
outBlock[ 0] = (byte)(X1[ARIA_BRF(t[0],3)] );
|
||||
outBlock[ 1] = (byte)(X2[ARIA_BRF(t[0],2)]>>8);
|
||||
outBlock[ 2] = (byte)(S1[ARIA_BRF(t[0],1)] );
|
||||
outBlock[ 3] = (byte)(S2[ARIA_BRF(t[0],0)] );
|
||||
outBlock[ 4] = (byte)(X1[ARIA_BRF(t[1],3)] );
|
||||
outBlock[ 5] = (byte)(X2[ARIA_BRF(t[1],2)]>>8);
|
||||
outBlock[ 6] = (byte)(S1[ARIA_BRF(t[1],1)] );
|
||||
outBlock[ 7] = (byte)(S2[ARIA_BRF(t[1],0)] );
|
||||
outBlock[ 8] = (byte)(X1[ARIA_BRF(t[2],3)] );
|
||||
outBlock[ 9] = (byte)(X2[ARIA_BRF(t[2],2)]>>8);
|
||||
outBlock[10] = (byte)(S1[ARIA_BRF(t[2],1)] );
|
||||
outBlock[11] = (byte)(S2[ARIA_BRF(t[2],0)] );
|
||||
outBlock[12] = (byte)(X1[ARIA_BRF(t[3],3)] );
|
||||
outBlock[13] = (byte)(X2[ARIA_BRF(t[3],2)]>>8);
|
||||
outBlock[14] = (byte)(S1[ARIA_BRF(t[3],1)] );
|
||||
outBlock[15] = (byte)(S2[ARIA_BRF(t[3],0)] );
|
||||
|
||||
// 'outBlock' and 'xorBlock' may be unaligned.
|
||||
if (xorBlock != NULLPTR)
|
||||
{
|
||||
vst1q_u8(outBlock,
|
||||
veorq_u8(
|
||||
vld1q_u8(xorBlock),
|
||||
veorq_u8(
|
||||
vld1q_u8(outBlock),
|
||||
vrev32q_u8(vld1q_u8((rk))))));
|
||||
}
|
||||
else
|
||||
{
|
||||
vst1q_u8(outBlock,
|
||||
veorq_u8(
|
||||
vld1q_u8(outBlock),
|
||||
vrev32q_u8(vld1q_u8(rk))));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
|
||||
#if (CRYPTOPP_SSSE3_AVAILABLE)
|
||||
|
||||
void ARIA_ProcessAndXorBlock_SSSE3(const byte* xorBlock, byte* outBlock, const byte *rk, word32 *t)
|
||||
{
|
||||
const __m128i MASK = _mm_set_epi8(12,13,14,15, 8,9,10,11, 4,5,6,7, 0,1,2,3);
|
||||
|
||||
outBlock[ 0] = (byte)(X1[ARIA_BRF(t[0],3)] );
|
||||
outBlock[ 1] = (byte)(X2[ARIA_BRF(t[0],2)]>>8);
|
||||
outBlock[ 2] = (byte)(S1[ARIA_BRF(t[0],1)] );
|
||||
outBlock[ 3] = (byte)(S2[ARIA_BRF(t[0],0)] );
|
||||
outBlock[ 4] = (byte)(X1[ARIA_BRF(t[1],3)] );
|
||||
outBlock[ 5] = (byte)(X2[ARIA_BRF(t[1],2)]>>8);
|
||||
outBlock[ 6] = (byte)(S1[ARIA_BRF(t[1],1)] );
|
||||
outBlock[ 7] = (byte)(S2[ARIA_BRF(t[1],0)] );
|
||||
outBlock[ 8] = (byte)(X1[ARIA_BRF(t[2],3)] );
|
||||
outBlock[ 9] = (byte)(X2[ARIA_BRF(t[2],2)]>>8);
|
||||
outBlock[10] = (byte)(S1[ARIA_BRF(t[2],1)] );
|
||||
outBlock[11] = (byte)(S2[ARIA_BRF(t[2],0)] );
|
||||
outBlock[12] = (byte)(X1[ARIA_BRF(t[3],3)] );
|
||||
outBlock[13] = (byte)(X2[ARIA_BRF(t[3],2)]>>8);
|
||||
outBlock[14] = (byte)(S1[ARIA_BRF(t[3],1)] );
|
||||
outBlock[15] = (byte)(S2[ARIA_BRF(t[3],0)] );
|
||||
|
||||
// 'outBlock' and 'xorBlock' may be unaligned.
|
||||
if (xorBlock != NULLPTR)
|
||||
{
|
||||
_mm_storeu_si128(M128_CAST(outBlock),
|
||||
_mm_xor_si128(
|
||||
_mm_loadu_si128(CONST_M128_CAST(xorBlock)),
|
||||
_mm_xor_si128(
|
||||
_mm_loadu_si128(CONST_M128_CAST(outBlock)),
|
||||
_mm_shuffle_epi8(_mm_load_si128(CONST_M128_CAST(rk)), MASK)))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mm_storeu_si128(M128_CAST(outBlock),
|
||||
_mm_xor_si128(_mm_loadu_si128(CONST_M128_CAST(outBlock)),
|
||||
_mm_shuffle_epi8(_mm_load_si128(CONST_M128_CAST(rk)), MASK)));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CRYPTOPP_SSSE3_AVAILABLE
|
||||
|
||||
NAMESPACE_END
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
// ariatab.cpp - written and placed in the public domain by Jeffrey Walton
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(ARIATab)
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(16)
|
||||
CRYPTOPP_TABLE
|
||||
const word32 S1[256]={
|
||||
0x00636363,0x007c7c7c,0x00777777,0x007b7b7b,0x00f2f2f2,0x006b6b6b,0x006f6f6f,0x00c5c5c5,
|
||||
0x00303030,0x00010101,0x00676767,0x002b2b2b,0x00fefefe,0x00d7d7d7,0x00ababab,0x00767676,
|
||||
0x00cacaca,0x00828282,0x00c9c9c9,0x007d7d7d,0x00fafafa,0x00595959,0x00474747,0x00f0f0f0,
|
||||
0x00adadad,0x00d4d4d4,0x00a2a2a2,0x00afafaf,0x009c9c9c,0x00a4a4a4,0x00727272,0x00c0c0c0,
|
||||
0x00b7b7b7,0x00fdfdfd,0x00939393,0x00262626,0x00363636,0x003f3f3f,0x00f7f7f7,0x00cccccc,
|
||||
0x00343434,0x00a5a5a5,0x00e5e5e5,0x00f1f1f1,0x00717171,0x00d8d8d8,0x00313131,0x00151515,
|
||||
0x00040404,0x00c7c7c7,0x00232323,0x00c3c3c3,0x00181818,0x00969696,0x00050505,0x009a9a9a,
|
||||
0x00070707,0x00121212,0x00808080,0x00e2e2e2,0x00ebebeb,0x00272727,0x00b2b2b2,0x00757575,
|
||||
0x00090909,0x00838383,0x002c2c2c,0x001a1a1a,0x001b1b1b,0x006e6e6e,0x005a5a5a,0x00a0a0a0,
|
||||
0x00525252,0x003b3b3b,0x00d6d6d6,0x00b3b3b3,0x00292929,0x00e3e3e3,0x002f2f2f,0x00848484,
|
||||
0x00535353,0x00d1d1d1,0x00000000,0x00ededed,0x00202020,0x00fcfcfc,0x00b1b1b1,0x005b5b5b,
|
||||
0x006a6a6a,0x00cbcbcb,0x00bebebe,0x00393939,0x004a4a4a,0x004c4c4c,0x00585858,0x00cfcfcf,
|
||||
0x00d0d0d0,0x00efefef,0x00aaaaaa,0x00fbfbfb,0x00434343,0x004d4d4d,0x00333333,0x00858585,
|
||||
0x00454545,0x00f9f9f9,0x00020202,0x007f7f7f,0x00505050,0x003c3c3c,0x009f9f9f,0x00a8a8a8,
|
||||
0x00515151,0x00a3a3a3,0x00404040,0x008f8f8f,0x00929292,0x009d9d9d,0x00383838,0x00f5f5f5,
|
||||
0x00bcbcbc,0x00b6b6b6,0x00dadada,0x00212121,0x00101010,0x00ffffff,0x00f3f3f3,0x00d2d2d2,
|
||||
0x00cdcdcd,0x000c0c0c,0x00131313,0x00ececec,0x005f5f5f,0x00979797,0x00444444,0x00171717,
|
||||
0x00c4c4c4,0x00a7a7a7,0x007e7e7e,0x003d3d3d,0x00646464,0x005d5d5d,0x00191919,0x00737373,
|
||||
0x00606060,0x00818181,0x004f4f4f,0x00dcdcdc,0x00222222,0x002a2a2a,0x00909090,0x00888888,
|
||||
0x00464646,0x00eeeeee,0x00b8b8b8,0x00141414,0x00dedede,0x005e5e5e,0x000b0b0b,0x00dbdbdb,
|
||||
0x00e0e0e0,0x00323232,0x003a3a3a,0x000a0a0a,0x00494949,0x00060606,0x00242424,0x005c5c5c,
|
||||
0x00c2c2c2,0x00d3d3d3,0x00acacac,0x00626262,0x00919191,0x00959595,0x00e4e4e4,0x00797979,
|
||||
0x00e7e7e7,0x00c8c8c8,0x00373737,0x006d6d6d,0x008d8d8d,0x00d5d5d5,0x004e4e4e,0x00a9a9a9,
|
||||
0x006c6c6c,0x00565656,0x00f4f4f4,0x00eaeaea,0x00656565,0x007a7a7a,0x00aeaeae,0x00080808,
|
||||
0x00bababa,0x00787878,0x00252525,0x002e2e2e,0x001c1c1c,0x00a6a6a6,0x00b4b4b4,0x00c6c6c6,
|
||||
0x00e8e8e8,0x00dddddd,0x00747474,0x001f1f1f,0x004b4b4b,0x00bdbdbd,0x008b8b8b,0x008a8a8a,
|
||||
0x00707070,0x003e3e3e,0x00b5b5b5,0x00666666,0x00484848,0x00030303,0x00f6f6f6,0x000e0e0e,
|
||||
0x00616161,0x00353535,0x00575757,0x00b9b9b9,0x00868686,0x00c1c1c1,0x001d1d1d,0x009e9e9e,
|
||||
0x00e1e1e1,0x00f8f8f8,0x00989898,0x00111111,0x00696969,0x00d9d9d9,0x008e8e8e,0x00949494,
|
||||
0x009b9b9b,0x001e1e1e,0x00878787,0x00e9e9e9,0x00cecece,0x00555555,0x00282828,0x00dfdfdf,
|
||||
0x008c8c8c,0x00a1a1a1,0x00898989,0x000d0d0d,0x00bfbfbf,0x00e6e6e6,0x00424242,0x00686868,
|
||||
0x00414141,0x00999999,0x002d2d2d,0x000f0f0f,0x00b0b0b0,0x00545454,0x00bbbbbb,0x00161616
|
||||
};
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(16)
|
||||
CRYPTOPP_TABLE
|
||||
const word32 S2[256]={
|
||||
0xe200e2e2,0x4e004e4e,0x54005454,0xfc00fcfc,0x94009494,0xc200c2c2,0x4a004a4a,0xcc00cccc,
|
||||
0x62006262,0x0d000d0d,0x6a006a6a,0x46004646,0x3c003c3c,0x4d004d4d,0x8b008b8b,0xd100d1d1,
|
||||
0x5e005e5e,0xfa00fafa,0x64006464,0xcb00cbcb,0xb400b4b4,0x97009797,0xbe00bebe,0x2b002b2b,
|
||||
0xbc00bcbc,0x77007777,0x2e002e2e,0x03000303,0xd300d3d3,0x19001919,0x59005959,0xc100c1c1,
|
||||
0x1d001d1d,0x06000606,0x41004141,0x6b006b6b,0x55005555,0xf000f0f0,0x99009999,0x69006969,
|
||||
0xea00eaea,0x9c009c9c,0x18001818,0xae00aeae,0x63006363,0xdf00dfdf,0xe700e7e7,0xbb00bbbb,
|
||||
0x00000000,0x73007373,0x66006666,0xfb00fbfb,0x96009696,0x4c004c4c,0x85008585,0xe400e4e4,
|
||||
0x3a003a3a,0x09000909,0x45004545,0xaa00aaaa,0x0f000f0f,0xee00eeee,0x10001010,0xeb00ebeb,
|
||||
0x2d002d2d,0x7f007f7f,0xf400f4f4,0x29002929,0xac00acac,0xcf00cfcf,0xad00adad,0x91009191,
|
||||
0x8d008d8d,0x78007878,0xc800c8c8,0x95009595,0xf900f9f9,0x2f002f2f,0xce00cece,0xcd00cdcd,
|
||||
0x08000808,0x7a007a7a,0x88008888,0x38003838,0x5c005c5c,0x83008383,0x2a002a2a,0x28002828,
|
||||
0x47004747,0xdb00dbdb,0xb800b8b8,0xc700c7c7,0x93009393,0xa400a4a4,0x12001212,0x53005353,
|
||||
0xff00ffff,0x87008787,0x0e000e0e,0x31003131,0x36003636,0x21002121,0x58005858,0x48004848,
|
||||
0x01000101,0x8e008e8e,0x37003737,0x74007474,0x32003232,0xca00caca,0xe900e9e9,0xb100b1b1,
|
||||
0xb700b7b7,0xab00abab,0x0c000c0c,0xd700d7d7,0xc400c4c4,0x56005656,0x42004242,0x26002626,
|
||||
0x07000707,0x98009898,0x60006060,0xd900d9d9,0xb600b6b6,0xb900b9b9,0x11001111,0x40004040,
|
||||
0xec00ecec,0x20002020,0x8c008c8c,0xbd00bdbd,0xa000a0a0,0xc900c9c9,0x84008484,0x04000404,
|
||||
0x49004949,0x23002323,0xf100f1f1,0x4f004f4f,0x50005050,0x1f001f1f,0x13001313,0xdc00dcdc,
|
||||
0xd800d8d8,0xc000c0c0,0x9e009e9e,0x57005757,0xe300e3e3,0xc300c3c3,0x7b007b7b,0x65006565,
|
||||
0x3b003b3b,0x02000202,0x8f008f8f,0x3e003e3e,0xe800e8e8,0x25002525,0x92009292,0xe500e5e5,
|
||||
0x15001515,0xdd00dddd,0xfd00fdfd,0x17001717,0xa900a9a9,0xbf00bfbf,0xd400d4d4,0x9a009a9a,
|
||||
0x7e007e7e,0xc500c5c5,0x39003939,0x67006767,0xfe00fefe,0x76007676,0x9d009d9d,0x43004343,
|
||||
0xa700a7a7,0xe100e1e1,0xd000d0d0,0xf500f5f5,0x68006868,0xf200f2f2,0x1b001b1b,0x34003434,
|
||||
0x70007070,0x05000505,0xa300a3a3,0x8a008a8a,0xd500d5d5,0x79007979,0x86008686,0xa800a8a8,
|
||||
0x30003030,0xc600c6c6,0x51005151,0x4b004b4b,0x1e001e1e,0xa600a6a6,0x27002727,0xf600f6f6,
|
||||
0x35003535,0xd200d2d2,0x6e006e6e,0x24002424,0x16001616,0x82008282,0x5f005f5f,0xda00dada,
|
||||
0xe600e6e6,0x75007575,0xa200a2a2,0xef00efef,0x2c002c2c,0xb200b2b2,0x1c001c1c,0x9f009f9f,
|
||||
0x5d005d5d,0x6f006f6f,0x80008080,0x0a000a0a,0x72007272,0x44004444,0x9b009b9b,0x6c006c6c,
|
||||
0x90009090,0x0b000b0b,0x5b005b5b,0x33003333,0x7d007d7d,0x5a005a5a,0x52005252,0xf300f3f3,
|
||||
0x61006161,0xa100a1a1,0xf700f7f7,0xb000b0b0,0xd600d6d6,0x3f003f3f,0x7c007c7c,0x6d006d6d,
|
||||
0xed00eded,0x14001414,0xe000e0e0,0xa500a5a5,0x3d003d3d,0x22002222,0xb300b3b3,0xf800f8f8,
|
||||
0x89008989,0xde00dede,0x71007171,0x1a001a1a,0xaf00afaf,0xba00baba,0xb500b5b5,0x81008181
|
||||
};
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(16)
|
||||
CRYPTOPP_TABLE
|
||||
const word32 X1[256]={
|
||||
0x52520052,0x09090009,0x6a6a006a,0xd5d500d5,0x30300030,0x36360036,0xa5a500a5,0x38380038,
|
||||
0xbfbf00bf,0x40400040,0xa3a300a3,0x9e9e009e,0x81810081,0xf3f300f3,0xd7d700d7,0xfbfb00fb,
|
||||
0x7c7c007c,0xe3e300e3,0x39390039,0x82820082,0x9b9b009b,0x2f2f002f,0xffff00ff,0x87870087,
|
||||
0x34340034,0x8e8e008e,0x43430043,0x44440044,0xc4c400c4,0xdede00de,0xe9e900e9,0xcbcb00cb,
|
||||
0x54540054,0x7b7b007b,0x94940094,0x32320032,0xa6a600a6,0xc2c200c2,0x23230023,0x3d3d003d,
|
||||
0xeeee00ee,0x4c4c004c,0x95950095,0x0b0b000b,0x42420042,0xfafa00fa,0xc3c300c3,0x4e4e004e,
|
||||
0x08080008,0x2e2e002e,0xa1a100a1,0x66660066,0x28280028,0xd9d900d9,0x24240024,0xb2b200b2,
|
||||
0x76760076,0x5b5b005b,0xa2a200a2,0x49490049,0x6d6d006d,0x8b8b008b,0xd1d100d1,0x25250025,
|
||||
0x72720072,0xf8f800f8,0xf6f600f6,0x64640064,0x86860086,0x68680068,0x98980098,0x16160016,
|
||||
0xd4d400d4,0xa4a400a4,0x5c5c005c,0xcccc00cc,0x5d5d005d,0x65650065,0xb6b600b6,0x92920092,
|
||||
0x6c6c006c,0x70700070,0x48480048,0x50500050,0xfdfd00fd,0xeded00ed,0xb9b900b9,0xdada00da,
|
||||
0x5e5e005e,0x15150015,0x46460046,0x57570057,0xa7a700a7,0x8d8d008d,0x9d9d009d,0x84840084,
|
||||
0x90900090,0xd8d800d8,0xabab00ab,0x00000000,0x8c8c008c,0xbcbc00bc,0xd3d300d3,0x0a0a000a,
|
||||
0xf7f700f7,0xe4e400e4,0x58580058,0x05050005,0xb8b800b8,0xb3b300b3,0x45450045,0x06060006,
|
||||
0xd0d000d0,0x2c2c002c,0x1e1e001e,0x8f8f008f,0xcaca00ca,0x3f3f003f,0x0f0f000f,0x02020002,
|
||||
0xc1c100c1,0xafaf00af,0xbdbd00bd,0x03030003,0x01010001,0x13130013,0x8a8a008a,0x6b6b006b,
|
||||
0x3a3a003a,0x91910091,0x11110011,0x41410041,0x4f4f004f,0x67670067,0xdcdc00dc,0xeaea00ea,
|
||||
0x97970097,0xf2f200f2,0xcfcf00cf,0xcece00ce,0xf0f000f0,0xb4b400b4,0xe6e600e6,0x73730073,
|
||||
0x96960096,0xacac00ac,0x74740074,0x22220022,0xe7e700e7,0xadad00ad,0x35350035,0x85850085,
|
||||
0xe2e200e2,0xf9f900f9,0x37370037,0xe8e800e8,0x1c1c001c,0x75750075,0xdfdf00df,0x6e6e006e,
|
||||
0x47470047,0xf1f100f1,0x1a1a001a,0x71710071,0x1d1d001d,0x29290029,0xc5c500c5,0x89890089,
|
||||
0x6f6f006f,0xb7b700b7,0x62620062,0x0e0e000e,0xaaaa00aa,0x18180018,0xbebe00be,0x1b1b001b,
|
||||
0xfcfc00fc,0x56560056,0x3e3e003e,0x4b4b004b,0xc6c600c6,0xd2d200d2,0x79790079,0x20200020,
|
||||
0x9a9a009a,0xdbdb00db,0xc0c000c0,0xfefe00fe,0x78780078,0xcdcd00cd,0x5a5a005a,0xf4f400f4,
|
||||
0x1f1f001f,0xdddd00dd,0xa8a800a8,0x33330033,0x88880088,0x07070007,0xc7c700c7,0x31310031,
|
||||
0xb1b100b1,0x12120012,0x10100010,0x59590059,0x27270027,0x80800080,0xecec00ec,0x5f5f005f,
|
||||
0x60600060,0x51510051,0x7f7f007f,0xa9a900a9,0x19190019,0xb5b500b5,0x4a4a004a,0x0d0d000d,
|
||||
0x2d2d002d,0xe5e500e5,0x7a7a007a,0x9f9f009f,0x93930093,0xc9c900c9,0x9c9c009c,0xefef00ef,
|
||||
0xa0a000a0,0xe0e000e0,0x3b3b003b,0x4d4d004d,0xaeae00ae,0x2a2a002a,0xf5f500f5,0xb0b000b0,
|
||||
0xc8c800c8,0xebeb00eb,0xbbbb00bb,0x3c3c003c,0x83830083,0x53530053,0x99990099,0x61610061,
|
||||
0x17170017,0x2b2b002b,0x04040004,0x7e7e007e,0xbaba00ba,0x77770077,0xd6d600d6,0x26260026,
|
||||
0xe1e100e1,0x69690069,0x14140014,0x63630063,0x55550055,0x21210021,0x0c0c000c,0x7d7d007d
|
||||
};
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(16)
|
||||
CRYPTOPP_TABLE
|
||||
const word32 X2[256]={
|
||||
0x30303000,0x68686800,0x99999900,0x1b1b1b00,0x87878700,0xb9b9b900,0x21212100,0x78787800,
|
||||
0x50505000,0x39393900,0xdbdbdb00,0xe1e1e100,0x72727200,0x09090900,0x62626200,0x3c3c3c00,
|
||||
0x3e3e3e00,0x7e7e7e00,0x5e5e5e00,0x8e8e8e00,0xf1f1f100,0xa0a0a000,0xcccccc00,0xa3a3a300,
|
||||
0x2a2a2a00,0x1d1d1d00,0xfbfbfb00,0xb6b6b600,0xd6d6d600,0x20202000,0xc4c4c400,0x8d8d8d00,
|
||||
0x81818100,0x65656500,0xf5f5f500,0x89898900,0xcbcbcb00,0x9d9d9d00,0x77777700,0xc6c6c600,
|
||||
0x57575700,0x43434300,0x56565600,0x17171700,0xd4d4d400,0x40404000,0x1a1a1a00,0x4d4d4d00,
|
||||
0xc0c0c000,0x63636300,0x6c6c6c00,0xe3e3e300,0xb7b7b700,0xc8c8c800,0x64646400,0x6a6a6a00,
|
||||
0x53535300,0xaaaaaa00,0x38383800,0x98989800,0x0c0c0c00,0xf4f4f400,0x9b9b9b00,0xededed00,
|
||||
0x7f7f7f00,0x22222200,0x76767600,0xafafaf00,0xdddddd00,0x3a3a3a00,0x0b0b0b00,0x58585800,
|
||||
0x67676700,0x88888800,0x06060600,0xc3c3c300,0x35353500,0x0d0d0d00,0x01010100,0x8b8b8b00,
|
||||
0x8c8c8c00,0xc2c2c200,0xe6e6e600,0x5f5f5f00,0x02020200,0x24242400,0x75757500,0x93939300,
|
||||
0x66666600,0x1e1e1e00,0xe5e5e500,0xe2e2e200,0x54545400,0xd8d8d800,0x10101000,0xcecece00,
|
||||
0x7a7a7a00,0xe8e8e800,0x08080800,0x2c2c2c00,0x12121200,0x97979700,0x32323200,0xababab00,
|
||||
0xb4b4b400,0x27272700,0x0a0a0a00,0x23232300,0xdfdfdf00,0xefefef00,0xcacaca00,0xd9d9d900,
|
||||
0xb8b8b800,0xfafafa00,0xdcdcdc00,0x31313100,0x6b6b6b00,0xd1d1d100,0xadadad00,0x19191900,
|
||||
0x49494900,0xbdbdbd00,0x51515100,0x96969600,0xeeeeee00,0xe4e4e400,0xa8a8a800,0x41414100,
|
||||
0xdadada00,0xffffff00,0xcdcdcd00,0x55555500,0x86868600,0x36363600,0xbebebe00,0x61616100,
|
||||
0x52525200,0xf8f8f800,0xbbbbbb00,0x0e0e0e00,0x82828200,0x48484800,0x69696900,0x9a9a9a00,
|
||||
0xe0e0e000,0x47474700,0x9e9e9e00,0x5c5c5c00,0x04040400,0x4b4b4b00,0x34343400,0x15151500,
|
||||
0x79797900,0x26262600,0xa7a7a700,0xdedede00,0x29292900,0xaeaeae00,0x92929200,0xd7d7d700,
|
||||
0x84848400,0xe9e9e900,0xd2d2d200,0xbababa00,0x5d5d5d00,0xf3f3f300,0xc5c5c500,0xb0b0b000,
|
||||
0xbfbfbf00,0xa4a4a400,0x3b3b3b00,0x71717100,0x44444400,0x46464600,0x2b2b2b00,0xfcfcfc00,
|
||||
0xebebeb00,0x6f6f6f00,0xd5d5d500,0xf6f6f600,0x14141400,0xfefefe00,0x7c7c7c00,0x70707000,
|
||||
0x5a5a5a00,0x7d7d7d00,0xfdfdfd00,0x2f2f2f00,0x18181800,0x83838300,0x16161600,0xa5a5a500,
|
||||
0x91919100,0x1f1f1f00,0x05050500,0x95959500,0x74747400,0xa9a9a900,0xc1c1c100,0x5b5b5b00,
|
||||
0x4a4a4a00,0x85858500,0x6d6d6d00,0x13131300,0x07070700,0x4f4f4f00,0x4e4e4e00,0x45454500,
|
||||
0xb2b2b200,0x0f0f0f00,0xc9c9c900,0x1c1c1c00,0xa6a6a600,0xbcbcbc00,0xececec00,0x73737300,
|
||||
0x90909000,0x7b7b7b00,0xcfcfcf00,0x59595900,0x8f8f8f00,0xa1a1a100,0xf9f9f900,0x2d2d2d00,
|
||||
0xf2f2f200,0xb1b1b100,0x00000000,0x94949400,0x37373700,0x9f9f9f00,0xd0d0d000,0x2e2e2e00,
|
||||
0x9c9c9c00,0x6e6e6e00,0x28282800,0x3f3f3f00,0x80808000,0xf0f0f000,0x3d3d3d00,0xd3d3d300,
|
||||
0x25252500,0x8a8a8a00,0xb5b5b500,0xe7e7e700,0x42424200,0xb3b3b300,0xc7c7c700,0xeaeaea00,
|
||||
0xf7f7f700,0x4c4c4c00,0x11111100,0x33333300,0x03030300,0xa2a2a200,0xacacac00,0x60606000
|
||||
};
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(16)
|
||||
CRYPTOPP_TABLE
|
||||
const word32 KRK[3][4] = {
|
||||
{0x517cc1b7, 0x27220a94, 0xfe13abe8, 0xfa9a6ee0},
|
||||
{0x6db14acc, 0x9e21c820, 0xff28b1d5, 0xef5de2b0},
|
||||
{0xdb92371d, 0x2126e970, 0x03249775, 0x04e8c90e}
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
NAMESPACE_END
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
// arm_simd.h - written and placed in public domain by Jeffrey Walton
|
||||
|
||||
/// \file arm_simd.h
|
||||
/// \brief Support functions for ARM and vector operations
|
||||
|
||||
#ifndef CRYPTOPP_ARM_SIMD_H
|
||||
#define CRYPTOPP_ARM_SIMD_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if (CRYPTOPP_ARM_NEON_HEADER)
|
||||
# include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ARM_ACLE_HEADER)
|
||||
# include <stdint.h>
|
||||
# include <arm_acle.h>
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ARM_PMULL_AVAILABLE) || defined(CRYPTOPP_DOXYGEN_PROCESSING)
|
||||
|
||||
/// \brief Polynomial multiplication
|
||||
/// \param a the first term
|
||||
/// \param b the second term
|
||||
/// \return vector product
|
||||
/// \details PMULL_00() performs polynomial multiplication and presents
|
||||
/// the result like Intel's <tt>c = _mm_clmulepi64_si128(a, b, 0x00)</tt>.
|
||||
/// The <tt>0x00</tt> indicates the low 64-bits of <tt>a</tt> and <tt>b</tt>
|
||||
/// are multiplied.
|
||||
/// \note An Intel XMM register is composed of 128-bits. The leftmost bit
|
||||
/// is MSB and numbered 127, while the the rightmost bit is LSB and
|
||||
/// numbered 0.
|
||||
/// \since Crypto++ 8.0
|
||||
inline uint64x2_t PMULL_00(const uint64x2_t a, const uint64x2_t b)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
const __n64 x = { vgetq_lane_u64(a, 0) };
|
||||
const __n64 y = { vgetq_lane_u64(b, 0) };
|
||||
return vmull_p64(x, y);
|
||||
#elif defined(__GNUC__)
|
||||
uint64x2_t r;
|
||||
__asm __volatile("pmull %0.1q, %1.1d, %2.1d \n\t"
|
||||
:"=w" (r) : "w" (a), "w" (b) );
|
||||
return r;
|
||||
#else
|
||||
return (uint64x2_t)(vmull_p64(
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(a),0),
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(b),0)));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Polynomial multiplication
|
||||
/// \param a the first term
|
||||
/// \param b the second term
|
||||
/// \return vector product
|
||||
/// \details PMULL_01 performs() polynomial multiplication and presents
|
||||
/// the result like Intel's <tt>c = _mm_clmulepi64_si128(a, b, 0x01)</tt>.
|
||||
/// The <tt>0x01</tt> indicates the low 64-bits of <tt>a</tt> and high
|
||||
/// 64-bits of <tt>b</tt> are multiplied.
|
||||
/// \note An Intel XMM register is composed of 128-bits. The leftmost bit
|
||||
/// is MSB and numbered 127, while the the rightmost bit is LSB and
|
||||
/// numbered 0.
|
||||
/// \since Crypto++ 8.0
|
||||
inline uint64x2_t PMULL_01(const uint64x2_t a, const uint64x2_t b)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
const __n64 x = { vgetq_lane_u64(a, 0) };
|
||||
const __n64 y = { vgetq_lane_u64(b, 1) };
|
||||
return vmull_p64(x, y);
|
||||
#elif defined(__GNUC__)
|
||||
uint64x2_t r;
|
||||
__asm __volatile("pmull %0.1q, %1.1d, %2.1d \n\t"
|
||||
:"=w" (r) : "w" (a), "w" (vget_high_u64(b)) );
|
||||
return r;
|
||||
#else
|
||||
return (uint64x2_t)(vmull_p64(
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(a),0),
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(b),1)));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Polynomial multiplication
|
||||
/// \param a the first term
|
||||
/// \param b the second term
|
||||
/// \return vector product
|
||||
/// \details PMULL_10() performs polynomial multiplication and presents
|
||||
/// the result like Intel's <tt>c = _mm_clmulepi64_si128(a, b, 0x10)</tt>.
|
||||
/// The <tt>0x10</tt> indicates the high 64-bits of <tt>a</tt> and low
|
||||
/// 64-bits of <tt>b</tt> are multiplied.
|
||||
/// \note An Intel XMM register is composed of 128-bits. The leftmost bit
|
||||
/// is MSB and numbered 127, while the the rightmost bit is LSB and
|
||||
/// numbered 0.
|
||||
/// \since Crypto++ 8.0
|
||||
inline uint64x2_t PMULL_10(const uint64x2_t a, const uint64x2_t b)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
const __n64 x = { vgetq_lane_u64(a, 1) };
|
||||
const __n64 y = { vgetq_lane_u64(b, 0) };
|
||||
return vmull_p64(x, y);
|
||||
#elif defined(__GNUC__)
|
||||
uint64x2_t r;
|
||||
__asm __volatile("pmull %0.1q, %1.1d, %2.1d \n\t"
|
||||
:"=w" (r) : "w" (vget_high_u64(a)), "w" (b) );
|
||||
return r;
|
||||
#else
|
||||
return (uint64x2_t)(vmull_p64(
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(a),1),
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(b),0)));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Polynomial multiplication
|
||||
/// \param a the first term
|
||||
/// \param b the second term
|
||||
/// \return vector product
|
||||
/// \details PMULL_11() performs polynomial multiplication and presents
|
||||
/// the result like Intel's <tt>c = _mm_clmulepi64_si128(a, b, 0x11)</tt>.
|
||||
/// The <tt>0x11</tt> indicates the high 64-bits of <tt>a</tt> and <tt>b</tt>
|
||||
/// are multiplied.
|
||||
/// \note An Intel XMM register is composed of 128-bits. The leftmost bit
|
||||
/// is MSB and numbered 127, while the the rightmost bit is LSB and
|
||||
/// numbered 0.
|
||||
/// \since Crypto++ 8.0
|
||||
inline uint64x2_t PMULL_11(const uint64x2_t a, const uint64x2_t b)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
const __n64 x = { vgetq_lane_u64(a, 1) };
|
||||
const __n64 y = { vgetq_lane_u64(b, 1) };
|
||||
return vmull_p64(x, y);
|
||||
#elif defined(__GNUC__)
|
||||
uint64x2_t r;
|
||||
__asm __volatile("pmull2 %0.1q, %1.2d, %2.2d \n\t"
|
||||
:"=w" (r) : "w" (a), "w" (b) );
|
||||
return r;
|
||||
#else
|
||||
return (uint64x2_t)(vmull_p64(
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(a),1),
|
||||
vgetq_lane_u64(vreinterpretq_u64_u8(b),1)));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Vector extraction
|
||||
/// \param a the first term
|
||||
/// \param b the second term
|
||||
/// \param c the byte count
|
||||
/// \return vector
|
||||
/// \details VEXT_U8() extracts the first <tt>c</tt> bytes of vector
|
||||
/// <tt>a</tt> and the remaining bytes in <tt>b</tt>.
|
||||
/// \since Crypto++ 8.0
|
||||
inline uint64x2_t VEXT_U8(uint64x2_t a, uint64x2_t b, unsigned int c)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
return (uint64x2_t)vextq_u8(
|
||||
vreinterpretq_u8_u64(a), vreinterpretq_u8_u64(b), c);
|
||||
#else
|
||||
uint64x2_t r;
|
||||
__asm __volatile("ext %0.16b, %1.16b, %2.16b, %3 \n\t"
|
||||
:"=w" (r) : "w" (a), "w" (b), "I" (c) );
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Vector extraction
|
||||
/// \tparam C the byte count
|
||||
/// \param a the first term
|
||||
/// \param b the second term
|
||||
/// \return vector
|
||||
/// \details VEXT_U8() extracts the first <tt>C</tt> bytes of vector
|
||||
/// <tt>a</tt> and the remaining bytes in <tt>b</tt>.
|
||||
/// \since Crypto++ 8.0
|
||||
template <unsigned int C>
|
||||
inline uint64x2_t VEXT_U8(uint64x2_t a, uint64x2_t b)
|
||||
{
|
||||
// https://github.com/weidai11/cryptopp/issues/366
|
||||
#if defined(_MSC_VER)
|
||||
return (uint64x2_t)vextq_u8(
|
||||
vreinterpretq_u8_u64(a), vreinterpretq_u8_u64(b), C);
|
||||
#else
|
||||
uint64x2_t r;
|
||||
__asm __volatile("ext %0.16b, %1.16b, %2.16b, %3 \n\t"
|
||||
:"=w" (r) : "w" (a), "w" (b), "I" (C) );
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // CRYPTOPP_ARM_PMULL_AVAILABLE
|
||||
|
||||
#endif // CRYPTOPP_ARM_SIMD_H
|
||||
+717
@@ -0,0 +1,717 @@
|
||||
// asn.cpp - originally written and placed in the public domain by Wei Dai
|
||||
// CryptoPP::Test namespace added by JW in February 2017
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "asn.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
size_t DERLengthEncode(BufferedTransformation &bt, lword length)
|
||||
{
|
||||
size_t i=0;
|
||||
if (length <= 0x7f)
|
||||
{
|
||||
bt.Put(byte(length));
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
bt.Put(byte(BytePrecision(length) | 0x80));
|
||||
i++;
|
||||
for (int j=BytePrecision(length); j; --j)
|
||||
{
|
||||
bt.Put(byte(length >> (j-1)*8));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
bool BERLengthDecode(BufferedTransformation &bt, lword &length, bool &definiteLength)
|
||||
{
|
||||
byte b;
|
||||
|
||||
if (!bt.Get(b))
|
||||
return false;
|
||||
|
||||
if (!(b & 0x80))
|
||||
{
|
||||
definiteLength = true;
|
||||
length = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int lengthBytes = b & 0x7f;
|
||||
|
||||
if (lengthBytes == 0)
|
||||
{
|
||||
definiteLength = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
definiteLength = true;
|
||||
length = 0;
|
||||
while (lengthBytes--)
|
||||
{
|
||||
if (length >> (8*(sizeof(length)-1)))
|
||||
BERDecodeError(); // length about to overflow
|
||||
|
||||
if (!bt.Get(b))
|
||||
return false;
|
||||
|
||||
length = (length << 8) | b;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BERLengthDecode(BufferedTransformation &bt, size_t &length)
|
||||
{
|
||||
lword lw = 0;
|
||||
bool definiteLength = false;
|
||||
if (!BERLengthDecode(bt, lw, definiteLength))
|
||||
BERDecodeError();
|
||||
if (!SafeConvert(lw, length))
|
||||
BERDecodeError();
|
||||
return definiteLength;
|
||||
}
|
||||
|
||||
void DEREncodeNull(BufferedTransformation &out)
|
||||
{
|
||||
out.Put(TAG_NULL);
|
||||
out.Put(0);
|
||||
}
|
||||
|
||||
void BERDecodeNull(BufferedTransformation &in)
|
||||
{
|
||||
byte b;
|
||||
if (!in.Get(b) || b != TAG_NULL)
|
||||
BERDecodeError();
|
||||
size_t length;
|
||||
if (!BERLengthDecode(in, length) || length != 0)
|
||||
BERDecodeError();
|
||||
}
|
||||
|
||||
/// ASN Strings
|
||||
size_t DEREncodeOctetString(BufferedTransformation &bt, const byte *str, size_t strLen)
|
||||
{
|
||||
bt.Put(OCTET_STRING);
|
||||
size_t lengthBytes = DERLengthEncode(bt, strLen);
|
||||
bt.Put(str, strLen);
|
||||
return 1+lengthBytes+strLen;
|
||||
}
|
||||
|
||||
size_t DEREncodeOctetString(BufferedTransformation &bt, const SecByteBlock &str)
|
||||
{
|
||||
return DEREncodeOctetString(bt, ConstBytePtr(str), BytePtrSize(str));
|
||||
}
|
||||
|
||||
size_t BERDecodeOctetString(BufferedTransformation &bt, SecByteBlock &str)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != OCTET_STRING)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
if (!BERLengthDecode(bt, bc))
|
||||
BERDecodeError();
|
||||
if (bc > bt.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
|
||||
str.New(bc);
|
||||
if (bc != bt.Get(BytePtr(str), bc))
|
||||
BERDecodeError();
|
||||
return bc;
|
||||
}
|
||||
|
||||
size_t BERDecodeOctetString(BufferedTransformation &bt, BufferedTransformation &str)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != OCTET_STRING)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
if (!BERLengthDecode(bt, bc))
|
||||
BERDecodeError();
|
||||
if (bc > bt.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
|
||||
bt.TransferTo(str, bc);
|
||||
return bc;
|
||||
}
|
||||
|
||||
size_t DEREncodeTextString(BufferedTransformation &bt, const byte* str, size_t strLen, byte asnTag)
|
||||
{
|
||||
bt.Put(asnTag);
|
||||
size_t lengthBytes = DERLengthEncode(bt, strLen);
|
||||
bt.Put(str, strLen);
|
||||
return 1+lengthBytes+strLen;
|
||||
}
|
||||
|
||||
size_t DEREncodeTextString(BufferedTransformation &bt, const SecByteBlock &str, byte asnTag)
|
||||
{
|
||||
return DEREncodeTextString(bt, ConstBytePtr(str), BytePtrSize(str), asnTag);
|
||||
}
|
||||
|
||||
size_t DEREncodeTextString(BufferedTransformation &bt, const std::string &str, byte asnTag)
|
||||
{
|
||||
return DEREncodeTextString(bt, ConstBytePtr(str), BytePtrSize(str), asnTag);
|
||||
}
|
||||
|
||||
size_t BERDecodeTextString(BufferedTransformation &bt, SecByteBlock &str, byte asnTag)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != asnTag)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
if (!BERLengthDecode(bt, bc))
|
||||
BERDecodeError();
|
||||
if (bc > bt.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
|
||||
str.resize(bc);
|
||||
if (bc != bt.Get(BytePtr(str), BytePtrSize(str)))
|
||||
BERDecodeError();
|
||||
|
||||
return bc;
|
||||
}
|
||||
|
||||
size_t BERDecodeTextString(BufferedTransformation &bt, std::string &str, byte asnTag)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != asnTag)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
if (!BERLengthDecode(bt, bc))
|
||||
BERDecodeError();
|
||||
if (bc > bt.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
|
||||
str.resize(bc);
|
||||
if (bc != bt.Get(BytePtr(str), BytePtrSize(str)))
|
||||
BERDecodeError();
|
||||
|
||||
return bc;
|
||||
}
|
||||
|
||||
size_t DEREncodeDate(BufferedTransformation &bt, const SecByteBlock &str, byte asnTag)
|
||||
{
|
||||
bt.Put(asnTag);
|
||||
size_t lengthBytes = DERLengthEncode(bt, str.size());
|
||||
bt.Put(ConstBytePtr(str), BytePtrSize(str));
|
||||
return 1+lengthBytes+str.size();
|
||||
}
|
||||
|
||||
size_t BERDecodeDate(BufferedTransformation &bt, SecByteBlock &str, byte asnTag)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != asnTag)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
if (!BERLengthDecode(bt, bc))
|
||||
BERDecodeError();
|
||||
if (bc > bt.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
|
||||
str.resize(bc);
|
||||
if (bc != bt.Get(BytePtr(str), BytePtrSize(str)))
|
||||
BERDecodeError();
|
||||
|
||||
return bc;
|
||||
}
|
||||
|
||||
size_t DEREncodeBitString(BufferedTransformation &bt, const byte *str, size_t strLen, unsigned int unusedBits)
|
||||
{
|
||||
bt.Put(BIT_STRING);
|
||||
size_t lengthBytes = DERLengthEncode(bt, strLen+1);
|
||||
bt.Put((byte)unusedBits);
|
||||
bt.Put(str, strLen);
|
||||
return 2+lengthBytes+strLen;
|
||||
}
|
||||
|
||||
size_t BERDecodeBitString(BufferedTransformation &bt, SecByteBlock &str, unsigned int &unusedBits)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != BIT_STRING)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
if (!BERLengthDecode(bt, bc))
|
||||
BERDecodeError();
|
||||
if (bc == 0)
|
||||
BERDecodeError();
|
||||
if (bc > bt.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
|
||||
// X.690, 8.6.2.2: "The number [of unused bits] shall be in the range zero to seven"
|
||||
byte unused;
|
||||
if (!bt.Get(unused) || unused > 7)
|
||||
BERDecodeError();
|
||||
unusedBits = unused;
|
||||
str.resize(bc-1);
|
||||
if ((bc-1) != bt.Get(BytePtr(str), bc-1))
|
||||
BERDecodeError();
|
||||
return bc-1;
|
||||
}
|
||||
|
||||
void DERReencode(BufferedTransformation &source, BufferedTransformation &dest)
|
||||
{
|
||||
byte tag;
|
||||
source.Peek(tag);
|
||||
BERGeneralDecoder decoder(source, tag);
|
||||
DERGeneralEncoder encoder(dest, tag);
|
||||
if (decoder.IsDefiniteLength())
|
||||
decoder.TransferTo(encoder, decoder.RemainingLength());
|
||||
else
|
||||
{
|
||||
while (!decoder.EndReached())
|
||||
DERReencode(decoder, encoder);
|
||||
}
|
||||
decoder.MessageEnd();
|
||||
encoder.MessageEnd();
|
||||
}
|
||||
|
||||
size_t BERDecodePeekLength(const BufferedTransformation &bt)
|
||||
{
|
||||
lword count = (std::min)(bt.MaxRetrievable(), static_cast<lword>(16));
|
||||
if (count == 0) return 0;
|
||||
|
||||
ByteQueue tagAndLength;
|
||||
bt.CopyTo(tagAndLength, count);
|
||||
|
||||
// Skip tag
|
||||
tagAndLength.Skip(1);
|
||||
|
||||
// BERLengthDecode fails for indefinite length.
|
||||
size_t length;
|
||||
if (!BERLengthDecode(tagAndLength, length))
|
||||
return 0;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void OID::EncodeValue(BufferedTransformation &bt, word32 v)
|
||||
{
|
||||
for (unsigned int i=RoundUpToMultipleOf(STDMAX(7U,BitPrecision(v)), 7U)-7; i != 0; i-=7)
|
||||
bt.Put((byte)(0x80 | ((v >> i) & 0x7f)));
|
||||
bt.Put((byte)(v & 0x7f));
|
||||
}
|
||||
|
||||
size_t OID::DecodeValue(BufferedTransformation &bt, word32 &v)
|
||||
{
|
||||
byte b;
|
||||
size_t i=0;
|
||||
v = 0;
|
||||
while (true)
|
||||
{
|
||||
if (!bt.Get(b))
|
||||
BERDecodeError();
|
||||
i++;
|
||||
if (v >> (8*sizeof(v)-7)) // v about to overflow
|
||||
BERDecodeError();
|
||||
v <<= 7;
|
||||
v += b & 0x7f;
|
||||
if (!(b & 0x80))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
void OID::DEREncode(BufferedTransformation &bt) const
|
||||
{
|
||||
CRYPTOPP_ASSERT(m_values.size() >= 2);
|
||||
ByteQueue temp;
|
||||
temp.Put(byte(m_values[0] * 40 + m_values[1]));
|
||||
for (size_t i=2; i<m_values.size(); i++)
|
||||
EncodeValue(temp, m_values[i]);
|
||||
bt.Put(OBJECT_IDENTIFIER);
|
||||
DERLengthEncode(bt, temp.CurrentSize());
|
||||
temp.TransferTo(bt);
|
||||
}
|
||||
|
||||
void OID::BERDecode(BufferedTransformation &bt)
|
||||
{
|
||||
byte b;
|
||||
if (!bt.Get(b) || b != OBJECT_IDENTIFIER)
|
||||
BERDecodeError();
|
||||
|
||||
size_t length;
|
||||
if (!BERLengthDecode(bt, length) || length < 1)
|
||||
BERDecodeError();
|
||||
|
||||
if (!bt.Get(b))
|
||||
BERDecodeError();
|
||||
|
||||
length--;
|
||||
m_values.resize(2);
|
||||
m_values[0] = b / 40;
|
||||
m_values[1] = b % 40;
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
word32 v;
|
||||
size_t valueLen = DecodeValue(bt, v);
|
||||
if (valueLen > length)
|
||||
BERDecodeError();
|
||||
m_values.push_back(v);
|
||||
length -= valueLen;
|
||||
}
|
||||
}
|
||||
|
||||
void OID::BERDecodeAndCheck(BufferedTransformation &bt) const
|
||||
{
|
||||
OID oid(bt);
|
||||
if (*this != oid)
|
||||
BERDecodeError();
|
||||
}
|
||||
|
||||
std::ostream& OID::Print(std::ostream& out) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < m_values.size(); ++i)
|
||||
{
|
||||
oss << m_values[i];
|
||||
if (i+1 < m_values.size())
|
||||
oss << ".";
|
||||
}
|
||||
return out << oss.str();
|
||||
}
|
||||
|
||||
inline BufferedTransformation & EncodedObjectFilter::CurrentTarget()
|
||||
{
|
||||
if (m_flags & PUT_OBJECTS)
|
||||
return *AttachedTransformation();
|
||||
else
|
||||
return TheBitBucket();
|
||||
}
|
||||
|
||||
void EncodedObjectFilter::Put(const byte *inString, size_t length)
|
||||
{
|
||||
if (m_nCurrentObject == m_nObjects)
|
||||
{
|
||||
AttachedTransformation()->Put(inString, length);
|
||||
return;
|
||||
}
|
||||
|
||||
LazyPutter lazyPutter(m_queue, inString, length);
|
||||
|
||||
while (m_queue.AnyRetrievable())
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case IDENTIFIER:
|
||||
if (!m_queue.Get(m_id))
|
||||
return;
|
||||
m_queue.TransferTo(CurrentTarget(), 1);
|
||||
m_state = LENGTH;
|
||||
// fall through
|
||||
case LENGTH:
|
||||
{
|
||||
byte b;
|
||||
if (m_level > 0 && m_id == 0 && m_queue.Peek(b) && b == 0)
|
||||
{
|
||||
m_queue.TransferTo(CurrentTarget(), 1);
|
||||
m_level--;
|
||||
m_state = IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
ByteQueue::Walker walker(m_queue);
|
||||
bool definiteLength = false;
|
||||
if (!BERLengthDecode(walker, m_lengthRemaining, definiteLength))
|
||||
return;
|
||||
m_queue.TransferTo(CurrentTarget(), walker.GetCurrentPosition());
|
||||
if (!((m_id & CONSTRUCTED) || definiteLength))
|
||||
BERDecodeError();
|
||||
if (!definiteLength)
|
||||
{
|
||||
if (!(m_id & CONSTRUCTED))
|
||||
BERDecodeError();
|
||||
m_level++;
|
||||
m_state = IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
m_state = BODY;
|
||||
}
|
||||
// fall through
|
||||
case BODY:
|
||||
m_lengthRemaining -= m_queue.TransferTo(CurrentTarget(), m_lengthRemaining);
|
||||
|
||||
if (m_lengthRemaining == 0)
|
||||
m_state = IDENTIFIER;
|
||||
// fall through
|
||||
case TAIL:
|
||||
case ALL_DONE:
|
||||
default: ;
|
||||
}
|
||||
|
||||
if (m_state == IDENTIFIER && m_level == 0)
|
||||
{
|
||||
// just finished processing a level 0 object
|
||||
++m_nCurrentObject;
|
||||
|
||||
if (m_flags & PUT_MESSANGE_END_AFTER_EACH_OBJECT)
|
||||
AttachedTransformation()->MessageEnd();
|
||||
|
||||
if (m_nCurrentObject == m_nObjects)
|
||||
{
|
||||
if (m_flags & PUT_MESSANGE_END_AFTER_ALL_OBJECTS)
|
||||
AttachedTransformation()->MessageEnd();
|
||||
|
||||
if (m_flags & PUT_MESSANGE_SERIES_END_AFTER_ALL_OBJECTS)
|
||||
AttachedTransformation()->MessageSeriesEnd();
|
||||
|
||||
m_queue.TransferAllTo(*AttachedTransformation());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BERGeneralDecoder::BERGeneralDecoder(BufferedTransformation &inQueue)
|
||||
: m_inQueue(inQueue), m_length(0), m_finished(false)
|
||||
{
|
||||
Init(DefaultTag);
|
||||
}
|
||||
|
||||
BERGeneralDecoder::BERGeneralDecoder(BufferedTransformation &inQueue, byte asnTag)
|
||||
: m_inQueue(inQueue), m_length(0), m_finished(false)
|
||||
{
|
||||
Init(asnTag);
|
||||
}
|
||||
|
||||
BERGeneralDecoder::BERGeneralDecoder(BERGeneralDecoder &inQueue, byte asnTag)
|
||||
: m_inQueue(inQueue), m_length(0), m_finished(false)
|
||||
{
|
||||
Init(asnTag);
|
||||
}
|
||||
|
||||
void BERGeneralDecoder::Init(byte asnTag)
|
||||
{
|
||||
byte b;
|
||||
if (!m_inQueue.Get(b) || b != asnTag)
|
||||
BERDecodeError();
|
||||
|
||||
if (!BERLengthDecode(m_inQueue, m_length, m_definiteLength))
|
||||
BERDecodeError();
|
||||
|
||||
if (!m_definiteLength && !(asnTag & CONSTRUCTED))
|
||||
BERDecodeError(); // cannot be primitive and have indefinite length
|
||||
}
|
||||
|
||||
BERGeneralDecoder::~BERGeneralDecoder()
|
||||
{
|
||||
try // avoid throwing in destructor
|
||||
{
|
||||
if (!m_finished)
|
||||
MessageEnd();
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
// CRYPTOPP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool BERGeneralDecoder::EndReached() const
|
||||
{
|
||||
if (m_definiteLength)
|
||||
return m_length == 0;
|
||||
else
|
||||
{ // check end-of-content octets
|
||||
word16 i;
|
||||
return (m_inQueue.PeekWord16(i)==2 && i==0);
|
||||
}
|
||||
}
|
||||
|
||||
byte BERGeneralDecoder::PeekByte() const
|
||||
{
|
||||
byte b;
|
||||
if (!Peek(b))
|
||||
BERDecodeError();
|
||||
return b;
|
||||
}
|
||||
|
||||
void BERGeneralDecoder::CheckByte(byte check)
|
||||
{
|
||||
byte b;
|
||||
if (!Get(b) || b != check)
|
||||
BERDecodeError();
|
||||
}
|
||||
|
||||
void BERGeneralDecoder::MessageEnd()
|
||||
{
|
||||
m_finished = true;
|
||||
if (m_definiteLength)
|
||||
{
|
||||
if (m_length != 0)
|
||||
BERDecodeError();
|
||||
}
|
||||
else
|
||||
{ // remove end-of-content octets
|
||||
word16 i;
|
||||
if (m_inQueue.GetWord16(i) != 2 || i != 0)
|
||||
BERDecodeError();
|
||||
}
|
||||
}
|
||||
|
||||
size_t BERGeneralDecoder::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking)
|
||||
{
|
||||
if (m_definiteLength && transferBytes > m_length)
|
||||
transferBytes = m_length;
|
||||
size_t blockedBytes = m_inQueue.TransferTo2(target, transferBytes, channel, blocking);
|
||||
ReduceLength(transferBytes);
|
||||
return blockedBytes;
|
||||
}
|
||||
|
||||
size_t BERGeneralDecoder::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const
|
||||
{
|
||||
if (m_definiteLength)
|
||||
end = STDMIN(m_length, end);
|
||||
return m_inQueue.CopyRangeTo2(target, begin, end, channel, blocking);
|
||||
}
|
||||
|
||||
lword BERGeneralDecoder::ReduceLength(lword delta)
|
||||
{
|
||||
if (m_definiteLength)
|
||||
{
|
||||
if (m_length < delta)
|
||||
BERDecodeError();
|
||||
m_length -= delta;
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
DERGeneralEncoder::DERGeneralEncoder(BufferedTransformation &outQueue)
|
||||
: m_outQueue(outQueue), m_asnTag(DefaultTag), m_finished(false)
|
||||
{
|
||||
}
|
||||
|
||||
DERGeneralEncoder::DERGeneralEncoder(BufferedTransformation &outQueue, byte asnTag)
|
||||
: m_outQueue(outQueue), m_asnTag(asnTag), m_finished(false)
|
||||
{
|
||||
}
|
||||
|
||||
DERGeneralEncoder::DERGeneralEncoder(DERGeneralEncoder &outQueue, byte asnTag)
|
||||
: m_outQueue(outQueue), m_asnTag(asnTag), m_finished(false)
|
||||
{
|
||||
}
|
||||
|
||||
DERGeneralEncoder::~DERGeneralEncoder()
|
||||
{
|
||||
try // avoid throwing in constructor
|
||||
{
|
||||
if (!m_finished)
|
||||
MessageEnd();
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
CRYPTOPP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
void DERGeneralEncoder::MessageEnd()
|
||||
{
|
||||
m_finished = true;
|
||||
lword length = CurrentSize();
|
||||
m_outQueue.Put(m_asnTag);
|
||||
DERLengthEncode(m_outQueue, length);
|
||||
TransferTo(m_outQueue);
|
||||
}
|
||||
|
||||
// *************************************************************
|
||||
|
||||
void X509PublicKey::BERDecode(BufferedTransformation &bt)
|
||||
{
|
||||
BERSequenceDecoder subjectPublicKeyInfo(bt);
|
||||
BERSequenceDecoder algorithm(subjectPublicKeyInfo);
|
||||
GetAlgorithmID().BERDecodeAndCheck(algorithm);
|
||||
bool parametersPresent = algorithm.EndReached() ? false : BERDecodeAlgorithmParameters(algorithm);
|
||||
algorithm.MessageEnd();
|
||||
|
||||
BERGeneralDecoder subjectPublicKey(subjectPublicKeyInfo, BIT_STRING);
|
||||
subjectPublicKey.CheckByte(0); // unused bits
|
||||
BERDecodePublicKey(subjectPublicKey, parametersPresent, (size_t)subjectPublicKey.RemainingLength());
|
||||
subjectPublicKey.MessageEnd();
|
||||
subjectPublicKeyInfo.MessageEnd();
|
||||
}
|
||||
|
||||
void X509PublicKey::DEREncode(BufferedTransformation &bt) const
|
||||
{
|
||||
DERSequenceEncoder subjectPublicKeyInfo(bt);
|
||||
|
||||
DERSequenceEncoder algorithm(subjectPublicKeyInfo);
|
||||
GetAlgorithmID().DEREncode(algorithm);
|
||||
DEREncodeAlgorithmParameters(algorithm);
|
||||
algorithm.MessageEnd();
|
||||
|
||||
DERGeneralEncoder subjectPublicKey(subjectPublicKeyInfo, BIT_STRING);
|
||||
subjectPublicKey.Put(0); // unused bits
|
||||
DEREncodePublicKey(subjectPublicKey);
|
||||
subjectPublicKey.MessageEnd();
|
||||
|
||||
subjectPublicKeyInfo.MessageEnd();
|
||||
}
|
||||
|
||||
void PKCS8PrivateKey::BERDecode(BufferedTransformation &bt)
|
||||
{
|
||||
BERSequenceDecoder privateKeyInfo(bt);
|
||||
word32 version;
|
||||
BERDecodeUnsigned<word32>(privateKeyInfo, version, INTEGER, 0, 0); // check version
|
||||
|
||||
BERSequenceDecoder algorithm(privateKeyInfo);
|
||||
GetAlgorithmID().BERDecodeAndCheck(algorithm);
|
||||
bool parametersPresent = algorithm.EndReached() ? false : BERDecodeAlgorithmParameters(algorithm);
|
||||
algorithm.MessageEnd();
|
||||
|
||||
BERGeneralDecoder octetString(privateKeyInfo, OCTET_STRING);
|
||||
BERDecodePrivateKey(octetString, parametersPresent, (size_t)privateKeyInfo.RemainingLength());
|
||||
octetString.MessageEnd();
|
||||
|
||||
if (!privateKeyInfo.EndReached())
|
||||
BERDecodeOptionalAttributes(privateKeyInfo);
|
||||
privateKeyInfo.MessageEnd();
|
||||
}
|
||||
|
||||
void PKCS8PrivateKey::DEREncode(BufferedTransformation &bt) const
|
||||
{
|
||||
DERSequenceEncoder privateKeyInfo(bt);
|
||||
DEREncodeUnsigned<word32>(privateKeyInfo, 0); // version
|
||||
|
||||
DERSequenceEncoder algorithm(privateKeyInfo);
|
||||
GetAlgorithmID().DEREncode(algorithm);
|
||||
DEREncodeAlgorithmParameters(algorithm);
|
||||
algorithm.MessageEnd();
|
||||
|
||||
DERGeneralEncoder octetString(privateKeyInfo, OCTET_STRING);
|
||||
DEREncodePrivateKey(octetString);
|
||||
octetString.MessageEnd();
|
||||
|
||||
DEREncodeOptionalAttributes(privateKeyInfo);
|
||||
privateKeyInfo.MessageEnd();
|
||||
}
|
||||
|
||||
void PKCS8PrivateKey::BERDecodeOptionalAttributes(BufferedTransformation &bt)
|
||||
{
|
||||
DERReencode(bt, m_optionalAttributes);
|
||||
}
|
||||
|
||||
void PKCS8PrivateKey::DEREncodeOptionalAttributes(BufferedTransformation &bt) const
|
||||
{
|
||||
m_optionalAttributes.CopyTo(bt);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
Vendored
+965
@@ -0,0 +1,965 @@
|
||||
// asn.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file asn.h
|
||||
/// \brief Classes and functions for working with ANS.1 objects
|
||||
|
||||
#ifndef CRYPTOPP_ASN_H
|
||||
#define CRYPTOPP_ASN_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "filters.h"
|
||||
#include "smartptr.h"
|
||||
#include "stdcpp.h"
|
||||
#include "queue.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
// Issue 340
|
||||
#if CRYPTOPP_GCC_DIAGNOSTIC_AVAILABLE
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief ASN.1 types
|
||||
/// \note These tags are not complete
|
||||
enum ASNTag
|
||||
{
|
||||
/// \brief ASN.1 Boolean
|
||||
BOOLEAN = 0x01,
|
||||
/// \brief ASN.1 Integer
|
||||
INTEGER = 0x02,
|
||||
/// \brief ASN.1 Bit string
|
||||
BIT_STRING = 0x03,
|
||||
/// \brief ASN.1 Octet string
|
||||
OCTET_STRING = 0x04,
|
||||
/// \brief ASN.1 Null
|
||||
TAG_NULL = 0x05,
|
||||
/// \brief ASN.1 Object identifier
|
||||
OBJECT_IDENTIFIER = 0x06,
|
||||
/// \brief ASN.1 Object descriptor
|
||||
OBJECT_DESCRIPTOR = 0x07,
|
||||
/// \brief ASN.1 External reference
|
||||
EXTERNAL = 0x08,
|
||||
/// \brief ASN.1 Real integer
|
||||
REAL = 0x09,
|
||||
/// \brief ASN.1 Enumerated value
|
||||
ENUMERATED = 0x0a,
|
||||
/// \brief ASN.1 UTF-8 string
|
||||
UTF8_STRING = 0x0c,
|
||||
/// \brief ASN.1 Sequence
|
||||
SEQUENCE = 0x10,
|
||||
/// \brief ASN.1 Set
|
||||
SET = 0x11,
|
||||
/// \brief ASN.1 Numeric string
|
||||
NUMERIC_STRING = 0x12,
|
||||
/// \brief ASN.1 Printable string
|
||||
PRINTABLE_STRING = 0x13,
|
||||
/// \brief ASN.1 T61 string
|
||||
T61_STRING = 0x14,
|
||||
/// \brief ASN.1 Videotext string
|
||||
VIDEOTEXT_STRING = 0x15,
|
||||
/// \brief ASN.1 IA5 string
|
||||
IA5_STRING = 0x16,
|
||||
/// \brief ASN.1 UTC time
|
||||
UTC_TIME = 0x17,
|
||||
/// \brief ASN.1 Generalized time
|
||||
GENERALIZED_TIME = 0x18,
|
||||
/// \brief ASN.1 Graphic string
|
||||
GRAPHIC_STRING = 0x19,
|
||||
/// \brief ASN.1 Visible string
|
||||
VISIBLE_STRING = 0x1a,
|
||||
/// \brief ASN.1 General string
|
||||
GENERAL_STRING = 0x1b,
|
||||
/// \brief ASN.1 Universal string
|
||||
UNIVERSAL_STRING = 0x1c,
|
||||
/// \brief ASN.1 BMP string
|
||||
BMP_STRING = 0x1e
|
||||
};
|
||||
|
||||
/// \brief ASN.1 flags
|
||||
/// \note These flags are not complete
|
||||
enum ASNIdFlag
|
||||
{
|
||||
/// \brief ASN.1 Universal class
|
||||
UNIVERSAL = 0x00,
|
||||
// DATA = 0x01,
|
||||
// HEADER = 0x02,
|
||||
/// \brief ASN.1 Primitive flag
|
||||
PRIMITIVE = 0x00,
|
||||
/// \brief ASN.1 Constructed flag
|
||||
CONSTRUCTED = 0x20,
|
||||
/// \brief ASN.1 Application class
|
||||
APPLICATION = 0x40,
|
||||
/// \brief ASN.1 Context specific class
|
||||
CONTEXT_SPECIFIC = 0x80,
|
||||
/// \brief ASN.1 Private class
|
||||
PRIVATE = 0xc0
|
||||
};
|
||||
|
||||
/// \brief Raises a BERDecodeErr
|
||||
inline void BERDecodeError() {throw BERDecodeErr();}
|
||||
|
||||
/// \brief Exception thrown when an unknown object identifier is encountered
|
||||
class CRYPTOPP_DLL UnknownOID : public BERDecodeErr
|
||||
{
|
||||
public:
|
||||
/// \brief Construct an UnknownOID
|
||||
UnknownOID() : BERDecodeErr("BER decode error: unknown object identifier") {}
|
||||
/// \brief Construct an UnknownOID
|
||||
/// \param err error message to use for the execption
|
||||
UnknownOID(const char *err) : BERDecodeErr(err) {}
|
||||
};
|
||||
|
||||
/// \brief DER encode a length
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param length the size to encode
|
||||
/// \return the number of octets used for the encoding
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DERLengthEncode(BufferedTransformation &bt, lword length);
|
||||
|
||||
/// \brief BER decode a length
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param length the decoded size
|
||||
/// \return true if the value was decoded
|
||||
/// \throw BERDecodeError if the value fails to decode or is too large for size_t
|
||||
/// \details BERLengthDecode() returns false if the encoding is indefinite length.
|
||||
CRYPTOPP_DLL bool CRYPTOPP_API BERLengthDecode(BufferedTransformation &bt, size_t &length);
|
||||
|
||||
/// \brief DER encode NULL
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
CRYPTOPP_DLL void CRYPTOPP_API DEREncodeNull(BufferedTransformation &bt);
|
||||
|
||||
/// \brief BER decode NULL
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
CRYPTOPP_DLL void CRYPTOPP_API BERDecodeNull(BufferedTransformation &bt);
|
||||
|
||||
/// \brief DER encode octet string
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param str the string to encode
|
||||
/// \param strLen the length of the string
|
||||
/// \return the number of octets used for the encoding
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeOctetString(BufferedTransformation &bt, const byte *str, size_t strLen);
|
||||
|
||||
/// \brief DER encode octet string
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the string to encode
|
||||
/// \return the number of octets used for the encoding
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeOctetString(BufferedTransformation &bt, const SecByteBlock &str);
|
||||
|
||||
/// \brief BER decode octet string
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the decoded string
|
||||
/// \return the number of octets used for the encoding
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeOctetString(BufferedTransformation &bt, SecByteBlock &str);
|
||||
|
||||
/// \brief BER decode octet string
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the decoded string
|
||||
/// \return the number of octets used for the encoding
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeOctetString(BufferedTransformation &bt, BufferedTransformation &str);
|
||||
|
||||
/// \brief DER encode text string
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param str the string to encode
|
||||
/// \param strLen the length of the string, in bytes
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \return the number of octets used for the encoding
|
||||
/// \details DEREncodeTextString() can be used for UTF8_STRING, PRINTABLE_STRING, and IA5_STRING
|
||||
/// \since Crypto++ 8.3
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeTextString(BufferedTransformation &bt, const byte* str, size_t strLen, byte asnTag);
|
||||
|
||||
/// \brief DER encode text string
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param str the string to encode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \return the number of octets used for the encoding
|
||||
/// \details DEREncodeTextString() can be used for UTF8_STRING, PRINTABLE_STRING, and IA5_STRING
|
||||
/// \since Crypto++ 8.3
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeTextString(BufferedTransformation &bt, const SecByteBlock &str, byte asnTag);
|
||||
|
||||
/// \brief DER encode text string
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param str the string to encode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \return the number of octets used for the encoding
|
||||
/// \details DEREncodeTextString() can be used for UTF8_STRING, PRINTABLE_STRING, and IA5_STRING
|
||||
/// \since Crypto++ 6.0
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeTextString(BufferedTransformation &bt, const std::string &str, byte asnTag);
|
||||
|
||||
/// \brief BER decode text string
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the string to decode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \details BERDecodeTextString() can be used for UTF8_STRING, PRINTABLE_STRING, and IA5_STRING
|
||||
/// \since Crypto++ 8.3
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeTextString(BufferedTransformation &bt, SecByteBlock &str, byte asnTag);
|
||||
|
||||
/// \brief BER decode text string
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the string to decode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \details BERDecodeTextString() can be used for UTF8_STRING, PRINTABLE_STRING, and IA5_STRING
|
||||
/// \since Crypto++ 6.0
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeTextString(BufferedTransformation &bt, std::string &str, byte asnTag);
|
||||
|
||||
/// \brief DER encode date
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param str the date to encode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \return the number of octets used for the encoding
|
||||
/// \details BERDecodeDate() can be used for UTC_TIME and GENERALIZED_TIME
|
||||
/// \since Crypto++ 8.3
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeDate(BufferedTransformation &bt, const SecByteBlock &str, byte asnTag);
|
||||
|
||||
/// \brief BER decode date
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the date to decode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \details BERDecodeDate() can be used for UTC_TIME and GENERALIZED_TIME
|
||||
/// \since Crypto++ 8.3
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeDate(BufferedTransformation &bt, SecByteBlock &str, byte asnTag);
|
||||
|
||||
/// \brief DER encode bit string
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param str the string to encode
|
||||
/// \param strLen the length of the string
|
||||
/// \param unusedBits the number of unused bits
|
||||
/// \return the number of octets used for the encoding
|
||||
/// \details The caller is responsible for shifting octets if unusedBits is
|
||||
/// not 0. For example, to DER encode a web server X.509 key usage, the 101b
|
||||
/// bit mask is often used (digitalSignature and keyEncipherment). In this
|
||||
/// case <tt>str</tt> is one octet with a value=0xa0 and unusedBits=5. The
|
||||
/// value 0xa0 is <tt>101b << 5</tt>.
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API DEREncodeBitString(BufferedTransformation &bt, const byte *str, size_t strLen, unsigned int unusedBits=0);
|
||||
|
||||
/// \brief DER decode bit string
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \param str the decoded string
|
||||
/// \param unusedBits the number of unused bits
|
||||
/// \details The caller is responsible for shifting octets if unusedBits is
|
||||
/// not 0. For example, to DER encode a web server X.509 key usage, the 101b
|
||||
/// bit mask is often used (digitalSignature and keyEncipherment). In this
|
||||
/// case <tt>str</tt> is one octet with a value=0xa0 and unusedBits=5. The
|
||||
/// value 0xa0 is <tt>101b << 5</tt>.
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodeBitString(BufferedTransformation &bt, SecByteBlock &str, unsigned int &unusedBits);
|
||||
|
||||
/// \brief BER decode and DER re-encode
|
||||
/// \param bt BufferedTransformation object for writing
|
||||
/// \param dest BufferedTransformation object
|
||||
CRYPTOPP_DLL void CRYPTOPP_API DERReencode(BufferedTransformation &bt, BufferedTransformation &dest);
|
||||
|
||||
/// \brief BER decode size
|
||||
/// \param bt BufferedTransformation object for reading
|
||||
/// \return the length of the ASN.1 value, in bytes
|
||||
/// \details BERDecodePeekLength() determines the length of a value without
|
||||
/// consuming octets in the stream. The stream must use definite length encoding.
|
||||
/// If indefinite length encoding is used or an error occurs, then 0 is returned.
|
||||
/// \since Crypto++ 8.3
|
||||
CRYPTOPP_DLL size_t CRYPTOPP_API BERDecodePeekLength(const BufferedTransformation &bt);
|
||||
|
||||
/// \brief Object Identifier
|
||||
class CRYPTOPP_DLL OID
|
||||
{
|
||||
public:
|
||||
virtual ~OID() {}
|
||||
|
||||
/// \brief Construct an OID
|
||||
OID() {}
|
||||
|
||||
/// \brief Construct an OID
|
||||
/// \param v value to initialize the OID
|
||||
OID(word32 v) : m_values(1, v) {}
|
||||
|
||||
/// \brief Construct an OID
|
||||
/// \param bt BufferedTransformation object
|
||||
OID(BufferedTransformation &bt) {
|
||||
BERDecode(bt);
|
||||
}
|
||||
|
||||
/// \brief Append a value to an OID
|
||||
/// \param rhs the value to append
|
||||
inline OID & operator+=(word32 rhs) {
|
||||
m_values.push_back(rhs); return *this;
|
||||
}
|
||||
|
||||
/// \brief DER encode this OID
|
||||
/// \param bt BufferedTransformation object
|
||||
void DEREncode(BufferedTransformation &bt) const;
|
||||
|
||||
/// \brief BER decode an OID
|
||||
/// \param bt BufferedTransformation object
|
||||
void BERDecode(BufferedTransformation &bt);
|
||||
|
||||
/// \brief BER decode an OID
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \throw BERDecodeErr() if decoded value doesn't match an expected OID
|
||||
/// \details BERDecodeAndCheck() can be used to parse an OID and verify it matches an expected.
|
||||
/// <pre>
|
||||
/// BERSequenceDecoder key(bt);
|
||||
/// ...
|
||||
/// BERSequenceDecoder algorithm(key);
|
||||
/// GetAlgorithmID().BERDecodeAndCheck(algorithm);
|
||||
/// </pre>
|
||||
void BERDecodeAndCheck(BufferedTransformation &bt) const;
|
||||
|
||||
/// \brief Determine if OID is empty
|
||||
/// \return true if OID has 0 elements, false otherwise
|
||||
/// \since Crypto++ 8.0
|
||||
bool Empty() const {
|
||||
return m_values.empty();
|
||||
}
|
||||
|
||||
/// \brief Retrieve OID value array
|
||||
/// \return OID value vector
|
||||
/// \since Crypto++ 8.0
|
||||
const std::vector<word32>& GetValues() const {
|
||||
return m_values;
|
||||
}
|
||||
|
||||
/// \brief Print an OID
|
||||
/// \param out ostream object
|
||||
/// \return ostream reference
|
||||
/// \details Print() writes the OID in a customary format, like
|
||||
/// 1.2.840.113549.1.1.11. The caller is reposnsible to convert the
|
||||
/// OID to a friendly name, like sha256WithRSAEncryption.
|
||||
/// \since Crypto++ 8.3
|
||||
std::ostream& Print(std::ostream& out) const;
|
||||
|
||||
protected:
|
||||
friend bool operator==(const OID &lhs, const OID &rhs);
|
||||
friend bool operator!=(const OID &lhs, const OID &rhs);
|
||||
friend bool operator<(const OID &lhs, const OID &rhs);
|
||||
friend bool operator<=(const OID &lhs, const OID &rhs);
|
||||
friend bool operator>=(const OID &lhs, const OID &rhs);
|
||||
|
||||
std::vector<word32> m_values;
|
||||
|
||||
private:
|
||||
static void EncodeValue(BufferedTransformation &bt, word32 v);
|
||||
static size_t DecodeValue(BufferedTransformation &bt, word32 &v);
|
||||
};
|
||||
|
||||
/// \brief ASN.1 encoded object filter
|
||||
class EncodedObjectFilter : public Filter
|
||||
{
|
||||
public:
|
||||
enum Flag {PUT_OBJECTS=1, PUT_MESSANGE_END_AFTER_EACH_OBJECT=2, PUT_MESSANGE_END_AFTER_ALL_OBJECTS=4, PUT_MESSANGE_SERIES_END_AFTER_ALL_OBJECTS=8};
|
||||
enum State {IDENTIFIER, LENGTH, BODY, TAIL, ALL_DONE} m_state;
|
||||
|
||||
virtual ~EncodedObjectFilter() {}
|
||||
|
||||
/// \brief Construct an EncodedObjectFilter
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \param nObjects the number of objects
|
||||
/// \param flags bitwise OR of EncodedObjectFilter::Flag
|
||||
EncodedObjectFilter(BufferedTransformation *attachment = NULLPTR, unsigned int nObjects = 1, word32 flags = 0);
|
||||
|
||||
/// \brief Input a byte buffer for processing
|
||||
/// \param inString the byte buffer to process
|
||||
/// \param length the size of the string, in bytes
|
||||
void Put(const byte *inString, size_t length);
|
||||
|
||||
unsigned int GetNumberOfCompletedObjects() const {return m_nCurrentObject;}
|
||||
unsigned long GetPositionOfObject(unsigned int i) const {return m_positions[i];}
|
||||
|
||||
private:
|
||||
BufferedTransformation & CurrentTarget();
|
||||
|
||||
ByteQueue m_queue;
|
||||
std::vector<unsigned int> m_positions;
|
||||
lword m_lengthRemaining;
|
||||
word32 m_nObjects, m_nCurrentObject, m_level, m_flags;
|
||||
byte m_id;
|
||||
};
|
||||
|
||||
/// \brief BER General Decoder
|
||||
class CRYPTOPP_DLL BERGeneralDecoder : public Store
|
||||
{
|
||||
public:
|
||||
/// \brief Default ASN.1 tag
|
||||
enum {DefaultTag = SEQUENCE | CONSTRUCTED};
|
||||
|
||||
virtual ~BERGeneralDecoder();
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \details BERGeneralDecoder uses DefaultTag
|
||||
explicit BERGeneralDecoder(BufferedTransformation &inQueue);
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit BERGeneralDecoder(BufferedTransformation &inQueue, byte asnTag);
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit BERGeneralDecoder(BERGeneralDecoder &inQueue, byte asnTag);
|
||||
|
||||
/// \brief Determine length encoding
|
||||
/// \return true if the ASN.1 object is definite length encoded, false otherwise
|
||||
bool IsDefiniteLength() const {
|
||||
return m_definiteLength;
|
||||
}
|
||||
|
||||
/// \brief Determine remaining length
|
||||
/// \return number of octets that remain to be consumed
|
||||
/// \details RemainingLength() is only valid if IsDefiniteLength()
|
||||
/// returns true.
|
||||
lword RemainingLength() const {
|
||||
CRYPTOPP_ASSERT(m_definiteLength);
|
||||
return IsDefiniteLength() ? m_length : 0;
|
||||
}
|
||||
|
||||
/// \brief Determine end of stream
|
||||
/// \return true if all octets have been consumed, false otherwise
|
||||
bool EndReached() const;
|
||||
|
||||
/// \brief Determine next octet
|
||||
/// \return next octet in the stream
|
||||
/// \details PeekByte does not consume the octet.
|
||||
/// \throw BERDecodeError if there are no octets remaining
|
||||
byte PeekByte() const;
|
||||
|
||||
/// \brief Determine next octet
|
||||
/// \details CheckByte reads the next byte in the stream and verifies
|
||||
/// the octet matches b.
|
||||
/// \throw BERDecodeError if the next octet is not b
|
||||
void CheckByte(byte b);
|
||||
|
||||
/// \brief Transfer bytes to another BufferedTransformation
|
||||
/// \param target the destination BufferedTransformation
|
||||
/// \param transferBytes the number of bytes to transfer
|
||||
/// \param channel the channel on which the transfer should occur
|
||||
/// \param blocking specifies whether the object should block when
|
||||
/// processing input
|
||||
/// \return the number of bytes that remain in the transfer block
|
||||
/// (i.e., bytes not transferred)
|
||||
/// \details TransferTo2() removes bytes and moves
|
||||
/// them to the destination. Transfer begins at the index position
|
||||
/// in the current stream, and not from an absolute position in the
|
||||
/// stream.
|
||||
/// \details transferBytes is an \a IN and \a OUT parameter. When
|
||||
/// the call is made, transferBytes is the requested size of the
|
||||
/// transfer. When the call returns, transferBytes is the number
|
||||
/// of bytes that were transferred.
|
||||
size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true);
|
||||
|
||||
/// \brief Copy bytes to another BufferedTransformation
|
||||
/// \param target the destination BufferedTransformation
|
||||
/// \param begin the 0-based index of the first byte to copy in
|
||||
/// the stream
|
||||
/// \param end the 0-based index of the last byte to copy in
|
||||
/// the stream
|
||||
/// \param channel the channel on which the transfer should occur
|
||||
/// \param blocking specifies whether the object should block when
|
||||
/// processing input
|
||||
/// \return the number of bytes that remain in the copy block
|
||||
/// (i.e., bytes not copied)
|
||||
/// \details CopyRangeTo2 copies bytes to the
|
||||
/// destination. The bytes are not removed from this object. Copying
|
||||
/// begins at the index position in the current stream, and not from
|
||||
/// an absolute position in the stream.
|
||||
/// \details begin is an \a IN and \a OUT parameter. When the call is
|
||||
/// made, begin is the starting position of the copy. When the call
|
||||
/// returns, begin is the position of the first byte that was \a not
|
||||
/// copied (which may be different than end). begin can be used for
|
||||
/// subsequent calls to CopyRangeTo2().
|
||||
size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const;
|
||||
|
||||
/// \brief Signals the end of messages to the object
|
||||
/// \details Call this to denote end of sequence
|
||||
void MessageEnd();
|
||||
|
||||
protected:
|
||||
BufferedTransformation &m_inQueue;
|
||||
lword m_length;
|
||||
bool m_finished, m_definiteLength;
|
||||
|
||||
private:
|
||||
void Init(byte asnTag);
|
||||
void StoreInitialize(const NameValuePairs ¶meters)
|
||||
{CRYPTOPP_UNUSED(parameters); CRYPTOPP_ASSERT(false);}
|
||||
lword ReduceLength(lword delta);
|
||||
};
|
||||
|
||||
/// \brief DER General Encoder
|
||||
class CRYPTOPP_DLL DERGeneralEncoder : public ByteQueue
|
||||
{
|
||||
public:
|
||||
/// \brief Default ASN.1 tag
|
||||
enum {DefaultTag = SEQUENCE | CONSTRUCTED};
|
||||
|
||||
virtual ~DERGeneralEncoder();
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \details DERGeneralEncoder uses DefaultTag
|
||||
explicit DERGeneralEncoder(BufferedTransformation &outQueue);
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit DERGeneralEncoder(BufferedTransformation &outQueue, byte asnTag);
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit DERGeneralEncoder(DERGeneralEncoder &outQueue, byte asnTag);
|
||||
|
||||
/// \brief Signals the end of messages to the object
|
||||
/// \details Call this to denote end of sequence
|
||||
void MessageEnd();
|
||||
|
||||
private:
|
||||
BufferedTransformation &m_outQueue;
|
||||
byte m_asnTag;
|
||||
bool m_finished;
|
||||
};
|
||||
|
||||
/// \brief BER Sequence Decoder
|
||||
class CRYPTOPP_DLL BERSequenceDecoder : public BERGeneralDecoder
|
||||
{
|
||||
public:
|
||||
/// \brief Default ASN.1 tag
|
||||
enum {DefaultTag = SEQUENCE | CONSTRUCTED};
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \details BERSequenceDecoder uses DefaultTag
|
||||
explicit BERSequenceDecoder(BufferedTransformation &inQueue)
|
||||
: BERGeneralDecoder(inQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit BERSequenceDecoder(BufferedTransformation &inQueue, byte asnTag)
|
||||
: BERGeneralDecoder(inQueue, asnTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \details BERSequenceDecoder uses DefaultTag
|
||||
explicit BERSequenceDecoder(BERSequenceDecoder &inQueue)
|
||||
: BERGeneralDecoder(inQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit BERSequenceDecoder(BERSequenceDecoder &inQueue, byte asnTag)
|
||||
: BERGeneralDecoder(inQueue, asnTag) {}
|
||||
};
|
||||
|
||||
/// \brief DER Sequence Encoder
|
||||
class CRYPTOPP_DLL DERSequenceEncoder : public DERGeneralEncoder
|
||||
{
|
||||
public:
|
||||
/// \brief Default ASN.1 tag
|
||||
enum {DefaultTag = SEQUENCE | CONSTRUCTED};
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \details DERSequenceEncoder uses DefaultTag
|
||||
explicit DERSequenceEncoder(BufferedTransformation &outQueue)
|
||||
: DERGeneralEncoder(outQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit DERSequenceEncoder(BufferedTransformation &outQueue, byte asnTag)
|
||||
: DERGeneralEncoder(outQueue, asnTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \details DERSequenceEncoder uses DefaultTag
|
||||
explicit DERSequenceEncoder(DERSequenceEncoder &outQueue)
|
||||
: DERGeneralEncoder(outQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit DERSequenceEncoder(DERSequenceEncoder &outQueue, byte asnTag)
|
||||
: DERGeneralEncoder(outQueue, asnTag) {}
|
||||
};
|
||||
|
||||
/// \brief BER Set Decoder
|
||||
class CRYPTOPP_DLL BERSetDecoder : public BERGeneralDecoder
|
||||
{
|
||||
public:
|
||||
/// \brief Default ASN.1 tag
|
||||
enum {DefaultTag = SET | CONSTRUCTED};
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \details BERSetDecoder uses DefaultTag
|
||||
explicit BERSetDecoder(BufferedTransformation &inQueue)
|
||||
: BERGeneralDecoder(inQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit BERSetDecoder(BufferedTransformation &inQueue, byte asnTag)
|
||||
: BERGeneralDecoder(inQueue, asnTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \details BERSetDecoder uses DefaultTag
|
||||
explicit BERSetDecoder(BERSetDecoder &inQueue)
|
||||
: BERGeneralDecoder(inQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 decoder
|
||||
/// \param inQueue input byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit BERSetDecoder(BERSetDecoder &inQueue, byte asnTag)
|
||||
: BERGeneralDecoder(inQueue, asnTag) {}
|
||||
};
|
||||
|
||||
/// \brief DER Set Encoder
|
||||
class CRYPTOPP_DLL DERSetEncoder : public DERGeneralEncoder
|
||||
{
|
||||
public:
|
||||
/// \brief Default ASN.1 tag
|
||||
enum {DefaultTag = SET | CONSTRUCTED};
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \details DERSetEncoder uses DefaultTag
|
||||
explicit DERSetEncoder(BufferedTransformation &outQueue)
|
||||
: DERGeneralEncoder(outQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit DERSetEncoder(BufferedTransformation &outQueue, byte asnTag)
|
||||
: DERGeneralEncoder(outQueue, asnTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \details DERSetEncoder uses DefaultTag
|
||||
explicit DERSetEncoder(DERSetEncoder &outQueue)
|
||||
: DERGeneralEncoder(outQueue, DefaultTag) {}
|
||||
|
||||
/// \brief Construct an ASN.1 encoder
|
||||
/// \param outQueue output byte queue
|
||||
/// \param asnTag ASN.1 tag
|
||||
explicit DERSetEncoder(DERSetEncoder &outQueue, byte asnTag)
|
||||
: DERGeneralEncoder(outQueue, asnTag) {}
|
||||
};
|
||||
|
||||
/// \brief Optional data encoder and decoder
|
||||
/// \tparam T class or type
|
||||
template <class T>
|
||||
class ASNOptional : public member_ptr<T>
|
||||
{
|
||||
public:
|
||||
/// \brief BER decode optional data
|
||||
/// \param seqDecoder sequence with the optional ASN.1 data
|
||||
/// \param tag ASN.1 tag to match as optional data
|
||||
/// \param mask the mask to apply when matching the tag
|
||||
/// \sa ASNTag and ASNIdFlag
|
||||
void BERDecode(BERSequenceDecoder &seqDecoder, byte tag, byte mask = ~CONSTRUCTED)
|
||||
{
|
||||
byte b;
|
||||
if (seqDecoder.Peek(b) && (b & mask) == tag)
|
||||
reset(new T(seqDecoder));
|
||||
}
|
||||
|
||||
/// \brief DER encode optional data
|
||||
/// \param out BufferedTransformation object
|
||||
void DEREncode(BufferedTransformation &out)
|
||||
{
|
||||
if (this->get() != NULLPTR)
|
||||
this->get()->DEREncode(out);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Encode and decode ASN.1 objects with additional information
|
||||
/// \tparam BASE base class or type
|
||||
/// \details Encodes and decodes public keys, private keys and group
|
||||
/// parameters with OID identifying the algorithm or scheme.
|
||||
template <class BASE>
|
||||
class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ASN1CryptoMaterial : public ASN1Object, public BASE
|
||||
{
|
||||
public:
|
||||
/// \brief DER encode ASN.1 object
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \details Save() will write the OID associated with algorithm or scheme.
|
||||
/// In the case of public and private keys, this function writes the
|
||||
/// subjectPubicKeyInfo and privateKeyInfo parts.
|
||||
void Save(BufferedTransformation &bt) const
|
||||
{BEREncode(bt);}
|
||||
|
||||
/// \brief BER decode ASN.1 object
|
||||
/// \param bt BufferedTransformation object
|
||||
void Load(BufferedTransformation &bt)
|
||||
{BERDecode(bt);}
|
||||
};
|
||||
|
||||
/// \brief Encodes and decodes subjectPublicKeyInfo
|
||||
class CRYPTOPP_DLL X509PublicKey : public ASN1CryptoMaterial<PublicKey>
|
||||
{
|
||||
public:
|
||||
virtual ~X509PublicKey() {}
|
||||
|
||||
void BERDecode(BufferedTransformation &bt);
|
||||
void DEREncode(BufferedTransformation &bt) const;
|
||||
|
||||
/// \brief Retrieves the OID of the algorithm
|
||||
/// \return OID of the algorithm
|
||||
virtual OID GetAlgorithmID() const =0;
|
||||
|
||||
/// \brief Decode algorithm parameters
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \sa BERDecodePublicKey, <A HREF="http://www.ietf.org/rfc/rfc2459.txt">RFC
|
||||
/// 2459, section 7.3.1</A>
|
||||
virtual bool BERDecodeAlgorithmParameters(BufferedTransformation &bt)
|
||||
{BERDecodeNull(bt); return false;}
|
||||
|
||||
/// \brief Encode algorithm parameters
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \sa DEREncodePublicKey, <A HREF="http://www.ietf.org/rfc/rfc2459.txt">RFC
|
||||
/// 2459, section 7.3.1</A>
|
||||
virtual bool DEREncodeAlgorithmParameters(BufferedTransformation &bt) const
|
||||
{DEREncodeNull(bt); return false;}
|
||||
|
||||
/// \brief Decode subjectPublicKey part of subjectPublicKeyInfo
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \param parametersPresent flag indicating if algorithm parameters are present
|
||||
/// \param size number of octets to read for the parameters, in bytes
|
||||
/// \details BERDecodePublicKey() the decodes subjectPublicKey part of
|
||||
/// subjectPublicKeyInfo, without the BIT STRING header.
|
||||
/// \details When <tt>parametersPresent = true</tt> then BERDecodePublicKey() calls
|
||||
/// BERDecodeAlgorithmParameters() to parse algorithm parameters.
|
||||
/// \sa BERDecodeAlgorithmParameters
|
||||
virtual void BERDecodePublicKey(BufferedTransformation &bt, bool parametersPresent, size_t size) =0;
|
||||
|
||||
/// \brief Encode subjectPublicKey part of subjectPublicKeyInfo
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \details DEREncodePublicKey() encodes the subjectPublicKey part of
|
||||
/// subjectPublicKeyInfo, without the BIT STRING header.
|
||||
/// \sa DEREncodeAlgorithmParameters
|
||||
virtual void DEREncodePublicKey(BufferedTransformation &bt) const =0;
|
||||
};
|
||||
|
||||
/// \brief Encodes and Decodes privateKeyInfo
|
||||
class CRYPTOPP_DLL PKCS8PrivateKey : public ASN1CryptoMaterial<PrivateKey>
|
||||
{
|
||||
public:
|
||||
virtual ~PKCS8PrivateKey() {}
|
||||
|
||||
void BERDecode(BufferedTransformation &bt);
|
||||
void DEREncode(BufferedTransformation &bt) const;
|
||||
|
||||
/// \brief Retrieves the OID of the algorithm
|
||||
/// \return OID of the algorithm
|
||||
virtual OID GetAlgorithmID() const =0;
|
||||
|
||||
/// \brief Decode optional parameters
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \sa BERDecodePrivateKey, <A HREF="http://www.ietf.org/rfc/rfc2459.txt">RFC
|
||||
/// 2459, section 7.3.1</A>
|
||||
virtual bool BERDecodeAlgorithmParameters(BufferedTransformation &bt)
|
||||
{BERDecodeNull(bt); return false;}
|
||||
|
||||
/// \brief Encode optional parameters
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \sa DEREncodePrivateKey, <A HREF="http://www.ietf.org/rfc/rfc2459.txt">RFC
|
||||
/// 2459, section 7.3.1</A>
|
||||
virtual bool DEREncodeAlgorithmParameters(BufferedTransformation &bt) const
|
||||
{DEREncodeNull(bt); return false;}
|
||||
|
||||
/// \brief Decode privateKey part of privateKeyInfo
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \param parametersPresent flag indicating if algorithm parameters are present
|
||||
/// \param size number of octets to read for the parameters, in bytes
|
||||
/// \details BERDecodePrivateKey() the decodes privateKey part of privateKeyInfo,
|
||||
/// without the OCTET STRING header.
|
||||
/// \details When <tt>parametersPresent = true</tt> then BERDecodePrivateKey() calls
|
||||
/// BERDecodeAlgorithmParameters() to parse algorithm parameters.
|
||||
/// \sa BERDecodeAlgorithmParameters
|
||||
virtual void BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t size) =0;
|
||||
|
||||
/// \brief Encode privateKey part of privateKeyInfo
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \details DEREncodePrivateKey() encodes the privateKey part of privateKeyInfo,
|
||||
/// without the OCTET STRING header.
|
||||
/// \sa DEREncodeAlgorithmParameters
|
||||
virtual void DEREncodePrivateKey(BufferedTransformation &bt) const =0;
|
||||
|
||||
/// \brief Decode optional attributes
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \details BERDecodeOptionalAttributes() decodes optional attributes including
|
||||
/// context-specific tag.
|
||||
/// \sa BERDecodeAlgorithmParameters, DEREncodeOptionalAttributes
|
||||
/// \note default implementation stores attributes to be output using
|
||||
/// DEREncodeOptionalAttributes
|
||||
virtual void BERDecodeOptionalAttributes(BufferedTransformation &bt);
|
||||
|
||||
/// \brief Encode optional attributes
|
||||
/// \param bt BufferedTransformation object
|
||||
/// \details DEREncodeOptionalAttributes() encodes optional attributes including
|
||||
/// context-specific tag.
|
||||
/// \sa BERDecodeAlgorithmParameters
|
||||
virtual void DEREncodeOptionalAttributes(BufferedTransformation &bt) const;
|
||||
|
||||
protected:
|
||||
ByteQueue m_optionalAttributes;
|
||||
};
|
||||
|
||||
// ********************************************************
|
||||
|
||||
/// \brief DER Encode unsigned value
|
||||
/// \tparam T class or type
|
||||
/// \param out BufferedTransformation object
|
||||
/// \param w unsigned value to encode
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \details DEREncodeUnsigned() can be used with INTEGER, BOOLEAN, and ENUM
|
||||
template <class T>
|
||||
size_t DEREncodeUnsigned(BufferedTransformation &out, T w, byte asnTag = INTEGER)
|
||||
{
|
||||
byte buf[sizeof(w)+1];
|
||||
unsigned int bc;
|
||||
if (asnTag == BOOLEAN)
|
||||
{
|
||||
buf[sizeof(w)] = w ? 0xff : 0;
|
||||
bc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[0] = 0;
|
||||
for (unsigned int i=0; i<sizeof(w); i++)
|
||||
buf[i+1] = byte(w >> (sizeof(w)-1-i)*8);
|
||||
bc = sizeof(w);
|
||||
while (bc > 1 && buf[sizeof(w)+1-bc] == 0)
|
||||
--bc;
|
||||
if (buf[sizeof(w)+1-bc] & 0x80)
|
||||
++bc;
|
||||
}
|
||||
out.Put(asnTag);
|
||||
size_t lengthBytes = DERLengthEncode(out, bc);
|
||||
out.Put(buf+sizeof(w)+1-bc, bc);
|
||||
return 1+lengthBytes+bc;
|
||||
}
|
||||
|
||||
/// \brief BER Decode unsigned value
|
||||
/// \tparam T fundamental C++ type
|
||||
/// \param in BufferedTransformation object
|
||||
/// \param w the decoded value
|
||||
/// \param asnTag the ASN.1 identifier
|
||||
/// \param minValue the minimum expected value
|
||||
/// \param maxValue the maximum expected value
|
||||
/// \throw BERDecodeErr() if the value cannot be parsed or the decoded value is not within range.
|
||||
/// \details DEREncodeUnsigned() can be used with INTEGER, BOOLEAN, and ENUM
|
||||
template <class T>
|
||||
void BERDecodeUnsigned(BufferedTransformation &in, T &w, byte asnTag = INTEGER,
|
||||
T minValue = 0, T maxValue = T(0xffffffff))
|
||||
{
|
||||
byte b;
|
||||
if (!in.Get(b) || b != asnTag)
|
||||
BERDecodeError();
|
||||
|
||||
size_t bc;
|
||||
bool definite = BERLengthDecode(in, bc);
|
||||
if (!definite)
|
||||
BERDecodeError();
|
||||
if (bc > in.MaxRetrievable()) // Issue 346
|
||||
BERDecodeError();
|
||||
if (asnTag == BOOLEAN && bc != 1) // X.690, 8.2.1
|
||||
BERDecodeError();
|
||||
if ((asnTag == INTEGER || asnTag == ENUMERATED) && bc == 0) // X.690, 8.3.1 and 8.4
|
||||
BERDecodeError();
|
||||
|
||||
SecByteBlock buf(bc);
|
||||
|
||||
if (bc != in.Get(buf, bc))
|
||||
BERDecodeError();
|
||||
|
||||
// This consumes leading 0 octets. According to X.690, 8.3.2, it could be non-conforming behavior.
|
||||
// X.690, 8.3.2 says "the bits of the first octet and bit 8 of the second octet ... (a) shall
|
||||
// not all be ones and (b) shall not all be zeros ... These rules ensure that an integer value
|
||||
// is always encoded in the smallest possible number of octet".
|
||||
// We invented AER (Alternate Encoding Rules), which is more relaxed than BER, CER, and DER.
|
||||
const byte *ptr = buf;
|
||||
while (bc > sizeof(w) && *ptr == 0)
|
||||
{
|
||||
bc--;
|
||||
ptr++;
|
||||
}
|
||||
if (bc > sizeof(w))
|
||||
BERDecodeError();
|
||||
|
||||
w = 0;
|
||||
for (unsigned int i=0; i<bc; i++)
|
||||
w = (w << 8) | ptr[i];
|
||||
|
||||
if (w < minValue || w > maxValue)
|
||||
BERDecodeError();
|
||||
}
|
||||
|
||||
#ifdef CRYPTOPP_DOXYGEN_PROCESSING
|
||||
/// \brief Compare two OIDs for equality
|
||||
/// \param lhs the first OID
|
||||
/// \param rhs the second OID
|
||||
/// \return true if the OIDs are equal, false otherwise
|
||||
inline bool operator==(const OID &lhs, const OID &rhs);
|
||||
/// \brief Compare two OIDs for inequality
|
||||
/// \param lhs the first OID
|
||||
/// \param rhs the second OID
|
||||
/// \return true if the OIDs are not equal, false otherwise
|
||||
inline bool operator!=(const OID &lhs, const OID &rhs);
|
||||
/// \brief Compare two OIDs for ordering
|
||||
/// \param lhs the first OID
|
||||
/// \param rhs the second OID
|
||||
/// \return true if the first OID is less than the second OID, false otherwise
|
||||
/// \details operator<() calls std::lexicographical_compare() on each element in the array of values.
|
||||
inline bool operator<(const OID &lhs, const OID &rhs);
|
||||
/// \brief Compare two OIDs for ordering
|
||||
/// \param lhs the first OID
|
||||
/// \param rhs the second OID
|
||||
/// \return true if the first OID is less than or equal to the second OID, false otherwise
|
||||
/// \details operator<=() is implemented in terms of operator==() and operator<().
|
||||
/// \since Crypto++ 8.3
|
||||
inline bool operator<=(const OID &lhs, const OID &rhs);
|
||||
/// \brief Compare two OIDs for ordering
|
||||
/// \param lhs the first OID
|
||||
/// \param rhs the second OID
|
||||
/// \return true if the first OID is greater than or equal to the second OID, false otherwise
|
||||
/// \details operator>=() is implemented in terms of operator<().
|
||||
/// \since Crypto++ 8.3
|
||||
inline bool operator>=(const OID &lhs, const OID &rhs);
|
||||
/// \brief Append a value to an OID
|
||||
/// \param lhs the OID
|
||||
/// \param rhs the value to append
|
||||
inline OID operator+(const OID &lhs, unsigned long rhs);
|
||||
/// \brief Print a OID value
|
||||
/// \param out the output stream
|
||||
/// \param oid the OID
|
||||
inline std::ostream& operator<<(std::ostream& out, const OID &oid)
|
||||
{ return oid.Print(out); }
|
||||
#else
|
||||
inline bool operator==(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs)
|
||||
{return lhs.m_values == rhs.m_values;}
|
||||
inline bool operator!=(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs)
|
||||
{return lhs.m_values != rhs.m_values;}
|
||||
inline bool operator<(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs)
|
||||
{return std::lexicographical_compare(lhs.m_values.begin(), lhs.m_values.end(), rhs.m_values.begin(), rhs.m_values.end());}
|
||||
inline bool operator<=(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs)
|
||||
{return lhs<rhs || lhs==rhs;}
|
||||
inline bool operator>=(const ::CryptoPP::OID &lhs, const ::CryptoPP::OID &rhs)
|
||||
{return ! (lhs<rhs);}
|
||||
inline ::CryptoPP::OID operator+(const ::CryptoPP::OID &lhs, unsigned long rhs)
|
||||
{return ::CryptoPP::OID(lhs)+=rhs;}
|
||||
inline std::ostream& operator<<(std::ostream& out, const OID &oid)
|
||||
{ return oid.Print(out); }
|
||||
#endif
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
// Issue 340
|
||||
#if CRYPTOPP_GCC_DIAGNOSTIC_AVAILABLE
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
// authenc.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "authenc.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void AuthenticatedSymmetricCipherBase::AuthenticateData(const byte *input, size_t len)
|
||||
{
|
||||
// UBsan finding with -std=c++03 using memcpy
|
||||
CRYPTOPP_ASSERT(input && len);
|
||||
if(!input || !len) return;
|
||||
|
||||
unsigned int blockSize = AuthenticationBlockSize();
|
||||
unsigned int &num = m_bufferedDataLength;
|
||||
byte* data = m_buffer.begin();
|
||||
|
||||
if (data && num) // process left over data
|
||||
{
|
||||
if (num+len >= blockSize)
|
||||
{
|
||||
memcpy(data+num, input, blockSize-num);
|
||||
AuthenticateBlocks(data, blockSize);
|
||||
input += (blockSize-num);
|
||||
len -= (blockSize-num);
|
||||
num = 0;
|
||||
// drop through and do the rest
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(data+num, input, len);
|
||||
num += (unsigned int)len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// now process the input data in blocks of blockSize bytes and save the leftovers to m_data
|
||||
if (len >= blockSize)
|
||||
{
|
||||
size_t leftOver = AuthenticateBlocks(input, len);
|
||||
input += (len - leftOver);
|
||||
len = leftOver;
|
||||
}
|
||||
|
||||
if (data && len)
|
||||
memcpy(data, input, len);
|
||||
num = (unsigned int)len;
|
||||
}
|
||||
|
||||
void AuthenticatedSymmetricCipherBase::SetKey(const byte *userKey, size_t keylength, const NameValuePairs ¶ms)
|
||||
{
|
||||
m_bufferedDataLength = 0;
|
||||
m_state = State_Start;
|
||||
|
||||
this->SetKeyWithoutResync(userKey, keylength, params);
|
||||
m_state = State_KeySet;
|
||||
|
||||
size_t length;
|
||||
const byte *iv = GetIVAndThrowIfInvalid(params, length);
|
||||
if (iv)
|
||||
Resynchronize(iv, (int)length);
|
||||
}
|
||||
|
||||
void AuthenticatedSymmetricCipherBase::Resynchronize(const byte *iv, int length)
|
||||
{
|
||||
if (m_state < State_KeySet)
|
||||
throw BadState(AlgorithmName(), "Resynchronize", "key is set");
|
||||
|
||||
m_bufferedDataLength = 0;
|
||||
m_totalHeaderLength = m_totalMessageLength = m_totalFooterLength = 0;
|
||||
m_state = State_KeySet;
|
||||
|
||||
Resync(iv, this->ThrowIfInvalidIVLength(length));
|
||||
m_state = State_IVSet;
|
||||
}
|
||||
|
||||
void AuthenticatedSymmetricCipherBase::Update(const byte *input, size_t length)
|
||||
{
|
||||
// Part of original authenc.cpp code. Don't remove it.
|
||||
if (length == 0) {return;}
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case State_Start:
|
||||
case State_KeySet:
|
||||
throw BadState(AlgorithmName(), "Update", "setting key and IV");
|
||||
case State_IVSet:
|
||||
AuthenticateData(input, length);
|
||||
m_totalHeaderLength += length;
|
||||
break;
|
||||
case State_AuthUntransformed:
|
||||
case State_AuthTransformed:
|
||||
AuthenticateLastConfidentialBlock();
|
||||
m_bufferedDataLength = 0;
|
||||
m_state = State_AuthFooter;
|
||||
// fall through
|
||||
case State_AuthFooter:
|
||||
AuthenticateData(input, length);
|
||||
m_totalFooterLength += length;
|
||||
break;
|
||||
default:
|
||||
CRYPTOPP_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthenticatedSymmetricCipherBase::ProcessData(byte *outString, const byte *inString, size_t length)
|
||||
{
|
||||
if (m_state >= State_IVSet && length > MaxMessageLength()-m_totalMessageLength)
|
||||
throw InvalidArgument(AlgorithmName() + ": message length exceeds maximum");
|
||||
m_totalMessageLength += length;
|
||||
|
||||
reswitch:
|
||||
switch (m_state)
|
||||
{
|
||||
case State_Start:
|
||||
case State_KeySet:
|
||||
throw BadState(AlgorithmName(), "ProcessData", "setting key and IV");
|
||||
case State_AuthFooter:
|
||||
throw BadState(AlgorithmName(), "ProcessData was called after footer input has started");
|
||||
case State_IVSet:
|
||||
AuthenticateLastHeaderBlock();
|
||||
m_bufferedDataLength = 0;
|
||||
m_state = AuthenticationIsOnPlaintext()==IsForwardTransformation() ? State_AuthUntransformed : State_AuthTransformed;
|
||||
goto reswitch;
|
||||
case State_AuthUntransformed:
|
||||
AuthenticateData(inString, length);
|
||||
AccessSymmetricCipher().ProcessData(outString, inString, length);
|
||||
break;
|
||||
case State_AuthTransformed:
|
||||
AccessSymmetricCipher().ProcessData(outString, inString, length);
|
||||
AuthenticateData(outString, length);
|
||||
break;
|
||||
default:
|
||||
CRYPTOPP_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthenticatedSymmetricCipherBase::TruncatedFinal(byte *mac, size_t macSize)
|
||||
{
|
||||
// https://github.com/weidai11/cryptopp/issues/954
|
||||
this->ThrowIfInvalidTruncatedSize(macSize);
|
||||
|
||||
if (m_totalHeaderLength > MaxHeaderLength())
|
||||
throw InvalidArgument(AlgorithmName() + ": header length of " + IntToString(m_totalHeaderLength) + " exceeds the maximum of " + IntToString(MaxHeaderLength()));
|
||||
|
||||
if (m_totalFooterLength > MaxFooterLength())
|
||||
{
|
||||
if (MaxFooterLength() == 0)
|
||||
throw InvalidArgument(AlgorithmName() + ": additional authenticated data (AAD) cannot be input after data to be encrypted or decrypted");
|
||||
else
|
||||
throw InvalidArgument(AlgorithmName() + ": footer length of " + IntToString(m_totalFooterLength) + " exceeds the maximum of " + IntToString(MaxFooterLength()));
|
||||
}
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case State_Start:
|
||||
case State_KeySet:
|
||||
throw BadState(AlgorithmName(), "TruncatedFinal", "setting key and IV");
|
||||
|
||||
case State_IVSet:
|
||||
AuthenticateLastHeaderBlock();
|
||||
m_bufferedDataLength = 0;
|
||||
// fall through
|
||||
|
||||
case State_AuthUntransformed:
|
||||
case State_AuthTransformed:
|
||||
AuthenticateLastConfidentialBlock();
|
||||
m_bufferedDataLength = 0;
|
||||
// fall through
|
||||
|
||||
case State_AuthFooter:
|
||||
AuthenticateLastFooterBlock(mac, macSize);
|
||||
m_bufferedDataLength = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
CRYPTOPP_ASSERT(false);
|
||||
}
|
||||
|
||||
m_state = State_KeySet;
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// authenc.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file
|
||||
/// \brief Classes for authenticated encryption modes of operation
|
||||
/// \details Authenticated encryption (AE) schemes combine confidentiality and authenticity
|
||||
/// into a single mode of operation They gained traction in the early 2000's because manually
|
||||
/// combining them was error prone for the typical developer. Around that time, the desire to
|
||||
/// authenticate but not ecrypt additional data (AAD) was also identified. When both features
|
||||
/// are available from a scheme, the system is referred to as an AEAD scheme.
|
||||
/// \details Crypto++ provides four authenticated encryption modes of operation - CCM, EAX, GCM
|
||||
/// and OCB mode. All modes derive from AuthenticatedSymmetricCipherBase() and the
|
||||
/// motivation for the API, like calling AAD a "header", can be found in Bellare,
|
||||
/// Rogaway and Wagner's <A HREF="http://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf">The EAX
|
||||
/// Mode of Operation</A>. The EAX paper suggested a basic API to help standardize AEAD
|
||||
/// schemes in software and promote adoption of the modes.
|
||||
/// \sa <A HREF="http://www.cryptopp.com/wiki/Authenticated_Encryption">Authenticated
|
||||
/// Encryption</A> on the Crypto++ wiki.
|
||||
/// \since Crypto++ 5.6.0
|
||||
|
||||
#ifndef CRYPTOPP_AUTHENC_H
|
||||
#define CRYPTOPP_AUTHENC_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Base class for authenticated encryption modes of operation
|
||||
/// \details AuthenticatedSymmetricCipherBase() serves as a base implementation for one direction
|
||||
/// (encryption or decryption) of a stream cipher or block cipher mode with authentication.
|
||||
/// \details Crypto++ provides four authenticated encryption modes of operation - CCM, EAX, GCM
|
||||
/// and OCB mode. All modes derive from AuthenticatedSymmetricCipherBase() and the
|
||||
/// motivation for the API, like calling AAD a "header", can be found in Bellare,
|
||||
/// Rogaway and Wagner's <A HREF="http://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf">The EAX
|
||||
/// Mode of Operation</A>. The EAX paper suggested a basic API to help standardize AEAD
|
||||
/// schemes in software and promote adoption of the modes.
|
||||
/// \sa <A HREF="http://www.cryptopp.com/wiki/Authenticated_Encryption">Authenticated
|
||||
/// Encryption</A> on the Crypto++ wiki.
|
||||
/// \since Crypto++ 5.6.0
|
||||
class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedSymmetricCipherBase : public AuthenticatedSymmetricCipher
|
||||
{
|
||||
public:
|
||||
AuthenticatedSymmetricCipherBase() : m_totalHeaderLength(0), m_totalMessageLength(0),
|
||||
m_totalFooterLength(0), m_bufferedDataLength(0), m_state(State_Start) {}
|
||||
|
||||
// StreamTransformation interface
|
||||
bool IsRandomAccess() const {return false;}
|
||||
bool IsSelfInverting() const {return true;}
|
||||
|
||||
void SetKey(const byte *userKey, size_t keylength, const NameValuePairs ¶ms);
|
||||
void Restart() {if (m_state > State_KeySet) m_state = State_KeySet;}
|
||||
void Resynchronize(const byte *iv, int length=-1);
|
||||
void Update(const byte *input, size_t length);
|
||||
void ProcessData(byte *outString, const byte *inString, size_t length);
|
||||
void TruncatedFinal(byte *mac, size_t macSize);
|
||||
|
||||
protected:
|
||||
void UncheckedSetKey(const byte * key, unsigned int length,const CryptoPP::NameValuePairs ¶ms)
|
||||
{CRYPTOPP_UNUSED(key), CRYPTOPP_UNUSED(length), CRYPTOPP_UNUSED(params); CRYPTOPP_ASSERT(false);}
|
||||
|
||||
void AuthenticateData(const byte *data, size_t len);
|
||||
const SymmetricCipher & GetSymmetricCipher() const
|
||||
{return const_cast<AuthenticatedSymmetricCipherBase *>(this)->AccessSymmetricCipher();}
|
||||
|
||||
virtual SymmetricCipher & AccessSymmetricCipher() =0;
|
||||
virtual bool AuthenticationIsOnPlaintext() const =0;
|
||||
virtual unsigned int AuthenticationBlockSize() const =0;
|
||||
virtual void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms) =0;
|
||||
virtual void Resync(const byte *iv, size_t len) =0;
|
||||
virtual size_t AuthenticateBlocks(const byte *data, size_t len) =0;
|
||||
virtual void AuthenticateLastHeaderBlock() =0;
|
||||
virtual void AuthenticateLastConfidentialBlock() {}
|
||||
virtual void AuthenticateLastFooterBlock(byte *mac, size_t macSize) =0;
|
||||
|
||||
// State_AuthUntransformed: authentication is applied to plain text (Authenticate-then-Encrypt)
|
||||
// State_AuthTransformed: authentication is applied to cipher text (Encrypt-then-Authenticate)
|
||||
enum State {State_Start, State_KeySet, State_IVSet, State_AuthUntransformed, State_AuthTransformed, State_AuthFooter};
|
||||
|
||||
AlignedSecByteBlock m_buffer;
|
||||
lword m_totalHeaderLength, m_totalMessageLength, m_totalFooterLength;
|
||||
unsigned int m_bufferedDataLength;
|
||||
State m_state;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// base32.cpp - written and placed in the public domain by Frank Palazzolo, based on hex.cpp by Wei Dai
|
||||
// extended hex alphabet added by JW in November, 2017.
|
||||
|
||||
#include "pch.h"
|
||||
#include "base32.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
const byte s_stdUpper[] = "ABCDEFGHIJKMNPQRSTUVWXYZ23456789";
|
||||
const byte s_stdLower[] = "abcdefghijkmnpqrstuvwxyz23456789";
|
||||
const byte s_hexUpper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
const byte s_hexLower[] = "0123456789abcdefghijklmnopqrstuv";
|
||||
|
||||
const int s_array[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 11, 12, -1,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 11, 12, -1,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
const int s_hexArray[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
void Base32Encoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
bool uppercase = parameters.GetValueWithDefault(Name::Uppercase(), true);
|
||||
m_filter->Initialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::EncodingLookupArray(), uppercase ? &s_stdUpper[0] : &s_stdLower[0], false)(Name::Log2Base(), 5, true)));
|
||||
}
|
||||
|
||||
void Base32Decoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
BaseN_Decoder::IsolatedInitialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::DecodingLookupArray(), GetDefaultDecodingLookupArray(), false)(Name::Log2Base(), 5, true)));
|
||||
}
|
||||
|
||||
// Unrolled initialization, http://github.com/weidai11/cryptopp/issues/376
|
||||
const int *Base32Decoder::GetDefaultDecodingLookupArray()
|
||||
{
|
||||
return s_array;
|
||||
}
|
||||
|
||||
void Base32HexEncoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
bool uppercase = parameters.GetValueWithDefault(Name::Uppercase(), true);
|
||||
m_filter->Initialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::EncodingLookupArray(), uppercase ? &s_hexUpper[0] : &s_hexLower[0], false)(Name::Log2Base(), 5, true)));
|
||||
}
|
||||
|
||||
void Base32HexDecoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
BaseN_Decoder::IsolatedInitialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::DecodingLookupArray(), GetDefaultDecodingLookupArray(), false)(Name::Log2Base(), 5, true)));
|
||||
}
|
||||
|
||||
// Unrolled initialization, http://github.com/weidai11/cryptopp/issues/376
|
||||
const int *Base32HexDecoder::GetDefaultDecodingLookupArray()
|
||||
{
|
||||
return s_hexArray;
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// base32.h - written and placed in the public domain by Frank Palazzolo, based on hex.cpp by Wei Dai
|
||||
// extended hex alphabet added by JW in November, 2017.
|
||||
|
||||
/// \file base32.h
|
||||
/// \brief Classes for Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
|
||||
#ifndef CRYPTOPP_BASE32_H
|
||||
#define CRYPTOPP_BASE32_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "basecode.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Base32 encodes data using DUDE encoding
|
||||
/// \details Converts data to base32 using DUDE encoding. The default code is based on <A HREF="http://www.ietf.org/proceedings/51/I-D/draft-ietf-idn-dude-02.txt">Differential Unicode Domain Encoding (DUDE) (draft-ietf-idn-dude-02.txt)</A>.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
class Base32Encoder : public SimpleProxyFilter
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base32Encoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \param uppercase a flag indicating uppercase output
|
||||
/// \param groupSize the size of the grouping
|
||||
/// \param separator the separator to use between groups
|
||||
/// \param terminator the terminator appeand after processing
|
||||
/// \details Base32Encoder() constructs a default encoder. The constructor lacks fields for padding and
|
||||
/// line breaks. You must use IsolatedInitialize() to change the default padding character or suppress it.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
Base32Encoder(BufferedTransformation *attachment = NULLPTR, bool uppercase = true, int groupSize = 0, const std::string &separator = ":", const std::string &terminator = "")
|
||||
: SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment)
|
||||
{
|
||||
IsolatedInitialize(MakeParameters(Name::Uppercase(), uppercase)(Name::GroupSize(), groupSize)(Name::Separator(), ConstByteArrayParameter(separator))(Name::Terminator(), ConstByteArrayParameter(terminator)));
|
||||
}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \details The following code modifies the padding and line break parameters for an encoder:
|
||||
/// <pre>
|
||||
/// Base32Encoder encoder;
|
||||
/// AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
|
||||
/// encoder.IsolatedInitialize(params);</pre>
|
||||
/// \details You can change the encoding to <A HREF="http://tools.ietf.org/html/rfc4648#page-10">RFC 4648, Base
|
||||
/// 32 Encoding with Extended Hex Alphabet</A> by performing the following:
|
||||
/// <pre>
|
||||
/// Base32Encoder encoder;
|
||||
/// const byte ALPHABET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
/// AlgorithmParameters params = MakeParameters(Name::EncodingLookupArray(),(const byte *)ALPHABET);
|
||||
/// encoder.IsolatedInitialize(params);</pre>
|
||||
/// \details If you change the encoding alphabet, then you will need to change the decoding alphabet \a and
|
||||
/// the decoder's lookup table.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
};
|
||||
|
||||
/// \brief Base32 decodes data using DUDE encoding
|
||||
/// \details Converts data from base32 using DUDE encoding. The default code is based on <A HREF="http://www.ietf.org/proceedings/51/I-D/draft-ietf-idn-dude-02.txt">Differential Unicode Domain Encoding (DUDE) (draft-ietf-idn-dude-02.txt)</A>.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
class Base32Decoder : public BaseN_Decoder
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base32Decoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \sa IsolatedInitialize() for an example of modifying a Base32Decoder after construction.
|
||||
Base32Decoder(BufferedTransformation *attachment = NULLPTR)
|
||||
: BaseN_Decoder(GetDefaultDecodingLookupArray(), 5, attachment) {}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \details You can change the encoding to <A HREF="http://tools.ietf.org/html/rfc4648#page-10">RFC 4648, Base
|
||||
/// 32 Encoding with Extended Hex Alphabet</A> by performing the following:
|
||||
/// <pre>
|
||||
/// int lookup[256];
|
||||
/// const byte ALPHABET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
/// Base32Decoder::InitializeDecodingLookupArray(lookup, ALPHABET, 32, true /*insensitive*/);
|
||||
///
|
||||
/// Base32Decoder decoder;
|
||||
/// AlgorithmParameters params = MakeParameters(Name::DecodingLookupArray(),(const int *)lookup);
|
||||
/// decoder.IsolatedInitialize(params);</pre>
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
|
||||
private:
|
||||
/// \brief Provides the default decoding lookup table
|
||||
/// \return default decoding lookup table
|
||||
static const int * CRYPTOPP_API GetDefaultDecodingLookupArray();
|
||||
};
|
||||
|
||||
/// \brief Base32 encodes data using extended hex
|
||||
/// \details Converts data to base32 using extended hex alphabet. The alphabet is different than Base32Encoder.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder, <A HREF="http://tools.ietf.org/html/rfc4648#page-10">RFC 4648, Base 32 Encoding with Extended Hex Alphabet</A>.
|
||||
/// \since Crypto++ 6.0
|
||||
class Base32HexEncoder : public SimpleProxyFilter
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base32HexEncoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \param uppercase a flag indicating uppercase output
|
||||
/// \param groupSize the size of the grouping
|
||||
/// \param separator the separator to use between groups
|
||||
/// \param terminator the terminator appeand after processing
|
||||
/// \details Base32HexEncoder() constructs a default encoder. The constructor lacks fields for padding and
|
||||
/// line breaks. You must use IsolatedInitialize() to change the default padding character or suppress it.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
Base32HexEncoder(BufferedTransformation *attachment = NULLPTR, bool uppercase = true, int groupSize = 0, const std::string &separator = ":", const std::string &terminator = "")
|
||||
: SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment)
|
||||
{
|
||||
IsolatedInitialize(MakeParameters(Name::Uppercase(), uppercase)(Name::GroupSize(), groupSize)(Name::Separator(), ConstByteArrayParameter(separator))(Name::Terminator(), ConstByteArrayParameter(terminator)));
|
||||
}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \details The following code modifies the padding and line break parameters for an encoder:
|
||||
/// <pre>
|
||||
/// Base32HexEncoder encoder;
|
||||
/// AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
|
||||
/// encoder.IsolatedInitialize(params);</pre>
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
};
|
||||
|
||||
/// \brief Base32 decodes data using extended hex
|
||||
/// \details Converts data from base32 using extended hex alphabet. The alphabet is different than Base32Decoder.
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder, <A HREF="http://tools.ietf.org/html/rfc4648#page-10">RFC 4648, Base 32 Encoding with Extended Hex Alphabet</A>.
|
||||
/// \since Crypto++ 6.0
|
||||
class Base32HexDecoder : public BaseN_Decoder
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base32HexDecoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \sa Base32Encoder, Base32Decoder, Base32HexEncoder and Base32HexDecoder
|
||||
Base32HexDecoder(BufferedTransformation *attachment = NULLPTR)
|
||||
: BaseN_Decoder(GetDefaultDecodingLookupArray(), 5, attachment) {}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
|
||||
private:
|
||||
/// \brief Provides the default decoding lookup table
|
||||
/// \return default decoding lookup table
|
||||
static const int * CRYPTOPP_API GetDefaultDecodingLookupArray();
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
// base64.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
#include "base64.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
const byte s_stdVec[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
const byte s_urlVec[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
const byte s_padding = '=';
|
||||
const int s_stdArray[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
const int s_urlArray[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
void Base64Encoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
bool insertLineBreaks = parameters.GetValueWithDefault(Name::InsertLineBreaks(), true);
|
||||
int maxLineLength = parameters.GetIntValueWithDefault(Name::MaxLineLength(), 72);
|
||||
|
||||
const char *lineBreak = insertLineBreaks ? "\n" : "";
|
||||
|
||||
m_filter->Initialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::EncodingLookupArray(), &s_stdVec[0], false)
|
||||
(Name::PaddingByte(), s_padding)
|
||||
(Name::GroupSize(), insertLineBreaks ? maxLineLength : 0)
|
||||
(Name::Separator(), ConstByteArrayParameter(lineBreak))
|
||||
(Name::Terminator(), ConstByteArrayParameter(lineBreak))
|
||||
(Name::Log2Base(), 6, true)));
|
||||
}
|
||||
|
||||
void Base64URLEncoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
bool insertLineBreaks = parameters.GetValueWithDefault(Name::InsertLineBreaks(), true);
|
||||
int maxLineLength = parameters.GetIntValueWithDefault(Name::MaxLineLength(), 72);
|
||||
|
||||
const char *lineBreak = insertLineBreaks ? "\n" : "";
|
||||
|
||||
m_filter->Initialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::EncodingLookupArray(), &s_urlVec[0], false)
|
||||
(Name::PaddingByte(), s_padding)
|
||||
(Name::GroupSize(), insertLineBreaks ? maxLineLength : 0)
|
||||
(Name::Separator(), ConstByteArrayParameter(lineBreak))
|
||||
(Name::Terminator(), ConstByteArrayParameter(lineBreak))
|
||||
(Name::Log2Base(), 6, true)));
|
||||
}
|
||||
|
||||
void Base64Decoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
BaseN_Decoder::IsolatedInitialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::DecodingLookupArray(), GetDecodingLookupArray(), false)(Name::Log2Base(), 6, true)));
|
||||
}
|
||||
|
||||
const int *Base64Decoder::GetDecodingLookupArray()
|
||||
{
|
||||
return s_stdArray;
|
||||
}
|
||||
|
||||
void Base64URLDecoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
BaseN_Decoder::IsolatedInitialize(CombinedNameValuePairs(
|
||||
parameters,
|
||||
MakeParameters(Name::DecodingLookupArray(), GetDecodingLookupArray(), false)(Name::Log2Base(), 6, true)));
|
||||
}
|
||||
|
||||
// Unrolled initialization, http://github.com/weidai11/cryptopp/issues/376
|
||||
const int *Base64URLDecoder::GetDecodingLookupArray()
|
||||
{
|
||||
return s_urlArray;
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// base64.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file base64.h
|
||||
/// \brief Classes for the Base64Encoder, Base64Decoder, Base64URLEncoder and Base64URLDecoder
|
||||
|
||||
#ifndef CRYPTOPP_BASE64_H
|
||||
#define CRYPTOPP_BASE64_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "basecode.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Base64 encodes data using DUDE
|
||||
/// \details Base64 encodes data per <A HREF="http://tools.ietf.org/html/rfc4648#section-4">RFC 4648, Base 64 Encoding</A>.
|
||||
class Base64Encoder : public SimpleProxyFilter
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base64Encoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \param insertLineBreaks a BufferedTrasformation to attach to this object
|
||||
/// \param maxLineLength the length of a line if line breaks are used
|
||||
/// \details Base64Encoder constructs a default encoder. The constructor lacks a parameter for padding, and you must
|
||||
/// use IsolatedInitialize() to modify the Base64Encoder after construction.
|
||||
/// \sa IsolatedInitialize() for an example of modifying an encoder after construction.
|
||||
Base64Encoder(BufferedTransformation *attachment = NULLPTR, bool insertLineBreaks = true, int maxLineLength = 72)
|
||||
: SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment)
|
||||
{
|
||||
IsolatedInitialize(MakeParameters(Name::InsertLineBreaks(), insertLineBreaks)(Name::MaxLineLength(), maxLineLength));
|
||||
}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \details The following code modifies the padding and line break parameters for an encoder:
|
||||
/// <pre>
|
||||
/// Base64Encoder encoder;
|
||||
/// AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
|
||||
/// encoder.IsolatedInitialize(params);</pre>
|
||||
/// \details You can change the encoding to RFC 4648 web safe alphabet by performing the following:
|
||||
/// <pre>
|
||||
/// Base64Encoder encoder;
|
||||
/// const byte ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
/// AlgorithmParameters params = MakeParameters(Name::EncodingLookupArray(),(const byte *)ALPHABET);
|
||||
/// encoder.IsolatedInitialize(params);</pre>
|
||||
/// \details If you change the encoding alphabet, then you will need to change the decoding alphabet \a and
|
||||
/// the decoder's lookup table.
|
||||
/// \sa Base64URLEncoder for an encoder that provides the web safe alphabet, and Base64Decoder::IsolatedInitialize()
|
||||
/// for an example of modifying a decoder's lookup table after construction.
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
};
|
||||
|
||||
/// \brief Base64 decodes data using DUDE
|
||||
/// \details Base64 encodes data per <A HREF="http://tools.ietf.org/html/rfc4648#section-4">RFC 4648, Base 64 Encoding</A>.
|
||||
class Base64Decoder : public BaseN_Decoder
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base64Decoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \sa IsolatedInitialize() for an example of modifying an encoder after construction.
|
||||
Base64Decoder(BufferedTransformation *attachment = NULLPTR)
|
||||
: BaseN_Decoder(GetDecodingLookupArray(), 6, attachment) {}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \details The default decoding alpahbet is RFC 4868. You can change the to RFC 4868 web safe alphabet
|
||||
/// by performing the following:
|
||||
/// <pre>
|
||||
/// int lookup[256];
|
||||
/// const byte ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
/// Base64Decoder::InitializeDecodingLookupArray(lookup, ALPHABET, 64, false);
|
||||
///
|
||||
/// Base64Decoder decoder;
|
||||
/// AlgorithmParameters params = MakeParameters(Name::DecodingLookupArray(),(const int *)lookup);
|
||||
/// decoder.IsolatedInitialize(params);</pre>
|
||||
/// \sa Base64URLDecoder for a decoder that provides the web safe alphabet, and Base64Encoder::IsolatedInitialize()
|
||||
/// for an example of modifying an encoder's alphabet after construction.
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
|
||||
private:
|
||||
/// \brief Provides the default decoding lookup table
|
||||
/// \return default decoding lookup table
|
||||
static const int * CRYPTOPP_API GetDecodingLookupArray();
|
||||
};
|
||||
|
||||
/// \brief Base64 encodes data using a web safe alphabet
|
||||
/// \details Base64 encodes data per <A HREF="http://tools.ietf.org/html/rfc4648#section-5">RFC 4648, Base 64 Encoding
|
||||
/// with URL and Filename Safe Alphabet</A>.
|
||||
class Base64URLEncoder : public SimpleProxyFilter
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base64URLEncoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \param insertLineBreaks a BufferedTrasformation to attach to this object
|
||||
/// \param maxLineLength the length of a line if line breaks are used
|
||||
/// \details Base64URLEncoder() constructs a default encoder using a web safe alphabet. The constructor ignores
|
||||
/// insertLineBreaks and maxLineLength because the web and URL safe specifications don't use them. They are
|
||||
/// present in the constructor for API compatibility with Base64Encoder so it is a drop-in replacement. The
|
||||
/// constructor also disables padding on the encoder for the same reason.
|
||||
/// \details If you need line breaks or padding, then you must use IsolatedInitialize() to set them
|
||||
/// after constructing a Base64URLEncoder.
|
||||
/// \sa Base64Encoder for an encoder that provides a classic alphabet, and Base64URLEncoder::IsolatedInitialize
|
||||
/// for an example of modifying an encoder after construction.
|
||||
Base64URLEncoder(BufferedTransformation *attachment = NULLPTR, bool insertLineBreaks = false, int maxLineLength = -1)
|
||||
: SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment)
|
||||
{
|
||||
CRYPTOPP_UNUSED(insertLineBreaks), CRYPTOPP_UNUSED(maxLineLength);
|
||||
IsolatedInitialize(MakeParameters(Name::InsertLineBreaks(), false)(Name::MaxLineLength(), -1)(Name::Pad(),false));
|
||||
}
|
||||
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on attached
|
||||
/// transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \details The following code modifies the padding and line break parameters for an encoder:
|
||||
/// <pre>
|
||||
/// Base64URLEncoder encoder;
|
||||
/// AlgorithmParameters params = MakeParameters(Name::Pad(), true)(Name::InsertLineBreaks(), true);
|
||||
/// encoder.IsolatedInitialize(params);</pre>
|
||||
/// \sa Base64Encoder for an encoder that provides a classic alphabet.
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
};
|
||||
|
||||
/// \brief Base64 decodes data using a web safe alphabet
|
||||
/// \details Base64 encodes data per <A HREF="http://tools.ietf.org/html/rfc4648#section-5">RFC 4648, Base 64 Encoding
|
||||
/// with URL and Filename Safe Alphabet</A>.
|
||||
class Base64URLDecoder : public BaseN_Decoder
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Base64URLDecoder
|
||||
/// \param attachment a BufferedTrasformation to attach to this object
|
||||
/// \details Base64URLDecoder() constructs a default decoder using a web safe alphabet.
|
||||
/// \sa Base64Decoder for a decoder that provides a classic alphabet.
|
||||
Base64URLDecoder(BufferedTransformation *attachment = NULLPTR)
|
||||
: BaseN_Decoder(GetDecodingLookupArray(), 6, attachment) {}
|
||||
|
||||
/// \brief Initialize or reinitialize this object, without signal propagation
|
||||
/// \param parameters a set of NameValuePairs used to initialize this object
|
||||
/// \details IsolatedInitialize() is used to initialize or reinitialize an object using a variable
|
||||
/// number of arbitrarily typed arguments. IsolatedInitialize() does not call Initialize() on
|
||||
/// attached transformations. If initialization should be propagated, then use the Initialize() function.
|
||||
/// \sa Base64Decoder for a decoder that provides a classic alphabet, and Base64URLEncoder::IsolatedInitialize
|
||||
/// for an example of modifying an encoder after construction.
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
|
||||
private:
|
||||
/// \brief Provides the default decoding lookup table
|
||||
/// \return default decoding lookup table
|
||||
static const int * CRYPTOPP_API GetDecodingLookupArray();
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
// basecode.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4100)
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_GCC_DIAGNOSTIC_AVAILABLE
|
||||
# pragma GCC diagnostic ignored "-Wunused-value"
|
||||
#endif
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "basecode.h"
|
||||
#include "fltrimpl.h"
|
||||
#include <ctype.h>
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void BaseN_Encoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
parameters.GetRequiredParameter("BaseN_Encoder", Name::EncodingLookupArray(), m_alphabet);
|
||||
|
||||
parameters.GetRequiredIntParameter("BaseN_Encoder", Name::Log2Base(), m_bitsPerChar);
|
||||
if (m_bitsPerChar <= 0 || m_bitsPerChar >= 8)
|
||||
throw InvalidArgument("BaseN_Encoder: Log2Base must be between 1 and 7 inclusive");
|
||||
|
||||
byte padding;
|
||||
bool pad;
|
||||
if (parameters.GetValue(Name::PaddingByte(), padding))
|
||||
pad = parameters.GetValueWithDefault(Name::Pad(), true);
|
||||
else
|
||||
pad = false;
|
||||
m_padding = pad ? padding : -1;
|
||||
|
||||
m_bytePos = m_bitPos = 0;
|
||||
|
||||
int i = 8;
|
||||
while (i%m_bitsPerChar != 0)
|
||||
i += 8;
|
||||
m_outputBlockSize = i/m_bitsPerChar;
|
||||
|
||||
m_outBuf.New(m_outputBlockSize);
|
||||
}
|
||||
|
||||
size_t BaseN_Encoder::Put2(const byte *begin, size_t length, int messageEnd, bool blocking)
|
||||
{
|
||||
FILTER_BEGIN;
|
||||
while (m_inputPosition < length)
|
||||
{
|
||||
if (m_bytePos == 0)
|
||||
memset(m_outBuf, 0, m_outputBlockSize);
|
||||
|
||||
{
|
||||
unsigned int b = begin[m_inputPosition++], bitsLeftInSource = 8;
|
||||
while (true)
|
||||
{
|
||||
CRYPTOPP_ASSERT(m_bitsPerChar-m_bitPos >= 0);
|
||||
unsigned int bitsLeftInTarget = (unsigned int)(m_bitsPerChar-m_bitPos);
|
||||
m_outBuf[m_bytePos] |= b >> (8-bitsLeftInTarget);
|
||||
if (bitsLeftInSource >= bitsLeftInTarget)
|
||||
{
|
||||
m_bitPos = 0;
|
||||
++m_bytePos;
|
||||
bitsLeftInSource -= bitsLeftInTarget;
|
||||
if (bitsLeftInSource == 0)
|
||||
break;
|
||||
b <<= bitsLeftInTarget;
|
||||
b &= 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bitPos += bitsLeftInSource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRYPTOPP_ASSERT(m_bytePos <= m_outputBlockSize);
|
||||
if (m_bytePos == m_outputBlockSize)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<m_bytePos; i++)
|
||||
{
|
||||
CRYPTOPP_ASSERT(m_outBuf[i] < (1 << m_bitsPerChar));
|
||||
m_outBuf[i] = m_alphabet[m_outBuf[i]];
|
||||
}
|
||||
FILTER_OUTPUT(1, m_outBuf, m_outputBlockSize, 0);
|
||||
|
||||
m_bytePos = m_bitPos = 0;
|
||||
}
|
||||
}
|
||||
if (messageEnd)
|
||||
{
|
||||
if (m_bitPos > 0)
|
||||
++m_bytePos;
|
||||
|
||||
int i;
|
||||
for (i=0; i<m_bytePos; i++)
|
||||
m_outBuf[i] = m_alphabet[m_outBuf[i]];
|
||||
|
||||
if (m_padding != -1 && m_bytePos > 0)
|
||||
{
|
||||
memset(m_outBuf+m_bytePos, m_padding, m_outputBlockSize-m_bytePos);
|
||||
m_bytePos = m_outputBlockSize;
|
||||
}
|
||||
FILTER_OUTPUT(2, m_outBuf, m_bytePos, messageEnd);
|
||||
m_bytePos = m_bitPos = 0;
|
||||
}
|
||||
FILTER_END_NO_MESSAGE_END;
|
||||
}
|
||||
|
||||
void BaseN_Decoder::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
parameters.GetRequiredParameter("BaseN_Decoder", Name::DecodingLookupArray(), m_lookup);
|
||||
|
||||
parameters.GetRequiredIntParameter("BaseN_Decoder", Name::Log2Base(), m_bitsPerChar);
|
||||
if (m_bitsPerChar <= 0 || m_bitsPerChar >= 8)
|
||||
throw InvalidArgument("BaseN_Decoder: Log2Base must be between 1 and 7 inclusive");
|
||||
|
||||
m_bytePos = m_bitPos = 0;
|
||||
|
||||
int i = m_bitsPerChar;
|
||||
while (i%8 != 0)
|
||||
i += m_bitsPerChar;
|
||||
m_outputBlockSize = i/8;
|
||||
|
||||
m_outBuf.New(m_outputBlockSize);
|
||||
}
|
||||
|
||||
size_t BaseN_Decoder::Put2(const byte *begin, size_t length, int messageEnd, bool blocking)
|
||||
{
|
||||
FILTER_BEGIN;
|
||||
while (m_inputPosition < length)
|
||||
{
|
||||
unsigned int value;
|
||||
value = m_lookup[begin[m_inputPosition++]];
|
||||
if (value >= 256)
|
||||
continue;
|
||||
|
||||
if (m_bytePos == 0 && m_bitPos == 0)
|
||||
memset(m_outBuf, 0, m_outputBlockSize);
|
||||
|
||||
{
|
||||
int newBitPos = m_bitPos + m_bitsPerChar;
|
||||
if (newBitPos <= 8)
|
||||
m_outBuf[m_bytePos] |= value << (8-newBitPos);
|
||||
else
|
||||
{
|
||||
m_outBuf[m_bytePos] |= value >> (newBitPos-8);
|
||||
m_outBuf[m_bytePos+1] |= value << (16-newBitPos);
|
||||
}
|
||||
|
||||
m_bitPos = newBitPos;
|
||||
while (m_bitPos >= 8)
|
||||
{
|
||||
m_bitPos -= 8;
|
||||
++m_bytePos;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bytePos == m_outputBlockSize)
|
||||
{
|
||||
FILTER_OUTPUT(1, m_outBuf, m_outputBlockSize, 0);
|
||||
m_bytePos = m_bitPos = 0;
|
||||
}
|
||||
}
|
||||
if (messageEnd)
|
||||
{
|
||||
FILTER_OUTPUT(2, m_outBuf, m_bytePos, messageEnd);
|
||||
m_bytePos = m_bitPos = 0;
|
||||
}
|
||||
FILTER_END_NO_MESSAGE_END;
|
||||
}
|
||||
|
||||
void BaseN_Decoder::InitializeDecodingLookupArray(int *lookup, const byte *alphabet, unsigned int base, bool caseInsensitive)
|
||||
{
|
||||
std::fill(lookup, lookup+256, -1);
|
||||
|
||||
for (unsigned int i=0; i<base; i++)
|
||||
{
|
||||
// Debug asserts for 'lookup[alphabet[i]] == -1' removed because the self tests
|
||||
// have unusal tests that try to break the encoders and decoders. Tests include
|
||||
// a string of the same characters. I.,e., a string of stars like '********...'.
|
||||
if (caseInsensitive && isalpha(alphabet[i]))
|
||||
{
|
||||
lookup[toupper(alphabet[i])] = i;
|
||||
lookup[tolower(alphabet[i])] = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
lookup[alphabet[i]] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Grouper::IsolatedInitialize(const NameValuePairs ¶meters)
|
||||
{
|
||||
m_groupSize = parameters.GetIntValueWithDefault(Name::GroupSize(), 0);
|
||||
ConstByteArrayParameter separator, terminator;
|
||||
if (m_groupSize)
|
||||
parameters.GetRequiredParameter("Grouper", Name::Separator(), separator);
|
||||
else
|
||||
parameters.GetValue(Name::Separator(), separator);
|
||||
parameters.GetValue(Name::Terminator(), terminator);
|
||||
|
||||
m_separator.Assign(separator.begin(), separator.size());
|
||||
m_terminator.Assign(terminator.begin(), terminator.size());
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
size_t Grouper::Put2(const byte *begin, size_t length, int messageEnd, bool blocking)
|
||||
{
|
||||
FILTER_BEGIN;
|
||||
if (m_groupSize)
|
||||
{
|
||||
while (m_inputPosition < length)
|
||||
{
|
||||
if (m_counter == m_groupSize)
|
||||
{
|
||||
FILTER_OUTPUT(1, m_separator, m_separator.size(), 0);
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
size_t len;
|
||||
FILTER_OUTPUT2(2, len = STDMIN(length-m_inputPosition, m_groupSize-m_counter),
|
||||
begin+m_inputPosition, len, 0);
|
||||
m_inputPosition += len;
|
||||
m_counter += len;
|
||||
}
|
||||
}
|
||||
else
|
||||
FILTER_OUTPUT(3, begin, length, 0);
|
||||
|
||||
if (messageEnd)
|
||||
{
|
||||
FILTER_OUTPUT(4, m_terminator, m_terminator.size(), messageEnd);
|
||||
m_counter = 0;
|
||||
}
|
||||
FILTER_END_NO_MESSAGE_END
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// basecode.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file
|
||||
/// \brief Base classes for working with encoders and decoders.
|
||||
|
||||
#ifndef CRYPTOPP_BASECODE_H
|
||||
#define CRYPTOPP_BASECODE_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "filters.h"
|
||||
#include "algparam.h"
|
||||
#include "argnames.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Encoder for bases that are a power of 2
|
||||
class CRYPTOPP_DLL BaseN_Encoder : public Unflushable<Filter>
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a BaseN_Encoder
|
||||
/// \param attachment a BufferedTransformation to attach to this object
|
||||
BaseN_Encoder(BufferedTransformation *attachment=NULLPTR)
|
||||
: m_alphabet(NULLPTR), m_padding(0), m_bitsPerChar(0)
|
||||
, m_outputBlockSize(0), m_bytePos(0), m_bitPos(0)
|
||||
{Detach(attachment);}
|
||||
|
||||
/// \brief Construct a BaseN_Encoder
|
||||
/// \param alphabet table of ASCII characters to use as the alphabet
|
||||
/// \param log2base the log<sub>2</sub>base
|
||||
/// \param attachment a BufferedTransformation to attach to this object
|
||||
/// \param padding the character to use as padding
|
||||
/// \pre log2base must be between 1 and 7 inclusive
|
||||
/// \throw InvalidArgument if log2base is not between 1 and 7
|
||||
BaseN_Encoder(const byte *alphabet, int log2base, BufferedTransformation *attachment=NULLPTR, int padding=-1)
|
||||
: m_alphabet(NULLPTR), m_padding(0), m_bitsPerChar(0)
|
||||
, m_outputBlockSize(0), m_bytePos(0), m_bitPos(0)
|
||||
{
|
||||
Detach(attachment);
|
||||
BaseN_Encoder::IsolatedInitialize(
|
||||
MakeParameters
|
||||
(Name::EncodingLookupArray(), alphabet)
|
||||
(Name::Log2Base(), log2base)
|
||||
(Name::Pad(), padding != -1)
|
||||
(Name::PaddingByte(), byte(padding)));
|
||||
}
|
||||
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking);
|
||||
|
||||
private:
|
||||
const byte *m_alphabet;
|
||||
int m_padding, m_bitsPerChar, m_outputBlockSize;
|
||||
int m_bytePos, m_bitPos;
|
||||
SecByteBlock m_outBuf;
|
||||
};
|
||||
|
||||
/// \brief Decoder for bases that are a power of 2
|
||||
class CRYPTOPP_DLL BaseN_Decoder : public Unflushable<Filter>
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a BaseN_Decoder
|
||||
/// \param attachment a BufferedTransformation to attach to this object
|
||||
/// \details padding is set to -1, which means use default padding. If not
|
||||
/// required, then the value must be set via IsolatedInitialize().
|
||||
BaseN_Decoder(BufferedTransformation *attachment=NULLPTR)
|
||||
: m_lookup(NULLPTR), m_bitsPerChar(0)
|
||||
, m_outputBlockSize(0), m_bytePos(0), m_bitPos(0)
|
||||
{Detach(attachment);}
|
||||
|
||||
/// \brief Construct a BaseN_Decoder
|
||||
/// \param lookup table of values
|
||||
/// \param log2base the log<sub>2</sub>base
|
||||
/// \param attachment a BufferedTransformation to attach to this object
|
||||
/// \details log2base is the exponent (like 5 in 2<sup>5</sup>), and not
|
||||
/// the number of elements (like 32).
|
||||
/// \details padding is set to -1, which means use default padding. If not
|
||||
/// required, then the value must be set via IsolatedInitialize().
|
||||
BaseN_Decoder(const int *lookup, int log2base, BufferedTransformation *attachment=NULLPTR)
|
||||
: m_lookup(NULLPTR), m_bitsPerChar(0)
|
||||
, m_outputBlockSize(0), m_bytePos(0), m_bitPos(0)
|
||||
{
|
||||
Detach(attachment);
|
||||
BaseN_Decoder::IsolatedInitialize(
|
||||
MakeParameters
|
||||
(Name::DecodingLookupArray(), lookup)
|
||||
(Name::Log2Base(), log2base));
|
||||
}
|
||||
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking);
|
||||
|
||||
/// \brief Initializes BaseN lookup array
|
||||
/// \param lookup table of values
|
||||
/// \param alphabet table of ASCII characters
|
||||
/// \param base the base for the encoder
|
||||
/// \param caseInsensitive flag indicating whether the alphabet is case sensitivie
|
||||
/// \pre COUNTOF(lookup) == 256
|
||||
/// \pre COUNTOF(alphabet) == base
|
||||
/// \details Internally, the function sets the first 256 elements in the lookup table to
|
||||
/// their value from the alphabet array or -1. base is the number of element (like 32),
|
||||
/// and not an exponent (like 5 in 2<sup>5</sup>)
|
||||
static void CRYPTOPP_API InitializeDecodingLookupArray(int *lookup, const byte *alphabet, unsigned int base, bool caseInsensitive);
|
||||
|
||||
private:
|
||||
const int *m_lookup;
|
||||
int m_bitsPerChar, m_outputBlockSize;
|
||||
int m_bytePos, m_bitPos;
|
||||
SecByteBlock m_outBuf;
|
||||
};
|
||||
|
||||
/// \brief Filter that breaks input stream into groups of fixed size
|
||||
class CRYPTOPP_DLL Grouper : public Bufferless<Filter>
|
||||
{
|
||||
public:
|
||||
/// \brief Construct a Grouper
|
||||
/// \param attachment a BufferedTransformation to attach to this object
|
||||
Grouper(BufferedTransformation *attachment=NULLPTR)
|
||||
: m_groupSize(0), m_counter(0) {Detach(attachment);}
|
||||
|
||||
/// \brief Construct a Grouper
|
||||
/// \param groupSize the size of the grouping
|
||||
/// \param separator the separator to use between groups
|
||||
/// \param terminator the terminator appeand after processing
|
||||
/// \param attachment a BufferedTransformation to attach to this object
|
||||
Grouper(int groupSize, const std::string &separator, const std::string &terminator, BufferedTransformation *attachment=NULLPTR)
|
||||
: m_groupSize(0), m_counter(0)
|
||||
{
|
||||
Detach(attachment);
|
||||
Grouper::IsolatedInitialize(
|
||||
MakeParameters
|
||||
(Name::GroupSize(), groupSize)
|
||||
(Name::Separator(), ConstByteArrayParameter(separator))
|
||||
(Name::Terminator(), ConstByteArrayParameter(terminator)));
|
||||
}
|
||||
|
||||
void IsolatedInitialize(const NameValuePairs ¶meters);
|
||||
size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking);
|
||||
|
||||
private:
|
||||
SecByteBlock m_separator, m_terminator;
|
||||
size_t m_groupSize, m_counter;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// bench.h - originally written and placed in the public domain by Wei Dai
|
||||
// CryptoPP::Test namespace added by JW in February 2017
|
||||
|
||||
#ifndef CRYPTOPP_BENCH_H
|
||||
#define CRYPTOPP_BENCH_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(Test)
|
||||
|
||||
// More granular control over benchmarks
|
||||
enum TestClass {
|
||||
/// \brief Random number generators
|
||||
UnkeyedRNG=(1<<0),
|
||||
/// \brief Message digests
|
||||
UnkeyedHash=(1<<1),
|
||||
/// \brief Other unkeyed algorithms
|
||||
UnkeyedOther=(1<<2),
|
||||
|
||||
/// \brief Message authentication codes
|
||||
SharedKeyMAC=(1<<3),
|
||||
/// \brief Stream ciphers
|
||||
SharedKeyStream=(1<<4),
|
||||
/// \brief Block ciphers ciphers
|
||||
SharedKeyBlock=(1<<5),
|
||||
/// \brief Other shared key algorithms
|
||||
SharedKeyOther=(1<<6),
|
||||
|
||||
/// \brief Key agreement algorithms over integers
|
||||
PublicKeyAgreement=(1<<7),
|
||||
/// \brief Encryption algorithms over integers
|
||||
PublicKeyEncryption=(1<<8),
|
||||
/// \brief Signature algorithms over integers
|
||||
PublicKeySignature=(1<<9),
|
||||
/// \brief Other public key algorithms over integers
|
||||
PublicKeyOther=(1<<10),
|
||||
|
||||
/// \brief Key agreement algorithms over EC
|
||||
PublicKeyAgreementEC=(1<<11),
|
||||
/// \brief Encryption algorithms over EC
|
||||
PublicKeyEncryptionEC=(1<<12),
|
||||
/// \brief Signature algorithms over EC
|
||||
PublicKeySignatureEC=(1<<13),
|
||||
/// \brief Other public key algorithms over EC
|
||||
PublicKeyOtherEC=(1<<14),
|
||||
|
||||
Unkeyed=UnkeyedRNG|UnkeyedHash|UnkeyedOther,
|
||||
SharedKey=SharedKeyMAC|SharedKeyStream|SharedKeyBlock|SharedKeyOther,
|
||||
PublicKey=PublicKeyAgreement|PublicKeyEncryption|PublicKeySignature|PublicKeyOther,
|
||||
PublicKeyEC=PublicKeyAgreementEC|PublicKeyEncryptionEC|PublicKeySignatureEC|PublicKeyOtherEC,
|
||||
|
||||
All=Unkeyed|SharedKey|PublicKey|PublicKeyEC,
|
||||
|
||||
TestFirst=(0), TestLast=(1<<15)
|
||||
};
|
||||
|
||||
extern const double CLOCK_TICKS_PER_SECOND;
|
||||
extern double g_allocatedTime;
|
||||
extern double g_hertz;
|
||||
extern double g_logTotal;
|
||||
extern unsigned int g_logCount;
|
||||
extern const byte defaultKey[];
|
||||
|
||||
// Test book keeping
|
||||
extern time_t g_testBegin;
|
||||
extern time_t g_testEnd;
|
||||
|
||||
// Benchmark command handler
|
||||
void BenchmarkWithCommand(int argc, const char* const argv[]);
|
||||
// Top level, prints preamble and postamble
|
||||
void Benchmark(Test::TestClass suites, double t, double hertz);
|
||||
// Unkeyed systems
|
||||
void BenchmarkUnkeyedAlgorithms(double t, double hertz);
|
||||
// Shared key systems
|
||||
void BenchmarkSharedKeyedAlgorithms(double t, double hertz);
|
||||
// Public key systems over integers
|
||||
void BenchmarkPublicKeyAlgorithms(double t, double hertz);
|
||||
// Public key systems over elliptic curves
|
||||
void BenchmarkEllipticCurveAlgorithms(double t, double hertz);
|
||||
|
||||
// These are defined in bench1.cpp
|
||||
extern void OutputResultKeying(double iterations, double timeTaken);
|
||||
extern void OutputResultBytes(const char *name, const char *provider, double length, double timeTaken);
|
||||
extern void OutputResultOperations(const char *name, const char *provider, const char *operation, bool pc, unsigned long iterations, double timeTaken);
|
||||
|
||||
// These are defined in bench1.cpp
|
||||
extern void BenchMark(const char *name, BufferedTransformation &bt, double timeTotal);
|
||||
extern void BenchMark(const char *name, StreamTransformation &cipher, double timeTotal);
|
||||
extern void BenchMark(const char *name, HashTransformation &ht, double timeTotal);
|
||||
extern void BenchMark(const char *name, RandomNumberGenerator &rng, double timeTotal);
|
||||
|
||||
// These are defined in bench2.cpp
|
||||
extern void BenchMarkKeying(SimpleKeyingInterface &c, size_t keyLength, const NameValuePairs ¶ms);
|
||||
extern void BenchMark(const char *name, AuthenticatedSymmetricCipher &cipher, double timeTotal);
|
||||
|
||||
NAMESPACE_END // Test
|
||||
NAMESPACE_END // CryptoPP
|
||||
|
||||
#endif
|
||||
+518
@@ -0,0 +1,518 @@
|
||||
// bench1.cpp - originally written and placed in the public domain by Wei Dai
|
||||
// CryptoPP::Test namespace added by JW in February 2017
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "bench.h"
|
||||
#include "validate.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "factory.h"
|
||||
#include "algparam.h"
|
||||
#include "argnames.h"
|
||||
#include "smartptr.h"
|
||||
#include "stdcpp.h"
|
||||
|
||||
#include "osrng.h"
|
||||
#include "drbg.h"
|
||||
#include "darn.h"
|
||||
#include "mersenne.h"
|
||||
#include "rdrand.h"
|
||||
#include "padlkrng.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4355)
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4505 4355)
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(Test)
|
||||
|
||||
#ifdef CLOCKS_PER_SEC
|
||||
const double CLOCK_TICKS_PER_SECOND = (double)CLOCKS_PER_SEC;
|
||||
#elif defined(CLK_TCK)
|
||||
const double CLOCK_TICKS_PER_SECOND = (double)CLK_TCK;
|
||||
#else
|
||||
const double CLOCK_TICKS_PER_SECOND = 1000000.0;
|
||||
#endif
|
||||
|
||||
extern const byte defaultKey[] = "0123456789" // 168 + NULL
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"00000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000000000000";
|
||||
|
||||
double g_allocatedTime = 0.0, g_hertz = 0.0, g_logTotal = 0.0;
|
||||
unsigned int g_logCount = 0;
|
||||
time_t g_testBegin, g_testEnd;
|
||||
|
||||
inline std::string HertzToString(double hertz)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss.precision(3);
|
||||
|
||||
if (hertz >= 0.999e+9)
|
||||
oss << hertz / 1e+9 << " GHz";
|
||||
else if (hertz >= 0.999e+6)
|
||||
oss << hertz / 1e+6 << " MHz";
|
||||
else if (hertz >= 0.999e+3)
|
||||
oss << hertz / 1e+3 << " KHz";
|
||||
else
|
||||
oss << hertz << " Hz";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void OutputResultBytes(const char *name, const char *provider, double length, double timeTaken)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
// Coverity finding
|
||||
if (length < 0.000001f) length = 0.000001f;
|
||||
if (timeTaken < 0.000001f) timeTaken = 0.000001f;
|
||||
|
||||
double mbs = length / timeTaken / (1024*1024);
|
||||
oss << "\n<TR><TD>" << name << "<TD>" << provider;
|
||||
oss << std::setiosflags(std::ios::fixed);
|
||||
oss << "<TD>" << std::setprecision(0) << std::setiosflags(std::ios::fixed) << mbs;
|
||||
if (g_hertz > 1.0f)
|
||||
{
|
||||
const double cpb = timeTaken * g_hertz / length;
|
||||
if (cpb < 24.0f)
|
||||
oss << "<TD>" << std::setprecision(2) << std::setiosflags(std::ios::fixed) << cpb;
|
||||
else
|
||||
oss << "<TD>" << std::setprecision(1) << std::setiosflags(std::ios::fixed) << cpb;
|
||||
}
|
||||
g_logTotal += log(mbs);
|
||||
g_logCount++;
|
||||
|
||||
std::cout << oss.str();
|
||||
}
|
||||
|
||||
void OutputResultKeying(double iterations, double timeTaken)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
// Coverity finding
|
||||
if (iterations < 0.000001f) iterations = 0.000001f;
|
||||
if (timeTaken < 0.000001f) timeTaken = 0.000001f;
|
||||
|
||||
oss << "<TD>" << std::setprecision(3) << std::setiosflags(std::ios::fixed) << (1000*1000*timeTaken/iterations);
|
||||
|
||||
// Coverity finding
|
||||
if (g_hertz > 1.0f)
|
||||
oss << "<TD>" << std::setprecision(0) << std::setiosflags(std::ios::fixed) << timeTaken * g_hertz / iterations;
|
||||
|
||||
std::cout << oss.str();
|
||||
}
|
||||
|
||||
void OutputResultOperations(const char *name, const char *provider, const char *operation, bool pc, unsigned long iterations, double timeTaken)
|
||||
{
|
||||
CRYPTOPP_UNUSED(provider);
|
||||
std::ostringstream oss;
|
||||
|
||||
// Coverity finding
|
||||
if (!iterations) iterations++;
|
||||
if (timeTaken < 0.000001f) timeTaken = 0.000001f;
|
||||
|
||||
oss << "\n<TR><TD>" << name << " " << operation << (pc ? " with precomputation" : "");
|
||||
//oss << "<TD>" << provider;
|
||||
oss << "<TD>" << std::setprecision(3) << std::setiosflags(std::ios::fixed) << (1000*timeTaken/iterations);
|
||||
|
||||
// Coverity finding
|
||||
if (g_hertz > 1.0f)
|
||||
{
|
||||
const double t = timeTaken * g_hertz / iterations / 1000000;
|
||||
oss << "<TD>" << std::setprecision(3) << std::setiosflags(std::ios::fixed) << t;
|
||||
}
|
||||
|
||||
g_logTotal += log(iterations/timeTaken);
|
||||
g_logCount++;
|
||||
|
||||
std::cout << oss.str();
|
||||
}
|
||||
|
||||
/*
|
||||
void BenchMark(const char *name, BlockTransformation &cipher, double timeTotal)
|
||||
{
|
||||
const int BUF_SIZE = RoundUpToMultipleOf(2048U, cipher.OptimalNumberOfParallelBlocks() * cipher.BlockSize());
|
||||
AlignedSecByteBlock buf(BUF_SIZE);
|
||||
buf.SetMark(16);
|
||||
|
||||
const int nBlocks = BUF_SIZE / cipher.BlockSize();
|
||||
unsigned long i=0, blocks=1;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
blocks *= 2;
|
||||
for (; i<blocks; i++)
|
||||
cipher.ProcessAndXorMultipleBlocks(buf, NULLPTR, buf, nBlocks);
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
}
|
||||
while (timeTaken < 2.0/3*timeTotal);
|
||||
|
||||
OutputResultBytes(name, double(blocks) * BUF_SIZE, timeTaken);
|
||||
}
|
||||
*/
|
||||
|
||||
void BenchMark(const char *name, StreamTransformation &cipher, double timeTotal)
|
||||
{
|
||||
const int BUF_SIZE=RoundUpToMultipleOf(2048U, cipher.OptimalBlockSize());
|
||||
AlignedSecByteBlock buf(BUF_SIZE);
|
||||
Test::GlobalRNG().GenerateBlock(buf, BUF_SIZE);
|
||||
buf.SetMark(16);
|
||||
|
||||
unsigned long i=0, blocks=1;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
blocks *= 2;
|
||||
for (; i<blocks; i++)
|
||||
cipher.ProcessString(buf, BUF_SIZE);
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
}
|
||||
while (timeTaken < 2.0/3*timeTotal);
|
||||
|
||||
std::string provider = cipher.AlgorithmProvider();
|
||||
OutputResultBytes(name, provider.c_str(), double(blocks) * BUF_SIZE, timeTaken);
|
||||
}
|
||||
|
||||
void BenchMark(const char *name, HashTransformation &ht, double timeTotal)
|
||||
{
|
||||
const int BUF_SIZE=2048U;
|
||||
AlignedSecByteBlock buf(BUF_SIZE);
|
||||
Test::GlobalRNG().GenerateBlock(buf, BUF_SIZE);
|
||||
buf.SetMark(16);
|
||||
|
||||
unsigned long i=0, blocks=1;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
blocks *= 2;
|
||||
for (; i<blocks; i++)
|
||||
ht.Update(buf, BUF_SIZE);
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
}
|
||||
while (timeTaken < 2.0/3*timeTotal);
|
||||
|
||||
std::string provider = ht.AlgorithmProvider();
|
||||
OutputResultBytes(name, provider.c_str(), double(blocks) * BUF_SIZE, timeTaken);
|
||||
}
|
||||
|
||||
void BenchMark(const char *name, BufferedTransformation &bt, double timeTotal)
|
||||
{
|
||||
const int BUF_SIZE=2048U;
|
||||
AlignedSecByteBlock buf(BUF_SIZE);
|
||||
Test::GlobalRNG().GenerateBlock(buf, BUF_SIZE);
|
||||
buf.SetMark(16);
|
||||
|
||||
unsigned long i=0, blocks=1;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
blocks *= 2;
|
||||
for (; i<blocks; i++)
|
||||
bt.Put(buf, BUF_SIZE);
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
}
|
||||
while (timeTaken < 2.0/3*timeTotal);
|
||||
|
||||
std::string provider = bt.AlgorithmProvider();
|
||||
OutputResultBytes(name, provider.c_str(), double(blocks) * BUF_SIZE, timeTaken);
|
||||
}
|
||||
|
||||
void BenchMark(const char *name, RandomNumberGenerator &rng, double timeTotal)
|
||||
{
|
||||
const int BUF_SIZE = 2048U;
|
||||
AlignedSecByteBlock buf(BUF_SIZE);
|
||||
Test::GlobalRNG().GenerateBlock(buf, BUF_SIZE);
|
||||
buf.SetMark(16);
|
||||
|
||||
SymmetricCipher * cipher = dynamic_cast<SymmetricCipher*>(&rng);
|
||||
if (cipher != NULLPTR)
|
||||
{
|
||||
const size_t size = cipher->DefaultKeyLength();
|
||||
if (cipher->IsResynchronizable())
|
||||
cipher->SetKeyWithIV(buf, size, buf+size);
|
||||
else
|
||||
cipher->SetKey(buf, size);
|
||||
}
|
||||
|
||||
unsigned long long blocks = 1;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
rng.GenerateBlock(buf, buf.size());
|
||||
blocks++;
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
} while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = rng.AlgorithmProvider();
|
||||
OutputResultBytes(name, provider.c_str(), double(blocks) * BUF_SIZE, timeTaken);
|
||||
}
|
||||
|
||||
// Hack, but we probably need a KeyedRandomNumberGenerator interface
|
||||
// and a few methods to generalize keying a RNG. X917RNG, Hash_DRBG,
|
||||
// HMAC_DRBG, AES/CFB RNG and a few others could use it. "A few others"
|
||||
// includes BLAKE2, ChaCha and Poly1305 when used as a RNG.
|
||||
void BenchMark(const char *name, NIST_DRBG &rng, double timeTotal)
|
||||
{
|
||||
const int BUF_SIZE = 2048U;
|
||||
AlignedSecByteBlock buf(BUF_SIZE);
|
||||
Test::GlobalRNG().GenerateBlock(buf, BUF_SIZE);
|
||||
buf.SetMark(16);
|
||||
|
||||
rng.IncorporateEntropy(buf, rng.MinEntropyLength());
|
||||
unsigned long long blocks = 1;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
rng.GenerateBlock(buf, buf.size());
|
||||
blocks++;
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
} while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = rng.AlgorithmProvider();
|
||||
OutputResultBytes(name, provider.c_str(), double(blocks) * BUF_SIZE, timeTaken);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BenchMarkByNameKeyLess(const char *factoryName, const char *displayName = NULLPTR, const NameValuePairs ¶ms = g_nullNameValuePairs)
|
||||
{
|
||||
CRYPTOPP_UNUSED(params);
|
||||
std::string name = factoryName;
|
||||
if (displayName)
|
||||
name = displayName;
|
||||
|
||||
member_ptr<T> obj(ObjectFactoryRegistry<T>::Registry().CreateObject(factoryName));
|
||||
BenchMark(name.c_str(), *obj, g_allocatedTime);
|
||||
}
|
||||
|
||||
void AddHtmlHeader()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
// HTML5
|
||||
oss << "<!DOCTYPE HTML>";
|
||||
oss << "\n<HTML lang=\"en\">";
|
||||
|
||||
oss << "\n<HEAD>";
|
||||
oss << "\n<META charset=\"UTF-8\">";
|
||||
oss << "\n<TITLE>Speed Comparison of Popular Crypto Algorithms</TITLE>";
|
||||
oss << "\n<STYLE>\n table {border-collapse: collapse;}";
|
||||
oss << "\n table, th, td, tr {border: 1px solid black;}\n</STYLE>";
|
||||
oss << "\n</HEAD>";
|
||||
|
||||
oss << "\n<BODY>";
|
||||
|
||||
oss << "\n<H1><A href=\"http://www.cryptopp.com\">Crypto++</A> " << CRYPTOPP_VERSION / 100;
|
||||
oss << '.' << (CRYPTOPP_VERSION % 100) / 10 << '.' << CRYPTOPP_VERSION % 10 << " Benchmarks</H1>";
|
||||
|
||||
oss << "\n<P>Here are speed benchmarks for some commonly used cryptographic algorithms.</P>";
|
||||
|
||||
if (g_hertz > 1.0f)
|
||||
oss << "\n<P>CPU frequency of the test platform is " << HertzToString(g_hertz) << ".</P>";
|
||||
else
|
||||
oss << "\n<P>CPU frequency of the test platform was not provided.</P>" << std::endl;
|
||||
|
||||
std::cout << oss.str();
|
||||
}
|
||||
|
||||
void AddHtmlFooter()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "\n</BODY>\n</HTML>\n";
|
||||
std::cout << oss.str();
|
||||
}
|
||||
|
||||
void BenchmarkWithCommand(int argc, const char* const argv[])
|
||||
{
|
||||
std::string command(argv[1]);
|
||||
float runningTime(argc >= 3 ? Test::StringToValue<float, true>(argv[2]) : 1.0f);
|
||||
float cpuFreq(argc >= 4 ? Test::StringToValue<float, true>(argv[3])*float(1e9) : 0.0f);
|
||||
std::string algoName(argc >= 5 ? argv[4] : "");
|
||||
|
||||
// https://github.com/weidai11/cryptopp/issues/983
|
||||
if (runningTime > 10.0f)
|
||||
runningTime = 10.0f;
|
||||
|
||||
if (command == "b") // All benchmarks
|
||||
Benchmark(Test::All, runningTime, cpuFreq);
|
||||
else if (command == "b4") // Public key algorithms over EC
|
||||
Test::Benchmark(Test::PublicKeyEC, runningTime, cpuFreq);
|
||||
else if (command == "b3") // Public key algorithms
|
||||
Test::Benchmark(Test::PublicKey, runningTime, cpuFreq);
|
||||
else if (command == "b2") // Shared key algorithms
|
||||
Test::Benchmark(Test::SharedKey, runningTime, cpuFreq);
|
||||
else if (command == "b1") // Unkeyed algorithms
|
||||
Test::Benchmark(Test::Unkeyed, runningTime, cpuFreq);
|
||||
}
|
||||
|
||||
void Benchmark(Test::TestClass suites, double t, double hertz)
|
||||
{
|
||||
g_allocatedTime = t;
|
||||
g_hertz = hertz;
|
||||
|
||||
// Add <br> in between tables
|
||||
size_t count_breaks = 0;
|
||||
|
||||
AddHtmlHeader();
|
||||
|
||||
g_testBegin = ::time(NULLPTR);
|
||||
|
||||
if (static_cast<int>(suites) == 0 || static_cast<int>(suites) > TestLast)
|
||||
suites = Test::All;
|
||||
|
||||
// Unkeyed algorithms
|
||||
if (suites & Test::Unkeyed)
|
||||
{
|
||||
if (count_breaks)
|
||||
std::cout << "\n<BR>";
|
||||
count_breaks++;
|
||||
|
||||
BenchmarkUnkeyedAlgorithms(t, hertz);
|
||||
}
|
||||
|
||||
// Shared key algorithms
|
||||
if (suites & Test::SharedKey)
|
||||
{
|
||||
if (count_breaks)
|
||||
std::cout << "\n<BR>";
|
||||
count_breaks++;
|
||||
|
||||
BenchmarkSharedKeyedAlgorithms(t, hertz);
|
||||
}
|
||||
|
||||
// Public key algorithms
|
||||
if (suites & Test::PublicKey)
|
||||
{
|
||||
if (count_breaks)
|
||||
std::cout << "\n<BR>";
|
||||
count_breaks++;
|
||||
|
||||
BenchmarkPublicKeyAlgorithms(t, hertz);
|
||||
}
|
||||
|
||||
// Public key algorithms over EC
|
||||
if (suites & Test::PublicKeyEC)
|
||||
{
|
||||
if (count_breaks)
|
||||
std::cout << "\n<BR>";
|
||||
count_breaks++;
|
||||
|
||||
BenchmarkEllipticCurveAlgorithms(t, hertz);
|
||||
}
|
||||
|
||||
g_testEnd = ::time(NULLPTR);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "\n<P>Throughput Geometric Average: " << std::setiosflags(std::ios::fixed);
|
||||
oss << std::exp(g_logTotal/(g_logCount > 0.0f ? g_logCount : 1.0f)) << std::endl;
|
||||
|
||||
oss << "\n<P>Test started at " << TimeToString(g_testBegin);
|
||||
oss << "\n<BR>Test ended at " << TimeToString(g_testEnd);
|
||||
oss << "\n";
|
||||
std::cout << oss.str();
|
||||
|
||||
AddHtmlFooter();
|
||||
}
|
||||
|
||||
void BenchmarkUnkeyedAlgorithms(double t, double hertz)
|
||||
{
|
||||
g_allocatedTime = t;
|
||||
g_hertz = hertz;
|
||||
|
||||
const char *cpb;
|
||||
if (g_hertz > 1.0f)
|
||||
cpb = "<TH>Cycles/Byte";
|
||||
else
|
||||
cpb = "";
|
||||
|
||||
std::cout << "\n<TABLE>";
|
||||
|
||||
std::cout << "\n<COLGROUP><COL style=\"text-align: left;\"><COL style=\"text-align: right;\">";
|
||||
std::cout << "<COL style=\"text-align: right;\">";
|
||||
std::cout << "\n<THEAD style=\"background: #F0F0F0\">";
|
||||
std::cout << "\n<TR><TH>Algorithm<TH>Provider<TH>MiB/Second" << cpb;
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
#ifdef NONBLOCKING_RNG_AVAILABLE
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("NonblockingRng");
|
||||
#endif
|
||||
#ifdef OS_RNG_AVAILABLE
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("AutoSeededRandomPool");
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("AutoSeededX917RNG(AES)");
|
||||
#endif
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("MT19937");
|
||||
#if (CRYPTOPP_BOOL_X86) && !defined(CRYPTOPP_DISABLE_ASM)
|
||||
if (HasPadlockRNG())
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("PadlockRNG");
|
||||
#endif
|
||||
#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) && !defined(CRYPTOPP_DISABLE_ASM)
|
||||
if (HasRDRAND())
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("RDRAND");
|
||||
if (HasRDSEED())
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("RDSEED");
|
||||
#endif
|
||||
#if (CRYPTOPP_BOOL_PPC32 || CRYPTOPP_BOOL_PPC64) && !defined(CRYPTOPP_DISABLE_ASM)
|
||||
if (HasDARN())
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("DARN");
|
||||
#endif
|
||||
BenchMarkByNameKeyLess<RandomNumberGenerator>("AES/OFB RNG");
|
||||
BenchMarkByNameKeyLess<NIST_DRBG>("Hash_DRBG(SHA1)");
|
||||
BenchMarkByNameKeyLess<NIST_DRBG>("Hash_DRBG(SHA256)");
|
||||
BenchMarkByNameKeyLess<NIST_DRBG>("HMAC_DRBG(SHA1)");
|
||||
BenchMarkByNameKeyLess<NIST_DRBG>("HMAC_DRBG(SHA256)");
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
BenchMarkByNameKeyLess<HashTransformation>("CRC32");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("CRC32C");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Adler32");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("MD5");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA-1");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA-256");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA-512");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA3-224");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA3-256");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA3-384");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SHA3-512");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Keccak-224");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Keccak-256");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Keccak-384");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Keccak-512");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Tiger");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("Whirlpool");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("RIPEMD-160");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("RIPEMD-320");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("RIPEMD-128");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("RIPEMD-256");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("SM3");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("BLAKE2s");
|
||||
BenchMarkByNameKeyLess<HashTransformation>("BLAKE2b");
|
||||
}
|
||||
|
||||
std::cout << "\n</TABLE>" << std::endl;
|
||||
}
|
||||
|
||||
NAMESPACE_END // Test
|
||||
NAMESPACE_END // CryptoPP
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
// bench2.cpp - originally written and placed in the public domain by Wei Dai
|
||||
// CryptoPP::Test namespace added by JW in February 2017
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "bench.h"
|
||||
#include "validate.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "factory.h"
|
||||
#include "algparam.h"
|
||||
#include "argnames.h"
|
||||
#include "smartptr.h"
|
||||
#include "stdcpp.h"
|
||||
|
||||
#include "vmac.h"
|
||||
#include "hmac.h"
|
||||
#include "ttmac.h"
|
||||
#include "cmac.h"
|
||||
#include "dmac.h"
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4355)
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4505 4355)
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(Test)
|
||||
|
||||
void BenchMarkKeying(SimpleKeyingInterface &c, size_t keyLength, const NameValuePairs ¶ms)
|
||||
{
|
||||
unsigned long iterations = 0;
|
||||
double timeTaken;
|
||||
|
||||
clock_t start = ::clock();
|
||||
do
|
||||
{
|
||||
for (unsigned int i=0; i<1024; i++)
|
||||
c.SetKey(defaultKey, keyLength, params);
|
||||
timeTaken = double(::clock() - start) / CLOCK_TICKS_PER_SECOND;
|
||||
iterations += 1024;
|
||||
}
|
||||
while (timeTaken < g_allocatedTime);
|
||||
|
||||
OutputResultKeying(iterations, timeTaken);
|
||||
}
|
||||
|
||||
void BenchMark(const char *name, AuthenticatedSymmetricCipher &cipher, double timeTotal)
|
||||
{
|
||||
if (cipher.NeedsPrespecifiedDataLengths())
|
||||
cipher.SpecifyDataLengths(0, cipher.MaxMessageLength(), 0);
|
||||
|
||||
BenchMark(name, static_cast<StreamTransformation &>(cipher), timeTotal);
|
||||
}
|
||||
|
||||
template <class T_FactoryOutput, class T_Interface>
|
||||
void BenchMarkByName2(const char *factoryName, size_t keyLength=0, const char *displayName=NULLPTR, const NameValuePairs ¶ms = g_nullNameValuePairs)
|
||||
{
|
||||
std::string name(factoryName ? factoryName : "");
|
||||
member_ptr<T_FactoryOutput> obj(ObjectFactoryRegistry<T_FactoryOutput>::Registry().CreateObject(name.c_str()));
|
||||
|
||||
if (keyLength == 0)
|
||||
keyLength = obj->DefaultKeyLength();
|
||||
|
||||
if (displayName != NULLPTR)
|
||||
name = displayName;
|
||||
else if (keyLength != 0)
|
||||
name += " (" + IntToString(keyLength * 8) + "-bit key)";
|
||||
|
||||
obj->SetKey(defaultKey, keyLength, CombinedNameValuePairs(params, MakeParameters(Name::IV(), ConstByteArrayParameter(defaultKey, obj->IVSize()), false)));
|
||||
BenchMark(name.c_str(), *static_cast<T_Interface *>(obj.get()), g_allocatedTime);
|
||||
BenchMarkKeying(*obj, keyLength, CombinedNameValuePairs(params, MakeParameters(Name::IV(), ConstByteArrayParameter(defaultKey, obj->IVSize()), false)));
|
||||
}
|
||||
|
||||
template <class T_FactoryOutput>
|
||||
void BenchMarkByName(const char *factoryName, size_t keyLength=0, const char *displayName=NULLPTR, const NameValuePairs ¶ms = g_nullNameValuePairs)
|
||||
{
|
||||
BenchMarkByName2<T_FactoryOutput,T_FactoryOutput>(factoryName, keyLength, displayName, params);
|
||||
}
|
||||
|
||||
void BenchmarkSharedKeyedAlgorithms(double t, double hertz)
|
||||
{
|
||||
g_allocatedTime = t;
|
||||
g_hertz = hertz;
|
||||
|
||||
const char *cpb, *cpk;
|
||||
if (g_hertz > 1.0f)
|
||||
{
|
||||
cpb = "<TH>Cycles/Byte";
|
||||
cpk = "<TH>Cycles to<BR>Setup Key and IV";
|
||||
}
|
||||
else
|
||||
{
|
||||
cpb = cpk = "";
|
||||
}
|
||||
|
||||
std::cout << "\n<TABLE>";
|
||||
std::cout << "\n<COLGROUP><COL style=\"text-align: left;\"><COL style=\"text-align: right;\"><COL style=";
|
||||
std::cout << "\"text-align: right;\"><COL style=\"text-align: right;\"><COL style=\"text-align: right;\">";
|
||||
std::cout << "\n<THEAD style=\"background: #F0F0F0\">";
|
||||
std::cout << "\n<TR><TH>Algorithm<TH>Provider<TH>MiB/Second" << cpb;
|
||||
std::cout << "<TH>Microseconds to<BR>Setup Key and IV" << cpk;
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
#if CRYPTOPP_AESNI_AVAILABLE
|
||||
if (HasCLMUL())
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, MessageAuthenticationCode>("AES/GCM", 0, "GMAC(AES)");
|
||||
else
|
||||
#elif CRYPTOPP_ARM_PMULL_AVAILABLE
|
||||
if (HasPMULL())
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, MessageAuthenticationCode>("AES/GCM", 0, "GMAC(AES)");
|
||||
else
|
||||
#elif CRYPTOPP_POWER8_VMULL_AVAILABLE
|
||||
if (HasPMULL())
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, MessageAuthenticationCode>("AES/GCM", 0, "GMAC(AES)");
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, MessageAuthenticationCode>("AES/GCM", 0, "GMAC(AES) (2K tables)", MakeParameters(Name::TableSize(), 2048));
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, MessageAuthenticationCode>("AES/GCM", 0, "GMAC(AES) (64K tables)", MakeParameters(Name::TableSize(), 64 * 1024));
|
||||
}
|
||||
|
||||
BenchMarkByName<MessageAuthenticationCode>("VMAC(AES)-64");
|
||||
BenchMarkByName<MessageAuthenticationCode>("VMAC(AES)-128");
|
||||
BenchMarkByName<MessageAuthenticationCode>("HMAC(SHA-1)");
|
||||
BenchMarkByName<MessageAuthenticationCode>("HMAC(SHA-256)");
|
||||
BenchMarkByName<MessageAuthenticationCode>("Two-Track-MAC");
|
||||
BenchMarkByName<MessageAuthenticationCode>("CMAC(AES)");
|
||||
BenchMarkByName<MessageAuthenticationCode>("DMAC(AES)");
|
||||
BenchMarkByName<MessageAuthenticationCode>("Poly1305(AES)");
|
||||
BenchMarkByName<MessageAuthenticationCode>("Poly1305TLS");
|
||||
BenchMarkByName<MessageAuthenticationCode>("BLAKE2s");
|
||||
BenchMarkByName<MessageAuthenticationCode>("BLAKE2b");
|
||||
BenchMarkByName<MessageAuthenticationCode>("SipHash-2-4");
|
||||
BenchMarkByName<MessageAuthenticationCode>("SipHash-4-8");
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
BenchMarkByName<SymmetricCipher>("Panama-LE");
|
||||
BenchMarkByName<SymmetricCipher>("Panama-BE");
|
||||
BenchMarkByName<SymmetricCipher>("Salsa20", 0, "Salsa20");
|
||||
BenchMarkByName<SymmetricCipher>("Salsa20", 0, "Salsa20/12", MakeParameters(Name::Rounds(), 12));
|
||||
BenchMarkByName<SymmetricCipher>("Salsa20", 0, "Salsa20/8", MakeParameters(Name::Rounds(), 8));
|
||||
BenchMarkByName<SymmetricCipher>("ChaCha", 0, "ChaCha20");
|
||||
BenchMarkByName<SymmetricCipher>("ChaCha", 0, "ChaCha12", MakeParameters(Name::Rounds(), 12));
|
||||
BenchMarkByName<SymmetricCipher>("ChaCha", 0, "ChaCha8", MakeParameters(Name::Rounds(), 8));
|
||||
BenchMarkByName<SymmetricCipher>("ChaChaTLS");
|
||||
BenchMarkByName<SymmetricCipher>("Sosemanuk");
|
||||
BenchMarkByName<SymmetricCipher>("Rabbit");
|
||||
BenchMarkByName<SymmetricCipher>("RabbitWithIV");
|
||||
BenchMarkByName<SymmetricCipher>("HC-128");
|
||||
BenchMarkByName<SymmetricCipher>("HC-256");
|
||||
BenchMarkByName<SymmetricCipher>("MARC4");
|
||||
BenchMarkByName<SymmetricCipher>("SEAL-3.0-LE");
|
||||
BenchMarkByName<SymmetricCipher>("WAKE-OFB-LE");
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
BenchMarkByName<SymmetricCipher>("AES/CTR", 16);
|
||||
BenchMarkByName<SymmetricCipher>("AES/CTR", 24);
|
||||
BenchMarkByName<SymmetricCipher>("AES/CTR", 32);
|
||||
BenchMarkByName<SymmetricCipher>("AES/CBC", 16);
|
||||
BenchMarkByName<SymmetricCipher>("AES/CBC", 24);
|
||||
BenchMarkByName<SymmetricCipher>("AES/CBC", 32);
|
||||
BenchMarkByName<SymmetricCipher>("AES/XTS", 32);
|
||||
BenchMarkByName<SymmetricCipher>("AES/XTS", 48);
|
||||
BenchMarkByName<SymmetricCipher>("AES/XTS", 64);
|
||||
BenchMarkByName<SymmetricCipher>("AES/OFB", 16);
|
||||
BenchMarkByName<SymmetricCipher>("AES/CFB", 16);
|
||||
BenchMarkByName<SymmetricCipher>("AES/ECB", 16);
|
||||
BenchMarkByName<SymmetricCipher>("ARIA/CTR", 16);
|
||||
BenchMarkByName<SymmetricCipher>("ARIA/CTR", 32);
|
||||
BenchMarkByName<SymmetricCipher>("HIGHT/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("Camellia/CTR", 16);
|
||||
BenchMarkByName<SymmetricCipher>("Camellia/CTR", 32);
|
||||
BenchMarkByName<SymmetricCipher>("Twofish/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("Threefish-256(256)/CTR", 32);
|
||||
BenchMarkByName<SymmetricCipher>("Threefish-512(512)/CTR", 64);
|
||||
BenchMarkByName<SymmetricCipher>("Threefish-1024(1024)/CTR", 128);
|
||||
BenchMarkByName<SymmetricCipher>("Serpent/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("CAST-128/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("CAST-256/CTR", 32);
|
||||
BenchMarkByName<SymmetricCipher>("RC6/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("MARS/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("SHACAL-2/CTR", 16);
|
||||
BenchMarkByName<SymmetricCipher>("SHACAL-2/CTR", 64);
|
||||
BenchMarkByName<SymmetricCipher>("DES/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("DES-XEX3/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("DES-EDE3/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("IDEA/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("RC5/CTR", 0, "RC5 (r=16)");
|
||||
BenchMarkByName<SymmetricCipher>("Blowfish/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("SKIPJACK/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("SEED/CTR", 0, "SEED/CTR (1/2 K table)");
|
||||
BenchMarkByName<SymmetricCipher>("SM4/CTR");
|
||||
|
||||
BenchMarkByName<SymmetricCipher>("Kalyna-128/CTR", 16, "Kalyna-128(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("Kalyna-128/CTR", 32, "Kalyna-128(256)/CTR (256-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("Kalyna-256/CTR", 32, "Kalyna-256(256)/CTR (256-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("Kalyna-256/CTR", 64, "Kalyna-256(512)/CTR (512-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("Kalyna-512/CTR", 64, "Kalyna-512(512)/CTR (512-bit key)");
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
BenchMarkByName<SymmetricCipher>("CHAM-64/CTR", 16, "CHAM-64(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("CHAM-128/CTR", 16, "CHAM-128(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("CHAM-128/CTR", 32, "CHAM-128(256)/CTR (256-bit key)");
|
||||
|
||||
BenchMarkByName<SymmetricCipher>("LEA-128/CTR", 16, "LEA-128(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("LEA-128/CTR", 24, "LEA-128(192)/CTR (192-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("LEA-128/CTR", 32, "LEA-128(256)/CTR (256-bit key)");
|
||||
|
||||
BenchMarkByName<SymmetricCipher>("SIMECK-32/CTR", 8, "SIMECK-32(64)/CTR (64-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SIMECK-64/CTR", 16, "SIMECK-64(128)/CTR (128-bit key)");
|
||||
|
||||
BenchMarkByName<SymmetricCipher>("SIMON-64/CTR", 12, "SIMON-64(96)/CTR (96-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SIMON-64/CTR", 16, "SIMON-64(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SIMON-128/CTR", 16, "SIMON-128(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SIMON-128/CTR", 24, "SIMON-128(192)/CTR (192-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SIMON-128/CTR", 32, "SIMON-128(256)/CTR (256-bit key)");
|
||||
|
||||
BenchMarkByName<SymmetricCipher>("SPECK-64/CTR", 12, "SPECK-64(96)/CTR (96-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SPECK-64/CTR", 16, "SPECK-64(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SPECK-128/CTR", 16, "SPECK-128(128)/CTR (128-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SPECK-128/CTR", 24, "SPECK-128(192)/CTR (192-bit key)");
|
||||
BenchMarkByName<SymmetricCipher>("SPECK-128/CTR", 32, "SPECK-128(256)/CTR (256-bit key)");
|
||||
|
||||
BenchMarkByName<SymmetricCipher>("TEA/CTR");
|
||||
BenchMarkByName<SymmetricCipher>("XTEA/CTR");
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
#if CRYPTOPP_AESNI_AVAILABLE
|
||||
if (HasCLMUL())
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/GCM", 0, "AES/GCM");
|
||||
else
|
||||
#elif CRYPTOPP_ARM_PMULL_AVAILABLE
|
||||
if (HasPMULL())
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/GCM", 0, "AES/GCM");
|
||||
else
|
||||
#elif CRYPTOPP_POWER8_VMULL_AVAILABLE
|
||||
if (HasPMULL())
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/GCM", 0, "AES/GCM");
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/GCM", 0, "AES/GCM (2K tables)", MakeParameters(Name::TableSize(), 2048));
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/GCM", 0, "AES/GCM (64K tables)", MakeParameters(Name::TableSize(), 64 * 1024));
|
||||
}
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/CCM");
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("AES/EAX");
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("ChaCha20/Poly1305");
|
||||
BenchMarkByName2<AuthenticatedSymmetricCipher, AuthenticatedSymmetricCipher>("XChaCha20/Poly1305");
|
||||
}
|
||||
|
||||
std::cout << "\n</TABLE>" << std::endl;
|
||||
}
|
||||
|
||||
NAMESPACE_END // Test
|
||||
NAMESPACE_END // CryptoPP
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
// bench3.cpp - originally written and placed in the public domain by Wei Dai
|
||||
// CryptoPP::Test namespace added by JW in February 2017
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "bench.h"
|
||||
#include "validate.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "factory.h"
|
||||
#include "algparam.h"
|
||||
#include "argnames.h"
|
||||
#include "smartptr.h"
|
||||
#include "stdcpp.h"
|
||||
|
||||
#include "pubkey.h"
|
||||
#include "gfpcrypt.h"
|
||||
#include "eccrypto.h"
|
||||
#include "pkcspad.h"
|
||||
|
||||
#include "files.h"
|
||||
#include "filters.h"
|
||||
#include "hex.h"
|
||||
#include "rsa.h"
|
||||
#include "nr.h"
|
||||
#include "dsa.h"
|
||||
#include "luc.h"
|
||||
#include "rw.h"
|
||||
#include "ecp.h"
|
||||
#include "ec2n.h"
|
||||
#include "asn.h"
|
||||
#include "dh.h"
|
||||
#include "mqv.h"
|
||||
#include "hmqv.h"
|
||||
#include "fhmqv.h"
|
||||
#include "xed25519.h"
|
||||
#include "xtrcrypt.h"
|
||||
#include "esign.h"
|
||||
#include "pssr.h"
|
||||
#include "oids.h"
|
||||
#include "randpool.h"
|
||||
#include "stdcpp.h"
|
||||
#include "hrtimer.h"
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4505 4355)
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
NAMESPACE_BEGIN(Test)
|
||||
|
||||
void BenchMarkEncryption(const char *name, PK_Encryptor &key, double timeTotal, bool pc = false)
|
||||
{
|
||||
unsigned int len = 16;
|
||||
SecByteBlock plaintext(len), ciphertext(key.CiphertextLength(len));
|
||||
Test::GlobalRNG().GenerateBlock(plaintext, len);
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
key.Encrypt(Test::GlobalRNG(), plaintext, len, ciphertext);
|
||||
++i; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = key.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Encryption", pc, i, timeTaken);
|
||||
|
||||
if (!pc && key.GetMaterial().SupportsPrecomputation())
|
||||
{
|
||||
key.AccessMaterial().Precompute(16);
|
||||
BenchMarkEncryption(name, key, timeTotal, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BenchMarkDecryption(const char *name, PK_Decryptor &priv, PK_Encryptor &pub, double timeTotal)
|
||||
{
|
||||
unsigned int len = 16;
|
||||
SecByteBlock ciphertext(pub.CiphertextLength(len));
|
||||
SecByteBlock plaintext(pub.MaxPlaintextLength(ciphertext.size()));
|
||||
Test::GlobalRNG().GenerateBlock(plaintext, len);
|
||||
pub.Encrypt(Test::GlobalRNG(), plaintext, len, ciphertext);
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
priv.Decrypt(Test::GlobalRNG(), ciphertext, ciphertext.size(), plaintext);
|
||||
++i; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = priv.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Decryption", false, i, timeTaken);
|
||||
}
|
||||
|
||||
void BenchMarkSigning(const char *name, PK_Signer &key, double timeTotal, bool pc=false)
|
||||
{
|
||||
unsigned int len = 16;
|
||||
AlignedSecByteBlock message(len), signature(key.SignatureLength());
|
||||
Test::GlobalRNG().GenerateBlock(message, len);
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
(void)key.SignMessage(Test::GlobalRNG(), message, len, signature);
|
||||
++i; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = key.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Signature", pc, i, timeTaken);
|
||||
|
||||
if (!pc && key.GetMaterial().SupportsPrecomputation())
|
||||
{
|
||||
key.AccessMaterial().Precompute(16);
|
||||
BenchMarkSigning(name, key, timeTotal, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BenchMarkVerification(const char *name, const PK_Signer &priv, PK_Verifier &pub, double timeTotal, bool pc=false)
|
||||
{
|
||||
unsigned int len = 16;
|
||||
AlignedSecByteBlock message(len), signature(pub.SignatureLength());
|
||||
Test::GlobalRNG().GenerateBlock(message, len);
|
||||
priv.SignMessage(Test::GlobalRNG(), message, len, signature);
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
(void)pub.VerifyMessage(message, len, signature, signature.size());
|
||||
++i; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = pub.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Verification", pc, i, timeTaken);
|
||||
|
||||
if (!pc && pub.GetMaterial().SupportsPrecomputation())
|
||||
{
|
||||
pub.AccessMaterial().Precompute(16);
|
||||
BenchMarkVerification(name, priv, pub, timeTotal, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BenchMarkKeyGen(const char *name, SimpleKeyAgreementDomain &d, double timeTotal, bool pc=false)
|
||||
{
|
||||
SecByteBlock priv(d.PrivateKeyLength()), pub(d.PublicKeyLength());
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
d.GenerateKeyPair(Test::GlobalRNG(), priv, pub);
|
||||
++i; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = d.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Key-Pair Generation", pc, i, timeTaken);
|
||||
|
||||
if (!pc && d.GetMaterial().SupportsPrecomputation())
|
||||
{
|
||||
d.AccessMaterial().Precompute(16);
|
||||
BenchMarkKeyGen(name, d, timeTotal, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BenchMarkKeyGen(const char *name, AuthenticatedKeyAgreementDomain &d, double timeTotal, bool pc=false)
|
||||
{
|
||||
SecByteBlock priv(d.EphemeralPrivateKeyLength()), pub(d.EphemeralPublicKeyLength());
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
d.GenerateEphemeralKeyPair(Test::GlobalRNG(), priv, pub);
|
||||
++i; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = d.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Key-Pair Generation", pc, i, timeTaken);
|
||||
|
||||
if (!pc && d.GetMaterial().SupportsPrecomputation())
|
||||
{
|
||||
d.AccessMaterial().Precompute(16);
|
||||
BenchMarkKeyGen(name, d, timeTotal, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BenchMarkAgreement(const char *name, SimpleKeyAgreementDomain &d, double timeTotal, bool pc=false)
|
||||
{
|
||||
SecByteBlock priv1(d.PrivateKeyLength()), priv2(d.PrivateKeyLength());
|
||||
SecByteBlock pub1(d.PublicKeyLength()), pub2(d.PublicKeyLength());
|
||||
d.GenerateKeyPair(Test::GlobalRNG(), priv1, pub1);
|
||||
d.GenerateKeyPair(Test::GlobalRNG(), priv2, pub2);
|
||||
SecByteBlock val(d.AgreedValueLength());
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
d.Agree(val, priv1, pub2);
|
||||
d.Agree(val, priv2, pub1);
|
||||
i+=2; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = d.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Key Agreement", pc, i, timeTaken);
|
||||
}
|
||||
|
||||
void BenchMarkAgreement(const char *name, AuthenticatedKeyAgreementDomain &d, double timeTotal, bool pc=false)
|
||||
{
|
||||
SecByteBlock spriv1(d.StaticPrivateKeyLength()), spriv2(d.StaticPrivateKeyLength());
|
||||
SecByteBlock epriv1(d.EphemeralPrivateKeyLength()), epriv2(d.EphemeralPrivateKeyLength());
|
||||
SecByteBlock spub1(d.StaticPublicKeyLength()), spub2(d.StaticPublicKeyLength());
|
||||
SecByteBlock epub1(d.EphemeralPublicKeyLength()), epub2(d.EphemeralPublicKeyLength());
|
||||
d.GenerateStaticKeyPair(Test::GlobalRNG(), spriv1, spub1);
|
||||
d.GenerateStaticKeyPair(Test::GlobalRNG(), spriv2, spub2);
|
||||
d.GenerateEphemeralKeyPair(Test::GlobalRNG(), epriv1, epub1);
|
||||
d.GenerateEphemeralKeyPair(Test::GlobalRNG(), epriv2, epub2);
|
||||
SecByteBlock val(d.AgreedValueLength());
|
||||
|
||||
unsigned int i = 0;
|
||||
double timeTaken;
|
||||
|
||||
ThreadUserTimer timer;
|
||||
timer.StartTimer();
|
||||
|
||||
do
|
||||
{
|
||||
d.Agree(val, spriv1, epriv1, spub2, epub2);
|
||||
d.Agree(val, spriv2, epriv2, spub1, epub1);
|
||||
i+=2; timeTaken = timer.ElapsedTimeAsDouble();
|
||||
}
|
||||
while (timeTaken < timeTotal);
|
||||
|
||||
std::string provider = d.AlgorithmProvider();
|
||||
OutputResultOperations(name, provider.c_str(), "Key Agreement", pc, i, timeTaken);
|
||||
}
|
||||
|
||||
template <class SCHEME>
|
||||
void BenchMarkCrypto(const char *filename, const char *name, double timeTotal)
|
||||
{
|
||||
FileSource f(DataDir(filename).c_str(), true, new HexDecoder);
|
||||
typename SCHEME::Decryptor priv(f);
|
||||
typename SCHEME::Encryptor pub(priv);
|
||||
BenchMarkEncryption(name, pub, timeTotal);
|
||||
BenchMarkDecryption(name, priv, pub, timeTotal);
|
||||
}
|
||||
|
||||
template <class SCHEME>
|
||||
void BenchMarkSignature(const char *filename, const char *name, double timeTotal)
|
||||
{
|
||||
FileSource f(DataDir(filename).c_str(), true, new HexDecoder);
|
||||
typename SCHEME::Signer priv(f);
|
||||
typename SCHEME::Verifier pub(priv);
|
||||
BenchMarkSigning(name, priv, timeTotal);
|
||||
BenchMarkVerification(name, priv, pub, timeTotal);
|
||||
}
|
||||
|
||||
template <class D>
|
||||
void BenchMarkKeyAgreement(const char *filename, const char *name, double timeTotal)
|
||||
{
|
||||
FileSource f(DataDir(filename).c_str(), true, new HexDecoder);
|
||||
D d(f);
|
||||
BenchMarkKeyGen(name, d, timeTotal);
|
||||
BenchMarkAgreement(name, d, timeTotal);
|
||||
}
|
||||
|
||||
void BenchmarkPublicKeyAlgorithms(double t, double hertz)
|
||||
{
|
||||
g_allocatedTime = t;
|
||||
g_hertz = hertz;
|
||||
|
||||
const char *mco;
|
||||
if (g_hertz > 1.0f)
|
||||
mco = "<TH>Megacycles/Operation";
|
||||
else
|
||||
mco = "";
|
||||
|
||||
std::cout << "\n<TABLE>";
|
||||
std::cout << "\n<COLGROUP><COL style=\"text-align: left;\"><COL style=";
|
||||
std::cout << "\"text-align: right;\"><COL style=\"text-align: right;\">";
|
||||
std::cout << "\n<THEAD style=\"background: #F0F0F0\">";
|
||||
std::cout << "\n<TR><TH>Operation<TH>Milliseconds/Operation" << mco;
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
BenchMarkCrypto<RSAES<OAEP<SHA1> > >("TestData/rsa1024.dat", "RSA 1024", t);
|
||||
BenchMarkCrypto<LUCES<OAEP<SHA1> > >("TestData/luc1024.dat", "LUC 1024", t);
|
||||
BenchMarkCrypto<DLIES<> >("TestData/dlie1024.dat", "DLIES 1024", t);
|
||||
BenchMarkCrypto<LUC_IES<> >("TestData/lucc512.dat", "LUCELG 512", t);
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
BenchMarkCrypto<RSAES<OAEP<SHA1> > >("TestData/rsa2048.dat", "RSA 2048", t);
|
||||
BenchMarkCrypto<LUCES<OAEP<SHA1> > >("TestData/luc2048.dat", "LUC 2048", t);
|
||||
BenchMarkCrypto<DLIES<> >("TestData/dlie2048.dat", "DLIES 2048", t);
|
||||
BenchMarkCrypto<LUC_IES<> >("TestData/lucc1024.dat", "LUCELG 1024", t);
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
BenchMarkSignature<RSASS<PSSR, SHA1> >("TestData/rsa1024.dat", "RSA 1024", t);
|
||||
BenchMarkSignature<RWSS<PSSR, SHA1> >("TestData/rw1024.dat", "RW 1024", t);
|
||||
BenchMarkSignature<LUCSS<PSSR, SHA1> >("TestData/luc1024.dat", "LUC 1024", t);
|
||||
BenchMarkSignature<NR<SHA1> >("TestData/nr1024.dat", "NR 1024", t);
|
||||
BenchMarkSignature<DSA>("TestData/dsa1024.dat", "DSA 1024", t);
|
||||
BenchMarkSignature<LUC_HMP<SHA1> >("TestData/lucs512.dat", "LUC-HMP 512", t);
|
||||
BenchMarkSignature<ESIGN<SHA1> >("TestData/esig1023.dat", "ESIGN 1023", t);
|
||||
BenchMarkSignature<ESIGN<SHA1> >("TestData/esig1536.dat", "ESIGN 1536", t);
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
BenchMarkSignature<RSASS<PSSR, SHA1> >("TestData/rsa2048.dat", "RSA 2048", t);
|
||||
BenchMarkSignature<RWSS<PSSR, SHA1> >("TestData/rw2048.dat", "RW 2048", t);
|
||||
BenchMarkSignature<LUCSS<PSSR, SHA1> >("TestData/luc2048.dat", "LUC 2048", t);
|
||||
BenchMarkSignature<NR<SHA1> >("TestData/nr2048.dat", "NR 2048", t);
|
||||
BenchMarkSignature<LUC_HMP<SHA1> >("TestData/lucs1024.dat", "LUC-HMP 1024", t);
|
||||
BenchMarkSignature<ESIGN<SHA1> >("TestData/esig2046.dat", "ESIGN 2046", t);
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
BenchMarkKeyAgreement<XTR_DH>("TestData/xtrdh171.dat", "XTR-DH 171", t);
|
||||
BenchMarkKeyAgreement<XTR_DH>("TestData/xtrdh342.dat", "XTR-DH 342", t);
|
||||
BenchMarkKeyAgreement<DH>("TestData/dh1024.dat", "DH 1024", t);
|
||||
BenchMarkKeyAgreement<DH>("TestData/dh2048.dat", "DH 2048", t);
|
||||
BenchMarkKeyAgreement<LUC_DH>("TestData/lucd512.dat", "LUCDIF 512", t);
|
||||
BenchMarkKeyAgreement<LUC_DH>("TestData/lucd1024.dat", "LUCDIF 1024", t);
|
||||
BenchMarkKeyAgreement<MQV>("TestData/mqv1024.dat", "MQV 1024", t);
|
||||
BenchMarkKeyAgreement<MQV>("TestData/mqv2048.dat", "MQV 2048", t);
|
||||
}
|
||||
|
||||
std::cout << "\n</TABLE>" << std::endl;
|
||||
}
|
||||
|
||||
void BenchmarkEllipticCurveAlgorithms(double t, double hertz)
|
||||
{
|
||||
g_allocatedTime = t;
|
||||
g_hertz = hertz;
|
||||
|
||||
const char *mco;
|
||||
if (g_hertz > 1.0f)
|
||||
mco = "<TH>Megacycles/Operation";
|
||||
else
|
||||
mco = "";
|
||||
|
||||
std::cout << "\n<TABLE>";
|
||||
std::cout << "\n<COLGROUP><COL style=\"text-align: left;\"><COL style=";
|
||||
std::cout << "\"text-align: right;\"><COL style=\"text-align: right;\">";
|
||||
std::cout << "\n<THEAD style=\"background: #F0F0F0\">";
|
||||
std::cout << "\n<TR><TH>Operation<TH>Milliseconds/Operation" << mco;
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
ed25519::Signer sign(Test::GlobalRNG());
|
||||
ed25519::Verifier verify(sign);
|
||||
x25519 agree(Test::GlobalRNG());
|
||||
|
||||
BenchMarkSigning("ed25519", sign, t);
|
||||
BenchMarkVerification("ed25519", sign, verify, t);
|
||||
BenchMarkKeyGen("x25519", agree, t);
|
||||
BenchMarkAgreement("x25519", agree, t);
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
BenchMarkKeyAgreement<ECMQV160>("TestData/mqv160.dat", "MQV P-160", t);
|
||||
BenchMarkKeyAgreement<ECMQV256>("TestData/mqv256.dat", "MQV P-256", t);
|
||||
BenchMarkKeyAgreement<ECMQV384>("TestData/mqv384.dat", "MQV P-384", t);
|
||||
BenchMarkKeyAgreement<ECMQV512>("TestData/mqv512.dat", "MQV P-521", t);
|
||||
|
||||
BenchMarkKeyAgreement<ECHMQV160>("TestData/hmqv160.dat", "HMQV P-160", t);
|
||||
BenchMarkKeyAgreement<ECHMQV256>("TestData/hmqv256.dat", "HMQV P-256", t);
|
||||
BenchMarkKeyAgreement<ECHMQV384>("TestData/hmqv384.dat", "HMQV P-384", t);
|
||||
BenchMarkKeyAgreement<ECHMQV512>("TestData/hmqv512.dat", "HMQV P-521", t);
|
||||
|
||||
BenchMarkKeyAgreement<ECFHMQV160>("TestData/fhmqv160.dat", "FHMQV P-160", t);
|
||||
BenchMarkKeyAgreement<ECFHMQV256>("TestData/fhmqv256.dat", "FHMQV P-256", t);
|
||||
BenchMarkKeyAgreement<ECFHMQV384>("TestData/fhmqv384.dat", "FHMQV P-384", t);
|
||||
BenchMarkKeyAgreement<ECFHMQV512>("TestData/fhmqv512.dat", "FHMQV P-521", t);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: yellow;\">";
|
||||
{
|
||||
ECIES<ECP>::Decryptor cpriv(Test::GlobalRNG(), ASN1::secp256k1());
|
||||
ECIES<ECP>::Encryptor cpub(cpriv);
|
||||
ECDSA<ECP, SHA1>::Signer spriv(cpriv);
|
||||
ECDSA<ECP, SHA1>::Verifier spub(spriv);
|
||||
ECDSA_RFC6979<ECP, SHA1>::Signer spriv2(cpriv);
|
||||
ECDSA_RFC6979<ECP, SHA1>::Verifier spub2(spriv);
|
||||
ECGDSA<ECP, SHA1>::Signer spriv3(Test::GlobalRNG(), ASN1::secp256k1());
|
||||
ECGDSA<ECP, SHA1>::Verifier spub3(spriv3);
|
||||
ECDH<ECP>::Domain ecdhc(ASN1::secp256k1());
|
||||
ECMQV<ECP>::Domain ecmqvc(ASN1::secp256k1());
|
||||
|
||||
BenchMarkEncryption("ECIES over GF(p) 256", cpub, t);
|
||||
BenchMarkDecryption("ECIES over GF(p) 256", cpriv, cpub, t);
|
||||
BenchMarkSigning("ECDSA over GF(p) 256", spriv, t);
|
||||
BenchMarkVerification("ECDSA over GF(p) 256", spriv, spub, t);
|
||||
BenchMarkSigning("ECDSA-RFC6979 over GF(p) 256", spriv2, t);
|
||||
BenchMarkVerification("ECDSA-RFC6979 over GF(p) 256", spriv2, spub2, t);
|
||||
BenchMarkSigning("ECGDSA over GF(p) 256", spriv3, t);
|
||||
BenchMarkVerification("ECGDSA over GF(p) 256", spriv3, spub3, t);
|
||||
BenchMarkKeyGen("ECDHC over GF(p) 256", ecdhc, t);
|
||||
BenchMarkAgreement("ECDHC over GF(p) 256", ecdhc, t);
|
||||
BenchMarkKeyGen("ECMQVC over GF(p) 256", ecmqvc, t);
|
||||
BenchMarkAgreement("ECMQVC over GF(p) 256", ecmqvc, t);
|
||||
}
|
||||
|
||||
std::cout << "\n<TBODY style=\"background: white;\">";
|
||||
{
|
||||
ECIES<EC2N>::Decryptor cpriv(Test::GlobalRNG(), ASN1::sect233r1());
|
||||
ECIES<EC2N>::Encryptor cpub(cpriv);
|
||||
ECDSA<EC2N, SHA1>::Signer spriv(cpriv);
|
||||
ECDSA<EC2N, SHA1>::Verifier spub(spriv);
|
||||
ECDSA_RFC6979<EC2N, SHA1>::Signer spriv2(cpriv);
|
||||
ECDSA_RFC6979<EC2N, SHA1>::Verifier spub2(spriv);
|
||||
ECGDSA<EC2N, SHA1>::Signer spriv3(Test::GlobalRNG(), ASN1::sect233r1());
|
||||
ECGDSA<EC2N, SHA1>::Verifier spub3(spriv3);
|
||||
ECDH<EC2N>::Domain ecdhc(ASN1::sect233r1());
|
||||
ECMQV<EC2N>::Domain ecmqvc(ASN1::sect233r1());
|
||||
|
||||
BenchMarkEncryption("ECIES over GF(2^n) 233", cpub, t);
|
||||
BenchMarkDecryption("ECIES over GF(2^n) 233", cpriv, cpub, t);
|
||||
BenchMarkSigning("ECDSA over GF(2^n) 233", spriv, t);
|
||||
BenchMarkVerification("ECDSA over GF(2^n) 233", spriv, spub, t);
|
||||
BenchMarkSigning("ECDSA-RFC6979 over GF(2^n) 233", spriv2, t);
|
||||
BenchMarkVerification("ECDSA-RFC6979 over GF(2^n) 233", spriv2, spub2, t);
|
||||
BenchMarkSigning("ECGDSA over GF(2^n) 233", spriv3, t);
|
||||
BenchMarkVerification("ECGDSA over GF(2^n) 233", spriv3, spub3, t);
|
||||
BenchMarkKeyGen("ECDHC over GF(2^n) 233", ecdhc, t);
|
||||
BenchMarkAgreement("ECDHC over GF(2^n) 233", ecdhc, t);
|
||||
BenchMarkKeyGen("ECMQVC over GF(2^n) 233", ecmqvc, t);
|
||||
BenchMarkAgreement("ECMQVC over GF(2^n) 233", ecmqvc, t);
|
||||
}
|
||||
|
||||
std::cout << "\n</TABLE>" << std::endl;
|
||||
}
|
||||
|
||||
NAMESPACE_END // Test
|
||||
NAMESPACE_END // CryptoPP
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
#include "pch.h"
|
||||
#include "blowfish.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
const word32 Blowfish::Base::p_init[Blowfish::ROUNDS+2] =
|
||||
{
|
||||
608135816U, 2242054355U, 320440878U, 57701188U,
|
||||
2752067618U, 698298832U, 137296536U, 3964562569U,
|
||||
1160258022U, 953160567U, 3193202383U, 887688300U,
|
||||
3232508343U, 3380367581U, 1065670069U, 3041331479U,
|
||||
2450970073U, 2306472731U
|
||||
} ;
|
||||
|
||||
const word32 Blowfish::Base::s_init[4*256] = {
|
||||
3509652390U, 2564797868U, 805139163U, 3491422135U,
|
||||
3101798381U, 1780907670U, 3128725573U, 4046225305U,
|
||||
614570311U, 3012652279U, 134345442U, 2240740374U,
|
||||
1667834072U, 1901547113U, 2757295779U, 4103290238U,
|
||||
227898511U, 1921955416U, 1904987480U, 2182433518U,
|
||||
2069144605U, 3260701109U, 2620446009U, 720527379U,
|
||||
3318853667U, 677414384U, 3393288472U, 3101374703U,
|
||||
2390351024U, 1614419982U, 1822297739U, 2954791486U,
|
||||
3608508353U, 3174124327U, 2024746970U, 1432378464U,
|
||||
3864339955U, 2857741204U, 1464375394U, 1676153920U,
|
||||
1439316330U, 715854006U, 3033291828U, 289532110U,
|
||||
2706671279U, 2087905683U, 3018724369U, 1668267050U,
|
||||
732546397U, 1947742710U, 3462151702U, 2609353502U,
|
||||
2950085171U, 1814351708U, 2050118529U, 680887927U,
|
||||
999245976U, 1800124847U, 3300911131U, 1713906067U,
|
||||
1641548236U, 4213287313U, 1216130144U, 1575780402U,
|
||||
4018429277U, 3917837745U, 3693486850U, 3949271944U,
|
||||
596196993U, 3549867205U, 258830323U, 2213823033U,
|
||||
772490370U, 2760122372U, 1774776394U, 2652871518U,
|
||||
566650946U, 4142492826U, 1728879713U, 2882767088U,
|
||||
1783734482U, 3629395816U, 2517608232U, 2874225571U,
|
||||
1861159788U, 326777828U, 3124490320U, 2130389656U,
|
||||
2716951837U, 967770486U, 1724537150U, 2185432712U,
|
||||
2364442137U, 1164943284U, 2105845187U, 998989502U,
|
||||
3765401048U, 2244026483U, 1075463327U, 1455516326U,
|
||||
1322494562U, 910128902U, 469688178U, 1117454909U,
|
||||
936433444U, 3490320968U, 3675253459U, 1240580251U,
|
||||
122909385U, 2157517691U, 634681816U, 4142456567U,
|
||||
3825094682U, 3061402683U, 2540495037U, 79693498U,
|
||||
3249098678U, 1084186820U, 1583128258U, 426386531U,
|
||||
1761308591U, 1047286709U, 322548459U, 995290223U,
|
||||
1845252383U, 2603652396U, 3431023940U, 2942221577U,
|
||||
3202600964U, 3727903485U, 1712269319U, 422464435U,
|
||||
3234572375U, 1170764815U, 3523960633U, 3117677531U,
|
||||
1434042557U, 442511882U, 3600875718U, 1076654713U,
|
||||
1738483198U, 4213154764U, 2393238008U, 3677496056U,
|
||||
1014306527U, 4251020053U, 793779912U, 2902807211U,
|
||||
842905082U, 4246964064U, 1395751752U, 1040244610U,
|
||||
2656851899U, 3396308128U, 445077038U, 3742853595U,
|
||||
3577915638U, 679411651U, 2892444358U, 2354009459U,
|
||||
1767581616U, 3150600392U, 3791627101U, 3102740896U,
|
||||
284835224U, 4246832056U, 1258075500U, 768725851U,
|
||||
2589189241U, 3069724005U, 3532540348U, 1274779536U,
|
||||
3789419226U, 2764799539U, 1660621633U, 3471099624U,
|
||||
4011903706U, 913787905U, 3497959166U, 737222580U,
|
||||
2514213453U, 2928710040U, 3937242737U, 1804850592U,
|
||||
3499020752U, 2949064160U, 2386320175U, 2390070455U,
|
||||
2415321851U, 4061277028U, 2290661394U, 2416832540U,
|
||||
1336762016U, 1754252060U, 3520065937U, 3014181293U,
|
||||
791618072U, 3188594551U, 3933548030U, 2332172193U,
|
||||
3852520463U, 3043980520U, 413987798U, 3465142937U,
|
||||
3030929376U, 4245938359U, 2093235073U, 3534596313U,
|
||||
375366246U, 2157278981U, 2479649556U, 555357303U,
|
||||
3870105701U, 2008414854U, 3344188149U, 4221384143U,
|
||||
3956125452U, 2067696032U, 3594591187U, 2921233993U,
|
||||
2428461U, 544322398U, 577241275U, 1471733935U,
|
||||
610547355U, 4027169054U, 1432588573U, 1507829418U,
|
||||
2025931657U, 3646575487U, 545086370U, 48609733U,
|
||||
2200306550U, 1653985193U, 298326376U, 1316178497U,
|
||||
3007786442U, 2064951626U, 458293330U, 2589141269U,
|
||||
3591329599U, 3164325604U, 727753846U, 2179363840U,
|
||||
146436021U, 1461446943U, 4069977195U, 705550613U,
|
||||
3059967265U, 3887724982U, 4281599278U, 3313849956U,
|
||||
1404054877U, 2845806497U, 146425753U, 1854211946U,
|
||||
|
||||
1266315497U, 3048417604U, 3681880366U, 3289982499U,
|
||||
2909710000U, 1235738493U, 2632868024U, 2414719590U,
|
||||
3970600049U, 1771706367U, 1449415276U, 3266420449U,
|
||||
422970021U, 1963543593U, 2690192192U, 3826793022U,
|
||||
1062508698U, 1531092325U, 1804592342U, 2583117782U,
|
||||
2714934279U, 4024971509U, 1294809318U, 4028980673U,
|
||||
1289560198U, 2221992742U, 1669523910U, 35572830U,
|
||||
157838143U, 1052438473U, 1016535060U, 1802137761U,
|
||||
1753167236U, 1386275462U, 3080475397U, 2857371447U,
|
||||
1040679964U, 2145300060U, 2390574316U, 1461121720U,
|
||||
2956646967U, 4031777805U, 4028374788U, 33600511U,
|
||||
2920084762U, 1018524850U, 629373528U, 3691585981U,
|
||||
3515945977U, 2091462646U, 2486323059U, 586499841U,
|
||||
988145025U, 935516892U, 3367335476U, 2599673255U,
|
||||
2839830854U, 265290510U, 3972581182U, 2759138881U,
|
||||
3795373465U, 1005194799U, 847297441U, 406762289U,
|
||||
1314163512U, 1332590856U, 1866599683U, 4127851711U,
|
||||
750260880U, 613907577U, 1450815602U, 3165620655U,
|
||||
3734664991U, 3650291728U, 3012275730U, 3704569646U,
|
||||
1427272223U, 778793252U, 1343938022U, 2676280711U,
|
||||
2052605720U, 1946737175U, 3164576444U, 3914038668U,
|
||||
3967478842U, 3682934266U, 1661551462U, 3294938066U,
|
||||
4011595847U, 840292616U, 3712170807U, 616741398U,
|
||||
312560963U, 711312465U, 1351876610U, 322626781U,
|
||||
1910503582U, 271666773U, 2175563734U, 1594956187U,
|
||||
70604529U, 3617834859U, 1007753275U, 1495573769U,
|
||||
4069517037U, 2549218298U, 2663038764U, 504708206U,
|
||||
2263041392U, 3941167025U, 2249088522U, 1514023603U,
|
||||
1998579484U, 1312622330U, 694541497U, 2582060303U,
|
||||
2151582166U, 1382467621U, 776784248U, 2618340202U,
|
||||
3323268794U, 2497899128U, 2784771155U, 503983604U,
|
||||
4076293799U, 907881277U, 423175695U, 432175456U,
|
||||
1378068232U, 4145222326U, 3954048622U, 3938656102U,
|
||||
3820766613U, 2793130115U, 2977904593U, 26017576U,
|
||||
3274890735U, 3194772133U, 1700274565U, 1756076034U,
|
||||
4006520079U, 3677328699U, 720338349U, 1533947780U,
|
||||
354530856U, 688349552U, 3973924725U, 1637815568U,
|
||||
332179504U, 3949051286U, 53804574U, 2852348879U,
|
||||
3044236432U, 1282449977U, 3583942155U, 3416972820U,
|
||||
4006381244U, 1617046695U, 2628476075U, 3002303598U,
|
||||
1686838959U, 431878346U, 2686675385U, 1700445008U,
|
||||
1080580658U, 1009431731U, 832498133U, 3223435511U,
|
||||
2605976345U, 2271191193U, 2516031870U, 1648197032U,
|
||||
4164389018U, 2548247927U, 300782431U, 375919233U,
|
||||
238389289U, 3353747414U, 2531188641U, 2019080857U,
|
||||
1475708069U, 455242339U, 2609103871U, 448939670U,
|
||||
3451063019U, 1395535956U, 2413381860U, 1841049896U,
|
||||
1491858159U, 885456874U, 4264095073U, 4001119347U,
|
||||
1565136089U, 3898914787U, 1108368660U, 540939232U,
|
||||
1173283510U, 2745871338U, 3681308437U, 4207628240U,
|
||||
3343053890U, 4016749493U, 1699691293U, 1103962373U,
|
||||
3625875870U, 2256883143U, 3830138730U, 1031889488U,
|
||||
3479347698U, 1535977030U, 4236805024U, 3251091107U,
|
||||
2132092099U, 1774941330U, 1199868427U, 1452454533U,
|
||||
157007616U, 2904115357U, 342012276U, 595725824U,
|
||||
1480756522U, 206960106U, 497939518U, 591360097U,
|
||||
863170706U, 2375253569U, 3596610801U, 1814182875U,
|
||||
2094937945U, 3421402208U, 1082520231U, 3463918190U,
|
||||
2785509508U, 435703966U, 3908032597U, 1641649973U,
|
||||
2842273706U, 3305899714U, 1510255612U, 2148256476U,
|
||||
2655287854U, 3276092548U, 4258621189U, 236887753U,
|
||||
3681803219U, 274041037U, 1734335097U, 3815195456U,
|
||||
3317970021U, 1899903192U, 1026095262U, 4050517792U,
|
||||
356393447U, 2410691914U, 3873677099U, 3682840055U,
|
||||
|
||||
3913112168U, 2491498743U, 4132185628U, 2489919796U,
|
||||
1091903735U, 1979897079U, 3170134830U, 3567386728U,
|
||||
3557303409U, 857797738U, 1136121015U, 1342202287U,
|
||||
507115054U, 2535736646U, 337727348U, 3213592640U,
|
||||
1301675037U, 2528481711U, 1895095763U, 1721773893U,
|
||||
3216771564U, 62756741U, 2142006736U, 835421444U,
|
||||
2531993523U, 1442658625U, 3659876326U, 2882144922U,
|
||||
676362277U, 1392781812U, 170690266U, 3921047035U,
|
||||
1759253602U, 3611846912U, 1745797284U, 664899054U,
|
||||
1329594018U, 3901205900U, 3045908486U, 2062866102U,
|
||||
2865634940U, 3543621612U, 3464012697U, 1080764994U,
|
||||
553557557U, 3656615353U, 3996768171U, 991055499U,
|
||||
499776247U, 1265440854U, 648242737U, 3940784050U,
|
||||
980351604U, 3713745714U, 1749149687U, 3396870395U,
|
||||
4211799374U, 3640570775U, 1161844396U, 3125318951U,
|
||||
1431517754U, 545492359U, 4268468663U, 3499529547U,
|
||||
1437099964U, 2702547544U, 3433638243U, 2581715763U,
|
||||
2787789398U, 1060185593U, 1593081372U, 2418618748U,
|
||||
4260947970U, 69676912U, 2159744348U, 86519011U,
|
||||
2512459080U, 3838209314U, 1220612927U, 3339683548U,
|
||||
133810670U, 1090789135U, 1078426020U, 1569222167U,
|
||||
845107691U, 3583754449U, 4072456591U, 1091646820U,
|
||||
628848692U, 1613405280U, 3757631651U, 526609435U,
|
||||
236106946U, 48312990U, 2942717905U, 3402727701U,
|
||||
1797494240U, 859738849U, 992217954U, 4005476642U,
|
||||
2243076622U, 3870952857U, 3732016268U, 765654824U,
|
||||
3490871365U, 2511836413U, 1685915746U, 3888969200U,
|
||||
1414112111U, 2273134842U, 3281911079U, 4080962846U,
|
||||
172450625U, 2569994100U, 980381355U, 4109958455U,
|
||||
2819808352U, 2716589560U, 2568741196U, 3681446669U,
|
||||
3329971472U, 1835478071U, 660984891U, 3704678404U,
|
||||
4045999559U, 3422617507U, 3040415634U, 1762651403U,
|
||||
1719377915U, 3470491036U, 2693910283U, 3642056355U,
|
||||
3138596744U, 1364962596U, 2073328063U, 1983633131U,
|
||||
926494387U, 3423689081U, 2150032023U, 4096667949U,
|
||||
1749200295U, 3328846651U, 309677260U, 2016342300U,
|
||||
1779581495U, 3079819751U, 111262694U, 1274766160U,
|
||||
443224088U, 298511866U, 1025883608U, 3806446537U,
|
||||
1145181785U, 168956806U, 3641502830U, 3584813610U,
|
||||
1689216846U, 3666258015U, 3200248200U, 1692713982U,
|
||||
2646376535U, 4042768518U, 1618508792U, 1610833997U,
|
||||
3523052358U, 4130873264U, 2001055236U, 3610705100U,
|
||||
2202168115U, 4028541809U, 2961195399U, 1006657119U,
|
||||
2006996926U, 3186142756U, 1430667929U, 3210227297U,
|
||||
1314452623U, 4074634658U, 4101304120U, 2273951170U,
|
||||
1399257539U, 3367210612U, 3027628629U, 1190975929U,
|
||||
2062231137U, 2333990788U, 2221543033U, 2438960610U,
|
||||
1181637006U, 548689776U, 2362791313U, 3372408396U,
|
||||
3104550113U, 3145860560U, 296247880U, 1970579870U,
|
||||
3078560182U, 3769228297U, 1714227617U, 3291629107U,
|
||||
3898220290U, 166772364U, 1251581989U, 493813264U,
|
||||
448347421U, 195405023U, 2709975567U, 677966185U,
|
||||
3703036547U, 1463355134U, 2715995803U, 1338867538U,
|
||||
1343315457U, 2802222074U, 2684532164U, 233230375U,
|
||||
2599980071U, 2000651841U, 3277868038U, 1638401717U,
|
||||
4028070440U, 3237316320U, 6314154U, 819756386U,
|
||||
300326615U, 590932579U, 1405279636U, 3267499572U,
|
||||
3150704214U, 2428286686U, 3959192993U, 3461946742U,
|
||||
1862657033U, 1266418056U, 963775037U, 2089974820U,
|
||||
2263052895U, 1917689273U, 448879540U, 3550394620U,
|
||||
3981727096U, 150775221U, 3627908307U, 1303187396U,
|
||||
508620638U, 2975983352U, 2726630617U, 1817252668U,
|
||||
1876281319U, 1457606340U, 908771278U, 3720792119U,
|
||||
3617206836U, 2455994898U, 1729034894U, 1080033504U,
|
||||
|
||||
976866871U, 3556439503U, 2881648439U, 1522871579U,
|
||||
1555064734U, 1336096578U, 3548522304U, 2579274686U,
|
||||
3574697629U, 3205460757U, 3593280638U, 3338716283U,
|
||||
3079412587U, 564236357U, 2993598910U, 1781952180U,
|
||||
1464380207U, 3163844217U, 3332601554U, 1699332808U,
|
||||
1393555694U, 1183702653U, 3581086237U, 1288719814U,
|
||||
691649499U, 2847557200U, 2895455976U, 3193889540U,
|
||||
2717570544U, 1781354906U, 1676643554U, 2592534050U,
|
||||
3230253752U, 1126444790U, 2770207658U, 2633158820U,
|
||||
2210423226U, 2615765581U, 2414155088U, 3127139286U,
|
||||
673620729U, 2805611233U, 1269405062U, 4015350505U,
|
||||
3341807571U, 4149409754U, 1057255273U, 2012875353U,
|
||||
2162469141U, 2276492801U, 2601117357U, 993977747U,
|
||||
3918593370U, 2654263191U, 753973209U, 36408145U,
|
||||
2530585658U, 25011837U, 3520020182U, 2088578344U,
|
||||
530523599U, 2918365339U, 1524020338U, 1518925132U,
|
||||
3760827505U, 3759777254U, 1202760957U, 3985898139U,
|
||||
3906192525U, 674977740U, 4174734889U, 2031300136U,
|
||||
2019492241U, 3983892565U, 4153806404U, 3822280332U,
|
||||
352677332U, 2297720250U, 60907813U, 90501309U,
|
||||
3286998549U, 1016092578U, 2535922412U, 2839152426U,
|
||||
457141659U, 509813237U, 4120667899U, 652014361U,
|
||||
1966332200U, 2975202805U, 55981186U, 2327461051U,
|
||||
676427537U, 3255491064U, 2882294119U, 3433927263U,
|
||||
1307055953U, 942726286U, 933058658U, 2468411793U,
|
||||
3933900994U, 4215176142U, 1361170020U, 2001714738U,
|
||||
2830558078U, 3274259782U, 1222529897U, 1679025792U,
|
||||
2729314320U, 3714953764U, 1770335741U, 151462246U,
|
||||
3013232138U, 1682292957U, 1483529935U, 471910574U,
|
||||
1539241949U, 458788160U, 3436315007U, 1807016891U,
|
||||
3718408830U, 978976581U, 1043663428U, 3165965781U,
|
||||
1927990952U, 4200891579U, 2372276910U, 3208408903U,
|
||||
3533431907U, 1412390302U, 2931980059U, 4132332400U,
|
||||
1947078029U, 3881505623U, 4168226417U, 2941484381U,
|
||||
1077988104U, 1320477388U, 886195818U, 18198404U,
|
||||
3786409000U, 2509781533U, 112762804U, 3463356488U,
|
||||
1866414978U, 891333506U, 18488651U, 661792760U,
|
||||
1628790961U, 3885187036U, 3141171499U, 876946877U,
|
||||
2693282273U, 1372485963U, 791857591U, 2686433993U,
|
||||
3759982718U, 3167212022U, 3472953795U, 2716379847U,
|
||||
445679433U, 3561995674U, 3504004811U, 3574258232U,
|
||||
54117162U, 3331405415U, 2381918588U, 3769707343U,
|
||||
4154350007U, 1140177722U, 4074052095U, 668550556U,
|
||||
3214352940U, 367459370U, 261225585U, 2610173221U,
|
||||
4209349473U, 3468074219U, 3265815641U, 314222801U,
|
||||
3066103646U, 3808782860U, 282218597U, 3406013506U,
|
||||
3773591054U, 379116347U, 1285071038U, 846784868U,
|
||||
2669647154U, 3771962079U, 3550491691U, 2305946142U,
|
||||
453669953U, 1268987020U, 3317592352U, 3279303384U,
|
||||
3744833421U, 2610507566U, 3859509063U, 266596637U,
|
||||
3847019092U, 517658769U, 3462560207U, 3443424879U,
|
||||
370717030U, 4247526661U, 2224018117U, 4143653529U,
|
||||
4112773975U, 2788324899U, 2477274417U, 1456262402U,
|
||||
2901442914U, 1517677493U, 1846949527U, 2295493580U,
|
||||
3734397586U, 2176403920U, 1280348187U, 1908823572U,
|
||||
3871786941U, 846861322U, 1172426758U, 3287448474U,
|
||||
3383383037U, 1655181056U, 3139813346U, 901632758U,
|
||||
1897031941U, 2986607138U, 3066810236U, 3447102507U,
|
||||
1393639104U, 373351379U, 950779232U, 625454576U,
|
||||
3124240540U, 4148612726U, 2007998917U, 544563296U,
|
||||
2244738638U, 2330496472U, 2058025392U, 1291430526U,
|
||||
424198748U, 50039436U, 29584100U, 3605783033U,
|
||||
2429876329U, 2791104160U, 1057563949U, 3255363231U,
|
||||
3075367218U, 3463963227U, 1469046755U, 985887462U
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
+797
@@ -0,0 +1,797 @@
|
||||
// blake2.cpp - written and placed in the public domain by Jeffrey Walton
|
||||
// and Zooko Wilcox-O'Hearn. Based on Aumasson, Neves,
|
||||
// Wilcox-O'Hearn and Winnerlein's reference BLAKE2
|
||||
// implementation at http://github.com/BLAKE2/BLAKE2.
|
||||
//
|
||||
// The BLAKE2b and BLAKE2s numbers are consistent with the BLAKE2 team's
|
||||
// numbers. However, we have an Altivec implementation of BLAKE2s,
|
||||
// and a POWER8 implementation of BLAKE2b (BLAKE2 team is missing them).
|
||||
// Altivec code is about 2x faster than C++ when using GCC 5.0 or
|
||||
// above. The POWER8 code is about 2.5x faster than C++ when using GCC 5.0
|
||||
// or above. If you use GCC 4.0 (PowerMac) or GCC 4.8 (GCC Compile Farm)
|
||||
// then the PowerPC code will be slower than C++. Be sure to use GCC 5.0
|
||||
// or above for PowerPC builds or disable Altivec for BLAKE2b and BLAKE2s
|
||||
// if using the old compilers.
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
#include "cryptlib.h"
|
||||
#include "argnames.h"
|
||||
#include "algparam.h"
|
||||
#include "blake2.h"
|
||||
#include "cpu.h"
|
||||
|
||||
// Uncomment for benchmarking C++ against SSE2 or NEON.
|
||||
// Do so in both blake2.cpp and blake2_simd.cpp.
|
||||
// #undef CRYPTOPP_SSE41_AVAILABLE
|
||||
// #undef CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
// #undef CRYPTOPP_ALTIVEC_AVAILABLE
|
||||
// #undef CRYPTOPP_POWER8_AVAILABLE
|
||||
|
||||
// Disable NEON/ASIMD for Cortex-A53 and A57. The shifts are too slow and C/C++ is about
|
||||
// 3 cpb faster than NEON/ASIMD. Also see http://github.com/weidai11/cryptopp/issues/367.
|
||||
#if (defined(__aarch32__) || defined(__aarch64__)) && defined(CRYPTOPP_SLOW_ARMV8_SHIFT)
|
||||
# undef CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
#endif
|
||||
|
||||
// BLAKE2s bug on AIX 7.1 (POWER7) with XLC 12.01
|
||||
// https://github.com/weidai11/cryptopp/issues/743
|
||||
#if defined(__xlC__) && (__xlC__ < 0x0d01)
|
||||
# define CRYPTOPP_DISABLE_ALTIVEC 1
|
||||
# undef CRYPTOPP_POWER7_AVAILABLE
|
||||
# undef CRYPTOPP_POWER8_AVAILABLE
|
||||
# undef CRYPTOPP_ALTIVEC_AVAILABLE
|
||||
#endif
|
||||
|
||||
// Can't use GetAlignmentOf<word64>() because of C++11 and constexpr
|
||||
// Can use 'const unsigned int' because of MSVC 2013
|
||||
#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
|
||||
# define ALIGN_SPEC32 16
|
||||
# define ALIGN_SPEC64 16
|
||||
#else
|
||||
# define ALIGN_SPEC32 4
|
||||
# define ALIGN_SPEC64 8
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
// Export the tables to the SIMD files
|
||||
extern const word32 BLAKE2S_IV[8];
|
||||
extern const word64 BLAKE2B_IV[8];
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(ALIGN_SPEC32)
|
||||
const word32 BLAKE2S_IV[8] = {
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(ALIGN_SPEC64)
|
||||
const word64 BLAKE2B_IV[8] = {
|
||||
W64LIT(0x6a09e667f3bcc908), W64LIT(0xbb67ae8584caa73b),
|
||||
W64LIT(0x3c6ef372fe94f82b), W64LIT(0xa54ff53a5f1d36f1),
|
||||
W64LIT(0x510e527fade682d1), W64LIT(0x9b05688c2b3e6c1f),
|
||||
W64LIT(0x1f83d9abfb41bd6b), W64LIT(0x5be0cd19137e2179)
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
using CryptoPP::byte;
|
||||
using CryptoPP::word32;
|
||||
using CryptoPP::word64;
|
||||
using CryptoPP::rotrConstant;
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(ALIGN_SPEC32)
|
||||
const byte BLAKE2S_SIGMA[10][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
|
||||
};
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(ALIGN_SPEC32)
|
||||
const byte BLAKE2B_SIGMA[12][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
|
||||
};
|
||||
|
||||
template <unsigned int R, unsigned int N>
|
||||
inline void BLAKE2B_G(const word64 m[16], word64& a, word64& b, word64& c, word64& d)
|
||||
{
|
||||
a = a + b + m[BLAKE2B_SIGMA[R][2*N+0]];
|
||||
d = rotrConstant<32>(d ^ a);
|
||||
c = c + d;
|
||||
b = rotrConstant<24>(b ^ c);
|
||||
a = a + b + m[BLAKE2B_SIGMA[R][2*N+1]];
|
||||
d = rotrConstant<16>(d ^ a);
|
||||
c = c + d;
|
||||
b = rotrConstant<63>(b ^ c);
|
||||
}
|
||||
|
||||
template <unsigned int R>
|
||||
inline void BLAKE2B_ROUND(const word64 m[16], word64 v[16])
|
||||
{
|
||||
BLAKE2B_G<R,0>(m,v[ 0],v[ 4],v[ 8],v[12]);
|
||||
BLAKE2B_G<R,1>(m,v[ 1],v[ 5],v[ 9],v[13]);
|
||||
BLAKE2B_G<R,2>(m,v[ 2],v[ 6],v[10],v[14]);
|
||||
BLAKE2B_G<R,3>(m,v[ 3],v[ 7],v[11],v[15]);
|
||||
BLAKE2B_G<R,4>(m,v[ 0],v[ 5],v[10],v[15]);
|
||||
BLAKE2B_G<R,5>(m,v[ 1],v[ 6],v[11],v[12]);
|
||||
BLAKE2B_G<R,6>(m,v[ 2],v[ 7],v[ 8],v[13]);
|
||||
BLAKE2B_G<R,7>(m,v[ 3],v[ 4],v[ 9],v[14]);
|
||||
}
|
||||
|
||||
template <unsigned int R, unsigned int N>
|
||||
inline void BLAKE2S_G(const word32 m[16], word32& a, word32& b, word32& c, word32& d)
|
||||
{
|
||||
a = a + b + m[BLAKE2S_SIGMA[R][2*N+0]];
|
||||
d = rotrConstant<16>(d ^ a);
|
||||
c = c + d;
|
||||
b = rotrConstant<12>(b ^ c);
|
||||
a = a + b + m[BLAKE2S_SIGMA[R][2*N+1]];
|
||||
d = rotrConstant<8>(d ^ a);
|
||||
c = c + d;
|
||||
b = rotrConstant<7>(b ^ c);
|
||||
}
|
||||
|
||||
template <unsigned int R>
|
||||
inline void BLAKE2S_ROUND(const word32 m[16], word32 v[])
|
||||
{
|
||||
BLAKE2S_G<R,0>(m,v[ 0],v[ 4],v[ 8],v[12]);
|
||||
BLAKE2S_G<R,1>(m,v[ 1],v[ 5],v[ 9],v[13]);
|
||||
BLAKE2S_G<R,2>(m,v[ 2],v[ 6],v[10],v[14]);
|
||||
BLAKE2S_G<R,3>(m,v[ 3],v[ 7],v[11],v[15]);
|
||||
BLAKE2S_G<R,4>(m,v[ 0],v[ 5],v[10],v[15]);
|
||||
BLAKE2S_G<R,5>(m,v[ 1],v[ 6],v[11],v[12]);
|
||||
BLAKE2S_G<R,6>(m,v[ 2],v[ 7],v[ 8],v[13]);
|
||||
BLAKE2S_G<R,7>(m,v[ 3],v[ 4],v[ 9],v[14]);
|
||||
}
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void BLAKE2_Compress32_CXX(const byte* input, BLAKE2s_State& state);
|
||||
void BLAKE2_Compress64_CXX(const byte* input, BLAKE2b_State& state);
|
||||
|
||||
#if CRYPTOPP_SSE41_AVAILABLE
|
||||
extern void BLAKE2_Compress32_SSE4(const byte* input, BLAKE2s_State& state);
|
||||
extern void BLAKE2_Compress64_SSE4(const byte* input, BLAKE2b_State& state);
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
extern void BLAKE2_Compress32_NEON(const byte* input, BLAKE2s_State& state);
|
||||
extern void BLAKE2_Compress64_NEON(const byte* input, BLAKE2b_State& state);
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_ALTIVEC_AVAILABLE
|
||||
extern void BLAKE2_Compress32_ALTIVEC(const byte* input, BLAKE2s_State& state);
|
||||
#endif
|
||||
|
||||
#if CRYPTOPP_POWER8_AVAILABLE
|
||||
extern void BLAKE2_Compress64_POWER8(const byte* input, BLAKE2b_State& state);
|
||||
#endif
|
||||
|
||||
unsigned int BLAKE2b::OptimalDataAlignment() const
|
||||
{
|
||||
#if defined(CRYPTOPP_SSE41_AVAILABLE)
|
||||
if (HasSSE41())
|
||||
return 16; // load __m128i
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
return 8; // load uint64x2_t
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_POWER8_AVAILABLE)
|
||||
if (HasPower8())
|
||||
return 16; // load vector long long
|
||||
else
|
||||
#endif
|
||||
return GetAlignmentOf<word64>();
|
||||
}
|
||||
|
||||
std::string BLAKE2b::AlgorithmProvider() const
|
||||
{
|
||||
#if defined(CRYPTOPP_SSE41_AVAILABLE)
|
||||
if (HasSSE41())
|
||||
return "SSE4.1";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
return "NEON";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_POWER8_AVAILABLE)
|
||||
if (HasPower8())
|
||||
return "Power8";
|
||||
else
|
||||
#endif
|
||||
return "C++";
|
||||
}
|
||||
|
||||
unsigned int BLAKE2s::OptimalDataAlignment() const
|
||||
{
|
||||
#if defined(CRYPTOPP_SSE41_AVAILABLE)
|
||||
if (HasSSE41())
|
||||
return 16; // load __m128i
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
return 4; // load uint32x4_t
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
if (HasAltivec())
|
||||
return 16; // load vector unsigned int
|
||||
else
|
||||
#endif
|
||||
return GetAlignmentOf<word32>();
|
||||
}
|
||||
|
||||
std::string BLAKE2s::AlgorithmProvider() const
|
||||
{
|
||||
#if defined(CRYPTOPP_SSE41_AVAILABLE)
|
||||
if (HasSSE41())
|
||||
return "SSE4.1";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
return "NEON";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
if (HasAltivec())
|
||||
return "Altivec";
|
||||
else
|
||||
#endif
|
||||
return "C++";
|
||||
}
|
||||
|
||||
void BLAKE2s_State::Reset()
|
||||
{
|
||||
std::memset(m_hft, 0x00, m_hft.SizeInBytes());
|
||||
m_len = 0;
|
||||
}
|
||||
|
||||
void BLAKE2b_State::Reset()
|
||||
{
|
||||
std::memset(m_hft, 0x00, m_hft.SizeInBytes());
|
||||
m_len = 0;
|
||||
}
|
||||
|
||||
BLAKE2s_ParameterBlock::BLAKE2s_ParameterBlock(size_t digestLen, size_t keyLen,
|
||||
const byte* saltStr, size_t saltLen,
|
||||
const byte* personalizationStr, size_t personalizationLen)
|
||||
{
|
||||
Reset(digestLen, keyLen);
|
||||
|
||||
if (saltStr && saltLen)
|
||||
memcpy_s(salt(), SALTSIZE, saltStr, saltLen);
|
||||
|
||||
if (personalizationStr && personalizationLen)
|
||||
memcpy_s(personalization(), PERSONALIZATIONSIZE, personalizationStr, personalizationLen);
|
||||
}
|
||||
|
||||
BLAKE2b_ParameterBlock::BLAKE2b_ParameterBlock(size_t digestLen, size_t keyLen,
|
||||
const byte* saltStr, size_t saltLen,
|
||||
const byte* personalizationStr, size_t personalizationLen)
|
||||
{
|
||||
Reset(digestLen, keyLen);
|
||||
|
||||
if (saltStr && saltLen)
|
||||
memcpy_s(salt(), SALTSIZE, saltStr, saltLen);
|
||||
|
||||
if (personalizationStr && personalizationLen)
|
||||
memcpy_s(personalization(), PERSONALIZATIONSIZE, personalizationStr, personalizationLen);
|
||||
}
|
||||
|
||||
void BLAKE2s_ParameterBlock::Reset(size_t digestLen, size_t keyLen)
|
||||
{
|
||||
std::memset(m_data, 0x00, m_data.size());
|
||||
m_data[DigestOff] = static_cast<byte>(digestLen);
|
||||
m_data[KeyOff] = static_cast<byte>(keyLen);
|
||||
m_data[FanoutOff] = m_data[DepthOff] = 1;
|
||||
}
|
||||
|
||||
void BLAKE2b_ParameterBlock::Reset(size_t digestLen, size_t keyLen)
|
||||
{
|
||||
std::memset(m_data, 0x00, m_data.size());
|
||||
m_data[DigestOff] = static_cast<byte>(digestLen);
|
||||
m_data[KeyOff] = static_cast<byte>(keyLen);
|
||||
m_data[FanoutOff] = m_data[DepthOff] = 1;
|
||||
}
|
||||
|
||||
BLAKE2s::BLAKE2s(bool treeMode, unsigned int digestSize)
|
||||
: m_digestSize(digestSize), m_keyLength(0), m_treeMode(treeMode)
|
||||
{
|
||||
CRYPTOPP_ASSERT(digestSize <= DIGESTSIZE);
|
||||
|
||||
UncheckedSetKey(NULLPTR, 0, MakeParameters
|
||||
(Name::DigestSize(), (int)digestSize)
|
||||
(Name::TreeMode(), treeMode));
|
||||
}
|
||||
|
||||
BLAKE2b::BLAKE2b(bool treeMode, unsigned int digestSize)
|
||||
: m_digestSize(digestSize), m_keyLength(0), m_treeMode(treeMode)
|
||||
{
|
||||
CRYPTOPP_ASSERT(digestSize <= DIGESTSIZE);
|
||||
|
||||
UncheckedSetKey(NULLPTR, 0, MakeParameters
|
||||
(Name::DigestSize(), (int)digestSize)
|
||||
(Name::TreeMode(), treeMode));
|
||||
}
|
||||
|
||||
BLAKE2s::BLAKE2s(unsigned int digestSize)
|
||||
: m_digestSize(digestSize), m_keyLength(0), m_treeMode(false)
|
||||
{
|
||||
CRYPTOPP_ASSERT(digestSize <= DIGESTSIZE);
|
||||
|
||||
UncheckedSetKey(NULLPTR, 0, MakeParameters
|
||||
(Name::DigestSize(), (int)digestSize)
|
||||
(Name::TreeMode(), false));
|
||||
}
|
||||
|
||||
BLAKE2b::BLAKE2b(unsigned int digestSize)
|
||||
: m_digestSize(digestSize), m_keyLength(0), m_treeMode(false)
|
||||
{
|
||||
CRYPTOPP_ASSERT(digestSize <= DIGESTSIZE);
|
||||
|
||||
UncheckedSetKey(NULLPTR, 0, MakeParameters
|
||||
(Name::DigestSize(), (int)digestSize)
|
||||
(Name::TreeMode(), false));
|
||||
}
|
||||
|
||||
BLAKE2s::BLAKE2s(const byte *key, size_t keyLength, const byte* salt, size_t saltLength,
|
||||
const byte* personalization, size_t personalizationLength, bool treeMode, unsigned int digestSize)
|
||||
: m_digestSize(digestSize), m_keyLength(static_cast<unsigned int>(keyLength)), m_treeMode(treeMode)
|
||||
{
|
||||
CRYPTOPP_ASSERT(keyLength <= MAX_KEYLENGTH);
|
||||
CRYPTOPP_ASSERT(digestSize <= DIGESTSIZE);
|
||||
CRYPTOPP_ASSERT(saltLength <= SALTSIZE);
|
||||
CRYPTOPP_ASSERT(personalizationLength <= PERSONALIZATIONSIZE);
|
||||
|
||||
UncheckedSetKey(key, static_cast<unsigned int>(keyLength), MakeParameters
|
||||
(Name::DigestSize(),(int)digestSize)
|
||||
(Name::TreeMode(),treeMode)
|
||||
(Name::Salt(), ConstByteArrayParameter(salt, saltLength))
|
||||
(Name::Personalization(), ConstByteArrayParameter(personalization, personalizationLength)));
|
||||
}
|
||||
|
||||
BLAKE2b::BLAKE2b(const byte *key, size_t keyLength, const byte* salt, size_t saltLength,
|
||||
const byte* personalization, size_t personalizationLength, bool treeMode, unsigned int digestSize)
|
||||
: m_digestSize(digestSize), m_keyLength(static_cast<unsigned int>(keyLength)), m_treeMode(treeMode)
|
||||
{
|
||||
CRYPTOPP_ASSERT(keyLength <= MAX_KEYLENGTH);
|
||||
CRYPTOPP_ASSERT(digestSize <= DIGESTSIZE);
|
||||
CRYPTOPP_ASSERT(saltLength <= SALTSIZE);
|
||||
CRYPTOPP_ASSERT(personalizationLength <= PERSONALIZATIONSIZE);
|
||||
|
||||
UncheckedSetKey(key, static_cast<unsigned int>(keyLength), MakeParameters
|
||||
(Name::DigestSize(),(int)digestSize)
|
||||
(Name::TreeMode(),treeMode)
|
||||
(Name::Salt(), ConstByteArrayParameter(salt, saltLength))
|
||||
(Name::Personalization(), ConstByteArrayParameter(personalization, personalizationLength)));
|
||||
}
|
||||
|
||||
void BLAKE2s::UncheckedSetKey(const byte *key, unsigned int length, const CryptoPP::NameValuePairs& params)
|
||||
{
|
||||
if (key && length)
|
||||
{
|
||||
m_key.New(BLOCKSIZE);
|
||||
std::memcpy(m_key, key, length);
|
||||
std::memset(m_key + length, 0x00, BLOCKSIZE - length);
|
||||
m_keyLength = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_key.resize(0);
|
||||
m_keyLength = 0;
|
||||
}
|
||||
|
||||
m_digestSize = static_cast<unsigned int>(params.GetIntValueWithDefault(
|
||||
Name::DigestSize(), static_cast<int>(m_digestSize)));
|
||||
|
||||
m_state.Reset();
|
||||
m_block.Reset(m_digestSize, m_keyLength);
|
||||
(void)params.GetValue(Name::TreeMode(), m_treeMode);
|
||||
|
||||
ConstByteArrayParameter t;
|
||||
if (params.GetValue(Name::Salt(), t) && t.begin() && t.size())
|
||||
memcpy_s(m_block.salt(), SALTSIZE, t.begin(), t.size());
|
||||
|
||||
if (params.GetValue(Name::Personalization(), t) && t.begin() && t.size())
|
||||
memcpy_s(m_block.personalization(), PERSONALIZATIONSIZE, t.begin(), t.size());
|
||||
|
||||
Restart();
|
||||
}
|
||||
|
||||
void BLAKE2b::UncheckedSetKey(const byte *key, unsigned int length, const CryptoPP::NameValuePairs& params)
|
||||
{
|
||||
if (key && length)
|
||||
{
|
||||
m_key.New(BLOCKSIZE);
|
||||
std::memcpy(m_key, key, length);
|
||||
std::memset(m_key + length, 0x00, BLOCKSIZE - length);
|
||||
m_keyLength = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_key.resize(0);
|
||||
m_keyLength = 0;
|
||||
}
|
||||
|
||||
m_digestSize = static_cast<unsigned int>(params.GetIntValueWithDefault(
|
||||
Name::DigestSize(), static_cast<int>(m_digestSize)));
|
||||
|
||||
m_state.Reset();
|
||||
m_block.Reset(m_digestSize, m_keyLength);
|
||||
(void)params.GetValue(Name::TreeMode(), m_treeMode);
|
||||
|
||||
ConstByteArrayParameter t;
|
||||
if (params.GetValue(Name::Salt(), t) && t.begin() && t.size())
|
||||
memcpy_s(m_block.salt(), SALTSIZE, t.begin(), t.size());
|
||||
|
||||
if (params.GetValue(Name::Personalization(), t) && t.begin() && t.size())
|
||||
memcpy_s(m_block.personalization(), PERSONALIZATIONSIZE, t.begin(), t.size());
|
||||
|
||||
Restart();
|
||||
}
|
||||
|
||||
void BLAKE2s::Restart()
|
||||
{
|
||||
static const word32 zero[2] = {0,0};
|
||||
Restart(m_block, zero);
|
||||
}
|
||||
|
||||
void BLAKE2b::Restart()
|
||||
{
|
||||
static const word64 zero[2] = {0,0};
|
||||
Restart(m_block, zero);
|
||||
}
|
||||
|
||||
void BLAKE2s::Restart(const BLAKE2s_ParameterBlock& block, const word32 counter[2])
|
||||
{
|
||||
// We take a counter as a parameter to allow customized state.
|
||||
m_state.Reset();
|
||||
if (counter != NULLPTR)
|
||||
{
|
||||
word32* t = m_state.t();
|
||||
t[0] = counter[0];
|
||||
t[1] = counter[1];
|
||||
}
|
||||
|
||||
// We take a parameter block as a parameter to allow customized state.
|
||||
// Avoid the copy of the parameter block when we are passing our own block.
|
||||
if (block.data() != m_block.data()) {
|
||||
std::memcpy(m_block.data(), block.data(), m_block.size());
|
||||
}
|
||||
|
||||
m_block.m_data[BLAKE2s_ParameterBlock::DigestOff] = (byte)m_digestSize;
|
||||
m_block.m_data[BLAKE2s_ParameterBlock::KeyOff] = (byte)m_keyLength;
|
||||
|
||||
const word32* iv = BLAKE2S_IV;
|
||||
PutBlock<word32, LittleEndian, true> put(m_block.data(), m_state.h());
|
||||
put(iv[0])(iv[1])(iv[2])(iv[3])(iv[4])(iv[5])(iv[6])(iv[7]);
|
||||
|
||||
// When BLAKE2 is keyed, the input stream is simply {key || 0 || message}.
|
||||
// The key is padded to a full Blocksize with 0. Key it during Restart to
|
||||
// avoid FirstPut and friends. Key size == 0 means no key.
|
||||
if (m_keyLength)
|
||||
Update(m_key, BLOCKSIZE);
|
||||
}
|
||||
|
||||
void BLAKE2b::Restart(const BLAKE2b_ParameterBlock& block, const word64 counter[2])
|
||||
{
|
||||
// We take a counter as a parameter to allow customized state.
|
||||
m_state.Reset();
|
||||
if (counter != NULLPTR)
|
||||
{
|
||||
word64* t = m_state.t();
|
||||
t[0] = counter[0];
|
||||
t[1] = counter[1];
|
||||
}
|
||||
|
||||
// We take a parameter block as a parameter to allow customized state.
|
||||
// Avoid the copy of the parameter block when we are passing our own block.
|
||||
if (block.data() != m_block.data()) {
|
||||
std::memcpy(m_block.data(), block.data(), m_block.size());
|
||||
}
|
||||
|
||||
m_block.m_data[BLAKE2b_ParameterBlock::DigestOff] = (byte)m_digestSize;
|
||||
m_block.m_data[BLAKE2b_ParameterBlock::KeyOff] = (byte)m_keyLength;
|
||||
|
||||
const word64* iv = BLAKE2B_IV;
|
||||
PutBlock<word64, LittleEndian, true> put(m_block.data(), m_state.h());
|
||||
put(iv[0])(iv[1])(iv[2])(iv[3])(iv[4])(iv[5])(iv[6])(iv[7]);
|
||||
|
||||
// When BLAKE2 is keyed, the input stream is simply {key || 0 || message}.
|
||||
// The key is padded to a full Blocksize with 0. Key it during Restart to
|
||||
// avoid FirstPut and friends. Key size == 0 means no key.
|
||||
if (m_keyLength)
|
||||
Update(m_key, BLOCKSIZE);
|
||||
}
|
||||
|
||||
void BLAKE2s::Update(const byte *input, size_t length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(input != NULLPTR || length == 0);
|
||||
|
||||
if (length > BLOCKSIZE - m_state.m_len)
|
||||
{
|
||||
if (m_state.m_len != 0)
|
||||
{
|
||||
// Complete current block
|
||||
const size_t fill = BLOCKSIZE - m_state.m_len;
|
||||
std::memcpy(m_state.m_buf+m_state.m_len, input, fill);
|
||||
|
||||
IncrementCounter(BLOCKSIZE);
|
||||
Compress(m_state.m_buf);
|
||||
m_state.m_len = 0;
|
||||
|
||||
length -= fill, input += fill;
|
||||
}
|
||||
|
||||
// Compress in-place to avoid copies
|
||||
while (length > BLOCKSIZE)
|
||||
{
|
||||
IncrementCounter(BLOCKSIZE);
|
||||
Compress(input);
|
||||
length -= BLOCKSIZE, input += BLOCKSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy tail bytes
|
||||
if (length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(length <= BLOCKSIZE - m_state.m_len);
|
||||
std::memcpy(m_state.m_buf+m_state.m_len, input, length);
|
||||
m_state.m_len += static_cast<unsigned int>(length);
|
||||
}
|
||||
}
|
||||
|
||||
void BLAKE2b::Update(const byte *input, size_t length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(input != NULLPTR || length == 0);
|
||||
|
||||
if (length > BLOCKSIZE - m_state.m_len)
|
||||
{
|
||||
if (m_state.m_len != 0)
|
||||
{
|
||||
// Complete current block
|
||||
const size_t fill = BLOCKSIZE - m_state.m_len;
|
||||
std::memcpy(m_state.m_buf+m_state.m_len, input, fill);
|
||||
|
||||
IncrementCounter(BLOCKSIZE);
|
||||
Compress(m_state.m_buf);
|
||||
m_state.m_len = 0;
|
||||
|
||||
length -= fill, input += fill;
|
||||
}
|
||||
|
||||
// Compress in-place to avoid copies
|
||||
while (length > BLOCKSIZE)
|
||||
{
|
||||
CRYPTOPP_ASSERT(m_state.m_len == 0);
|
||||
IncrementCounter(BLOCKSIZE);
|
||||
Compress(input);
|
||||
length -= BLOCKSIZE, input += BLOCKSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy tail bytes
|
||||
if (length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(length <= BLOCKSIZE - m_state.m_len);
|
||||
std::memcpy(m_state.m_buf + m_state.m_len, input, length);
|
||||
m_state.m_len += static_cast<unsigned int>(length);
|
||||
}
|
||||
}
|
||||
|
||||
void BLAKE2s::TruncatedFinal(byte *hash, size_t size)
|
||||
{
|
||||
CRYPTOPP_ASSERT(hash != NULLPTR);
|
||||
this->ThrowIfInvalidTruncatedSize(size);
|
||||
word32* f = m_state.f();
|
||||
|
||||
// Set last block unconditionally
|
||||
f[0] = ~static_cast<word32>(0);
|
||||
|
||||
// Set last node if tree mode
|
||||
if (m_treeMode)
|
||||
f[1] = ~static_cast<word32>(0);
|
||||
|
||||
// Increment counter for tail bytes only
|
||||
IncrementCounter(m_state.m_len);
|
||||
|
||||
std::memset(m_state.m_buf + m_state.m_len, 0x00, BLOCKSIZE - m_state.m_len);
|
||||
Compress(m_state.m_buf);
|
||||
|
||||
// Copy to caller buffer
|
||||
std::memcpy(hash, m_state.h(), size);
|
||||
|
||||
Restart();
|
||||
}
|
||||
|
||||
void BLAKE2b::TruncatedFinal(byte *hash, size_t size)
|
||||
{
|
||||
CRYPTOPP_ASSERT(hash != NULLPTR);
|
||||
this->ThrowIfInvalidTruncatedSize(size);
|
||||
word64* f = m_state.f();
|
||||
|
||||
// Set last block unconditionally
|
||||
f[0] = ~static_cast<word64>(0);
|
||||
|
||||
// Set last node if tree mode
|
||||
if (m_treeMode)
|
||||
f[1] = ~static_cast<word64>(0);
|
||||
|
||||
// Increment counter for tail bytes only
|
||||
IncrementCounter(m_state.m_len);
|
||||
|
||||
std::memset(m_state.m_buf + m_state.m_len, 0x00, BLOCKSIZE - m_state.m_len);
|
||||
Compress(m_state.m_buf);
|
||||
|
||||
// Copy to caller buffer
|
||||
std::memcpy(hash, m_state.h(), size);
|
||||
|
||||
Restart();
|
||||
}
|
||||
|
||||
void BLAKE2s::IncrementCounter(size_t count)
|
||||
{
|
||||
word32* t = m_state.t();
|
||||
t[0] += static_cast<word32>(count);
|
||||
t[1] += !!(t[0] < count);
|
||||
}
|
||||
|
||||
void BLAKE2b::IncrementCounter(size_t count)
|
||||
{
|
||||
word64* t = m_state.t();
|
||||
t[0] += static_cast<word64>(count);
|
||||
t[1] += !!(t[0] < count);
|
||||
}
|
||||
|
||||
void BLAKE2s::Compress(const byte *input)
|
||||
{
|
||||
#if CRYPTOPP_SSE41_AVAILABLE
|
||||
if(HasSSE41())
|
||||
{
|
||||
return BLAKE2_Compress32_SSE4(input, m_state);
|
||||
}
|
||||
#endif
|
||||
#if CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
if(HasNEON())
|
||||
{
|
||||
return BLAKE2_Compress32_NEON(input, m_state);
|
||||
}
|
||||
#endif
|
||||
#if CRYPTOPP_ALTIVEC_AVAILABLE
|
||||
if(HasAltivec())
|
||||
{
|
||||
return BLAKE2_Compress32_ALTIVEC(input, m_state);
|
||||
}
|
||||
#endif
|
||||
return BLAKE2_Compress32_CXX(input, m_state);
|
||||
}
|
||||
|
||||
void BLAKE2b::Compress(const byte *input)
|
||||
{
|
||||
#if CRYPTOPP_SSE41_AVAILABLE
|
||||
if(HasSSE41())
|
||||
{
|
||||
return BLAKE2_Compress64_SSE4(input, m_state);
|
||||
}
|
||||
#endif
|
||||
#if CRYPTOPP_ARM_NEON_AVAILABLE
|
||||
if(HasNEON())
|
||||
{
|
||||
return BLAKE2_Compress64_NEON(input, m_state);
|
||||
}
|
||||
#endif
|
||||
#if CRYPTOPP_POWER8_AVAILABLE
|
||||
if(HasPower8())
|
||||
{
|
||||
return BLAKE2_Compress64_POWER8(input, m_state);
|
||||
}
|
||||
#endif
|
||||
return BLAKE2_Compress64_CXX(input, m_state);
|
||||
}
|
||||
|
||||
void BLAKE2_Compress64_CXX(const byte* input, BLAKE2b_State& state)
|
||||
{
|
||||
word64 m[16], v[16];
|
||||
|
||||
GetBlock<word64, LittleEndian, true> get1(input);
|
||||
get1(m[0])(m[1])(m[2])(m[3])(m[4])(m[5])(m[6])(m[7])(m[8])(m[9])(m[10])(m[11])(m[12])(m[13])(m[14])(m[15]);
|
||||
|
||||
GetBlock<word64, LittleEndian, true> get2(state.h());
|
||||
get2(v[0])(v[1])(v[2])(v[3])(v[4])(v[5])(v[6])(v[7]);
|
||||
|
||||
const word64* iv = BLAKE2B_IV;
|
||||
const word64* tf = state.t();
|
||||
v[ 8] = iv[0];
|
||||
v[ 9] = iv[1];
|
||||
v[10] = iv[2];
|
||||
v[11] = iv[3];
|
||||
v[12] = tf[0] ^ iv[4];
|
||||
v[13] = tf[1] ^ iv[5];
|
||||
v[14] = tf[2] ^ iv[6];
|
||||
v[15] = tf[3] ^ iv[7];
|
||||
|
||||
BLAKE2B_ROUND<0>(m, v);
|
||||
BLAKE2B_ROUND<1>(m, v);
|
||||
BLAKE2B_ROUND<2>(m, v);
|
||||
BLAKE2B_ROUND<3>(m, v);
|
||||
BLAKE2B_ROUND<4>(m, v);
|
||||
BLAKE2B_ROUND<5>(m, v);
|
||||
BLAKE2B_ROUND<6>(m, v);
|
||||
BLAKE2B_ROUND<7>(m, v);
|
||||
BLAKE2B_ROUND<8>(m, v);
|
||||
BLAKE2B_ROUND<9>(m, v);
|
||||
BLAKE2B_ROUND<10>(m, v);
|
||||
BLAKE2B_ROUND<11>(m, v);
|
||||
|
||||
word64* h = state.h();
|
||||
for (unsigned int i = 0; i < 8; ++i)
|
||||
h[i] = h[i] ^ ConditionalByteReverse(LITTLE_ENDIAN_ORDER, v[i] ^ v[i + 8]);
|
||||
}
|
||||
|
||||
void BLAKE2_Compress32_CXX(const byte* input, BLAKE2s_State& state)
|
||||
{
|
||||
word32 m[16], v[16];
|
||||
|
||||
GetBlock<word32, LittleEndian, true> get1(input);
|
||||
get1(m[0])(m[1])(m[2])(m[3])(m[4])(m[5])(m[6])(m[7])(m[8])(m[9])(m[10])(m[11])(m[12])(m[13])(m[14])(m[15]);
|
||||
|
||||
GetBlock<word32, LittleEndian, true> get2(state.h());
|
||||
get2(v[0])(v[1])(v[2])(v[3])(v[4])(v[5])(v[6])(v[7]);
|
||||
|
||||
const word32* iv = BLAKE2S_IV;
|
||||
const word32* tf = state.t();
|
||||
v[ 8] = iv[0];
|
||||
v[ 9] = iv[1];
|
||||
v[10] = iv[2];
|
||||
v[11] = iv[3];
|
||||
v[12] = tf[0] ^ iv[4];
|
||||
v[13] = tf[1] ^ iv[5];
|
||||
v[14] = tf[2] ^ iv[6];
|
||||
v[15] = tf[3] ^ iv[7];
|
||||
|
||||
BLAKE2S_ROUND<0>(m, v);
|
||||
BLAKE2S_ROUND<1>(m, v);
|
||||
BLAKE2S_ROUND<2>(m, v);
|
||||
BLAKE2S_ROUND<3>(m, v);
|
||||
BLAKE2S_ROUND<4>(m, v);
|
||||
BLAKE2S_ROUND<5>(m, v);
|
||||
BLAKE2S_ROUND<6>(m, v);
|
||||
BLAKE2S_ROUND<7>(m, v);
|
||||
BLAKE2S_ROUND<8>(m, v);
|
||||
BLAKE2S_ROUND<9>(m, v);
|
||||
|
||||
word32* h = state.h();
|
||||
for (unsigned int i = 0; i < 8; ++i)
|
||||
h[i] = h[i] ^ ConditionalByteReverse(LITTLE_ENDIAN_ORDER, v[i] ^ v[i + 8]);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+444
@@ -0,0 +1,444 @@
|
||||
// blake2.h - written and placed in the public domain by Jeffrey Walton
|
||||
// and Zooko Wilcox-O'Hearn. Based on Aumasson, Neves,
|
||||
// Wilcox-O'Hearn and Winnerlein's reference BLAKE2
|
||||
// implementation at http://github.com/BLAKE2/BLAKE2.
|
||||
|
||||
/// \file blake2.h
|
||||
/// \brief Classes for BLAKE2b and BLAKE2s message digests and keyed message digests
|
||||
/// \details This implementation follows Aumasson, Neves, Wilcox-O'Hearn and Winnerlein's
|
||||
/// <A HREF="http://blake2.net/blake2.pdf">BLAKE2: simpler, smaller, fast as MD5</A> (2013.01.29).
|
||||
/// Static algorithm name return either "BLAKE2b" or "BLAKE2s". An object algorithm name follows
|
||||
/// the naming described in <A HREF="http://tools.ietf.org/html/rfc7693#section-4">RFC 7693, The
|
||||
/// BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)</A>.
|
||||
/// \since C++ since Crypto++ 5.6.4, SSE since Crypto++ 5.6.4, NEON since Crypto++ 6.0,
|
||||
/// Power8 since Crypto++ 8.0
|
||||
|
||||
#ifndef CRYPTOPP_BLAKE2_H
|
||||
#define CRYPTOPP_BLAKE2_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "secblock.h"
|
||||
#include "seckey.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief BLAKE2s hash information
|
||||
/// \since Crypto++ 5.6.4
|
||||
struct BLAKE2s_Info : public VariableKeyLength<32,0,32,1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE>
|
||||
{
|
||||
typedef VariableKeyLength<32,0,32,1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE> KeyBase;
|
||||
CRYPTOPP_CONSTANT(MIN_KEYLENGTH = KeyBase::MIN_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(MAX_KEYLENGTH = KeyBase::MAX_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH = KeyBase::DEFAULT_KEYLENGTH);
|
||||
|
||||
CRYPTOPP_CONSTANT(BLOCKSIZE = 64);
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = 32);
|
||||
CRYPTOPP_CONSTANT(SALTSIZE = 8);
|
||||
CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = 8);
|
||||
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "BLAKE2s";}
|
||||
};
|
||||
|
||||
/// \brief BLAKE2b hash information
|
||||
/// \since Crypto++ 5.6.4
|
||||
struct BLAKE2b_Info : public VariableKeyLength<64,0,64,1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE>
|
||||
{
|
||||
typedef VariableKeyLength<64,0,64,1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE> KeyBase;
|
||||
CRYPTOPP_CONSTANT(MIN_KEYLENGTH = KeyBase::MIN_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(MAX_KEYLENGTH = KeyBase::MAX_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH = KeyBase::DEFAULT_KEYLENGTH);
|
||||
|
||||
CRYPTOPP_CONSTANT(BLOCKSIZE = 128);
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = 64);
|
||||
CRYPTOPP_CONSTANT(SALTSIZE = 16);
|
||||
CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = 16);
|
||||
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "BLAKE2b";}
|
||||
};
|
||||
|
||||
/// \brief BLAKE2s parameter block
|
||||
struct CRYPTOPP_NO_VTABLE BLAKE2s_ParameterBlock
|
||||
{
|
||||
CRYPTOPP_CONSTANT(SALTSIZE = BLAKE2s_Info::SALTSIZE);
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2s_Info::DIGESTSIZE);
|
||||
CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = BLAKE2s_Info::PERSONALIZATIONSIZE);
|
||||
|
||||
BLAKE2s_ParameterBlock()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
BLAKE2s_ParameterBlock(size_t digestSize)
|
||||
{
|
||||
Reset(digestSize);
|
||||
}
|
||||
|
||||
BLAKE2s_ParameterBlock(size_t digestSize, size_t keyLength, const byte* salt, size_t saltLength,
|
||||
const byte* personalization, size_t personalizationLength);
|
||||
|
||||
void Reset(size_t digestLength=DIGESTSIZE, size_t keyLength=0);
|
||||
|
||||
byte* data() {
|
||||
return m_data.data();
|
||||
}
|
||||
|
||||
const byte* data() const {
|
||||
return m_data.data();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
byte* salt() {
|
||||
return m_data + SaltOff;
|
||||
}
|
||||
|
||||
byte* personalization() {
|
||||
return m_data + PersonalizationOff;
|
||||
}
|
||||
|
||||
// Offsets into the byte array
|
||||
enum {
|
||||
DigestOff = 0, KeyOff = 1, FanoutOff = 2, DepthOff = 3, LeafOff = 4, NodeOff = 8,
|
||||
NodeDepthOff = 14, InnerOff = 15, SaltOff = 16, PersonalizationOff = 24
|
||||
};
|
||||
|
||||
FixedSizeAlignedSecBlock<byte, 32, true> m_data;
|
||||
};
|
||||
|
||||
/// \brief BLAKE2b parameter block
|
||||
struct CRYPTOPP_NO_VTABLE BLAKE2b_ParameterBlock
|
||||
{
|
||||
CRYPTOPP_CONSTANT(SALTSIZE = BLAKE2b_Info::SALTSIZE);
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2b_Info::DIGESTSIZE);
|
||||
CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = BLAKE2b_Info::PERSONALIZATIONSIZE);
|
||||
|
||||
BLAKE2b_ParameterBlock()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
BLAKE2b_ParameterBlock(size_t digestSize)
|
||||
{
|
||||
Reset(digestSize);
|
||||
}
|
||||
|
||||
BLAKE2b_ParameterBlock(size_t digestSize, size_t keyLength, const byte* salt, size_t saltLength,
|
||||
const byte* personalization, size_t personalizationLength);
|
||||
|
||||
void Reset(size_t digestLength=DIGESTSIZE, size_t keyLength=0);
|
||||
|
||||
byte* data() {
|
||||
return m_data.data();
|
||||
}
|
||||
|
||||
const byte* data() const {
|
||||
return m_data.data();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
byte* salt() {
|
||||
return m_data + SaltOff;
|
||||
}
|
||||
|
||||
byte* personalization() {
|
||||
return m_data + PersonalizationOff;
|
||||
}
|
||||
|
||||
// Offsets into the byte array
|
||||
enum {
|
||||
DigestOff = 0, KeyOff = 1, FanoutOff = 2, DepthOff = 3, LeafOff = 4, NodeOff = 8,
|
||||
NodeDepthOff = 16, InnerOff = 17, RfuOff = 18, SaltOff = 32, PersonalizationOff = 48
|
||||
};
|
||||
|
||||
FixedSizeAlignedSecBlock<byte, 64, true> m_data;
|
||||
};
|
||||
|
||||
/// \brief BLAKE2s state information
|
||||
/// \since Crypto++ 5.6.4
|
||||
struct CRYPTOPP_NO_VTABLE BLAKE2s_State
|
||||
{
|
||||
BLAKE2s_State() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset();
|
||||
|
||||
inline word32* h() {
|
||||
return m_hft.data();
|
||||
}
|
||||
|
||||
inline word32* t() {
|
||||
return m_hft.data() + 8;
|
||||
}
|
||||
|
||||
inline word32* f() {
|
||||
return m_hft.data() + 10;
|
||||
}
|
||||
|
||||
inline byte* data() {
|
||||
return m_buf.data();
|
||||
}
|
||||
|
||||
// SSE4, Power7 and NEON depend upon t[] and f[] being side-by-side
|
||||
CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE2s_Info::BLOCKSIZE);
|
||||
FixedSizeAlignedSecBlock<word32, 8+2+2, true> m_hft;
|
||||
FixedSizeAlignedSecBlock<byte, BLOCKSIZE, true> m_buf;
|
||||
size_t m_len;
|
||||
};
|
||||
|
||||
/// \brief BLAKE2b state information
|
||||
/// \since Crypto++ 5.6.4
|
||||
struct CRYPTOPP_NO_VTABLE BLAKE2b_State
|
||||
{
|
||||
BLAKE2b_State() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset();
|
||||
|
||||
inline word64* h() {
|
||||
return m_hft.data();
|
||||
}
|
||||
|
||||
inline word64* t() {
|
||||
return m_hft.data() + 8;
|
||||
}
|
||||
|
||||
inline word64* f() {
|
||||
return m_hft.data() + 10;
|
||||
}
|
||||
|
||||
inline byte* data() {
|
||||
return m_buf.data();
|
||||
}
|
||||
|
||||
// SSE4, Power8 and NEON depend upon t[] and f[] being side-by-side
|
||||
CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE2b_Info::BLOCKSIZE);
|
||||
FixedSizeAlignedSecBlock<word64, 8+2+2, true> m_hft;
|
||||
FixedSizeAlignedSecBlock<byte, BLOCKSIZE, true> m_buf;
|
||||
size_t m_len;
|
||||
};
|
||||
|
||||
/// \brief The BLAKE2s cryptographic hash function
|
||||
/// \details BLAKE2s can function as both a hash and keyed hash. If you want only the hash,
|
||||
/// then use the BLAKE2s constructor that accepts no parameters or digest size. If you
|
||||
/// want a keyed hash, then use the constructor that accpts the key as a parameter.
|
||||
/// Once a key and digest size are selected, its effectively immutable. The Restart()
|
||||
/// method that accepts a ParameterBlock does not allow you to change it.
|
||||
/// \sa Aumasson, Neves, Wilcox-O'Hearn and Winnerlein's
|
||||
/// <A HREF="http://blake2.net/blake2.pdf">BLAKE2: simpler, smaller, fast as MD5</A> (2013.01.29).
|
||||
/// \since C++ since Crypto++ 5.6.4, SSE since Crypto++ 5.6.4, NEON since Crypto++ 6.0,
|
||||
/// Power8 since Crypto++ 8.0
|
||||
class BLAKE2s : public SimpleKeyingInterfaceImpl<MessageAuthenticationCode, BLAKE2s_Info>
|
||||
{
|
||||
public:
|
||||
CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH = BLAKE2s_Info::DEFAULT_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(MIN_KEYLENGTH = BLAKE2s_Info::MIN_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(MAX_KEYLENGTH = BLAKE2s_Info::MAX_KEYLENGTH);
|
||||
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2s_Info::DIGESTSIZE);
|
||||
CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE2s_Info::BLOCKSIZE);
|
||||
CRYPTOPP_CONSTANT(SALTSIZE = BLAKE2s_Info::SALTSIZE);
|
||||
CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = BLAKE2s_Info::PERSONALIZATIONSIZE);
|
||||
|
||||
typedef BLAKE2s_State State;
|
||||
typedef BLAKE2s_ParameterBlock ParameterBlock;
|
||||
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "BLAKE2s";}
|
||||
|
||||
virtual ~BLAKE2s() {}
|
||||
|
||||
/// \brief Construct a BLAKE2s hash
|
||||
/// \param digestSize the digest size, in bytes
|
||||
/// \param treeMode flag indicating tree mode
|
||||
/// \since Crypto++ 5.6.4
|
||||
BLAKE2s(bool treeMode=false, unsigned int digestSize = DIGESTSIZE);
|
||||
|
||||
/// \brief Construct a BLAKE2s hash
|
||||
/// \param digestSize the digest size, in bytes
|
||||
/// \details treeMode flag is set to false
|
||||
/// \since Crypto++ 8.2
|
||||
BLAKE2s(unsigned int digestSize);
|
||||
|
||||
/// \brief Construct a BLAKE2s hash
|
||||
/// \param key a byte array used to key the cipher
|
||||
/// \param keyLength the size of the byte array
|
||||
/// \param salt a byte array used as salt
|
||||
/// \param saltLength the size of the byte array
|
||||
/// \param personalization a byte array used as personalization string
|
||||
/// \param personalizationLength the size of the byte array
|
||||
/// \param treeMode flag indicating tree mode
|
||||
/// \param digestSize the digest size, in bytes
|
||||
/// \since Crypto++ 5.6.4
|
||||
BLAKE2s(const byte *key, size_t keyLength, const byte* salt = NULLPTR, size_t saltLength = 0,
|
||||
const byte* personalization = NULLPTR, size_t personalizationLength = 0,
|
||||
bool treeMode=false, unsigned int digestSize = DIGESTSIZE);
|
||||
|
||||
/// \brief Retrieve the object's name
|
||||
/// \return the object's algorithm name following RFC 7693
|
||||
/// \details Object algorithm name follows the naming described in
|
||||
/// <A HREF="http://tools.ietf.org/html/rfc7693#section-4">RFC 7693, The BLAKE2 Cryptographic Hash and
|
||||
/// Message Authentication Code (MAC)</A>. For example, "BLAKE2b-512" and "BLAKE2s-256".
|
||||
std::string AlgorithmName() const {return std::string(BLAKE2s_Info::StaticAlgorithmName()) + "-" + IntToString(DigestSize()*8);}
|
||||
|
||||
unsigned int BlockSize() const {return BLOCKSIZE;}
|
||||
unsigned int DigestSize() const {return m_digestSize;}
|
||||
unsigned int OptimalDataAlignment() const;
|
||||
|
||||
void Update(const byte *input, size_t length);
|
||||
void Restart();
|
||||
|
||||
/// \brief Restart a hash with parameter block and counter
|
||||
/// \param block parameter block
|
||||
/// \param counter counter array
|
||||
/// \details Parameter block is persisted across calls to Restart().
|
||||
void Restart(const BLAKE2s_ParameterBlock& block, const word32 counter[2]);
|
||||
|
||||
/// \brief Set tree mode
|
||||
/// \param mode the new tree mode
|
||||
/// \details BLAKE2 has two finalization flags, called State::f[0] and State::f[1].
|
||||
/// If <tt>treeMode=false</tt> (default), then State::f[1] is never set. If
|
||||
/// <tt>treeMode=true</tt>, then State::f[1] is set when State::f[0] is set.
|
||||
/// Tree mode is persisted across calls to Restart().
|
||||
void SetTreeMode(bool mode) {m_treeMode=mode;}
|
||||
|
||||
/// \brief Get tree mode
|
||||
/// \return the current tree mode
|
||||
/// \details Tree mode is persisted across calls to Restart().
|
||||
bool GetTreeMode() const {return m_treeMode;}
|
||||
|
||||
void TruncatedFinal(byte *hash, size_t size);
|
||||
|
||||
std::string AlgorithmProvider() const;
|
||||
|
||||
protected:
|
||||
// Operates on state buffer and/or input. Must be BLOCKSIZE, final block will pad with 0's.
|
||||
void Compress(const byte *input);
|
||||
inline void IncrementCounter(size_t count=BLOCKSIZE);
|
||||
|
||||
void UncheckedSetKey(const byte* key, unsigned int length, const CryptoPP::NameValuePairs& params);
|
||||
|
||||
private:
|
||||
State m_state;
|
||||
ParameterBlock m_block;
|
||||
AlignedSecByteBlock m_key;
|
||||
word32 m_digestSize, m_keyLength;
|
||||
bool m_treeMode;
|
||||
};
|
||||
|
||||
/// \brief The BLAKE2b cryptographic hash function
|
||||
/// \details BLAKE2b can function as both a hash and keyed hash. If you want only the hash,
|
||||
/// then use the BLAKE2b constructor that accepts no parameters or digest size. If you
|
||||
/// want a keyed hash, then use the constructor that accpts the key as a parameter.
|
||||
/// Once a key and digest size are selected, its effectively immutable. The Restart()
|
||||
/// method that accepts a ParameterBlock does not allow you to change it.
|
||||
/// \sa Aumasson, Neves, Wilcox-O'Hearn and Winnerlein's
|
||||
/// <A HREF="http://blake2.net/blake2.pdf">BLAKE2: simpler, smaller, fast as MD5</A> (2013.01.29).
|
||||
/// \since C++ since Crypto++ 5.6.4, SSE since Crypto++ 5.6.4, NEON since Crypto++ 6.0,
|
||||
/// Power8 since Crypto++ 8.0
|
||||
class BLAKE2b : public SimpleKeyingInterfaceImpl<MessageAuthenticationCode, BLAKE2b_Info>
|
||||
{
|
||||
public:
|
||||
CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH = BLAKE2b_Info::DEFAULT_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(MIN_KEYLENGTH = BLAKE2b_Info::MIN_KEYLENGTH);
|
||||
CRYPTOPP_CONSTANT(MAX_KEYLENGTH = BLAKE2b_Info::MAX_KEYLENGTH);
|
||||
|
||||
CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2b_Info::DIGESTSIZE);
|
||||
CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE2b_Info::BLOCKSIZE);
|
||||
CRYPTOPP_CONSTANT(SALTSIZE = BLAKE2b_Info::SALTSIZE);
|
||||
CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = BLAKE2b_Info::PERSONALIZATIONSIZE);
|
||||
|
||||
typedef BLAKE2b_State State;
|
||||
typedef BLAKE2b_ParameterBlock ParameterBlock;
|
||||
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "BLAKE2b";}
|
||||
|
||||
virtual ~BLAKE2b() {}
|
||||
|
||||
/// \brief Construct a BLAKE2b hash
|
||||
/// \param digestSize the digest size, in bytes
|
||||
/// \param treeMode flag indicating tree mode
|
||||
/// \since Crypto++ 5.6.4
|
||||
BLAKE2b(bool treeMode=false, unsigned int digestSize = DIGESTSIZE);
|
||||
|
||||
/// \brief Construct a BLAKE2s hash
|
||||
/// \param digestSize the digest size, in bytes
|
||||
/// \details treeMode flag is set to false
|
||||
/// \since Crypto++ 8.2
|
||||
BLAKE2b(unsigned int digestSize);
|
||||
|
||||
/// \brief Construct a BLAKE2b hash
|
||||
/// \param key a byte array used to key the cipher
|
||||
/// \param keyLength the size of the byte array
|
||||
/// \param salt a byte array used as salt
|
||||
/// \param saltLength the size of the byte array
|
||||
/// \param personalization a byte array used as personalization string
|
||||
/// \param personalizationLength the size of the byte array
|
||||
/// \param treeMode flag indicating tree mode
|
||||
/// \param digestSize the digest size, in bytes
|
||||
/// \since Crypto++ 5.6.4
|
||||
BLAKE2b(const byte *key, size_t keyLength, const byte* salt = NULLPTR, size_t saltLength = 0,
|
||||
const byte* personalization = NULLPTR, size_t personalizationLength = 0,
|
||||
bool treeMode=false, unsigned int digestSize = DIGESTSIZE);
|
||||
|
||||
/// \brief Retrieve the object's name
|
||||
/// \return the object's algorithm name following RFC 7693
|
||||
/// \details Object algorithm name follows the naming described in
|
||||
/// <A HREF="http://tools.ietf.org/html/rfc7693#section-4">RFC 7693, The BLAKE2 Cryptographic Hash and
|
||||
/// Message Authentication Code (MAC)</A>. For example, "BLAKE2b-512" and "BLAKE2s-256".
|
||||
std::string AlgorithmName() const {return std::string(BLAKE2b_Info::StaticAlgorithmName()) + "-" + IntToString(DigestSize()*8);}
|
||||
|
||||
unsigned int BlockSize() const {return BLOCKSIZE;}
|
||||
unsigned int DigestSize() const {return m_digestSize;}
|
||||
unsigned int OptimalDataAlignment() const;
|
||||
|
||||
void Update(const byte *input, size_t length);
|
||||
void Restart();
|
||||
|
||||
/// \brief Restart a hash with parameter block and counter
|
||||
/// \param block parameter block
|
||||
/// \param counter counter array
|
||||
/// \details Parameter block is persisted across calls to Restart().
|
||||
void Restart(const BLAKE2b_ParameterBlock& block, const word64 counter[2]);
|
||||
|
||||
/// \brief Set tree mode
|
||||
/// \param mode the new tree mode
|
||||
/// \details BLAKE2 has two finalization flags, called State::f[0] and State::f[1].
|
||||
/// If <tt>treeMode=false</tt> (default), then State::f[1] is never set. If
|
||||
/// <tt>treeMode=true</tt>, then State::f[1] is set when State::f[0] is set.
|
||||
/// Tree mode is persisted across calls to Restart().
|
||||
void SetTreeMode(bool mode) {m_treeMode=mode;}
|
||||
|
||||
/// \brief Get tree mode
|
||||
/// \return the current tree mode
|
||||
/// \details Tree mode is persisted across calls to Restart().
|
||||
bool GetTreeMode() const {return m_treeMode;}
|
||||
|
||||
void TruncatedFinal(byte *hash, size_t size);
|
||||
|
||||
std::string AlgorithmProvider() const;
|
||||
|
||||
protected:
|
||||
|
||||
// Operates on state buffer and/or input. Must be BLOCKSIZE, final block will pad with 0's.
|
||||
void Compress(const byte *input);
|
||||
inline void IncrementCounter(size_t count=BLOCKSIZE);
|
||||
|
||||
void UncheckedSetKey(const byte* key, unsigned int length, const CryptoPP::NameValuePairs& params);
|
||||
|
||||
private:
|
||||
State m_state;
|
||||
ParameterBlock m_block;
|
||||
AlignedSecByteBlock m_key;
|
||||
word32 m_digestSize, m_keyLength;
|
||||
bool m_treeMode;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+1276
File diff suppressed because it is too large
Load Diff
+1091
File diff suppressed because it is too large
Load Diff
+99
@@ -0,0 +1,99 @@
|
||||
// blowfish.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
#include "blowfish.h"
|
||||
#include "misc.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void Blowfish::Base::UncheckedSetKey(const byte *key_string, unsigned int keylength, const NameValuePairs &)
|
||||
{
|
||||
AssertValidKeyLength(keylength);
|
||||
|
||||
unsigned i, j=0, k;
|
||||
word32 data, dspace[2] = {0, 0};
|
||||
|
||||
memcpy(pbox, p_init, sizeof(p_init));
|
||||
memcpy(sbox, s_init, sizeof(s_init));
|
||||
|
||||
// Xor key string into encryption key vector
|
||||
for (i=0 ; i<ROUNDS+2 ; ++i)
|
||||
{
|
||||
data = 0 ;
|
||||
for (k=0 ; k<4 ; ++k )
|
||||
data = (data << 8) | key_string[j++ % keylength];
|
||||
pbox[i] ^= data;
|
||||
}
|
||||
|
||||
crypt_block(dspace, pbox);
|
||||
|
||||
for (i=0; i<ROUNDS; i+=2)
|
||||
crypt_block(pbox+i, pbox+i+2);
|
||||
|
||||
crypt_block(pbox+ROUNDS, sbox);
|
||||
|
||||
for (i=0; i<4*256-2; i+=2)
|
||||
crypt_block(sbox+i, sbox+i+2);
|
||||
|
||||
if (!IsForwardTransformation())
|
||||
for (i=0; i<(ROUNDS+2)/2; i++)
|
||||
std::swap(pbox[i], pbox[ROUNDS+1-i]);
|
||||
}
|
||||
|
||||
// this version is only used to make pbox and sbox
|
||||
void Blowfish::Base::crypt_block(const word32 in[2], word32 out[2]) const
|
||||
{
|
||||
word32 left = in[0];
|
||||
word32 right = in[1];
|
||||
|
||||
const word32 *const s=sbox;
|
||||
const word32 *p=pbox;
|
||||
|
||||
left ^= p[0];
|
||||
|
||||
for (unsigned i=0; i<ROUNDS/2; i++)
|
||||
{
|
||||
right ^= (((s[GETBYTE(left,3)] + s[256+GETBYTE(left,2)])
|
||||
^ s[2*256+GETBYTE(left,1)]) + s[3*256+GETBYTE(left,0)])
|
||||
^ p[2*i+1];
|
||||
|
||||
left ^= (((s[GETBYTE(right,3)] + s[256+GETBYTE(right,2)])
|
||||
^ s[2*256+GETBYTE(right,1)]) + s[3*256+GETBYTE(right,0)])
|
||||
^ p[2*i+2];
|
||||
}
|
||||
|
||||
right ^= p[ROUNDS+1];
|
||||
|
||||
out[0] = right;
|
||||
out[1] = left;
|
||||
}
|
||||
|
||||
void Blowfish::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
typedef BlockGetAndPut<word32, BigEndian> Block;
|
||||
|
||||
word32 left, right;
|
||||
Block::Get(inBlock)(left)(right);
|
||||
|
||||
const word32 *const s=sbox;
|
||||
const word32 *p=pbox;
|
||||
|
||||
left ^= p[0];
|
||||
|
||||
for (unsigned i=0; i<ROUNDS/2; i++)
|
||||
{
|
||||
right ^= (((s[GETBYTE(left,3)] + s[256+GETBYTE(left,2)])
|
||||
^ s[2*256+GETBYTE(left,1)]) + s[3*256+GETBYTE(left,0)])
|
||||
^ p[2*i+1];
|
||||
|
||||
left ^= (((s[GETBYTE(right,3)] + s[256+GETBYTE(right,2)])
|
||||
^ s[2*256+GETBYTE(right,1)]) + s[3*256+GETBYTE(right,0)])
|
||||
^ p[2*i+2];
|
||||
}
|
||||
|
||||
right ^= p[ROUNDS+1];
|
||||
|
||||
Block::Put(xorBlock, outBlock)(right)(left);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// blowfish.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file blowfish.h
|
||||
/// \brief Classes for the Blowfish block cipher
|
||||
|
||||
#ifndef CRYPTOPP_BLOWFISH_H
|
||||
#define CRYPTOPP_BLOWFISH_H
|
||||
|
||||
#include "seckey.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Blowfish block cipher information
|
||||
struct Blowfish_Info : public FixedBlockSize<8>, public VariableKeyLength<16, 4, 56>, public FixedRounds<16>
|
||||
{
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "Blowfish";}
|
||||
};
|
||||
|
||||
// <a href="http://www.cryptopp.com/wiki/Blowfish">Blowfish</a>
|
||||
|
||||
/// \brief Blowfish block cipher
|
||||
/// \since Crypto++ 1.0
|
||||
class Blowfish : public Blowfish_Info, public BlockCipherDocumentation
|
||||
{
|
||||
/// \brief Class specific implementation and overrides used to operate the cipher.
|
||||
/// \details Implementations and overrides in \p Base apply to both \p ENCRYPTION and \p DECRYPTION directions
|
||||
class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl<Blowfish_Info>
|
||||
{
|
||||
public:
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
void UncheckedSetKey(const byte *key_string, unsigned int keylength, const NameValuePairs ¶ms);
|
||||
|
||||
private:
|
||||
void crypt_block(const word32 in[2], word32 out[2]) const;
|
||||
|
||||
static const word32 p_init[ROUNDS+2];
|
||||
static const word32 s_init[4*256];
|
||||
|
||||
FixedSizeSecBlock<word32, ROUNDS+2> pbox;
|
||||
FixedSizeSecBlock<word32, 4*256> sbox;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BlockCipherFinal<ENCRYPTION, Base> Encryption;
|
||||
typedef BlockCipherFinal<DECRYPTION, Base> Decryption;
|
||||
};
|
||||
|
||||
typedef Blowfish::Encryption BlowfishEncryption;
|
||||
typedef Blowfish::Decryption BlowfishDecryption;
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// blumshub.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
#include "blumshub.h"
|
||||
#include "integer.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
PublicBlumBlumShub::PublicBlumBlumShub(const Integer &n, const Integer &seed)
|
||||
: modn(n),
|
||||
current(modn.Square(modn.Square(seed))),
|
||||
maxBits(BitPrecision(n.BitCount())-1),
|
||||
bitsLeft(maxBits)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned int PublicBlumBlumShub::GenerateBit()
|
||||
{
|
||||
if (bitsLeft==0)
|
||||
{
|
||||
current = modn.Square(current);
|
||||
bitsLeft = maxBits;
|
||||
}
|
||||
|
||||
return static_cast<unsigned int>(current.GetBit(--bitsLeft));
|
||||
}
|
||||
|
||||
byte PublicBlumBlumShub::GenerateByte()
|
||||
{
|
||||
byte b=0;
|
||||
for (int i=0; i<8; i++)
|
||||
b = byte((b << 1) | PublicBlumBlumShub::GenerateBit());
|
||||
return b;
|
||||
}
|
||||
|
||||
void PublicBlumBlumShub::GenerateBlock(byte *output, size_t size)
|
||||
{
|
||||
while (size--)
|
||||
*output++ = PublicBlumBlumShub::GenerateByte();
|
||||
}
|
||||
|
||||
void PublicBlumBlumShub::ProcessData(byte *outString, const byte *inString, size_t length)
|
||||
{
|
||||
while (length--)
|
||||
*outString++ = *inString++ ^ PublicBlumBlumShub::GenerateByte();
|
||||
}
|
||||
|
||||
BlumBlumShub::BlumBlumShub(const Integer &p, const Integer &q, const Integer &seed)
|
||||
: PublicBlumBlumShub(p*q, seed),
|
||||
p(p), q(q),
|
||||
x0(modn.Square(seed))
|
||||
{
|
||||
}
|
||||
|
||||
void BlumBlumShub::Seek(lword index)
|
||||
{
|
||||
Integer i(Integer::POSITIVE, index);
|
||||
i *= 8;
|
||||
Integer e = a_exp_b_mod_c (2, i / maxBits + 1, (p-1)*(q-1));
|
||||
current = modn.Exponentiate(x0, e);
|
||||
bitsLeft = maxBits - i % maxBits;
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// blumshub.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file blumshub.h
|
||||
/// \brief Classes for Blum Blum Shub generator
|
||||
|
||||
#ifndef CRYPTOPP_BLUMSHUB_H
|
||||
#define CRYPTOPP_BLUMSHUB_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "modarith.h"
|
||||
#include "integer.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief BlumBlumShub without factorization of the modulus
|
||||
/// \details You should reseed the generator after a fork() to avoid multiple generators
|
||||
/// with the same internal state.
|
||||
class PublicBlumBlumShub : public RandomNumberGenerator,
|
||||
public StreamTransformation
|
||||
{
|
||||
public:
|
||||
virtual ~PublicBlumBlumShub() {}
|
||||
|
||||
/// \brief Construct a PublicBlumBlumShub
|
||||
/// \param n the modulus
|
||||
/// \param seed the seed for the generator
|
||||
/// \details seed is the secret key and should be about as large as n.
|
||||
PublicBlumBlumShub(const Integer &n, const Integer &seed);
|
||||
|
||||
unsigned int GenerateBit();
|
||||
byte GenerateByte();
|
||||
void GenerateBlock(byte *output, size_t size);
|
||||
void ProcessData(byte *outString, const byte *inString, size_t length);
|
||||
|
||||
bool IsSelfInverting() const {return true;}
|
||||
bool IsForwardTransformation() const {return true;}
|
||||
|
||||
protected:
|
||||
ModularArithmetic modn;
|
||||
Integer current;
|
||||
word maxBits, bitsLeft;
|
||||
};
|
||||
|
||||
/// \brief BlumBlumShub with factorization of the modulus
|
||||
/// \details You should reseed the generator after a fork() to avoid multiple generators
|
||||
/// with the same internal state.
|
||||
class BlumBlumShub : public PublicBlumBlumShub
|
||||
{
|
||||
public:
|
||||
virtual ~BlumBlumShub() {}
|
||||
|
||||
/// \brief Construct a BlumBlumShub
|
||||
/// \param p the first prime factor
|
||||
/// \param q the second prime factor
|
||||
/// \param seed the seed for the generator
|
||||
/// \details Esure p and q are both primes congruent to 3 mod 4 and at least 512 bits long.
|
||||
/// seed is the secret key and should be about as large as p*q.
|
||||
BlumBlumShub(const Integer &p, const Integer &q, const Integer &seed);
|
||||
|
||||
bool IsRandomAccess() const {return true;}
|
||||
void Seek(lword index);
|
||||
|
||||
protected:
|
||||
const Integer p, q;
|
||||
const Integer x0;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+539
@@ -0,0 +1,539 @@
|
||||
// camellia.cpp - by Kevin Springle, 2003
|
||||
// This code is hereby placed in the public domain.
|
||||
|
||||
/*
|
||||
Optimisations and defense against timing attacks added in Jan 2007 by Wei Dai.
|
||||
|
||||
The first 2 rounds and the last round seem especially vulnerable to timing
|
||||
attacks. The protection is similar to what was implemented for Rijndael.
|
||||
See comments at top of rijndael.cpp for more details.
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4456)
|
||||
# if (CRYPTOPP_MSC_VERSION >= 1400)
|
||||
# pragma warning(disable: 6246)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "camellia.h"
|
||||
#include "misc.h"
|
||||
#include "cpu.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
// round implementation that uses a small table for protection against timing attacks
|
||||
#define SLOW_ROUND(lh, ll, rh, rl, kh, kl) { \
|
||||
word32 zr = ll ^ kl; \
|
||||
word32 zl = lh ^ kh; \
|
||||
zr= rotlConstant<1>(s1[GETBYTE(zr, 3)]) | \
|
||||
(rotrConstant<1>(s1[GETBYTE(zr, 2)]) << 24) | \
|
||||
(s1[rotlConstant<1>(CRYPTOPP_GET_BYTE_AS_BYTE(zr, 1))] << 16) | \
|
||||
(s1[GETBYTE(zr, 0)] << 8); \
|
||||
zl= (s1[GETBYTE(zl, 3)] << 24) | \
|
||||
(rotlConstant<1>(s1[GETBYTE(zl, 2)]) << 16) | \
|
||||
(rotrConstant<1>(s1[GETBYTE(zl, 1)]) << 8) | \
|
||||
s1[rotlConstant<1>(CRYPTOPP_GET_BYTE_AS_BYTE(zl, 0))]; \
|
||||
zl ^= zr; \
|
||||
zr = zl ^ rotlConstant<8>(zr); \
|
||||
zl = zr ^ rotrConstant<8>(zl); \
|
||||
rh ^= rotlConstant<16>(zr); \
|
||||
rh ^= zl; \
|
||||
rl ^= rotlConstant<8>(zl); \
|
||||
}
|
||||
|
||||
// normal round - same output as above but using larger tables for faster speed
|
||||
#define ROUND(lh, ll, rh, rl, kh, kl) { \
|
||||
word32 th = lh ^ kh; \
|
||||
word32 tl = ll ^ kl; \
|
||||
word32 d = SP[0][GETBYTE(tl,0)] ^ SP[1][GETBYTE(tl,3)] ^ SP[2][GETBYTE(tl,2)] ^ SP[3][GETBYTE(tl,1)]; \
|
||||
word32 u = SP[0][GETBYTE(th,3)] ^ SP[1][GETBYTE(th,2)] ^ SP[2][GETBYTE(th,1)] ^ SP[3][GETBYTE(th,0)]; \
|
||||
d ^= u; \
|
||||
rh ^= d; \
|
||||
rl ^= d; \
|
||||
rl ^= rotrConstant<8>(u);}
|
||||
|
||||
#define DOUBLE_ROUND(lh, ll, rh, rl, k0, k1, k2, k3) \
|
||||
ROUND(lh, ll, rh, rl, k0, k1) \
|
||||
ROUND(rh, rl, lh, ll, k2, k3)
|
||||
|
||||
#if (CRYPTOPP_LITTLE_ENDIAN)
|
||||
#define EFI(i) (1-(i))
|
||||
#else
|
||||
#define EFI(i) (i)
|
||||
#endif
|
||||
|
||||
void Camellia::Base::UncheckedSetKey(const byte *key, unsigned int keylen, const NameValuePairs &)
|
||||
{
|
||||
m_rounds = (keylen >= 24) ? 4 : 3;
|
||||
unsigned int kslen = (8 * m_rounds + 2);
|
||||
m_key.New(kslen*2);
|
||||
word32 *ks32 = m_key.data();
|
||||
int m=0, a=0;
|
||||
if (!IsForwardTransformation())
|
||||
m = -1, a = kslen-1;
|
||||
|
||||
word32 kl0, kl1, kl2, kl3;
|
||||
GetBlock<word32, BigEndian> getBlock(key);
|
||||
getBlock(kl0)(kl1)(kl2)(kl3);
|
||||
word32 k0=kl0, k1=kl1, k2=kl2, k3=kl3;
|
||||
|
||||
#define CALC_ADDR2(base, i, j) ((byte *)(base)+8*(i)+4*(j)+((-16*(i))&m))
|
||||
#define CALC_ADDR(base, i) CALC_ADDR2(base, i, 0)
|
||||
|
||||
#if 1
|
||||
word64 kwl, kwr;
|
||||
ks32 += 2*a;
|
||||
#define PREPARE_KS_ROUNDS \
|
||||
kwl = (word64(k0) << 32) | k1; \
|
||||
kwr = (word64(k2) << 32) | k3
|
||||
#define KS_ROUND_0(i) \
|
||||
CRYPTOPP_ASSERT(IsAlignedOn(CALC_ADDR(ks32, i+EFI(0)),GetAlignmentOf<word64>())); \
|
||||
CRYPTOPP_ASSERT(IsAlignedOn(CALC_ADDR(ks32, i+EFI(1)),GetAlignmentOf<word64>())); \
|
||||
*(word64*)(void*)CALC_ADDR(ks32, i+EFI(0)) = kwl; \
|
||||
*(word64*)(void*)CALC_ADDR(ks32, i+EFI(1)) = kwr
|
||||
#define KS_ROUND(i, r, which) \
|
||||
CRYPTOPP_ASSERT(IsAlignedOn(CALC_ADDR(ks32, i+EFI(r<64)),GetAlignmentOf<word64>())); \
|
||||
CRYPTOPP_ASSERT(IsAlignedOn(CALC_ADDR(ks32, i+EFI(r>64)),GetAlignmentOf<word64>())); \
|
||||
if (which & (1<<int(r<64))) *(word64*)(void*)CALC_ADDR(ks32, i+EFI(r<64)) = (kwr << (r%64)) | (kwl >> (64 - (r%64))); \
|
||||
if (which & (1<<int(r>64))) *(word64*)(void*)CALC_ADDR(ks32, i+EFI(r>64)) = (kwl << (r%64)) | (kwr >> (64 - (r%64)))
|
||||
#else
|
||||
// SSE2 version is 30% faster on Intel Core 2. Doesn't seem worth the hassle of maintenance, but left here
|
||||
// #if'd out in case someone needs it.
|
||||
__m128i kw, kw2;
|
||||
__m128i *ks128 = (__m128i *)ks32+a/2;
|
||||
ks32 += 2*a;
|
||||
#define PREPARE_KS_ROUNDS \
|
||||
kw = _mm_set_epi32(k0, k1, k2, k3); \
|
||||
if (m) kw2 = kw, kw = _mm_shuffle_epi32(kw, _MM_SHUFFLE(1, 0, 3, 2)); \
|
||||
else kw2 = _mm_shuffle_epi32(kw, _MM_SHUFFLE(1, 0, 3, 2))
|
||||
#define KS_ROUND_0(i) \
|
||||
_mm_store_si128((__m128i *)CALC_ADDR(ks128, i), kw)
|
||||
#define KS_ROUND(i, r, which) { \
|
||||
__m128i temp; \
|
||||
if (r<64 && (which!=1 || m)) temp = _mm_or_si128(_mm_slli_epi64(kw, r%64), _mm_srli_epi64(kw2, 64-r%64)); \
|
||||
else temp = _mm_or_si128(_mm_slli_epi64(kw2, r%64), _mm_srli_epi64(kw, 64-r%64)); \
|
||||
if (which & 2) _mm_store_si128((__m128i *)CALC_ADDR(ks128, i), temp); \
|
||||
else _mm_storel_epi64((__m128i*)CALC_ADDR(ks32, i+EFI(0)), temp); \
|
||||
}
|
||||
#endif
|
||||
|
||||
if (keylen == 16)
|
||||
{
|
||||
// KL
|
||||
PREPARE_KS_ROUNDS;
|
||||
KS_ROUND_0(0);
|
||||
KS_ROUND(4, 15, 3);
|
||||
KS_ROUND(10, 45, 3);
|
||||
KS_ROUND(12, 60, 2);
|
||||
KS_ROUND(16, 77, 3);
|
||||
KS_ROUND(18, 94, 3);
|
||||
KS_ROUND(22, 111, 3);
|
||||
|
||||
// KA
|
||||
k0=kl0, k1=kl1, k2=kl2, k3=kl3;
|
||||
DOUBLE_ROUND(k0, k1, k2, k3, 0xA09E667Ful, 0x3BCC908Bul, 0xB67AE858ul, 0x4CAA73B2ul);
|
||||
k0^=kl0, k1^=kl1, k2^=kl2, k3^=kl3;
|
||||
DOUBLE_ROUND(k0, k1, k2, k3, 0xC6EF372Ful, 0xE94F82BEul, 0x54FF53A5ul, 0xF1D36F1Cul);
|
||||
|
||||
PREPARE_KS_ROUNDS;
|
||||
KS_ROUND_0(2);
|
||||
KS_ROUND(6, 15, 3);
|
||||
KS_ROUND(8, 30, 3);
|
||||
KS_ROUND(12, 45, 1);
|
||||
KS_ROUND(14, 60, 3);
|
||||
KS_ROUND(20, 94, 3);
|
||||
KS_ROUND(24, 47, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// KL
|
||||
PREPARE_KS_ROUNDS;
|
||||
KS_ROUND_0(0);
|
||||
KS_ROUND(12, 45, 3);
|
||||
KS_ROUND(16, 60, 3);
|
||||
KS_ROUND(22, 77, 3);
|
||||
KS_ROUND(30, 111, 3);
|
||||
|
||||
// KR
|
||||
word32 kr0, kr1, kr2, kr3;
|
||||
GetBlock<word32, BigEndian>(key+16)(kr0)(kr1);
|
||||
if (keylen == 24)
|
||||
kr2 = ~kr0, kr3 = ~kr1;
|
||||
else
|
||||
GetBlock<word32, BigEndian>(key+24)(kr2)(kr3);
|
||||
k0=kr0, k1=kr1, k2=kr2, k3=kr3;
|
||||
|
||||
PREPARE_KS_ROUNDS;
|
||||
KS_ROUND(4, 15, 3);
|
||||
KS_ROUND(8, 30, 3);
|
||||
KS_ROUND(18, 60, 3);
|
||||
KS_ROUND(26, 94, 3);
|
||||
|
||||
// KA
|
||||
k0^=kl0, k1^=kl1, k2^=kl2, k3^=kl3;
|
||||
DOUBLE_ROUND(k0, k1, k2, k3, 0xA09E667Ful, 0x3BCC908Bul, 0xB67AE858ul, 0x4CAA73B2ul);
|
||||
k0^=kl0, k1^=kl1, k2^=kl2, k3^=kl3;
|
||||
DOUBLE_ROUND(k0, k1, k2, k3, 0xC6EF372Ful, 0xE94F82BEul, 0x54FF53A5ul, 0xF1D36F1Cul);
|
||||
|
||||
PREPARE_KS_ROUNDS;
|
||||
KS_ROUND(6, 15, 3);
|
||||
KS_ROUND(14, 45, 3);
|
||||
KS_ROUND(24, 77, 3);
|
||||
KS_ROUND(28, 94, 3);
|
||||
|
||||
// KB
|
||||
k0^=kr0, k1^=kr1, k2^=kr2, k3^=kr3;
|
||||
DOUBLE_ROUND(k0, k1, k2, k3, 0x10E527FAul, 0xDE682D1Dul, 0xB05688C2ul, 0xB3E6C1FDul);
|
||||
|
||||
PREPARE_KS_ROUNDS;
|
||||
KS_ROUND_0(2);
|
||||
KS_ROUND(10, 30, 3);
|
||||
KS_ROUND(20, 60, 3);
|
||||
KS_ROUND(32, 47, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void Camellia::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
#define KS(i, j) ks[i*4 + EFI(j/2)*2 + EFI(j%2)]
|
||||
|
||||
#define FL(klh, kll, krh, krl) \
|
||||
ll ^= rotlConstant<1>(lh & klh);\
|
||||
lh ^= (ll | kll); \
|
||||
rh ^= (rl | krl); \
|
||||
rl ^= rotlConstant<1>(rh & krh);
|
||||
|
||||
word32 lh, ll, rh, rl;
|
||||
typedef BlockGetAndPut<word32, BigEndian> Block;
|
||||
Block::Get(inBlock)(lh)(ll)(rh)(rl);
|
||||
const word32 *ks = m_key.data();
|
||||
lh ^= KS(0,0);
|
||||
ll ^= KS(0,1);
|
||||
rh ^= KS(0,2);
|
||||
rl ^= KS(0,3);
|
||||
|
||||
// Timing attack countermeasure. see comments in Rijndael for more details
|
||||
const int cacheLineSize = GetCacheLineSize();
|
||||
unsigned int i;
|
||||
volatile word32 _u = 0;
|
||||
word32 u = _u;
|
||||
|
||||
for (i=0; i<256; i+=cacheLineSize)
|
||||
u &= *(const word32 *)(void*)(s1+i);
|
||||
u &= *(const word32 *)(void*)(s1+252);
|
||||
lh |= u; ll |= u;
|
||||
|
||||
SLOW_ROUND(lh, ll, rh, rl, KS(1,0), KS(1,1))
|
||||
SLOW_ROUND(rh, rl, lh, ll, KS(1,2), KS(1,3))
|
||||
for (i = m_rounds-1; i > 0; --i)
|
||||
{
|
||||
DOUBLE_ROUND(lh, ll, rh, rl, KS(2,0), KS(2,1), KS(2,2), KS(2,3))
|
||||
DOUBLE_ROUND(lh, ll, rh, rl, KS(3,0), KS(3,1), KS(3,2), KS(3,3))
|
||||
FL(KS(4,0), KS(4,1), KS(4,2), KS(4,3));
|
||||
DOUBLE_ROUND(lh, ll, rh, rl, KS(5,0), KS(5,1), KS(5,2), KS(5,3))
|
||||
ks += 16;
|
||||
}
|
||||
DOUBLE_ROUND(lh, ll, rh, rl, KS(2,0), KS(2,1), KS(2,2), KS(2,3))
|
||||
ROUND(lh, ll, rh, rl, KS(3,0), KS(3,1))
|
||||
SLOW_ROUND(rh, rl, lh, ll, KS(3,2), KS(3,3))
|
||||
lh ^= KS(4,0);
|
||||
ll ^= KS(4,1);
|
||||
rh ^= KS(4,2);
|
||||
rl ^= KS(4,3);
|
||||
Block::Put(xorBlock, outBlock)(rh)(rl)(lh)(ll);
|
||||
}
|
||||
|
||||
// The Camellia s-boxes
|
||||
|
||||
CRYPTOPP_ALIGN_DATA(4)
|
||||
const byte Camellia::Base::s1[256] =
|
||||
{
|
||||
112,130,44,236,179,39,192,229,228,133,87,53,234,12,174,65,
|
||||
35,239,107,147,69,25,165,33,237,14,79,78,29,101,146,189,
|
||||
134,184,175,143,124,235,31,206,62,48,220,95,94,197,11,26,
|
||||
166,225,57,202,213,71,93,61,217,1,90,214,81,86,108,77,
|
||||
139,13,154,102,251,204,176,45,116,18,43,32,240,177,132,153,
|
||||
223,76,203,194,52,126,118,5,109,183,169,49,209,23,4,215,
|
||||
20,88,58,97,222,27,17,28,50,15,156,22,83,24,242,34,
|
||||
254,68,207,178,195,181,122,145,36,8,232,168,96,252,105,80,
|
||||
170,208,160,125,161,137,98,151,84,91,30,149,224,255,100,210,
|
||||
16,196,0,72,163,247,117,219,138,3,230,218,9,63,221,148,
|
||||
135,92,131,2,205,74,144,51,115,103,246,243,157,127,191,226,
|
||||
82,155,216,38,200,55,198,59,129,150,111,75,19,190,99,46,
|
||||
233,121,167,140,159,110,188,142,41,245,249,182,47,253,180,89,
|
||||
120,152,6,106,231,70,113,186,212,37,171,66,136,162,141,250,
|
||||
114,7,185,85,248,238,172,10,54,73,42,104,60,56,241,164,
|
||||
64,40,211,123,187,201,67,193,21,227,173,244,119,199,128,158
|
||||
};
|
||||
|
||||
const word32 Camellia::Base::SP[4][256] = {
|
||||
{
|
||||
0x70707000, 0x82828200, 0x2c2c2c00, 0xececec00,
|
||||
0xb3b3b300, 0x27272700, 0xc0c0c000, 0xe5e5e500,
|
||||
0xe4e4e400, 0x85858500, 0x57575700, 0x35353500,
|
||||
0xeaeaea00, 0x0c0c0c00, 0xaeaeae00, 0x41414100,
|
||||
0x23232300, 0xefefef00, 0x6b6b6b00, 0x93939300,
|
||||
0x45454500, 0x19191900, 0xa5a5a500, 0x21212100,
|
||||
0xededed00, 0x0e0e0e00, 0x4f4f4f00, 0x4e4e4e00,
|
||||
0x1d1d1d00, 0x65656500, 0x92929200, 0xbdbdbd00,
|
||||
0x86868600, 0xb8b8b800, 0xafafaf00, 0x8f8f8f00,
|
||||
0x7c7c7c00, 0xebebeb00, 0x1f1f1f00, 0xcecece00,
|
||||
0x3e3e3e00, 0x30303000, 0xdcdcdc00, 0x5f5f5f00,
|
||||
0x5e5e5e00, 0xc5c5c500, 0x0b0b0b00, 0x1a1a1a00,
|
||||
0xa6a6a600, 0xe1e1e100, 0x39393900, 0xcacaca00,
|
||||
0xd5d5d500, 0x47474700, 0x5d5d5d00, 0x3d3d3d00,
|
||||
0xd9d9d900, 0x01010100, 0x5a5a5a00, 0xd6d6d600,
|
||||
0x51515100, 0x56565600, 0x6c6c6c00, 0x4d4d4d00,
|
||||
0x8b8b8b00, 0x0d0d0d00, 0x9a9a9a00, 0x66666600,
|
||||
0xfbfbfb00, 0xcccccc00, 0xb0b0b000, 0x2d2d2d00,
|
||||
0x74747400, 0x12121200, 0x2b2b2b00, 0x20202000,
|
||||
0xf0f0f000, 0xb1b1b100, 0x84848400, 0x99999900,
|
||||
0xdfdfdf00, 0x4c4c4c00, 0xcbcbcb00, 0xc2c2c200,
|
||||
0x34343400, 0x7e7e7e00, 0x76767600, 0x05050500,
|
||||
0x6d6d6d00, 0xb7b7b700, 0xa9a9a900, 0x31313100,
|
||||
0xd1d1d100, 0x17171700, 0x04040400, 0xd7d7d700,
|
||||
0x14141400, 0x58585800, 0x3a3a3a00, 0x61616100,
|
||||
0xdedede00, 0x1b1b1b00, 0x11111100, 0x1c1c1c00,
|
||||
0x32323200, 0x0f0f0f00, 0x9c9c9c00, 0x16161600,
|
||||
0x53535300, 0x18181800, 0xf2f2f200, 0x22222200,
|
||||
0xfefefe00, 0x44444400, 0xcfcfcf00, 0xb2b2b200,
|
||||
0xc3c3c300, 0xb5b5b500, 0x7a7a7a00, 0x91919100,
|
||||
0x24242400, 0x08080800, 0xe8e8e800, 0xa8a8a800,
|
||||
0x60606000, 0xfcfcfc00, 0x69696900, 0x50505000,
|
||||
0xaaaaaa00, 0xd0d0d000, 0xa0a0a000, 0x7d7d7d00,
|
||||
0xa1a1a100, 0x89898900, 0x62626200, 0x97979700,
|
||||
0x54545400, 0x5b5b5b00, 0x1e1e1e00, 0x95959500,
|
||||
0xe0e0e000, 0xffffff00, 0x64646400, 0xd2d2d200,
|
||||
0x10101000, 0xc4c4c400, 0x00000000, 0x48484800,
|
||||
0xa3a3a300, 0xf7f7f700, 0x75757500, 0xdbdbdb00,
|
||||
0x8a8a8a00, 0x03030300, 0xe6e6e600, 0xdadada00,
|
||||
0x09090900, 0x3f3f3f00, 0xdddddd00, 0x94949400,
|
||||
0x87878700, 0x5c5c5c00, 0x83838300, 0x02020200,
|
||||
0xcdcdcd00, 0x4a4a4a00, 0x90909000, 0x33333300,
|
||||
0x73737300, 0x67676700, 0xf6f6f600, 0xf3f3f300,
|
||||
0x9d9d9d00, 0x7f7f7f00, 0xbfbfbf00, 0xe2e2e200,
|
||||
0x52525200, 0x9b9b9b00, 0xd8d8d800, 0x26262600,
|
||||
0xc8c8c800, 0x37373700, 0xc6c6c600, 0x3b3b3b00,
|
||||
0x81818100, 0x96969600, 0x6f6f6f00, 0x4b4b4b00,
|
||||
0x13131300, 0xbebebe00, 0x63636300, 0x2e2e2e00,
|
||||
0xe9e9e900, 0x79797900, 0xa7a7a700, 0x8c8c8c00,
|
||||
0x9f9f9f00, 0x6e6e6e00, 0xbcbcbc00, 0x8e8e8e00,
|
||||
0x29292900, 0xf5f5f500, 0xf9f9f900, 0xb6b6b600,
|
||||
0x2f2f2f00, 0xfdfdfd00, 0xb4b4b400, 0x59595900,
|
||||
0x78787800, 0x98989800, 0x06060600, 0x6a6a6a00,
|
||||
0xe7e7e700, 0x46464600, 0x71717100, 0xbababa00,
|
||||
0xd4d4d400, 0x25252500, 0xababab00, 0x42424200,
|
||||
0x88888800, 0xa2a2a200, 0x8d8d8d00, 0xfafafa00,
|
||||
0x72727200, 0x07070700, 0xb9b9b900, 0x55555500,
|
||||
0xf8f8f800, 0xeeeeee00, 0xacacac00, 0x0a0a0a00,
|
||||
0x36363600, 0x49494900, 0x2a2a2a00, 0x68686800,
|
||||
0x3c3c3c00, 0x38383800, 0xf1f1f100, 0xa4a4a400,
|
||||
0x40404000, 0x28282800, 0xd3d3d300, 0x7b7b7b00,
|
||||
0xbbbbbb00, 0xc9c9c900, 0x43434300, 0xc1c1c100,
|
||||
0x15151500, 0xe3e3e300, 0xadadad00, 0xf4f4f400,
|
||||
0x77777700, 0xc7c7c700, 0x80808000, 0x9e9e9e00
|
||||
},
|
||||
{
|
||||
0x00e0e0e0, 0x00050505, 0x00585858, 0x00d9d9d9,
|
||||
0x00676767, 0x004e4e4e, 0x00818181, 0x00cbcbcb,
|
||||
0x00c9c9c9, 0x000b0b0b, 0x00aeaeae, 0x006a6a6a,
|
||||
0x00d5d5d5, 0x00181818, 0x005d5d5d, 0x00828282,
|
||||
0x00464646, 0x00dfdfdf, 0x00d6d6d6, 0x00272727,
|
||||
0x008a8a8a, 0x00323232, 0x004b4b4b, 0x00424242,
|
||||
0x00dbdbdb, 0x001c1c1c, 0x009e9e9e, 0x009c9c9c,
|
||||
0x003a3a3a, 0x00cacaca, 0x00252525, 0x007b7b7b,
|
||||
0x000d0d0d, 0x00717171, 0x005f5f5f, 0x001f1f1f,
|
||||
0x00f8f8f8, 0x00d7d7d7, 0x003e3e3e, 0x009d9d9d,
|
||||
0x007c7c7c, 0x00606060, 0x00b9b9b9, 0x00bebebe,
|
||||
0x00bcbcbc, 0x008b8b8b, 0x00161616, 0x00343434,
|
||||
0x004d4d4d, 0x00c3c3c3, 0x00727272, 0x00959595,
|
||||
0x00ababab, 0x008e8e8e, 0x00bababa, 0x007a7a7a,
|
||||
0x00b3b3b3, 0x00020202, 0x00b4b4b4, 0x00adadad,
|
||||
0x00a2a2a2, 0x00acacac, 0x00d8d8d8, 0x009a9a9a,
|
||||
0x00171717, 0x001a1a1a, 0x00353535, 0x00cccccc,
|
||||
0x00f7f7f7, 0x00999999, 0x00616161, 0x005a5a5a,
|
||||
0x00e8e8e8, 0x00242424, 0x00565656, 0x00404040,
|
||||
0x00e1e1e1, 0x00636363, 0x00090909, 0x00333333,
|
||||
0x00bfbfbf, 0x00989898, 0x00979797, 0x00858585,
|
||||
0x00686868, 0x00fcfcfc, 0x00ececec, 0x000a0a0a,
|
||||
0x00dadada, 0x006f6f6f, 0x00535353, 0x00626262,
|
||||
0x00a3a3a3, 0x002e2e2e, 0x00080808, 0x00afafaf,
|
||||
0x00282828, 0x00b0b0b0, 0x00747474, 0x00c2c2c2,
|
||||
0x00bdbdbd, 0x00363636, 0x00222222, 0x00383838,
|
||||
0x00646464, 0x001e1e1e, 0x00393939, 0x002c2c2c,
|
||||
0x00a6a6a6, 0x00303030, 0x00e5e5e5, 0x00444444,
|
||||
0x00fdfdfd, 0x00888888, 0x009f9f9f, 0x00656565,
|
||||
0x00878787, 0x006b6b6b, 0x00f4f4f4, 0x00232323,
|
||||
0x00484848, 0x00101010, 0x00d1d1d1, 0x00515151,
|
||||
0x00c0c0c0, 0x00f9f9f9, 0x00d2d2d2, 0x00a0a0a0,
|
||||
0x00555555, 0x00a1a1a1, 0x00414141, 0x00fafafa,
|
||||
0x00434343, 0x00131313, 0x00c4c4c4, 0x002f2f2f,
|
||||
0x00a8a8a8, 0x00b6b6b6, 0x003c3c3c, 0x002b2b2b,
|
||||
0x00c1c1c1, 0x00ffffff, 0x00c8c8c8, 0x00a5a5a5,
|
||||
0x00202020, 0x00898989, 0x00000000, 0x00909090,
|
||||
0x00474747, 0x00efefef, 0x00eaeaea, 0x00b7b7b7,
|
||||
0x00151515, 0x00060606, 0x00cdcdcd, 0x00b5b5b5,
|
||||
0x00121212, 0x007e7e7e, 0x00bbbbbb, 0x00292929,
|
||||
0x000f0f0f, 0x00b8b8b8, 0x00070707, 0x00040404,
|
||||
0x009b9b9b, 0x00949494, 0x00212121, 0x00666666,
|
||||
0x00e6e6e6, 0x00cecece, 0x00ededed, 0x00e7e7e7,
|
||||
0x003b3b3b, 0x00fefefe, 0x007f7f7f, 0x00c5c5c5,
|
||||
0x00a4a4a4, 0x00373737, 0x00b1b1b1, 0x004c4c4c,
|
||||
0x00919191, 0x006e6e6e, 0x008d8d8d, 0x00767676,
|
||||
0x00030303, 0x002d2d2d, 0x00dedede, 0x00969696,
|
||||
0x00262626, 0x007d7d7d, 0x00c6c6c6, 0x005c5c5c,
|
||||
0x00d3d3d3, 0x00f2f2f2, 0x004f4f4f, 0x00191919,
|
||||
0x003f3f3f, 0x00dcdcdc, 0x00797979, 0x001d1d1d,
|
||||
0x00525252, 0x00ebebeb, 0x00f3f3f3, 0x006d6d6d,
|
||||
0x005e5e5e, 0x00fbfbfb, 0x00696969, 0x00b2b2b2,
|
||||
0x00f0f0f0, 0x00313131, 0x000c0c0c, 0x00d4d4d4,
|
||||
0x00cfcfcf, 0x008c8c8c, 0x00e2e2e2, 0x00757575,
|
||||
0x00a9a9a9, 0x004a4a4a, 0x00575757, 0x00848484,
|
||||
0x00111111, 0x00454545, 0x001b1b1b, 0x00f5f5f5,
|
||||
0x00e4e4e4, 0x000e0e0e, 0x00737373, 0x00aaaaaa,
|
||||
0x00f1f1f1, 0x00dddddd, 0x00595959, 0x00141414,
|
||||
0x006c6c6c, 0x00929292, 0x00545454, 0x00d0d0d0,
|
||||
0x00787878, 0x00707070, 0x00e3e3e3, 0x00494949,
|
||||
0x00808080, 0x00505050, 0x00a7a7a7, 0x00f6f6f6,
|
||||
0x00777777, 0x00939393, 0x00868686, 0x00838383,
|
||||
0x002a2a2a, 0x00c7c7c7, 0x005b5b5b, 0x00e9e9e9,
|
||||
0x00eeeeee, 0x008f8f8f, 0x00010101, 0x003d3d3d
|
||||
},
|
||||
{
|
||||
0x38003838, 0x41004141, 0x16001616, 0x76007676,
|
||||
0xd900d9d9, 0x93009393, 0x60006060, 0xf200f2f2,
|
||||
0x72007272, 0xc200c2c2, 0xab00abab, 0x9a009a9a,
|
||||
0x75007575, 0x06000606, 0x57005757, 0xa000a0a0,
|
||||
0x91009191, 0xf700f7f7, 0xb500b5b5, 0xc900c9c9,
|
||||
0xa200a2a2, 0x8c008c8c, 0xd200d2d2, 0x90009090,
|
||||
0xf600f6f6, 0x07000707, 0xa700a7a7, 0x27002727,
|
||||
0x8e008e8e, 0xb200b2b2, 0x49004949, 0xde00dede,
|
||||
0x43004343, 0x5c005c5c, 0xd700d7d7, 0xc700c7c7,
|
||||
0x3e003e3e, 0xf500f5f5, 0x8f008f8f, 0x67006767,
|
||||
0x1f001f1f, 0x18001818, 0x6e006e6e, 0xaf00afaf,
|
||||
0x2f002f2f, 0xe200e2e2, 0x85008585, 0x0d000d0d,
|
||||
0x53005353, 0xf000f0f0, 0x9c009c9c, 0x65006565,
|
||||
0xea00eaea, 0xa300a3a3, 0xae00aeae, 0x9e009e9e,
|
||||
0xec00ecec, 0x80008080, 0x2d002d2d, 0x6b006b6b,
|
||||
0xa800a8a8, 0x2b002b2b, 0x36003636, 0xa600a6a6,
|
||||
0xc500c5c5, 0x86008686, 0x4d004d4d, 0x33003333,
|
||||
0xfd00fdfd, 0x66006666, 0x58005858, 0x96009696,
|
||||
0x3a003a3a, 0x09000909, 0x95009595, 0x10001010,
|
||||
0x78007878, 0xd800d8d8, 0x42004242, 0xcc00cccc,
|
||||
0xef00efef, 0x26002626, 0xe500e5e5, 0x61006161,
|
||||
0x1a001a1a, 0x3f003f3f, 0x3b003b3b, 0x82008282,
|
||||
0xb600b6b6, 0xdb00dbdb, 0xd400d4d4, 0x98009898,
|
||||
0xe800e8e8, 0x8b008b8b, 0x02000202, 0xeb00ebeb,
|
||||
0x0a000a0a, 0x2c002c2c, 0x1d001d1d, 0xb000b0b0,
|
||||
0x6f006f6f, 0x8d008d8d, 0x88008888, 0x0e000e0e,
|
||||
0x19001919, 0x87008787, 0x4e004e4e, 0x0b000b0b,
|
||||
0xa900a9a9, 0x0c000c0c, 0x79007979, 0x11001111,
|
||||
0x7f007f7f, 0x22002222, 0xe700e7e7, 0x59005959,
|
||||
0xe100e1e1, 0xda00dada, 0x3d003d3d, 0xc800c8c8,
|
||||
0x12001212, 0x04000404, 0x74007474, 0x54005454,
|
||||
0x30003030, 0x7e007e7e, 0xb400b4b4, 0x28002828,
|
||||
0x55005555, 0x68006868, 0x50005050, 0xbe00bebe,
|
||||
0xd000d0d0, 0xc400c4c4, 0x31003131, 0xcb00cbcb,
|
||||
0x2a002a2a, 0xad00adad, 0x0f000f0f, 0xca00caca,
|
||||
0x70007070, 0xff00ffff, 0x32003232, 0x69006969,
|
||||
0x08000808, 0x62006262, 0x00000000, 0x24002424,
|
||||
0xd100d1d1, 0xfb00fbfb, 0xba00baba, 0xed00eded,
|
||||
0x45004545, 0x81008181, 0x73007373, 0x6d006d6d,
|
||||
0x84008484, 0x9f009f9f, 0xee00eeee, 0x4a004a4a,
|
||||
0xc300c3c3, 0x2e002e2e, 0xc100c1c1, 0x01000101,
|
||||
0xe600e6e6, 0x25002525, 0x48004848, 0x99009999,
|
||||
0xb900b9b9, 0xb300b3b3, 0x7b007b7b, 0xf900f9f9,
|
||||
0xce00cece, 0xbf00bfbf, 0xdf00dfdf, 0x71007171,
|
||||
0x29002929, 0xcd00cdcd, 0x6c006c6c, 0x13001313,
|
||||
0x64006464, 0x9b009b9b, 0x63006363, 0x9d009d9d,
|
||||
0xc000c0c0, 0x4b004b4b, 0xb700b7b7, 0xa500a5a5,
|
||||
0x89008989, 0x5f005f5f, 0xb100b1b1, 0x17001717,
|
||||
0xf400f4f4, 0xbc00bcbc, 0xd300d3d3, 0x46004646,
|
||||
0xcf00cfcf, 0x37003737, 0x5e005e5e, 0x47004747,
|
||||
0x94009494, 0xfa00fafa, 0xfc00fcfc, 0x5b005b5b,
|
||||
0x97009797, 0xfe00fefe, 0x5a005a5a, 0xac00acac,
|
||||
0x3c003c3c, 0x4c004c4c, 0x03000303, 0x35003535,
|
||||
0xf300f3f3, 0x23002323, 0xb800b8b8, 0x5d005d5d,
|
||||
0x6a006a6a, 0x92009292, 0xd500d5d5, 0x21002121,
|
||||
0x44004444, 0x51005151, 0xc600c6c6, 0x7d007d7d,
|
||||
0x39003939, 0x83008383, 0xdc00dcdc, 0xaa00aaaa,
|
||||
0x7c007c7c, 0x77007777, 0x56005656, 0x05000505,
|
||||
0x1b001b1b, 0xa400a4a4, 0x15001515, 0x34003434,
|
||||
0x1e001e1e, 0x1c001c1c, 0xf800f8f8, 0x52005252,
|
||||
0x20002020, 0x14001414, 0xe900e9e9, 0xbd00bdbd,
|
||||
0xdd00dddd, 0xe400e4e4, 0xa100a1a1, 0xe000e0e0,
|
||||
0x8a008a8a, 0xf100f1f1, 0xd600d6d6, 0x7a007a7a,
|
||||
0xbb00bbbb, 0xe300e3e3, 0x40004040, 0x4f004f4f
|
||||
},
|
||||
{
|
||||
0x70700070, 0x2c2c002c, 0xb3b300b3, 0xc0c000c0,
|
||||
0xe4e400e4, 0x57570057, 0xeaea00ea, 0xaeae00ae,
|
||||
0x23230023, 0x6b6b006b, 0x45450045, 0xa5a500a5,
|
||||
0xeded00ed, 0x4f4f004f, 0x1d1d001d, 0x92920092,
|
||||
0x86860086, 0xafaf00af, 0x7c7c007c, 0x1f1f001f,
|
||||
0x3e3e003e, 0xdcdc00dc, 0x5e5e005e, 0x0b0b000b,
|
||||
0xa6a600a6, 0x39390039, 0xd5d500d5, 0x5d5d005d,
|
||||
0xd9d900d9, 0x5a5a005a, 0x51510051, 0x6c6c006c,
|
||||
0x8b8b008b, 0x9a9a009a, 0xfbfb00fb, 0xb0b000b0,
|
||||
0x74740074, 0x2b2b002b, 0xf0f000f0, 0x84840084,
|
||||
0xdfdf00df, 0xcbcb00cb, 0x34340034, 0x76760076,
|
||||
0x6d6d006d, 0xa9a900a9, 0xd1d100d1, 0x04040004,
|
||||
0x14140014, 0x3a3a003a, 0xdede00de, 0x11110011,
|
||||
0x32320032, 0x9c9c009c, 0x53530053, 0xf2f200f2,
|
||||
0xfefe00fe, 0xcfcf00cf, 0xc3c300c3, 0x7a7a007a,
|
||||
0x24240024, 0xe8e800e8, 0x60600060, 0x69690069,
|
||||
0xaaaa00aa, 0xa0a000a0, 0xa1a100a1, 0x62620062,
|
||||
0x54540054, 0x1e1e001e, 0xe0e000e0, 0x64640064,
|
||||
0x10100010, 0x00000000, 0xa3a300a3, 0x75750075,
|
||||
0x8a8a008a, 0xe6e600e6, 0x09090009, 0xdddd00dd,
|
||||
0x87870087, 0x83830083, 0xcdcd00cd, 0x90900090,
|
||||
0x73730073, 0xf6f600f6, 0x9d9d009d, 0xbfbf00bf,
|
||||
0x52520052, 0xd8d800d8, 0xc8c800c8, 0xc6c600c6,
|
||||
0x81810081, 0x6f6f006f, 0x13130013, 0x63630063,
|
||||
0xe9e900e9, 0xa7a700a7, 0x9f9f009f, 0xbcbc00bc,
|
||||
0x29290029, 0xf9f900f9, 0x2f2f002f, 0xb4b400b4,
|
||||
0x78780078, 0x06060006, 0xe7e700e7, 0x71710071,
|
||||
0xd4d400d4, 0xabab00ab, 0x88880088, 0x8d8d008d,
|
||||
0x72720072, 0xb9b900b9, 0xf8f800f8, 0xacac00ac,
|
||||
0x36360036, 0x2a2a002a, 0x3c3c003c, 0xf1f100f1,
|
||||
0x40400040, 0xd3d300d3, 0xbbbb00bb, 0x43430043,
|
||||
0x15150015, 0xadad00ad, 0x77770077, 0x80800080,
|
||||
0x82820082, 0xecec00ec, 0x27270027, 0xe5e500e5,
|
||||
0x85850085, 0x35350035, 0x0c0c000c, 0x41410041,
|
||||
0xefef00ef, 0x93930093, 0x19190019, 0x21210021,
|
||||
0x0e0e000e, 0x4e4e004e, 0x65650065, 0xbdbd00bd,
|
||||
0xb8b800b8, 0x8f8f008f, 0xebeb00eb, 0xcece00ce,
|
||||
0x30300030, 0x5f5f005f, 0xc5c500c5, 0x1a1a001a,
|
||||
0xe1e100e1, 0xcaca00ca, 0x47470047, 0x3d3d003d,
|
||||
0x01010001, 0xd6d600d6, 0x56560056, 0x4d4d004d,
|
||||
0x0d0d000d, 0x66660066, 0xcccc00cc, 0x2d2d002d,
|
||||
0x12120012, 0x20200020, 0xb1b100b1, 0x99990099,
|
||||
0x4c4c004c, 0xc2c200c2, 0x7e7e007e, 0x05050005,
|
||||
0xb7b700b7, 0x31310031, 0x17170017, 0xd7d700d7,
|
||||
0x58580058, 0x61610061, 0x1b1b001b, 0x1c1c001c,
|
||||
0x0f0f000f, 0x16160016, 0x18180018, 0x22220022,
|
||||
0x44440044, 0xb2b200b2, 0xb5b500b5, 0x91910091,
|
||||
0x08080008, 0xa8a800a8, 0xfcfc00fc, 0x50500050,
|
||||
0xd0d000d0, 0x7d7d007d, 0x89890089, 0x97970097,
|
||||
0x5b5b005b, 0x95950095, 0xffff00ff, 0xd2d200d2,
|
||||
0xc4c400c4, 0x48480048, 0xf7f700f7, 0xdbdb00db,
|
||||
0x03030003, 0xdada00da, 0x3f3f003f, 0x94940094,
|
||||
0x5c5c005c, 0x02020002, 0x4a4a004a, 0x33330033,
|
||||
0x67670067, 0xf3f300f3, 0x7f7f007f, 0xe2e200e2,
|
||||
0x9b9b009b, 0x26260026, 0x37370037, 0x3b3b003b,
|
||||
0x96960096, 0x4b4b004b, 0xbebe00be, 0x2e2e002e,
|
||||
0x79790079, 0x8c8c008c, 0x6e6e006e, 0x8e8e008e,
|
||||
0xf5f500f5, 0xb6b600b6, 0xfdfd00fd, 0x59590059,
|
||||
0x98980098, 0x6a6a006a, 0x46460046, 0xbaba00ba,
|
||||
0x25250025, 0x42420042, 0xa2a200a2, 0xfafa00fa,
|
||||
0x07070007, 0x55550055, 0xeeee00ee, 0x0a0a000a,
|
||||
0x49490049, 0x68680068, 0x38380038, 0xa4a400a4,
|
||||
0x28280028, 0x7b7b007b, 0xc9c900c9, 0xc1c100c1,
|
||||
0xe3e300e3, 0xf4f400f4, 0xc7c700c7, 0x9e9e009e
|
||||
}};
|
||||
|
||||
NAMESPACE_END
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// camellia.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file camellia.h
|
||||
/// \brief Classes for the Camellia block cipher
|
||||
|
||||
#ifndef CRYPTOPP_CAMELLIA_H
|
||||
#define CRYPTOPP_CAMELLIA_H
|
||||
|
||||
#include "config.h"
|
||||
#include "seckey.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief Camellia block cipher information
|
||||
struct Camellia_Info : public FixedBlockSize<16>, public VariableKeyLength<16, 16, 32, 8>
|
||||
{
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "Camellia";}
|
||||
};
|
||||
|
||||
/// \brief Camellia block cipher
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/Camellia">Camellia</a>
|
||||
class Camellia : public Camellia_Info, public BlockCipherDocumentation
|
||||
{
|
||||
class CRYPTOPP_NO_VTABLE Base : public BlockCipherImpl<Camellia_Info>
|
||||
{
|
||||
public:
|
||||
void UncheckedSetKey(const byte *key, unsigned int keylen, const NameValuePairs ¶ms);
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
|
||||
protected:
|
||||
CRYPTOPP_ALIGN_DATA(4) static const byte s1[256];
|
||||
static const word32 SP[4][256];
|
||||
|
||||
unsigned int m_rounds;
|
||||
SecBlock<word32> m_key;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BlockCipherFinal<ENCRYPTION, Base> Encryption;
|
||||
typedef BlockCipherFinal<DECRYPTION, Base> Decryption;
|
||||
};
|
||||
|
||||
typedef Camellia::Encryption CamelliaEncryption;
|
||||
typedef Camellia::Decryption CamelliaDecryption;
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
// cast.cpp - originally written and placed in the public domain by Wei Dai and Leonard Janke
|
||||
// based on Steve Reid's public domain cast.c
|
||||
|
||||
#include "pch.h"
|
||||
#include "cast.h"
|
||||
#include "misc.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/* Macros to access 8-bit bytes out of a 32-bit word */
|
||||
#define U8a(x) GETBYTE(x,3)
|
||||
#define U8b(x) GETBYTE(x,2)
|
||||
#define U8c(x) GETBYTE(x,1)
|
||||
#define U8d(x) GETBYTE(x,0)
|
||||
|
||||
/* CAST uses three different round functions */
|
||||
#define f1(l, r, km, kr) \
|
||||
t = rotlVariable(km + r, kr); \
|
||||
l ^= ((S[0][U8a(t)] ^ S[1][U8b(t)]) - \
|
||||
S[2][U8c(t)]) + S[3][U8d(t)];
|
||||
#define f2(l, r, km, kr) \
|
||||
t = rotlVariable(km ^ r, kr); \
|
||||
l ^= ((S[0][U8a(t)] - S[1][U8b(t)]) + \
|
||||
S[2][U8c(t)]) ^ S[3][U8d(t)];
|
||||
#define f3(l, r, km, kr) \
|
||||
t = rotlVariable(km - r, kr); \
|
||||
l ^= ((S[0][U8a(t)] + S[1][U8b(t)]) ^ \
|
||||
S[2][U8c(t)]) - S[3][U8d(t)];
|
||||
|
||||
#define F1(l, r, i, j) f1(l, r, K[i], K[i+j])
|
||||
#define F2(l, r, i, j) f2(l, r, K[i], K[i+j])
|
||||
#define F3(l, r, i, j) f3(l, r, K[i], K[i+j])
|
||||
|
||||
typedef BlockGetAndPut<word32, BigEndian> Block;
|
||||
|
||||
void CAST128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
word32 &t=m_t[0], &l=m_t[1], &r=m_t[2];
|
||||
|
||||
/* Get inblock into l,r */
|
||||
Block::Get(inBlock)(l)(r);
|
||||
/* Do the work */
|
||||
F1(l, r, 0, 16);
|
||||
F2(r, l, 1, 16);
|
||||
F3(l, r, 2, 16);
|
||||
F1(r, l, 3, 16);
|
||||
F2(l, r, 4, 16);
|
||||
F3(r, l, 5, 16);
|
||||
F1(l, r, 6, 16);
|
||||
F2(r, l, 7, 16);
|
||||
F3(l, r, 8, 16);
|
||||
F1(r, l, 9, 16);
|
||||
F2(l, r, 10, 16);
|
||||
F3(r, l, 11, 16);
|
||||
/* Only do full 16 rounds if key length > 80 bits */
|
||||
if (!reduced) {
|
||||
F1(l, r, 12, 16);
|
||||
F2(r, l, 13, 16);
|
||||
F3(l, r, 14, 16);
|
||||
F1(r, l, 15, 16);
|
||||
}
|
||||
/* Put l,r into outblock */
|
||||
Block::Put(xorBlock, outBlock)(r)(l);
|
||||
}
|
||||
|
||||
void CAST128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
word32 &t=m_t[0], &l=m_t[1], &r=m_t[2];
|
||||
|
||||
/* Get inblock into l,r */
|
||||
Block::Get(inBlock)(r)(l);
|
||||
/* Only do full 16 rounds if key length > 80 bits */
|
||||
if (!reduced) {
|
||||
F1(r, l, 15, 16);
|
||||
F3(l, r, 14, 16);
|
||||
F2(r, l, 13, 16);
|
||||
F1(l, r, 12, 16);
|
||||
}
|
||||
F3(r, l, 11, 16);
|
||||
F2(l, r, 10, 16);
|
||||
F1(r, l, 9, 16);
|
||||
F3(l, r, 8, 16);
|
||||
F2(r, l, 7, 16);
|
||||
F1(l, r, 6, 16);
|
||||
F3(r, l, 5, 16);
|
||||
F2(l, r, 4, 16);
|
||||
F1(r, l, 3, 16);
|
||||
F3(l, r, 2, 16);
|
||||
F2(r, l, 1, 16);
|
||||
F1(l, r, 0, 16);
|
||||
/* Put l,r into outblock */
|
||||
Block::Put(xorBlock, outBlock)(l)(r);
|
||||
}
|
||||
|
||||
void CAST128::Base::UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs &)
|
||||
{
|
||||
AssertValidKeyLength(keylength);
|
||||
|
||||
reduced = (keylength <= 10);
|
||||
|
||||
word32 X[4], Z[4]={0};
|
||||
GetUserKey(BIG_ENDIAN_ORDER, X, 4, userKey, keylength);
|
||||
|
||||
#define x(i) GETBYTE(X[i/4], 3-i%4)
|
||||
#define z(i) GETBYTE(Z[i/4], 3-i%4)
|
||||
|
||||
unsigned int i;
|
||||
for (i=0; i<=16; i+=16)
|
||||
{
|
||||
// this part is copied directly from RFC 2144 (with some search and replace) by Wei Dai
|
||||
Z[0] = X[0] ^ S[4][x(0xD)] ^ S[5][x(0xF)] ^ S[6][x(0xC)] ^ S[7][x(0xE)] ^ S[6][x(0x8)];
|
||||
Z[1] = X[2] ^ S[4][z(0x0)] ^ S[5][z(0x2)] ^ S[6][z(0x1)] ^ S[7][z(0x3)] ^ S[7][x(0xA)];
|
||||
Z[2] = X[3] ^ S[4][z(0x7)] ^ S[5][z(0x6)] ^ S[6][z(0x5)] ^ S[7][z(0x4)] ^ S[4][x(0x9)];
|
||||
Z[3] = X[1] ^ S[4][z(0xA)] ^ S[5][z(0x9)] ^ S[6][z(0xB)] ^ S[7][z(0x8)] ^ S[5][x(0xB)];
|
||||
K[i+0] = S[4][z(0x8)] ^ S[5][z(0x9)] ^ S[6][z(0x7)] ^ S[7][z(0x6)] ^ S[4][z(0x2)];
|
||||
K[i+1] = S[4][z(0xA)] ^ S[5][z(0xB)] ^ S[6][z(0x5)] ^ S[7][z(0x4)] ^ S[5][z(0x6)];
|
||||
K[i+2] = S[4][z(0xC)] ^ S[5][z(0xD)] ^ S[6][z(0x3)] ^ S[7][z(0x2)] ^ S[6][z(0x9)];
|
||||
K[i+3] = S[4][z(0xE)] ^ S[5][z(0xF)] ^ S[6][z(0x1)] ^ S[7][z(0x0)] ^ S[7][z(0xC)];
|
||||
X[0] = Z[2] ^ S[4][z(0x5)] ^ S[5][z(0x7)] ^ S[6][z(0x4)] ^ S[7][z(0x6)] ^ S[6][z(0x0)];
|
||||
X[1] = Z[0] ^ S[4][x(0x0)] ^ S[5][x(0x2)] ^ S[6][x(0x1)] ^ S[7][x(0x3)] ^ S[7][z(0x2)];
|
||||
X[2] = Z[1] ^ S[4][x(0x7)] ^ S[5][x(0x6)] ^ S[6][x(0x5)] ^ S[7][x(0x4)] ^ S[4][z(0x1)];
|
||||
X[3] = Z[3] ^ S[4][x(0xA)] ^ S[5][x(0x9)] ^ S[6][x(0xB)] ^ S[7][x(0x8)] ^ S[5][z(0x3)];
|
||||
K[i+4] = S[4][x(0x3)] ^ S[5][x(0x2)] ^ S[6][x(0xC)] ^ S[7][x(0xD)] ^ S[4][x(0x8)];
|
||||
K[i+5] = S[4][x(0x1)] ^ S[5][x(0x0)] ^ S[6][x(0xE)] ^ S[7][x(0xF)] ^ S[5][x(0xD)];
|
||||
K[i+6] = S[4][x(0x7)] ^ S[5][x(0x6)] ^ S[6][x(0x8)] ^ S[7][x(0x9)] ^ S[6][x(0x3)];
|
||||
K[i+7] = S[4][x(0x5)] ^ S[5][x(0x4)] ^ S[6][x(0xA)] ^ S[7][x(0xB)] ^ S[7][x(0x7)];
|
||||
Z[0] = X[0] ^ S[4][x(0xD)] ^ S[5][x(0xF)] ^ S[6][x(0xC)] ^ S[7][x(0xE)] ^ S[6][x(0x8)];
|
||||
Z[1] = X[2] ^ S[4][z(0x0)] ^ S[5][z(0x2)] ^ S[6][z(0x1)] ^ S[7][z(0x3)] ^ S[7][x(0xA)];
|
||||
Z[2] = X[3] ^ S[4][z(0x7)] ^ S[5][z(0x6)] ^ S[6][z(0x5)] ^ S[7][z(0x4)] ^ S[4][x(0x9)];
|
||||
Z[3] = X[1] ^ S[4][z(0xA)] ^ S[5][z(0x9)] ^ S[6][z(0xB)] ^ S[7][z(0x8)] ^ S[5][x(0xB)];
|
||||
K[i+8] = S[4][z(0x3)] ^ S[5][z(0x2)] ^ S[6][z(0xC)] ^ S[7][z(0xD)] ^ S[4][z(0x9)];
|
||||
K[i+9] = S[4][z(0x1)] ^ S[5][z(0x0)] ^ S[6][z(0xE)] ^ S[7][z(0xF)] ^ S[5][z(0xC)];
|
||||
K[i+10] = S[4][z(0x7)] ^ S[5][z(0x6)] ^ S[6][z(0x8)] ^ S[7][z(0x9)] ^ S[6][z(0x2)];
|
||||
K[i+11] = S[4][z(0x5)] ^ S[5][z(0x4)] ^ S[6][z(0xA)] ^ S[7][z(0xB)] ^ S[7][z(0x6)];
|
||||
X[0] = Z[2] ^ S[4][z(0x5)] ^ S[5][z(0x7)] ^ S[6][z(0x4)] ^ S[7][z(0x6)] ^ S[6][z(0x0)];
|
||||
X[1] = Z[0] ^ S[4][x(0x0)] ^ S[5][x(0x2)] ^ S[6][x(0x1)] ^ S[7][x(0x3)] ^ S[7][z(0x2)];
|
||||
X[2] = Z[1] ^ S[4][x(0x7)] ^ S[5][x(0x6)] ^ S[6][x(0x5)] ^ S[7][x(0x4)] ^ S[4][z(0x1)];
|
||||
X[3] = Z[3] ^ S[4][x(0xA)] ^ S[5][x(0x9)] ^ S[6][x(0xB)] ^ S[7][x(0x8)] ^ S[5][z(0x3)];
|
||||
K[i+12] = S[4][x(0x8)] ^ S[5][x(0x9)] ^ S[6][x(0x7)] ^ S[7][x(0x6)] ^ S[4][x(0x3)];
|
||||
K[i+13] = S[4][x(0xA)] ^ S[5][x(0xB)] ^ S[6][x(0x5)] ^ S[7][x(0x4)] ^ S[5][x(0x7)];
|
||||
K[i+14] = S[4][x(0xC)] ^ S[5][x(0xD)] ^ S[6][x(0x3)] ^ S[7][x(0x2)] ^ S[6][x(0x8)];
|
||||
K[i+15] = S[4][x(0xE)] ^ S[5][x(0xF)] ^ S[6][x(0x1)] ^ S[7][x(0x0)] ^ S[7][x(0xD)];
|
||||
}
|
||||
|
||||
for (i=16; i<32; i++)
|
||||
K[i] &= 0x1f;
|
||||
}
|
||||
|
||||
// The following CAST-256 implementation was contributed by Leonard Janke
|
||||
|
||||
const word32 CAST256::Base::t_m[8][24]={
|
||||
{ 0x5a827999, 0xd151d6a1, 0x482133a9, 0xbef090b1, 0x35bfedb9, 0xac8f4ac1,
|
||||
0x235ea7c9, 0x9a2e04d1, 0x10fd61d9, 0x87ccbee1, 0xfe9c1be9, 0x756b78f1,
|
||||
0xec3ad5f9, 0x630a3301, 0xd9d99009, 0x50a8ed11, 0xc7784a19, 0x3e47a721,
|
||||
0xb5170429, 0x2be66131, 0xa2b5be39, 0x19851b41, 0x90547849, 0x0723d551},
|
||||
{ 0xc95c653a, 0x402bc242, 0xb6fb1f4a, 0x2dca7c52, 0xa499d95a, 0x1b693662,
|
||||
0x9238936a, 0x0907f072, 0x7fd74d7a, 0xf6a6aa82, 0x6d76078a, 0xe4456492,
|
||||
0x5b14c19a, 0xd1e41ea2, 0x48b37baa, 0xbf82d8b2, 0x365235ba, 0xad2192c2,
|
||||
0x23f0efca, 0x9ac04cd2, 0x118fa9da, 0x885f06e2, 0xff2e63ea, 0x75fdc0f2},
|
||||
{ 0x383650db, 0xaf05ade3, 0x25d50aeb, 0x9ca467f3, 0x1373c4fb, 0x8a432203,
|
||||
0x01127f0b, 0x77e1dc13, 0xeeb1391b, 0x65809623, 0xdc4ff32b, 0x531f5033,
|
||||
0xc9eead3b, 0x40be0a43, 0xb78d674b, 0x2e5cc453, 0xa52c215b, 0x1bfb7e63,
|
||||
0x92cadb6b, 0x099a3873, 0x8069957b, 0xf738f283, 0x6e084f8b, 0xe4d7ac93},
|
||||
{ 0xa7103c7c, 0x1ddf9984, 0x94aef68c, 0x0b7e5394, 0x824db09c, 0xf91d0da4,
|
||||
0x6fec6aac, 0xe6bbc7b4, 0x5d8b24bc, 0xd45a81c4, 0x4b29decc, 0xc1f93bd4,
|
||||
0x38c898dc, 0xaf97f5e4, 0x266752ec, 0x9d36aff4, 0x14060cfc, 0x8ad56a04,
|
||||
0x01a4c70c, 0x78742414, 0xef43811c, 0x6612de24, 0xdce23b2c, 0x53b19834},
|
||||
{ 0x15ea281d, 0x8cb98525, 0x0388e22d, 0x7a583f35, 0xf1279c3d, 0x67f6f945,
|
||||
0xdec6564d, 0x5595b355, 0xcc65105d, 0x43346d65, 0xba03ca6d, 0x30d32775,
|
||||
0xa7a2847d, 0x1e71e185, 0x95413e8d, 0x0c109b95, 0x82dff89d, 0xf9af55a5,
|
||||
0x707eb2ad, 0xe74e0fb5, 0x5e1d6cbd, 0xd4ecc9c5, 0x4bbc26cd, 0xc28b83d5},
|
||||
{ 0x84c413be, 0xfb9370c6, 0x7262cdce, 0xe9322ad6, 0x600187de, 0xd6d0e4e6,
|
||||
0x4da041ee, 0xc46f9ef6, 0x3b3efbfe, 0xb20e5906, 0x28ddb60e, 0x9fad1316,
|
||||
0x167c701e, 0x8d4bcd26, 0x041b2a2e, 0x7aea8736, 0xf1b9e43e, 0x68894146,
|
||||
0xdf589e4e, 0x5627fb56, 0xccf7585e, 0x43c6b566, 0xba96126e, 0x31656f76},
|
||||
{ 0xf39dff5f, 0x6a6d5c67, 0xe13cb96f, 0x580c1677, 0xcedb737f, 0x45aad087,
|
||||
0xbc7a2d8f, 0x33498a97, 0xaa18e79f, 0x20e844a7, 0x97b7a1af, 0x0e86feb7,
|
||||
0x85565bbf, 0xfc25b8c7, 0x72f515cf, 0xe9c472d7, 0x6093cfdf, 0xd7632ce7,
|
||||
0x4e3289ef, 0xc501e6f7, 0x3bd143ff, 0xb2a0a107, 0x296ffe0f, 0xa03f5b17},
|
||||
{ 0x6277eb00, 0xd9474808, 0x5016a510, 0xc6e60218, 0x3db55f20, 0xb484bc28,
|
||||
0x2b541930, 0xa2237638, 0x18f2d340, 0x8fc23048, 0x06918d50, 0x7d60ea58,
|
||||
0xf4304760, 0x6affa468, 0xe1cf0170, 0x589e5e78, 0xcf6dbb80, 0x463d1888,
|
||||
0xbd0c7590, 0x33dbd298, 0xaaab2fa0, 0x217a8ca8, 0x9849e9b0, 0x0f1946b8}
|
||||
};
|
||||
|
||||
const unsigned int CAST256::Base::t_r[8][24]={
|
||||
{19, 27, 3, 11, 19, 27, 3, 11, 19, 27, 3, 11, 19, 27, 3, 11, 19, 27, 3, 11, 19, 27, 3, 11},
|
||||
{4, 12, 20, 28, 4, 12, 20, 28, 4, 12, 20, 28, 4, 12, 20, 28, 4, 12, 20, 28, 4, 12, 20, 28},
|
||||
{21, 29, 5, 13, 21, 29, 5, 13, 21, 29, 5, 13, 21, 29, 5, 13, 21, 29, 5, 13, 21, 29, 5, 13},
|
||||
{6, 14, 22, 30, 6, 14, 22, 30, 6, 14, 22, 30, 6, 14, 22, 30, 6, 14, 22, 30, 6, 14, 22, 30},
|
||||
{23, 31, 7, 15, 23, 31, 7, 15, 23, 31, 7, 15, 23, 31, 7, 15, 23, 31, 7, 15, 23, 31, 7, 15},
|
||||
{8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0},
|
||||
{25, 1, 9, 17, 25, 1, 9, 17, 25, 1, 9, 17, 25, 1, 9, 17, 25, 1, 9, 17, 25, 1, 9, 17},
|
||||
{10, 18, 26, 2, 10, 18, 26, 2, 10, 18, 26, 2, 10, 18, 26, 2, 10, 18, 26, 2, 10, 18, 26, 2}
|
||||
};
|
||||
|
||||
#define Q(i) \
|
||||
F1(block[2],block[3],8*i+4,-4); \
|
||||
F2(block[1],block[2],8*i+5,-4); \
|
||||
F3(block[0],block[1],8*i+6,-4); \
|
||||
F1(block[3],block[0],8*i+7,-4);
|
||||
|
||||
#define QBar(i) \
|
||||
F1(block[3],block[0],8*i+7,-4); \
|
||||
F3(block[0],block[1],8*i+6,-4); \
|
||||
F2(block[1],block[2],8*i+5,-4); \
|
||||
F1(block[2],block[3],8*i+4,-4);
|
||||
|
||||
/* CAST256's encrypt/decrypt functions are identical except for the order that
|
||||
the keys are used */
|
||||
|
||||
void CAST256::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
// TODO: add a SecBlock workspace to the class when the ABI can change
|
||||
word32 t, block[4];
|
||||
Block::Get(inBlock)(block[0])(block[1])(block[2])(block[3]);
|
||||
|
||||
// Perform 6 forward quad rounds
|
||||
Q(0);
|
||||
Q(1);
|
||||
Q(2);
|
||||
Q(3);
|
||||
Q(4);
|
||||
Q(5);
|
||||
|
||||
// Perform 6 reverse quad rounds
|
||||
QBar(6);
|
||||
QBar(7);
|
||||
QBar(8);
|
||||
QBar(9);
|
||||
QBar(10);
|
||||
QBar(11);
|
||||
|
||||
Block::Put(xorBlock, outBlock)(block[0])(block[1])(block[2])(block[3]);
|
||||
}
|
||||
|
||||
/* Set up a CAST-256 key */
|
||||
|
||||
void CAST256::Base::Omega(int i, word32 kappa[8])
|
||||
{
|
||||
word32 t;
|
||||
|
||||
f1(kappa[6],kappa[7],t_m[0][i],t_r[0][i]);
|
||||
f2(kappa[5],kappa[6],t_m[1][i],t_r[1][i]);
|
||||
f3(kappa[4],kappa[5],t_m[2][i],t_r[2][i]);
|
||||
f1(kappa[3],kappa[4],t_m[3][i],t_r[3][i]);
|
||||
f2(kappa[2],kappa[3],t_m[4][i],t_r[4][i]);
|
||||
f3(kappa[1],kappa[2],t_m[5][i],t_r[5][i]);
|
||||
f1(kappa[0],kappa[1],t_m[6][i],t_r[6][i]);
|
||||
f2(kappa[7],kappa[0],t_m[7][i],t_r[7][i]);
|
||||
}
|
||||
|
||||
void CAST256::Base::UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs &)
|
||||
{
|
||||
AssertValidKeyLength(keylength);
|
||||
|
||||
GetUserKey(BIG_ENDIAN_ORDER, kappa.begin(), 8, userKey, keylength);
|
||||
|
||||
for(int i=0; i<12; ++i)
|
||||
{
|
||||
Omega(2*i,kappa);
|
||||
Omega(2*i+1,kappa);
|
||||
|
||||
K[8*i]=kappa[0] & 31;
|
||||
K[8*i+1]=kappa[2] & 31;
|
||||
K[8*i+2]=kappa[4] & 31;
|
||||
K[8*i+3]=kappa[6] & 31;
|
||||
K[8*i+4]=kappa[7];
|
||||
K[8*i+5]=kappa[5];
|
||||
K[8*i+6]=kappa[3];
|
||||
K[8*i+7]=kappa[1];
|
||||
}
|
||||
|
||||
if (!IsForwardTransformation())
|
||||
{
|
||||
for(int j=0; j<6; ++j)
|
||||
{
|
||||
for(int i=0; i<4; ++i)
|
||||
{
|
||||
int i1=8*j+i;
|
||||
int i2=8*(11-j)+i;
|
||||
|
||||
CRYPTOPP_ASSERT(i1<i2);
|
||||
|
||||
std::swap(K[i1],K[i2]);
|
||||
std::swap(K[i1+4],K[i2+4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// cast.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file cast.h
|
||||
/// \brief Classes for the CAST-128 and CAST-256 block ciphers
|
||||
/// \since Crypto++ 2.2
|
||||
|
||||
#ifndef CRYPTOPP_CAST_H
|
||||
#define CRYPTOPP_CAST_H
|
||||
|
||||
#include "seckey.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief CAST block cipher base
|
||||
/// \since Crypto++ 2.2
|
||||
class CAST
|
||||
{
|
||||
protected:
|
||||
static const word32 S[8][256];
|
||||
};
|
||||
|
||||
/// \brief CAST128 block cipher information
|
||||
/// \since Crypto++ 2.2
|
||||
struct CAST128_Info : public FixedBlockSize<8>, public VariableKeyLength<16, 5, 16>
|
||||
{
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "CAST-128";}
|
||||
};
|
||||
|
||||
/// \brief CAST128 block cipher
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/CAST-128">CAST-128</a>
|
||||
/// \since Crypto++ 2.2
|
||||
class CAST128 : public CAST128_Info, public BlockCipherDocumentation
|
||||
{
|
||||
/// \brief CAST128 block cipher default operation
|
||||
class CRYPTOPP_NO_VTABLE Base : public CAST, public BlockCipherImpl<CAST128_Info>
|
||||
{
|
||||
public:
|
||||
void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms);
|
||||
|
||||
protected:
|
||||
bool reduced;
|
||||
FixedSizeSecBlock<word32, 32> K;
|
||||
mutable FixedSizeSecBlock<word32, 3> m_t;
|
||||
};
|
||||
|
||||
/// \brief CAST128 block cipher encryption operation
|
||||
class CRYPTOPP_NO_VTABLE Enc : public Base
|
||||
{
|
||||
public:
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
};
|
||||
|
||||
/// \brief CAST128 block cipher decryption operation
|
||||
class CRYPTOPP_NO_VTABLE Dec : public Base
|
||||
{
|
||||
public:
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BlockCipherFinal<ENCRYPTION, Enc> Encryption;
|
||||
typedef BlockCipherFinal<DECRYPTION, Dec> Decryption;
|
||||
};
|
||||
|
||||
/// \brief CAST256 block cipher information
|
||||
/// \since Crypto++ 4.0
|
||||
struct CAST256_Info : public FixedBlockSize<16>, public VariableKeyLength<16, 16, 32, 4>
|
||||
{
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "CAST-256";}
|
||||
};
|
||||
|
||||
/// \brief CAST256 block cipher
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/CAST-256">CAST-256</a>
|
||||
/// \since Crypto++ 4.0
|
||||
class CAST256 : public CAST256_Info, public BlockCipherDocumentation
|
||||
{
|
||||
/// \brief CAST256 block cipher default operation
|
||||
class CRYPTOPP_NO_VTABLE Base : public CAST, public BlockCipherImpl<CAST256_Info>
|
||||
{
|
||||
public:
|
||||
void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms);
|
||||
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const;
|
||||
|
||||
protected:
|
||||
static const word32 t_m[8][24];
|
||||
static const unsigned int t_r[8][24];
|
||||
|
||||
static void Omega(int i, word32 kappa[8]);
|
||||
|
||||
FixedSizeSecBlock<word32, 8*12> K;
|
||||
mutable FixedSizeSecBlock<word32, 8> kappa;
|
||||
mutable FixedSizeSecBlock<word32, 3> m_t;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BlockCipherFinal<ENCRYPTION, Base> Encryption;
|
||||
typedef BlockCipherFinal<DECRYPTION, Base> Decryption;
|
||||
};
|
||||
|
||||
typedef CAST128::Encryption CAST128Encryption;
|
||||
typedef CAST128::Decryption CAST128Decryption;
|
||||
|
||||
typedef CAST256::Encryption CAST256Encryption;
|
||||
typedef CAST256::Decryption CAST256Decryption;
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
#include "pch.h"
|
||||
#include "cast.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
// CAST S-boxes
|
||||
|
||||
const word32 CAST::S[8][256] = {
|
||||
{
|
||||
0x30FB40D4UL, 0x9FA0FF0BUL, 0x6BECCD2FUL, 0x3F258C7AUL,
|
||||
0x1E213F2FUL, 0x9C004DD3UL, 0x6003E540UL, 0xCF9FC949UL,
|
||||
0xBFD4AF27UL, 0x88BBBDB5UL, 0xE2034090UL, 0x98D09675UL,
|
||||
0x6E63A0E0UL, 0x15C361D2UL, 0xC2E7661DUL, 0x22D4FF8EUL,
|
||||
0x28683B6FUL, 0xC07FD059UL, 0xFF2379C8UL, 0x775F50E2UL,
|
||||
0x43C340D3UL, 0xDF2F8656UL, 0x887CA41AUL, 0xA2D2BD2DUL,
|
||||
0xA1C9E0D6UL, 0x346C4819UL, 0x61B76D87UL, 0x22540F2FUL,
|
||||
0x2ABE32E1UL, 0xAA54166BUL, 0x22568E3AUL, 0xA2D341D0UL,
|
||||
0x66DB40C8UL, 0xA784392FUL, 0x004DFF2FUL, 0x2DB9D2DEUL,
|
||||
0x97943FACUL, 0x4A97C1D8UL, 0x527644B7UL, 0xB5F437A7UL,
|
||||
0xB82CBAEFUL, 0xD751D159UL, 0x6FF7F0EDUL, 0x5A097A1FUL,
|
||||
0x827B68D0UL, 0x90ECF52EUL, 0x22B0C054UL, 0xBC8E5935UL,
|
||||
0x4B6D2F7FUL, 0x50BB64A2UL, 0xD2664910UL, 0xBEE5812DUL,
|
||||
0xB7332290UL, 0xE93B159FUL, 0xB48EE411UL, 0x4BFF345DUL,
|
||||
0xFD45C240UL, 0xAD31973FUL, 0xC4F6D02EUL, 0x55FC8165UL,
|
||||
0xD5B1CAADUL, 0xA1AC2DAEUL, 0xA2D4B76DUL, 0xC19B0C50UL,
|
||||
0x882240F2UL, 0x0C6E4F38UL, 0xA4E4BFD7UL, 0x4F5BA272UL,
|
||||
0x564C1D2FUL, 0xC59C5319UL, 0xB949E354UL, 0xB04669FEUL,
|
||||
0xB1B6AB8AUL, 0xC71358DDUL, 0x6385C545UL, 0x110F935DUL,
|
||||
0x57538AD5UL, 0x6A390493UL, 0xE63D37E0UL, 0x2A54F6B3UL,
|
||||
0x3A787D5FUL, 0x6276A0B5UL, 0x19A6FCDFUL, 0x7A42206AUL,
|
||||
0x29F9D4D5UL, 0xF61B1891UL, 0xBB72275EUL, 0xAA508167UL,
|
||||
0x38901091UL, 0xC6B505EBUL, 0x84C7CB8CUL, 0x2AD75A0FUL,
|
||||
0x874A1427UL, 0xA2D1936BUL, 0x2AD286AFUL, 0xAA56D291UL,
|
||||
0xD7894360UL, 0x425C750DUL, 0x93B39E26UL, 0x187184C9UL,
|
||||
0x6C00B32DUL, 0x73E2BB14UL, 0xA0BEBC3CUL, 0x54623779UL,
|
||||
0x64459EABUL, 0x3F328B82UL, 0x7718CF82UL, 0x59A2CEA6UL,
|
||||
0x04EE002EUL, 0x89FE78E6UL, 0x3FAB0950UL, 0x325FF6C2UL,
|
||||
0x81383F05UL, 0x6963C5C8UL, 0x76CB5AD6UL, 0xD49974C9UL,
|
||||
0xCA180DCFUL, 0x380782D5UL, 0xC7FA5CF6UL, 0x8AC31511UL,
|
||||
0x35E79E13UL, 0x47DA91D0UL, 0xF40F9086UL, 0xA7E2419EUL,
|
||||
0x31366241UL, 0x051EF495UL, 0xAA573B04UL, 0x4A805D8DUL,
|
||||
0x548300D0UL, 0x00322A3CUL, 0xBF64CDDFUL, 0xBA57A68EUL,
|
||||
0x75C6372BUL, 0x50AFD341UL, 0xA7C13275UL, 0x915A0BF5UL,
|
||||
0x6B54BFABUL, 0x2B0B1426UL, 0xAB4CC9D7UL, 0x449CCD82UL,
|
||||
0xF7FBF265UL, 0xAB85C5F3UL, 0x1B55DB94UL, 0xAAD4E324UL,
|
||||
0xCFA4BD3FUL, 0x2DEAA3E2UL, 0x9E204D02UL, 0xC8BD25ACUL,
|
||||
0xEADF55B3UL, 0xD5BD9E98UL, 0xE31231B2UL, 0x2AD5AD6CUL,
|
||||
0x954329DEUL, 0xADBE4528UL, 0xD8710F69UL, 0xAA51C90FUL,
|
||||
0xAA786BF6UL, 0x22513F1EUL, 0xAA51A79BUL, 0x2AD344CCUL,
|
||||
0x7B5A41F0UL, 0xD37CFBADUL, 0x1B069505UL, 0x41ECE491UL,
|
||||
0xB4C332E6UL, 0x032268D4UL, 0xC9600ACCUL, 0xCE387E6DUL,
|
||||
0xBF6BB16CUL, 0x6A70FB78UL, 0x0D03D9C9UL, 0xD4DF39DEUL,
|
||||
0xE01063DAUL, 0x4736F464UL, 0x5AD328D8UL, 0xB347CC96UL,
|
||||
0x75BB0FC3UL, 0x98511BFBUL, 0x4FFBCC35UL, 0xB58BCF6AUL,
|
||||
0xE11F0ABCUL, 0xBFC5FE4AUL, 0xA70AEC10UL, 0xAC39570AUL,
|
||||
0x3F04442FUL, 0x6188B153UL, 0xE0397A2EUL, 0x5727CB79UL,
|
||||
0x9CEB418FUL, 0x1CACD68DUL, 0x2AD37C96UL, 0x0175CB9DUL,
|
||||
0xC69DFF09UL, 0xC75B65F0UL, 0xD9DB40D8UL, 0xEC0E7779UL,
|
||||
0x4744EAD4UL, 0xB11C3274UL, 0xDD24CB9EUL, 0x7E1C54BDUL,
|
||||
0xF01144F9UL, 0xD2240EB1UL, 0x9675B3FDUL, 0xA3AC3755UL,
|
||||
0xD47C27AFUL, 0x51C85F4DUL, 0x56907596UL, 0xA5BB15E6UL,
|
||||
0x580304F0UL, 0xCA042CF1UL, 0x011A37EAUL, 0x8DBFAADBUL,
|
||||
0x35BA3E4AUL, 0x3526FFA0UL, 0xC37B4D09UL, 0xBC306ED9UL,
|
||||
0x98A52666UL, 0x5648F725UL, 0xFF5E569DUL, 0x0CED63D0UL,
|
||||
0x7C63B2CFUL, 0x700B45E1UL, 0xD5EA50F1UL, 0x85A92872UL,
|
||||
0xAF1FBDA7UL, 0xD4234870UL, 0xA7870BF3UL, 0x2D3B4D79UL,
|
||||
0x42E04198UL, 0x0CD0EDE7UL, 0x26470DB8UL, 0xF881814CUL,
|
||||
0x474D6AD7UL, 0x7C0C5E5CUL, 0xD1231959UL, 0x381B7298UL,
|
||||
0xF5D2F4DBUL, 0xAB838653UL, 0x6E2F1E23UL, 0x83719C9EUL,
|
||||
0xBD91E046UL, 0x9A56456EUL, 0xDC39200CUL, 0x20C8C571UL,
|
||||
0x962BDA1CUL, 0xE1E696FFUL, 0xB141AB08UL, 0x7CCA89B9UL,
|
||||
0x1A69E783UL, 0x02CC4843UL, 0xA2F7C579UL, 0x429EF47DUL,
|
||||
0x427B169CUL, 0x5AC9F049UL, 0xDD8F0F00UL, 0x5C8165BFUL
|
||||
},
|
||||
|
||||
{
|
||||
0x1F201094UL, 0xEF0BA75BUL, 0x69E3CF7EUL, 0x393F4380UL,
|
||||
0xFE61CF7AUL, 0xEEC5207AUL, 0x55889C94UL, 0x72FC0651UL,
|
||||
0xADA7EF79UL, 0x4E1D7235UL, 0xD55A63CEUL, 0xDE0436BAUL,
|
||||
0x99C430EFUL, 0x5F0C0794UL, 0x18DCDB7DUL, 0xA1D6EFF3UL,
|
||||
0xA0B52F7BUL, 0x59E83605UL, 0xEE15B094UL, 0xE9FFD909UL,
|
||||
0xDC440086UL, 0xEF944459UL, 0xBA83CCB3UL, 0xE0C3CDFBUL,
|
||||
0xD1DA4181UL, 0x3B092AB1UL, 0xF997F1C1UL, 0xA5E6CF7BUL,
|
||||
0x01420DDBUL, 0xE4E7EF5BUL, 0x25A1FF41UL, 0xE180F806UL,
|
||||
0x1FC41080UL, 0x179BEE7AUL, 0xD37AC6A9UL, 0xFE5830A4UL,
|
||||
0x98DE8B7FUL, 0x77E83F4EUL, 0x79929269UL, 0x24FA9F7BUL,
|
||||
0xE113C85BUL, 0xACC40083UL, 0xD7503525UL, 0xF7EA615FUL,
|
||||
0x62143154UL, 0x0D554B63UL, 0x5D681121UL, 0xC866C359UL,
|
||||
0x3D63CF73UL, 0xCEE234C0UL, 0xD4D87E87UL, 0x5C672B21UL,
|
||||
0x071F6181UL, 0x39F7627FUL, 0x361E3084UL, 0xE4EB573BUL,
|
||||
0x602F64A4UL, 0xD63ACD9CUL, 0x1BBC4635UL, 0x9E81032DUL,
|
||||
0x2701F50CUL, 0x99847AB4UL, 0xA0E3DF79UL, 0xBA6CF38CUL,
|
||||
0x10843094UL, 0x2537A95EUL, 0xF46F6FFEUL, 0xA1FF3B1FUL,
|
||||
0x208CFB6AUL, 0x8F458C74UL, 0xD9E0A227UL, 0x4EC73A34UL,
|
||||
0xFC884F69UL, 0x3E4DE8DFUL, 0xEF0E0088UL, 0x3559648DUL,
|
||||
0x8A45388CUL, 0x1D804366UL, 0x721D9BFDUL, 0xA58684BBUL,
|
||||
0xE8256333UL, 0x844E8212UL, 0x128D8098UL, 0xFED33FB4UL,
|
||||
0xCE280AE1UL, 0x27E19BA5UL, 0xD5A6C252UL, 0xE49754BDUL,
|
||||
0xC5D655DDUL, 0xEB667064UL, 0x77840B4DUL, 0xA1B6A801UL,
|
||||
0x84DB26A9UL, 0xE0B56714UL, 0x21F043B7UL, 0xE5D05860UL,
|
||||
0x54F03084UL, 0x066FF472UL, 0xA31AA153UL, 0xDADC4755UL,
|
||||
0xB5625DBFUL, 0x68561BE6UL, 0x83CA6B94UL, 0x2D6ED23BUL,
|
||||
0xECCF01DBUL, 0xA6D3D0BAUL, 0xB6803D5CUL, 0xAF77A709UL,
|
||||
0x33B4A34CUL, 0x397BC8D6UL, 0x5EE22B95UL, 0x5F0E5304UL,
|
||||
0x81ED6F61UL, 0x20E74364UL, 0xB45E1378UL, 0xDE18639BUL,
|
||||
0x881CA122UL, 0xB96726D1UL, 0x8049A7E8UL, 0x22B7DA7BUL,
|
||||
0x5E552D25UL, 0x5272D237UL, 0x79D2951CUL, 0xC60D894CUL,
|
||||
0x488CB402UL, 0x1BA4FE5BUL, 0xA4B09F6BUL, 0x1CA815CFUL,
|
||||
0xA20C3005UL, 0x8871DF63UL, 0xB9DE2FCBUL, 0x0CC6C9E9UL,
|
||||
0x0BEEFF53UL, 0xE3214517UL, 0xB4542835UL, 0x9F63293CUL,
|
||||
0xEE41E729UL, 0x6E1D2D7CUL, 0x50045286UL, 0x1E6685F3UL,
|
||||
0xF33401C6UL, 0x30A22C95UL, 0x31A70850UL, 0x60930F13UL,
|
||||
0x73F98417UL, 0xA1269859UL, 0xEC645C44UL, 0x52C877A9UL,
|
||||
0xCDFF33A6UL, 0xA02B1741UL, 0x7CBAD9A2UL, 0x2180036FUL,
|
||||
0x50D99C08UL, 0xCB3F4861UL, 0xC26BD765UL, 0x64A3F6ABUL,
|
||||
0x80342676UL, 0x25A75E7BUL, 0xE4E6D1FCUL, 0x20C710E6UL,
|
||||
0xCDF0B680UL, 0x17844D3BUL, 0x31EEF84DUL, 0x7E0824E4UL,
|
||||
0x2CCB49EBUL, 0x846A3BAEUL, 0x8FF77888UL, 0xEE5D60F6UL,
|
||||
0x7AF75673UL, 0x2FDD5CDBUL, 0xA11631C1UL, 0x30F66F43UL,
|
||||
0xB3FAEC54UL, 0x157FD7FAUL, 0xEF8579CCUL, 0xD152DE58UL,
|
||||
0xDB2FFD5EUL, 0x8F32CE19UL, 0x306AF97AUL, 0x02F03EF8UL,
|
||||
0x99319AD5UL, 0xC242FA0FUL, 0xA7E3EBB0UL, 0xC68E4906UL,
|
||||
0xB8DA230CUL, 0x80823028UL, 0xDCDEF3C8UL, 0xD35FB171UL,
|
||||
0x088A1BC8UL, 0xBEC0C560UL, 0x61A3C9E8UL, 0xBCA8F54DUL,
|
||||
0xC72FEFFAUL, 0x22822E99UL, 0x82C570B4UL, 0xD8D94E89UL,
|
||||
0x8B1C34BCUL, 0x301E16E6UL, 0x273BE979UL, 0xB0FFEAA6UL,
|
||||
0x61D9B8C6UL, 0x00B24869UL, 0xB7FFCE3FUL, 0x08DC283BUL,
|
||||
0x43DAF65AUL, 0xF7E19798UL, 0x7619B72FUL, 0x8F1C9BA4UL,
|
||||
0xDC8637A0UL, 0x16A7D3B1UL, 0x9FC393B7UL, 0xA7136EEBUL,
|
||||
0xC6BCC63EUL, 0x1A513742UL, 0xEF6828BCUL, 0x520365D6UL,
|
||||
0x2D6A77ABUL, 0x3527ED4BUL, 0x821FD216UL, 0x095C6E2EUL,
|
||||
0xDB92F2FBUL, 0x5EEA29CBUL, 0x145892F5UL, 0x91584F7FUL,
|
||||
0x5483697BUL, 0x2667A8CCUL, 0x85196048UL, 0x8C4BACEAUL,
|
||||
0x833860D4UL, 0x0D23E0F9UL, 0x6C387E8AUL, 0x0AE6D249UL,
|
||||
0xB284600CUL, 0xD835731DUL, 0xDCB1C647UL, 0xAC4C56EAUL,
|
||||
0x3EBD81B3UL, 0x230EABB0UL, 0x6438BC87UL, 0xF0B5B1FAUL,
|
||||
0x8F5EA2B3UL, 0xFC184642UL, 0x0A036B7AUL, 0x4FB089BDUL,
|
||||
0x649DA589UL, 0xA345415EUL, 0x5C038323UL, 0x3E5D3BB9UL,
|
||||
0x43D79572UL, 0x7E6DD07CUL, 0x06DFDF1EUL, 0x6C6CC4EFUL,
|
||||
0x7160A539UL, 0x73BFBE70UL, 0x83877605UL, 0x4523ECF1UL
|
||||
},
|
||||
|
||||
{
|
||||
0x8DEFC240UL, 0x25FA5D9FUL, 0xEB903DBFUL, 0xE810C907UL,
|
||||
0x47607FFFUL, 0x369FE44BUL, 0x8C1FC644UL, 0xAECECA90UL,
|
||||
0xBEB1F9BFUL, 0xEEFBCAEAUL, 0xE8CF1950UL, 0x51DF07AEUL,
|
||||
0x920E8806UL, 0xF0AD0548UL, 0xE13C8D83UL, 0x927010D5UL,
|
||||
0x11107D9FUL, 0x07647DB9UL, 0xB2E3E4D4UL, 0x3D4F285EUL,
|
||||
0xB9AFA820UL, 0xFADE82E0UL, 0xA067268BUL, 0x8272792EUL,
|
||||
0x553FB2C0UL, 0x489AE22BUL, 0xD4EF9794UL, 0x125E3FBCUL,
|
||||
0x21FFFCEEUL, 0x825B1BFDUL, 0x9255C5EDUL, 0x1257A240UL,
|
||||
0x4E1A8302UL, 0xBAE07FFFUL, 0x528246E7UL, 0x8E57140EUL,
|
||||
0x3373F7BFUL, 0x8C9F8188UL, 0xA6FC4EE8UL, 0xC982B5A5UL,
|
||||
0xA8C01DB7UL, 0x579FC264UL, 0x67094F31UL, 0xF2BD3F5FUL,
|
||||
0x40FFF7C1UL, 0x1FB78DFCUL, 0x8E6BD2C1UL, 0x437BE59BUL,
|
||||
0x99B03DBFUL, 0xB5DBC64BUL, 0x638DC0E6UL, 0x55819D99UL,
|
||||
0xA197C81CUL, 0x4A012D6EUL, 0xC5884A28UL, 0xCCC36F71UL,
|
||||
0xB843C213UL, 0x6C0743F1UL, 0x8309893CUL, 0x0FEDDD5FUL,
|
||||
0x2F7FE850UL, 0xD7C07F7EUL, 0x02507FBFUL, 0x5AFB9A04UL,
|
||||
0xA747D2D0UL, 0x1651192EUL, 0xAF70BF3EUL, 0x58C31380UL,
|
||||
0x5F98302EUL, 0x727CC3C4UL, 0x0A0FB402UL, 0x0F7FEF82UL,
|
||||
0x8C96FDADUL, 0x5D2C2AAEUL, 0x8EE99A49UL, 0x50DA88B8UL,
|
||||
0x8427F4A0UL, 0x1EAC5790UL, 0x796FB449UL, 0x8252DC15UL,
|
||||
0xEFBD7D9BUL, 0xA672597DUL, 0xADA840D8UL, 0x45F54504UL,
|
||||
0xFA5D7403UL, 0xE83EC305UL, 0x4F91751AUL, 0x925669C2UL,
|
||||
0x23EFE941UL, 0xA903F12EUL, 0x60270DF2UL, 0x0276E4B6UL,
|
||||
0x94FD6574UL, 0x927985B2UL, 0x8276DBCBUL, 0x02778176UL,
|
||||
0xF8AF918DUL, 0x4E48F79EUL, 0x8F616DDFUL, 0xE29D840EUL,
|
||||
0x842F7D83UL, 0x340CE5C8UL, 0x96BBB682UL, 0x93B4B148UL,
|
||||
0xEF303CABUL, 0x984FAF28UL, 0x779FAF9BUL, 0x92DC560DUL,
|
||||
0x224D1E20UL, 0x8437AA88UL, 0x7D29DC96UL, 0x2756D3DCUL,
|
||||
0x8B907CEEUL, 0xB51FD240UL, 0xE7C07CE3UL, 0xE566B4A1UL,
|
||||
0xC3E9615EUL, 0x3CF8209DUL, 0x6094D1E3UL, 0xCD9CA341UL,
|
||||
0x5C76460EUL, 0x00EA983BUL, 0xD4D67881UL, 0xFD47572CUL,
|
||||
0xF76CEDD9UL, 0xBDA8229CUL, 0x127DADAAUL, 0x438A074EUL,
|
||||
0x1F97C090UL, 0x081BDB8AUL, 0x93A07EBEUL, 0xB938CA15UL,
|
||||
0x97B03CFFUL, 0x3DC2C0F8UL, 0x8D1AB2ECUL, 0x64380E51UL,
|
||||
0x68CC7BFBUL, 0xD90F2788UL, 0x12490181UL, 0x5DE5FFD4UL,
|
||||
0xDD7EF86AUL, 0x76A2E214UL, 0xB9A40368UL, 0x925D958FUL,
|
||||
0x4B39FFFAUL, 0xBA39AEE9UL, 0xA4FFD30BUL, 0xFAF7933BUL,
|
||||
0x6D498623UL, 0x193CBCFAUL, 0x27627545UL, 0x825CF47AUL,
|
||||
0x61BD8BA0UL, 0xD11E42D1UL, 0xCEAD04F4UL, 0x127EA392UL,
|
||||
0x10428DB7UL, 0x8272A972UL, 0x9270C4A8UL, 0x127DE50BUL,
|
||||
0x285BA1C8UL, 0x3C62F44FUL, 0x35C0EAA5UL, 0xE805D231UL,
|
||||
0x428929FBUL, 0xB4FCDF82UL, 0x4FB66A53UL, 0x0E7DC15BUL,
|
||||
0x1F081FABUL, 0x108618AEUL, 0xFCFD086DUL, 0xF9FF2889UL,
|
||||
0x694BCC11UL, 0x236A5CAEUL, 0x12DECA4DUL, 0x2C3F8CC5UL,
|
||||
0xD2D02DFEUL, 0xF8EF5896UL, 0xE4CF52DAUL, 0x95155B67UL,
|
||||
0x494A488CUL, 0xB9B6A80CUL, 0x5C8F82BCUL, 0x89D36B45UL,
|
||||
0x3A609437UL, 0xEC00C9A9UL, 0x44715253UL, 0x0A874B49UL,
|
||||
0xD773BC40UL, 0x7C34671CUL, 0x02717EF6UL, 0x4FEB5536UL,
|
||||
0xA2D02FFFUL, 0xD2BF60C4UL, 0xD43F03C0UL, 0x50B4EF6DUL,
|
||||
0x07478CD1UL, 0x006E1888UL, 0xA2E53F55UL, 0xB9E6D4BCUL,
|
||||
0xA2048016UL, 0x97573833UL, 0xD7207D67UL, 0xDE0F8F3DUL,
|
||||
0x72F87B33UL, 0xABCC4F33UL, 0x7688C55DUL, 0x7B00A6B0UL,
|
||||
0x947B0001UL, 0x570075D2UL, 0xF9BB88F8UL, 0x8942019EUL,
|
||||
0x4264A5FFUL, 0x856302E0UL, 0x72DBD92BUL, 0xEE971B69UL,
|
||||
0x6EA22FDEUL, 0x5F08AE2BUL, 0xAF7A616DUL, 0xE5C98767UL,
|
||||
0xCF1FEBD2UL, 0x61EFC8C2UL, 0xF1AC2571UL, 0xCC8239C2UL,
|
||||
0x67214CB8UL, 0xB1E583D1UL, 0xB7DC3E62UL, 0x7F10BDCEUL,
|
||||
0xF90A5C38UL, 0x0FF0443DUL, 0x606E6DC6UL, 0x60543A49UL,
|
||||
0x5727C148UL, 0x2BE98A1DUL, 0x8AB41738UL, 0x20E1BE24UL,
|
||||
0xAF96DA0FUL, 0x68458425UL, 0x99833BE5UL, 0x600D457DUL,
|
||||
0x282F9350UL, 0x8334B362UL, 0xD91D1120UL, 0x2B6D8DA0UL,
|
||||
0x642B1E31UL, 0x9C305A00UL, 0x52BCE688UL, 0x1B03588AUL,
|
||||
0xF7BAEFD5UL, 0x4142ED9CUL, 0xA4315C11UL, 0x83323EC5UL,
|
||||
0xDFEF4636UL, 0xA133C501UL, 0xE9D3531CUL, 0xEE353783UL
|
||||
},
|
||||
|
||||
{
|
||||
0x9DB30420UL, 0x1FB6E9DEUL, 0xA7BE7BEFUL, 0xD273A298UL,
|
||||
0x4A4F7BDBUL, 0x64AD8C57UL, 0x85510443UL, 0xFA020ED1UL,
|
||||
0x7E287AFFUL, 0xE60FB663UL, 0x095F35A1UL, 0x79EBF120UL,
|
||||
0xFD059D43UL, 0x6497B7B1UL, 0xF3641F63UL, 0x241E4ADFUL,
|
||||
0x28147F5FUL, 0x4FA2B8CDUL, 0xC9430040UL, 0x0CC32220UL,
|
||||
0xFDD30B30UL, 0xC0A5374FUL, 0x1D2D00D9UL, 0x24147B15UL,
|
||||
0xEE4D111AUL, 0x0FCA5167UL, 0x71FF904CUL, 0x2D195FFEUL,
|
||||
0x1A05645FUL, 0x0C13FEFEUL, 0x081B08CAUL, 0x05170121UL,
|
||||
0x80530100UL, 0xE83E5EFEUL, 0xAC9AF4F8UL, 0x7FE72701UL,
|
||||
0xD2B8EE5FUL, 0x06DF4261UL, 0xBB9E9B8AUL, 0x7293EA25UL,
|
||||
0xCE84FFDFUL, 0xF5718801UL, 0x3DD64B04UL, 0xA26F263BUL,
|
||||
0x7ED48400UL, 0x547EEBE6UL, 0x446D4CA0UL, 0x6CF3D6F5UL,
|
||||
0x2649ABDFUL, 0xAEA0C7F5UL, 0x36338CC1UL, 0x503F7E93UL,
|
||||
0xD3772061UL, 0x11B638E1UL, 0x72500E03UL, 0xF80EB2BBUL,
|
||||
0xABE0502EUL, 0xEC8D77DEUL, 0x57971E81UL, 0xE14F6746UL,
|
||||
0xC9335400UL, 0x6920318FUL, 0x081DBB99UL, 0xFFC304A5UL,
|
||||
0x4D351805UL, 0x7F3D5CE3UL, 0xA6C866C6UL, 0x5D5BCCA9UL,
|
||||
0xDAEC6FEAUL, 0x9F926F91UL, 0x9F46222FUL, 0x3991467DUL,
|
||||
0xA5BF6D8EUL, 0x1143C44FUL, 0x43958302UL, 0xD0214EEBUL,
|
||||
0x022083B8UL, 0x3FB6180CUL, 0x18F8931EUL, 0x281658E6UL,
|
||||
0x26486E3EUL, 0x8BD78A70UL, 0x7477E4C1UL, 0xB506E07CUL,
|
||||
0xF32D0A25UL, 0x79098B02UL, 0xE4EABB81UL, 0x28123B23UL,
|
||||
0x69DEAD38UL, 0x1574CA16UL, 0xDF871B62UL, 0x211C40B7UL,
|
||||
0xA51A9EF9UL, 0x0014377BUL, 0x041E8AC8UL, 0x09114003UL,
|
||||
0xBD59E4D2UL, 0xE3D156D5UL, 0x4FE876D5UL, 0x2F91A340UL,
|
||||
0x557BE8DEUL, 0x00EAE4A7UL, 0x0CE5C2ECUL, 0x4DB4BBA6UL,
|
||||
0xE756BDFFUL, 0xDD3369ACUL, 0xEC17B035UL, 0x06572327UL,
|
||||
0x99AFC8B0UL, 0x56C8C391UL, 0x6B65811CUL, 0x5E146119UL,
|
||||
0x6E85CB75UL, 0xBE07C002UL, 0xC2325577UL, 0x893FF4ECUL,
|
||||
0x5BBFC92DUL, 0xD0EC3B25UL, 0xB7801AB7UL, 0x8D6D3B24UL,
|
||||
0x20C763EFUL, 0xC366A5FCUL, 0x9C382880UL, 0x0ACE3205UL,
|
||||
0xAAC9548AUL, 0xECA1D7C7UL, 0x041AFA32UL, 0x1D16625AUL,
|
||||
0x6701902CUL, 0x9B757A54UL, 0x31D477F7UL, 0x9126B031UL,
|
||||
0x36CC6FDBUL, 0xC70B8B46UL, 0xD9E66A48UL, 0x56E55A79UL,
|
||||
0x026A4CEBUL, 0x52437EFFUL, 0x2F8F76B4UL, 0x0DF980A5UL,
|
||||
0x8674CDE3UL, 0xEDDA04EBUL, 0x17A9BE04UL, 0x2C18F4DFUL,
|
||||
0xB7747F9DUL, 0xAB2AF7B4UL, 0xEFC34D20UL, 0x2E096B7CUL,
|
||||
0x1741A254UL, 0xE5B6A035UL, 0x213D42F6UL, 0x2C1C7C26UL,
|
||||
0x61C2F50FUL, 0x6552DAF9UL, 0xD2C231F8UL, 0x25130F69UL,
|
||||
0xD8167FA2UL, 0x0418F2C8UL, 0x001A96A6UL, 0x0D1526ABUL,
|
||||
0x63315C21UL, 0x5E0A72ECUL, 0x49BAFEFDUL, 0x187908D9UL,
|
||||
0x8D0DBD86UL, 0x311170A7UL, 0x3E9B640CUL, 0xCC3E10D7UL,
|
||||
0xD5CAD3B6UL, 0x0CAEC388UL, 0xF73001E1UL, 0x6C728AFFUL,
|
||||
0x71EAE2A1UL, 0x1F9AF36EUL, 0xCFCBD12FUL, 0xC1DE8417UL,
|
||||
0xAC07BE6BUL, 0xCB44A1D8UL, 0x8B9B0F56UL, 0x013988C3UL,
|
||||
0xB1C52FCAUL, 0xB4BE31CDUL, 0xD8782806UL, 0x12A3A4E2UL,
|
||||
0x6F7DE532UL, 0x58FD7EB6UL, 0xD01EE900UL, 0x24ADFFC2UL,
|
||||
0xF4990FC5UL, 0x9711AAC5UL, 0x001D7B95UL, 0x82E5E7D2UL,
|
||||
0x109873F6UL, 0x00613096UL, 0xC32D9521UL, 0xADA121FFUL,
|
||||
0x29908415UL, 0x7FBB977FUL, 0xAF9EB3DBUL, 0x29C9ED2AUL,
|
||||
0x5CE2A465UL, 0xA730F32CUL, 0xD0AA3FE8UL, 0x8A5CC091UL,
|
||||
0xD49E2CE7UL, 0x0CE454A9UL, 0xD60ACD86UL, 0x015F1919UL,
|
||||
0x77079103UL, 0xDEA03AF6UL, 0x78A8565EUL, 0xDEE356DFUL,
|
||||
0x21F05CBEUL, 0x8B75E387UL, 0xB3C50651UL, 0xB8A5C3EFUL,
|
||||
0xD8EEB6D2UL, 0xE523BE77UL, 0xC2154529UL, 0x2F69EFDFUL,
|
||||
0xAFE67AFBUL, 0xF470C4B2UL, 0xF3E0EB5BUL, 0xD6CC9876UL,
|
||||
0x39E4460CUL, 0x1FDA8538UL, 0x1987832FUL, 0xCA007367UL,
|
||||
0xA99144F8UL, 0x296B299EUL, 0x492FC295UL, 0x9266BEABUL,
|
||||
0xB5676E69UL, 0x9BD3DDDAUL, 0xDF7E052FUL, 0xDB25701CUL,
|
||||
0x1B5E51EEUL, 0xF65324E6UL, 0x6AFCE36CUL, 0x0316CC04UL,
|
||||
0x8644213EUL, 0xB7DC59D0UL, 0x7965291FUL, 0xCCD6FD43UL,
|
||||
0x41823979UL, 0x932BCDF6UL, 0xB657C34DUL, 0x4EDFD282UL,
|
||||
0x7AE5290CUL, 0x3CB9536BUL, 0x851E20FEUL, 0x9833557EUL,
|
||||
0x13ECF0B0UL, 0xD3FFB372UL, 0x3F85C5C1UL, 0x0AEF7ED2UL
|
||||
},
|
||||
|
||||
{
|
||||
0x7EC90C04UL, 0x2C6E74B9UL, 0x9B0E66DFUL, 0xA6337911UL,
|
||||
0xB86A7FFFUL, 0x1DD358F5UL, 0x44DD9D44UL, 0x1731167FUL,
|
||||
0x08FBF1FAUL, 0xE7F511CCUL, 0xD2051B00UL, 0x735ABA00UL,
|
||||
0x2AB722D8UL, 0x386381CBUL, 0xACF6243AUL, 0x69BEFD7AUL,
|
||||
0xE6A2E77FUL, 0xF0C720CDUL, 0xC4494816UL, 0xCCF5C180UL,
|
||||
0x38851640UL, 0x15B0A848UL, 0xE68B18CBUL, 0x4CAADEFFUL,
|
||||
0x5F480A01UL, 0x0412B2AAUL, 0x259814FCUL, 0x41D0EFE2UL,
|
||||
0x4E40B48DUL, 0x248EB6FBUL, 0x8DBA1CFEUL, 0x41A99B02UL,
|
||||
0x1A550A04UL, 0xBA8F65CBUL, 0x7251F4E7UL, 0x95A51725UL,
|
||||
0xC106ECD7UL, 0x97A5980AUL, 0xC539B9AAUL, 0x4D79FE6AUL,
|
||||
0xF2F3F763UL, 0x68AF8040UL, 0xED0C9E56UL, 0x11B4958BUL,
|
||||
0xE1EB5A88UL, 0x8709E6B0UL, 0xD7E07156UL, 0x4E29FEA7UL,
|
||||
0x6366E52DUL, 0x02D1C000UL, 0xC4AC8E05UL, 0x9377F571UL,
|
||||
0x0C05372AUL, 0x578535F2UL, 0x2261BE02UL, 0xD642A0C9UL,
|
||||
0xDF13A280UL, 0x74B55BD2UL, 0x682199C0UL, 0xD421E5ECUL,
|
||||
0x53FB3CE8UL, 0xC8ADEDB3UL, 0x28A87FC9UL, 0x3D959981UL,
|
||||
0x5C1FF900UL, 0xFE38D399UL, 0x0C4EFF0BUL, 0x062407EAUL,
|
||||
0xAA2F4FB1UL, 0x4FB96976UL, 0x90C79505UL, 0xB0A8A774UL,
|
||||
0xEF55A1FFUL, 0xE59CA2C2UL, 0xA6B62D27UL, 0xE66A4263UL,
|
||||
0xDF65001FUL, 0x0EC50966UL, 0xDFDD55BCUL, 0x29DE0655UL,
|
||||
0x911E739AUL, 0x17AF8975UL, 0x32C7911CUL, 0x89F89468UL,
|
||||
0x0D01E980UL, 0x524755F4UL, 0x03B63CC9UL, 0x0CC844B2UL,
|
||||
0xBCF3F0AAUL, 0x87AC36E9UL, 0xE53A7426UL, 0x01B3D82BUL,
|
||||
0x1A9E7449UL, 0x64EE2D7EUL, 0xCDDBB1DAUL, 0x01C94910UL,
|
||||
0xB868BF80UL, 0x0D26F3FDUL, 0x9342EDE7UL, 0x04A5C284UL,
|
||||
0x636737B6UL, 0x50F5B616UL, 0xF24766E3UL, 0x8ECA36C1UL,
|
||||
0x136E05DBUL, 0xFEF18391UL, 0xFB887A37UL, 0xD6E7F7D4UL,
|
||||
0xC7FB7DC9UL, 0x3063FCDFUL, 0xB6F589DEUL, 0xEC2941DAUL,
|
||||
0x26E46695UL, 0xB7566419UL, 0xF654EFC5UL, 0xD08D58B7UL,
|
||||
0x48925401UL, 0xC1BACB7FUL, 0xE5FF550FUL, 0xB6083049UL,
|
||||
0x5BB5D0E8UL, 0x87D72E5AUL, 0xAB6A6EE1UL, 0x223A66CEUL,
|
||||
0xC62BF3CDUL, 0x9E0885F9UL, 0x68CB3E47UL, 0x086C010FUL,
|
||||
0xA21DE820UL, 0xD18B69DEUL, 0xF3F65777UL, 0xFA02C3F6UL,
|
||||
0x407EDAC3UL, 0xCBB3D550UL, 0x1793084DUL, 0xB0D70EBAUL,
|
||||
0x0AB378D5UL, 0xD951FB0CUL, 0xDED7DA56UL, 0x4124BBE4UL,
|
||||
0x94CA0B56UL, 0x0F5755D1UL, 0xE0E1E56EUL, 0x6184B5BEUL,
|
||||
0x580A249FUL, 0x94F74BC0UL, 0xE327888EUL, 0x9F7B5561UL,
|
||||
0xC3DC0280UL, 0x05687715UL, 0x646C6BD7UL, 0x44904DB3UL,
|
||||
0x66B4F0A3UL, 0xC0F1648AUL, 0x697ED5AFUL, 0x49E92FF6UL,
|
||||
0x309E374FUL, 0x2CB6356AUL, 0x85808573UL, 0x4991F840UL,
|
||||
0x76F0AE02UL, 0x083BE84DUL, 0x28421C9AUL, 0x44489406UL,
|
||||
0x736E4CB8UL, 0xC1092910UL, 0x8BC95FC6UL, 0x7D869CF4UL,
|
||||
0x134F616FUL, 0x2E77118DUL, 0xB31B2BE1UL, 0xAA90B472UL,
|
||||
0x3CA5D717UL, 0x7D161BBAUL, 0x9CAD9010UL, 0xAF462BA2UL,
|
||||
0x9FE459D2UL, 0x45D34559UL, 0xD9F2DA13UL, 0xDBC65487UL,
|
||||
0xF3E4F94EUL, 0x176D486FUL, 0x097C13EAUL, 0x631DA5C7UL,
|
||||
0x445F7382UL, 0x175683F4UL, 0xCDC66A97UL, 0x70BE0288UL,
|
||||
0xB3CDCF72UL, 0x6E5DD2F3UL, 0x20936079UL, 0x459B80A5UL,
|
||||
0xBE60E2DBUL, 0xA9C23101UL, 0xEBA5315CUL, 0x224E42F2UL,
|
||||
0x1C5C1572UL, 0xF6721B2CUL, 0x1AD2FFF3UL, 0x8C25404EUL,
|
||||
0x324ED72FUL, 0x4067B7FDUL, 0x0523138EUL, 0x5CA3BC78UL,
|
||||
0xDC0FD66EUL, 0x75922283UL, 0x784D6B17UL, 0x58EBB16EUL,
|
||||
0x44094F85UL, 0x3F481D87UL, 0xFCFEAE7BUL, 0x77B5FF76UL,
|
||||
0x8C2302BFUL, 0xAAF47556UL, 0x5F46B02AUL, 0x2B092801UL,
|
||||
0x3D38F5F7UL, 0x0CA81F36UL, 0x52AF4A8AUL, 0x66D5E7C0UL,
|
||||
0xDF3B0874UL, 0x95055110UL, 0x1B5AD7A8UL, 0xF61ED5ADUL,
|
||||
0x6CF6E479UL, 0x20758184UL, 0xD0CEFA65UL, 0x88F7BE58UL,
|
||||
0x4A046826UL, 0x0FF6F8F3UL, 0xA09C7F70UL, 0x5346ABA0UL,
|
||||
0x5CE96C28UL, 0xE176EDA3UL, 0x6BAC307FUL, 0x376829D2UL,
|
||||
0x85360FA9UL, 0x17E3FE2AUL, 0x24B79767UL, 0xF5A96B20UL,
|
||||
0xD6CD2595UL, 0x68FF1EBFUL, 0x7555442CUL, 0xF19F06BEUL,
|
||||
0xF9E0659AUL, 0xEEB9491DUL, 0x34010718UL, 0xBB30CAB8UL,
|
||||
0xE822FE15UL, 0x88570983UL, 0x750E6249UL, 0xDA627E55UL,
|
||||
0x5E76FFA8UL, 0xB1534546UL, 0x6D47DE08UL, 0xEFE9E7D4UL
|
||||
},
|
||||
|
||||
{
|
||||
0xF6FA8F9DUL, 0x2CAC6CE1UL, 0x4CA34867UL, 0xE2337F7CUL,
|
||||
0x95DB08E7UL, 0x016843B4UL, 0xECED5CBCUL, 0x325553ACUL,
|
||||
0xBF9F0960UL, 0xDFA1E2EDUL, 0x83F0579DUL, 0x63ED86B9UL,
|
||||
0x1AB6A6B8UL, 0xDE5EBE39UL, 0xF38FF732UL, 0x8989B138UL,
|
||||
0x33F14961UL, 0xC01937BDUL, 0xF506C6DAUL, 0xE4625E7EUL,
|
||||
0xA308EA99UL, 0x4E23E33CUL, 0x79CBD7CCUL, 0x48A14367UL,
|
||||
0xA3149619UL, 0xFEC94BD5UL, 0xA114174AUL, 0xEAA01866UL,
|
||||
0xA084DB2DUL, 0x09A8486FUL, 0xA888614AUL, 0x2900AF98UL,
|
||||
0x01665991UL, 0xE1992863UL, 0xC8F30C60UL, 0x2E78EF3CUL,
|
||||
0xD0D51932UL, 0xCF0FEC14UL, 0xF7CA07D2UL, 0xD0A82072UL,
|
||||
0xFD41197EUL, 0x9305A6B0UL, 0xE86BE3DAUL, 0x74BED3CDUL,
|
||||
0x372DA53CUL, 0x4C7F4448UL, 0xDAB5D440UL, 0x6DBA0EC3UL,
|
||||
0x083919A7UL, 0x9FBAEED9UL, 0x49DBCFB0UL, 0x4E670C53UL,
|
||||
0x5C3D9C01UL, 0x64BDB941UL, 0x2C0E636AUL, 0xBA7DD9CDUL,
|
||||
0xEA6F7388UL, 0xE70BC762UL, 0x35F29ADBUL, 0x5C4CDD8DUL,
|
||||
0xF0D48D8CUL, 0xB88153E2UL, 0x08A19866UL, 0x1AE2EAC8UL,
|
||||
0x284CAF89UL, 0xAA928223UL, 0x9334BE53UL, 0x3B3A21BFUL,
|
||||
0x16434BE3UL, 0x9AEA3906UL, 0xEFE8C36EUL, 0xF890CDD9UL,
|
||||
0x80226DAEUL, 0xC340A4A3UL, 0xDF7E9C09UL, 0xA694A807UL,
|
||||
0x5B7C5ECCUL, 0x221DB3A6UL, 0x9A69A02FUL, 0x68818A54UL,
|
||||
0xCEB2296FUL, 0x53C0843AUL, 0xFE893655UL, 0x25BFE68AUL,
|
||||
0xB4628ABCUL, 0xCF222EBFUL, 0x25AC6F48UL, 0xA9A99387UL,
|
||||
0x53BDDB65UL, 0xE76FFBE7UL, 0xE967FD78UL, 0x0BA93563UL,
|
||||
0x8E342BC1UL, 0xE8A11BE9UL, 0x4980740DUL, 0xC8087DFCUL,
|
||||
0x8DE4BF99UL, 0xA11101A0UL, 0x7FD37975UL, 0xDA5A26C0UL,
|
||||
0xE81F994FUL, 0x9528CD89UL, 0xFD339FEDUL, 0xB87834BFUL,
|
||||
0x5F04456DUL, 0x22258698UL, 0xC9C4C83BUL, 0x2DC156BEUL,
|
||||
0x4F628DAAUL, 0x57F55EC5UL, 0xE2220ABEUL, 0xD2916EBFUL,
|
||||
0x4EC75B95UL, 0x24F2C3C0UL, 0x42D15D99UL, 0xCD0D7FA0UL,
|
||||
0x7B6E27FFUL, 0xA8DC8AF0UL, 0x7345C106UL, 0xF41E232FUL,
|
||||
0x35162386UL, 0xE6EA8926UL, 0x3333B094UL, 0x157EC6F2UL,
|
||||
0x372B74AFUL, 0x692573E4UL, 0xE9A9D848UL, 0xF3160289UL,
|
||||
0x3A62EF1DUL, 0xA787E238UL, 0xF3A5F676UL, 0x74364853UL,
|
||||
0x20951063UL, 0x4576698DUL, 0xB6FAD407UL, 0x592AF950UL,
|
||||
0x36F73523UL, 0x4CFB6E87UL, 0x7DA4CEC0UL, 0x6C152DAAUL,
|
||||
0xCB0396A8UL, 0xC50DFE5DUL, 0xFCD707ABUL, 0x0921C42FUL,
|
||||
0x89DFF0BBUL, 0x5FE2BE78UL, 0x448F4F33UL, 0x754613C9UL,
|
||||
0x2B05D08DUL, 0x48B9D585UL, 0xDC049441UL, 0xC8098F9BUL,
|
||||
0x7DEDE786UL, 0xC39A3373UL, 0x42410005UL, 0x6A091751UL,
|
||||
0x0EF3C8A6UL, 0x890072D6UL, 0x28207682UL, 0xA9A9F7BEUL,
|
||||
0xBF32679DUL, 0xD45B5B75UL, 0xB353FD00UL, 0xCBB0E358UL,
|
||||
0x830F220AUL, 0x1F8FB214UL, 0xD372CF08UL, 0xCC3C4A13UL,
|
||||
0x8CF63166UL, 0x061C87BEUL, 0x88C98F88UL, 0x6062E397UL,
|
||||
0x47CF8E7AUL, 0xB6C85283UL, 0x3CC2ACFBUL, 0x3FC06976UL,
|
||||
0x4E8F0252UL, 0x64D8314DUL, 0xDA3870E3UL, 0x1E665459UL,
|
||||
0xC10908F0UL, 0x513021A5UL, 0x6C5B68B7UL, 0x822F8AA0UL,
|
||||
0x3007CD3EUL, 0x74719EEFUL, 0xDC872681UL, 0x073340D4UL,
|
||||
0x7E432FD9UL, 0x0C5EC241UL, 0x8809286CUL, 0xF592D891UL,
|
||||
0x08A930F6UL, 0x957EF305UL, 0xB7FBFFBDUL, 0xC266E96FUL,
|
||||
0x6FE4AC98UL, 0xB173ECC0UL, 0xBC60B42AUL, 0x953498DAUL,
|
||||
0xFBA1AE12UL, 0x2D4BD736UL, 0x0F25FAABUL, 0xA4F3FCEBUL,
|
||||
0xE2969123UL, 0x257F0C3DUL, 0x9348AF49UL, 0x361400BCUL,
|
||||
0xE8816F4AUL, 0x3814F200UL, 0xA3F94043UL, 0x9C7A54C2UL,
|
||||
0xBC704F57UL, 0xDA41E7F9UL, 0xC25AD33AUL, 0x54F4A084UL,
|
||||
0xB17F5505UL, 0x59357CBEUL, 0xEDBD15C8UL, 0x7F97C5ABUL,
|
||||
0xBA5AC7B5UL, 0xB6F6DEAFUL, 0x3A479C3AUL, 0x5302DA25UL,
|
||||
0x653D7E6AUL, 0x54268D49UL, 0x51A477EAUL, 0x5017D55BUL,
|
||||
0xD7D25D88UL, 0x44136C76UL, 0x0404A8C8UL, 0xB8E5A121UL,
|
||||
0xB81A928AUL, 0x60ED5869UL, 0x97C55B96UL, 0xEAEC991BUL,
|
||||
0x29935913UL, 0x01FDB7F1UL, 0x088E8DFAUL, 0x9AB6F6F5UL,
|
||||
0x3B4CBF9FUL, 0x4A5DE3ABUL, 0xE6051D35UL, 0xA0E1D855UL,
|
||||
0xD36B4CF1UL, 0xF544EDEBUL, 0xB0E93524UL, 0xBEBB8FBDUL,
|
||||
0xA2D762CFUL, 0x49C92F54UL, 0x38B5F331UL, 0x7128A454UL,
|
||||
0x48392905UL, 0xA65B1DB8UL, 0x851C97BDUL, 0xD675CF2FUL
|
||||
},
|
||||
|
||||
{
|
||||
0x85E04019UL, 0x332BF567UL, 0x662DBFFFUL, 0xCFC65693UL,
|
||||
0x2A8D7F6FUL, 0xAB9BC912UL, 0xDE6008A1UL, 0x2028DA1FUL,
|
||||
0x0227BCE7UL, 0x4D642916UL, 0x18FAC300UL, 0x50F18B82UL,
|
||||
0x2CB2CB11UL, 0xB232E75CUL, 0x4B3695F2UL, 0xB28707DEUL,
|
||||
0xA05FBCF6UL, 0xCD4181E9UL, 0xE150210CUL, 0xE24EF1BDUL,
|
||||
0xB168C381UL, 0xFDE4E789UL, 0x5C79B0D8UL, 0x1E8BFD43UL,
|
||||
0x4D495001UL, 0x38BE4341UL, 0x913CEE1DUL, 0x92A79C3FUL,
|
||||
0x089766BEUL, 0xBAEEADF4UL, 0x1286BECFUL, 0xB6EACB19UL,
|
||||
0x2660C200UL, 0x7565BDE4UL, 0x64241F7AUL, 0x8248DCA9UL,
|
||||
0xC3B3AD66UL, 0x28136086UL, 0x0BD8DFA8UL, 0x356D1CF2UL,
|
||||
0x107789BEUL, 0xB3B2E9CEUL, 0x0502AA8FUL, 0x0BC0351EUL,
|
||||
0x166BF52AUL, 0xEB12FF82UL, 0xE3486911UL, 0xD34D7516UL,
|
||||
0x4E7B3AFFUL, 0x5F43671BUL, 0x9CF6E037UL, 0x4981AC83UL,
|
||||
0x334266CEUL, 0x8C9341B7UL, 0xD0D854C0UL, 0xCB3A6C88UL,
|
||||
0x47BC2829UL, 0x4725BA37UL, 0xA66AD22BUL, 0x7AD61F1EUL,
|
||||
0x0C5CBAFAUL, 0x4437F107UL, 0xB6E79962UL, 0x42D2D816UL,
|
||||
0x0A961288UL, 0xE1A5C06EUL, 0x13749E67UL, 0x72FC081AUL,
|
||||
0xB1D139F7UL, 0xF9583745UL, 0xCF19DF58UL, 0xBEC3F756UL,
|
||||
0xC06EBA30UL, 0x07211B24UL, 0x45C28829UL, 0xC95E317FUL,
|
||||
0xBC8EC511UL, 0x38BC46E9UL, 0xC6E6FA14UL, 0xBAE8584AUL,
|
||||
0xAD4EBC46UL, 0x468F508BUL, 0x7829435FUL, 0xF124183BUL,
|
||||
0x821DBA9FUL, 0xAFF60FF4UL, 0xEA2C4E6DUL, 0x16E39264UL,
|
||||
0x92544A8BUL, 0x009B4FC3UL, 0xABA68CEDUL, 0x9AC96F78UL,
|
||||
0x06A5B79AUL, 0xB2856E6EUL, 0x1AEC3CA9UL, 0xBE838688UL,
|
||||
0x0E0804E9UL, 0x55F1BE56UL, 0xE7E5363BUL, 0xB3A1F25DUL,
|
||||
0xF7DEBB85UL, 0x61FE033CUL, 0x16746233UL, 0x3C034C28UL,
|
||||
0xDA6D0C74UL, 0x79AAC56CUL, 0x3CE4E1ADUL, 0x51F0C802UL,
|
||||
0x98F8F35AUL, 0x1626A49FUL, 0xEED82B29UL, 0x1D382FE3UL,
|
||||
0x0C4FB99AUL, 0xBB325778UL, 0x3EC6D97BUL, 0x6E77A6A9UL,
|
||||
0xCB658B5CUL, 0xD45230C7UL, 0x2BD1408BUL, 0x60C03EB7UL,
|
||||
0xB9068D78UL, 0xA33754F4UL, 0xF430C87DUL, 0xC8A71302UL,
|
||||
0xB96D8C32UL, 0xEBD4E7BEUL, 0xBE8B9D2DUL, 0x7979FB06UL,
|
||||
0xE7225308UL, 0x8B75CF77UL, 0x11EF8DA4UL, 0xE083C858UL,
|
||||
0x8D6B786FUL, 0x5A6317A6UL, 0xFA5CF7A0UL, 0x5DDA0033UL,
|
||||
0xF28EBFB0UL, 0xF5B9C310UL, 0xA0EAC280UL, 0x08B9767AUL,
|
||||
0xA3D9D2B0UL, 0x79D34217UL, 0x021A718DUL, 0x9AC6336AUL,
|
||||
0x2711FD60UL, 0x438050E3UL, 0x069908A8UL, 0x3D7FEDC4UL,
|
||||
0x826D2BEFUL, 0x4EEB8476UL, 0x488DCF25UL, 0x36C9D566UL,
|
||||
0x28E74E41UL, 0xC2610ACAUL, 0x3D49A9CFUL, 0xBAE3B9DFUL,
|
||||
0xB65F8DE6UL, 0x92AEAF64UL, 0x3AC7D5E6UL, 0x9EA80509UL,
|
||||
0xF22B017DUL, 0xA4173F70UL, 0xDD1E16C3UL, 0x15E0D7F9UL,
|
||||
0x50B1B887UL, 0x2B9F4FD5UL, 0x625ABA82UL, 0x6A017962UL,
|
||||
0x2EC01B9CUL, 0x15488AA9UL, 0xD716E740UL, 0x40055A2CUL,
|
||||
0x93D29A22UL, 0xE32DBF9AUL, 0x058745B9UL, 0x3453DC1EUL,
|
||||
0xD699296EUL, 0x496CFF6FUL, 0x1C9F4986UL, 0xDFE2ED07UL,
|
||||
0xB87242D1UL, 0x19DE7EAEUL, 0x053E561AUL, 0x15AD6F8CUL,
|
||||
0x66626C1CUL, 0x7154C24CUL, 0xEA082B2AUL, 0x93EB2939UL,
|
||||
0x17DCB0F0UL, 0x58D4F2AEUL, 0x9EA294FBUL, 0x52CF564CUL,
|
||||
0x9883FE66UL, 0x2EC40581UL, 0x763953C3UL, 0x01D6692EUL,
|
||||
0xD3A0C108UL, 0xA1E7160EUL, 0xE4F2DFA6UL, 0x693ED285UL,
|
||||
0x74904698UL, 0x4C2B0EDDUL, 0x4F757656UL, 0x5D393378UL,
|
||||
0xA132234FUL, 0x3D321C5DUL, 0xC3F5E194UL, 0x4B269301UL,
|
||||
0xC79F022FUL, 0x3C997E7EUL, 0x5E4F9504UL, 0x3FFAFBBDUL,
|
||||
0x76F7AD0EUL, 0x296693F4UL, 0x3D1FCE6FUL, 0xC61E45BEUL,
|
||||
0xD3B5AB34UL, 0xF72BF9B7UL, 0x1B0434C0UL, 0x4E72B567UL,
|
||||
0x5592A33DUL, 0xB5229301UL, 0xCFD2A87FUL, 0x60AEB767UL,
|
||||
0x1814386BUL, 0x30BCC33DUL, 0x38A0C07DUL, 0xFD1606F2UL,
|
||||
0xC363519BUL, 0x589DD390UL, 0x5479F8E6UL, 0x1CB8D647UL,
|
||||
0x97FD61A9UL, 0xEA7759F4UL, 0x2D57539DUL, 0x569A58CFUL,
|
||||
0xE84E63ADUL, 0x462E1B78UL, 0x6580F87EUL, 0xF3817914UL,
|
||||
0x91DA55F4UL, 0x40A230F3UL, 0xD1988F35UL, 0xB6E318D2UL,
|
||||
0x3FFA50BCUL, 0x3D40F021UL, 0xC3C0BDAEUL, 0x4958C24CUL,
|
||||
0x518F36B2UL, 0x84B1D370UL, 0x0FEDCE83UL, 0x878DDADAUL,
|
||||
0xF2A279C7UL, 0x94E01BE8UL, 0x90716F4BUL, 0x954B8AA3UL
|
||||
},
|
||||
|
||||
{
|
||||
0xE216300DUL, 0xBBDDFFFCUL, 0xA7EBDABDUL, 0x35648095UL,
|
||||
0x7789F8B7UL, 0xE6C1121BUL, 0x0E241600UL, 0x052CE8B5UL,
|
||||
0x11A9CFB0UL, 0xE5952F11UL, 0xECE7990AUL, 0x9386D174UL,
|
||||
0x2A42931CUL, 0x76E38111UL, 0xB12DEF3AUL, 0x37DDDDFCUL,
|
||||
0xDE9ADEB1UL, 0x0A0CC32CUL, 0xBE197029UL, 0x84A00940UL,
|
||||
0xBB243A0FUL, 0xB4D137CFUL, 0xB44E79F0UL, 0x049EEDFDUL,
|
||||
0x0B15A15DUL, 0x480D3168UL, 0x8BBBDE5AUL, 0x669DED42UL,
|
||||
0xC7ECE831UL, 0x3F8F95E7UL, 0x72DF191BUL, 0x7580330DUL,
|
||||
0x94074251UL, 0x5C7DCDFAUL, 0xABBE6D63UL, 0xAA402164UL,
|
||||
0xB301D40AUL, 0x02E7D1CAUL, 0x53571DAEUL, 0x7A3182A2UL,
|
||||
0x12A8DDECUL, 0xFDAA335DUL, 0x176F43E8UL, 0x71FB46D4UL,
|
||||
0x38129022UL, 0xCE949AD4UL, 0xB84769ADUL, 0x965BD862UL,
|
||||
0x82F3D055UL, 0x66FB9767UL, 0x15B80B4EUL, 0x1D5B47A0UL,
|
||||
0x4CFDE06FUL, 0xC28EC4B8UL, 0x57E8726EUL, 0x647A78FCUL,
|
||||
0x99865D44UL, 0x608BD593UL, 0x6C200E03UL, 0x39DC5FF6UL,
|
||||
0x5D0B00A3UL, 0xAE63AFF2UL, 0x7E8BD632UL, 0x70108C0CUL,
|
||||
0xBBD35049UL, 0x2998DF04UL, 0x980CF42AUL, 0x9B6DF491UL,
|
||||
0x9E7EDD53UL, 0x06918548UL, 0x58CB7E07UL, 0x3B74EF2EUL,
|
||||
0x522FFFB1UL, 0xD24708CCUL, 0x1C7E27CDUL, 0xA4EB215BUL,
|
||||
0x3CF1D2E2UL, 0x19B47A38UL, 0x424F7618UL, 0x35856039UL,
|
||||
0x9D17DEE7UL, 0x27EB35E6UL, 0xC9AFF67BUL, 0x36BAF5B8UL,
|
||||
0x09C467CDUL, 0xC18910B1UL, 0xE11DBF7BUL, 0x06CD1AF8UL,
|
||||
0x7170C608UL, 0x2D5E3354UL, 0xD4DE495AUL, 0x64C6D006UL,
|
||||
0xBCC0C62CUL, 0x3DD00DB3UL, 0x708F8F34UL, 0x77D51B42UL,
|
||||
0x264F620FUL, 0x24B8D2BFUL, 0x15C1B79EUL, 0x46A52564UL,
|
||||
0xF8D7E54EUL, 0x3E378160UL, 0x7895CDA5UL, 0x859C15A5UL,
|
||||
0xE6459788UL, 0xC37BC75FUL, 0xDB07BA0CUL, 0x0676A3ABUL,
|
||||
0x7F229B1EUL, 0x31842E7BUL, 0x24259FD7UL, 0xF8BEF472UL,
|
||||
0x835FFCB8UL, 0x6DF4C1F2UL, 0x96F5B195UL, 0xFD0AF0FCUL,
|
||||
0xB0FE134CUL, 0xE2506D3DUL, 0x4F9B12EAUL, 0xF215F225UL,
|
||||
0xA223736FUL, 0x9FB4C428UL, 0x25D04979UL, 0x34C713F8UL,
|
||||
0xC4618187UL, 0xEA7A6E98UL, 0x7CD16EFCUL, 0x1436876CUL,
|
||||
0xF1544107UL, 0xBEDEEE14UL, 0x56E9AF27UL, 0xA04AA441UL,
|
||||
0x3CF7C899UL, 0x92ECBAE6UL, 0xDD67016DUL, 0x151682EBUL,
|
||||
0xA842EEDFUL, 0xFDBA60B4UL, 0xF1907B75UL, 0x20E3030FUL,
|
||||
0x24D8C29EUL, 0xE139673BUL, 0xEFA63FB8UL, 0x71873054UL,
|
||||
0xB6F2CF3BUL, 0x9F326442UL, 0xCB15A4CCUL, 0xB01A4504UL,
|
||||
0xF1E47D8DUL, 0x844A1BE5UL, 0xBAE7DFDCUL, 0x42CBDA70UL,
|
||||
0xCD7DAE0AUL, 0x57E85B7AUL, 0xD53F5AF6UL, 0x20CF4D8CUL,
|
||||
0xCEA4D428UL, 0x79D130A4UL, 0x3486EBFBUL, 0x33D3CDDCUL,
|
||||
0x77853B53UL, 0x37EFFCB5UL, 0xC5068778UL, 0xE580B3E6UL,
|
||||
0x4E68B8F4UL, 0xC5C8B37EUL, 0x0D809EA2UL, 0x398FEB7CUL,
|
||||
0x132A4F94UL, 0x43B7950EUL, 0x2FEE7D1CUL, 0x223613BDUL,
|
||||
0xDD06CAA2UL, 0x37DF932BUL, 0xC4248289UL, 0xACF3EBC3UL,
|
||||
0x5715F6B7UL, 0xEF3478DDUL, 0xF267616FUL, 0xC148CBE4UL,
|
||||
0x9052815EUL, 0x5E410FABUL, 0xB48A2465UL, 0x2EDA7FA4UL,
|
||||
0xE87B40E4UL, 0xE98EA084UL, 0x5889E9E1UL, 0xEFD390FCUL,
|
||||
0xDD07D35BUL, 0xDB485694UL, 0x38D7E5B2UL, 0x57720101UL,
|
||||
0x730EDEBCUL, 0x5B643113UL, 0x94917E4FUL, 0x503C2FBAUL,
|
||||
0x646F1282UL, 0x7523D24AUL, 0xE0779695UL, 0xF9C17A8FUL,
|
||||
0x7A5B2121UL, 0xD187B896UL, 0x29263A4DUL, 0xBA510CDFUL,
|
||||
0x81F47C9FUL, 0xAD1163EDUL, 0xEA7B5965UL, 0x1A00726EUL,
|
||||
0x11403092UL, 0x00DA6D77UL, 0x4A0CDD61UL, 0xAD1F4603UL,
|
||||
0x605BDFB0UL, 0x9EEDC364UL, 0x22EBE6A8UL, 0xCEE7D28AUL,
|
||||
0xA0E736A0UL, 0x5564A6B9UL, 0x10853209UL, 0xC7EB8F37UL,
|
||||
0x2DE705CAUL, 0x8951570FUL, 0xDF09822BUL, 0xBD691A6CUL,
|
||||
0xAA12E4F2UL, 0x87451C0FUL, 0xE0F6A27AUL, 0x3ADA4819UL,
|
||||
0x4CF1764FUL, 0x0D771C2BUL, 0x67CDB156UL, 0x350D8384UL,
|
||||
0x5938FA0FUL, 0x42399EF3UL, 0x36997B07UL, 0x0E84093DUL,
|
||||
0x4AA93E61UL, 0x8360D87BUL, 0x1FA98B0CUL, 0x1149382CUL,
|
||||
0xE97625A5UL, 0x0614D1B7UL, 0x0E25244BUL, 0x0C768347UL,
|
||||
0x589E8D82UL, 0x0D2059D1UL, 0xA466BB1EUL, 0xF8DA0A82UL,
|
||||
0x04F19130UL, 0xBA6E4EC0UL, 0x99265164UL, 0x1EE7230DUL,
|
||||
0x50B2AD80UL, 0xEAEE6801UL, 0x8DB2A283UL, 0xEA8BF59EUL
|
||||
}};
|
||||
|
||||
NAMESPACE_END
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#include "pch.h"
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "cbcmac.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void CBC_MAC_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms)
|
||||
{
|
||||
AccessCipher().SetKey(key, length, params);
|
||||
m_reg.CleanNew(AccessCipher().BlockSize());
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
void CBC_MAC_Base::Update(const byte *input, size_t length)
|
||||
{
|
||||
unsigned int blockSize = AccessCipher().BlockSize();
|
||||
|
||||
while (m_counter && length)
|
||||
{
|
||||
m_reg[m_counter++] ^= *input++;
|
||||
if (m_counter == blockSize)
|
||||
ProcessBuf();
|
||||
length--;
|
||||
}
|
||||
|
||||
if (length >= blockSize)
|
||||
{
|
||||
size_t leftOver = AccessCipher().AdvancedProcessBlocks(m_reg, input, m_reg, length, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput);
|
||||
input += (length - leftOver);
|
||||
length = leftOver;
|
||||
}
|
||||
|
||||
while (length--)
|
||||
{
|
||||
m_reg[m_counter++] ^= *input++;
|
||||
if (m_counter == blockSize)
|
||||
ProcessBuf();
|
||||
}
|
||||
}
|
||||
|
||||
void CBC_MAC_Base::TruncatedFinal(byte *mac, size_t size)
|
||||
{
|
||||
ThrowIfInvalidTruncatedSize(size);
|
||||
|
||||
if (m_counter)
|
||||
ProcessBuf();
|
||||
|
||||
memcpy(mac, m_reg, size);
|
||||
memset(m_reg, 0, AccessCipher().BlockSize());
|
||||
}
|
||||
|
||||
void CBC_MAC_Base::ProcessBuf()
|
||||
{
|
||||
AccessCipher().ProcessBlock(m_reg);
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// cbcmac.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file
|
||||
/// \brief Classes for CBC MAC
|
||||
/// \since Crypto++ 3.1
|
||||
|
||||
#ifndef CRYPTOPP_CBCMAC_H
|
||||
#define CRYPTOPP_CBCMAC_H
|
||||
|
||||
#include "seckey.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief CBC-MAC base class
|
||||
/// \since Crypto++ 3.1
|
||||
class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CBC_MAC_Base : public MessageAuthenticationCode
|
||||
{
|
||||
public:
|
||||
CBC_MAC_Base() : m_counter(0) {}
|
||||
|
||||
void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms);
|
||||
void Update(const byte *input, size_t length);
|
||||
void TruncatedFinal(byte *mac, size_t size);
|
||||
unsigned int DigestSize() const {return const_cast<CBC_MAC_Base*>(this)->AccessCipher().BlockSize();}
|
||||
|
||||
protected:
|
||||
virtual BlockCipher & AccessCipher() =0;
|
||||
|
||||
private:
|
||||
void ProcessBuf();
|
||||
SecByteBlock m_reg;
|
||||
unsigned int m_counter;
|
||||
};
|
||||
|
||||
/// \brief CBC-MAC
|
||||
/// \tparam T BlockCipherDocumentation derived class
|
||||
/// \details CBC-MAC is compatible with FIPS 113. The MAC is secure only for fixed
|
||||
/// length messages. For variable length messages use CMAC or DMAC.
|
||||
/// \sa <a href="http://www.weidai.com/scan-mirror/mac.html#CBC-MAC">CBC-MAC</a>
|
||||
/// \since Crypto++ 3.1
|
||||
template <class T>
|
||||
class CBC_MAC : public MessageAuthenticationCodeImpl<CBC_MAC_Base, CBC_MAC<T> >, public SameKeyLengthAs<T>
|
||||
{
|
||||
public:
|
||||
CBC_MAC() {}
|
||||
CBC_MAC(const byte *key, size_t length=SameKeyLengthAs<T>::DEFAULT_KEYLENGTH)
|
||||
{this->SetKey(key, length);}
|
||||
|
||||
static std::string StaticAlgorithmName() {return std::string("CBC-MAC(") + T::StaticAlgorithmName() + ")";}
|
||||
|
||||
private:
|
||||
BlockCipher & AccessCipher() {return m_cipher;}
|
||||
typename T::Encryption m_cipher;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
// ccm.cpp - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#ifndef CRYPTOPP_IMPORTS
|
||||
|
||||
#include "ccm.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
void CCM_Base::SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms)
|
||||
{
|
||||
BlockCipher &blockCipher = AccessBlockCipher();
|
||||
blockCipher.SetKey(userKey, keylength, params);
|
||||
|
||||
if (blockCipher.BlockSize() != REQUIRED_BLOCKSIZE)
|
||||
throw InvalidArgument(AlgorithmName() + ": block size of underlying block cipher is not 16");
|
||||
|
||||
m_digestSize = params.GetIntValueWithDefault(Name::DigestSize(), DefaultDigestSize());
|
||||
if (m_digestSize % 2 > 0 || m_digestSize < 4 || m_digestSize > 16)
|
||||
throw InvalidArgument(AlgorithmName() + ": DigestSize must be 4, 6, 8, 10, 12, 14, or 16");
|
||||
|
||||
m_buffer.Grow(2*REQUIRED_BLOCKSIZE);
|
||||
m_L = 8;
|
||||
}
|
||||
|
||||
void CCM_Base::Resync(const byte *iv, size_t len)
|
||||
{
|
||||
BlockCipher &cipher = AccessBlockCipher();
|
||||
|
||||
m_L = REQUIRED_BLOCKSIZE-1-(int)len;
|
||||
CRYPTOPP_ASSERT(m_L >= 2);
|
||||
if (m_L > 8)
|
||||
m_L = 8;
|
||||
|
||||
m_buffer[0] = byte(m_L-1); // flag
|
||||
memcpy(m_buffer+1, iv, len);
|
||||
memset(m_buffer+1+len, 0, REQUIRED_BLOCKSIZE-1-len);
|
||||
|
||||
if (m_state >= State_IVSet)
|
||||
m_ctr.Resynchronize(m_buffer, REQUIRED_BLOCKSIZE);
|
||||
else
|
||||
m_ctr.SetCipherWithIV(cipher, m_buffer);
|
||||
|
||||
m_ctr.Seek(REQUIRED_BLOCKSIZE);
|
||||
m_aadLength = 0;
|
||||
m_messageLength = 0;
|
||||
}
|
||||
|
||||
void CCM_Base::UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword /*footerLength*/)
|
||||
{
|
||||
if (m_state != State_IVSet)
|
||||
throw BadState(AlgorithmName(), "SpecifyDataLengths", "or after State_IVSet");
|
||||
|
||||
m_aadLength = headerLength;
|
||||
m_messageLength = messageLength;
|
||||
|
||||
byte *cbcBuffer = CBC_Buffer();
|
||||
const BlockCipher &cipher = GetBlockCipher();
|
||||
|
||||
cbcBuffer[0] = byte(64*(headerLength>0) + 8*((m_digestSize-2)/2) + (m_L-1)); // flag
|
||||
PutWord<word64>(true, BIG_ENDIAN_ORDER, cbcBuffer+REQUIRED_BLOCKSIZE-8, m_messageLength);
|
||||
memcpy(cbcBuffer+1, m_buffer+1, REQUIRED_BLOCKSIZE-1-m_L);
|
||||
cipher.ProcessBlock(cbcBuffer);
|
||||
|
||||
if (headerLength>0)
|
||||
{
|
||||
CRYPTOPP_ASSERT(m_bufferedDataLength == 0);
|
||||
|
||||
if (headerLength < ((1<<16) - (1<<8)))
|
||||
{
|
||||
PutWord<word16>(true, BIG_ENDIAN_ORDER, m_buffer, (word16)headerLength);
|
||||
m_bufferedDataLength = 2;
|
||||
}
|
||||
else if (headerLength < (W64LIT(1)<<32))
|
||||
{
|
||||
m_buffer[0] = 0xff;
|
||||
m_buffer[1] = 0xfe;
|
||||
PutWord<word32>(false, BIG_ENDIAN_ORDER, m_buffer+2, (word32)headerLength);
|
||||
m_bufferedDataLength = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buffer[0] = 0xff;
|
||||
m_buffer[1] = 0xff;
|
||||
PutWord<word64>(false, BIG_ENDIAN_ORDER, m_buffer+2, headerLength);
|
||||
m_bufferedDataLength = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t CCM_Base::AuthenticateBlocks(const byte *data, size_t len)
|
||||
{
|
||||
byte *cbcBuffer = CBC_Buffer();
|
||||
const BlockCipher &cipher = GetBlockCipher();
|
||||
return cipher.AdvancedProcessBlocks(cbcBuffer, data, cbcBuffer, len, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput);
|
||||
}
|
||||
|
||||
void CCM_Base::AuthenticateLastHeaderBlock()
|
||||
{
|
||||
byte *cbcBuffer = CBC_Buffer();
|
||||
const BlockCipher &cipher = GetBlockCipher();
|
||||
|
||||
if (m_aadLength != m_totalHeaderLength)
|
||||
throw InvalidArgument(AlgorithmName() + ": header length doesn't match that given in SpecifyDataLengths");
|
||||
|
||||
if (m_bufferedDataLength > 0)
|
||||
{
|
||||
xorbuf(cbcBuffer, m_buffer, m_bufferedDataLength);
|
||||
cipher.ProcessBlock(cbcBuffer);
|
||||
m_bufferedDataLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CCM_Base::AuthenticateLastConfidentialBlock()
|
||||
{
|
||||
byte *cbcBuffer = CBC_Buffer();
|
||||
const BlockCipher &cipher = GetBlockCipher();
|
||||
|
||||
if (m_messageLength != m_totalMessageLength)
|
||||
throw InvalidArgument(AlgorithmName() + ": message length doesn't match that given in SpecifyDataLengths");
|
||||
|
||||
if (m_bufferedDataLength > 0)
|
||||
{
|
||||
xorbuf(cbcBuffer, m_buffer, m_bufferedDataLength);
|
||||
cipher.ProcessBlock(cbcBuffer);
|
||||
m_bufferedDataLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CCM_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
|
||||
{
|
||||
m_ctr.Seek(0);
|
||||
m_ctr.ProcessData(mac, CBC_Buffer(), macSize);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
// ccm.h - originally written and placed in the public domain by Wei Dai
|
||||
|
||||
/// \file ccm.h
|
||||
/// \brief CCM block cipher mode of operation
|
||||
/// \since Crypto++ 5.6.0
|
||||
|
||||
#ifndef CRYPTOPP_CCM_H
|
||||
#define CRYPTOPP_CCM_H
|
||||
|
||||
#include "authenc.h"
|
||||
#include "modes.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
/// \brief CCM block cipher base implementation
|
||||
/// \details Base implementation of the AuthenticatedSymmetricCipher interface
|
||||
/// \since Crypto++ 5.6.0
|
||||
class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CCM_Base : public AuthenticatedSymmetricCipherBase
|
||||
{
|
||||
public:
|
||||
CCM_Base()
|
||||
: m_digestSize(0), m_L(0), m_messageLength(0), m_aadLength(0) {}
|
||||
|
||||
// AuthenticatedSymmetricCipher
|
||||
std::string AlgorithmName() const
|
||||
{return GetBlockCipher().AlgorithmName() + std::string("/CCM");}
|
||||
std::string AlgorithmProvider() const
|
||||
{return GetBlockCipher().AlgorithmProvider();}
|
||||
size_t MinKeyLength() const
|
||||
{return GetBlockCipher().MinKeyLength();}
|
||||
size_t MaxKeyLength() const
|
||||
{return GetBlockCipher().MaxKeyLength();}
|
||||
size_t DefaultKeyLength() const
|
||||
{return GetBlockCipher().DefaultKeyLength();}
|
||||
size_t GetValidKeyLength(size_t keylength) const
|
||||
{return GetBlockCipher().GetValidKeyLength(keylength);}
|
||||
bool IsValidKeyLength(size_t keylength) const
|
||||
{return GetBlockCipher().IsValidKeyLength(keylength);}
|
||||
unsigned int OptimalDataAlignment() const
|
||||
{return GetBlockCipher().OptimalDataAlignment();}
|
||||
IV_Requirement IVRequirement() const
|
||||
{return UNIQUE_IV;}
|
||||
unsigned int IVSize() const
|
||||
{return 8;}
|
||||
unsigned int MinIVLength() const
|
||||
{return 7;}
|
||||
unsigned int MaxIVLength() const
|
||||
{return 13;}
|
||||
unsigned int DigestSize() const
|
||||
{return m_digestSize;}
|
||||
lword MaxHeaderLength() const
|
||||
{return W64LIT(0)-1;}
|
||||
lword MaxMessageLength() const
|
||||
{return m_L<8 ? (W64LIT(1)<<(8*m_L))-1 : W64LIT(0)-1;}
|
||||
bool NeedsPrespecifiedDataLengths() const
|
||||
{return true;}
|
||||
void UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength);
|
||||
|
||||
protected:
|
||||
// AuthenticatedSymmetricCipherBase
|
||||
bool AuthenticationIsOnPlaintext() const
|
||||
{return true;}
|
||||
unsigned int AuthenticationBlockSize() const
|
||||
{return GetBlockCipher().BlockSize();}
|
||||
void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms);
|
||||
void Resync(const byte *iv, size_t len);
|
||||
size_t AuthenticateBlocks(const byte *data, size_t len);
|
||||
void AuthenticateLastHeaderBlock();
|
||||
void AuthenticateLastConfidentialBlock();
|
||||
void AuthenticateLastFooterBlock(byte *mac, size_t macSize);
|
||||
SymmetricCipher & AccessSymmetricCipher() {return m_ctr;}
|
||||
|
||||
virtual BlockCipher & AccessBlockCipher() =0;
|
||||
virtual int DefaultDigestSize() const =0;
|
||||
|
||||
const BlockCipher & GetBlockCipher() const {return const_cast<CCM_Base *>(this)->AccessBlockCipher();}
|
||||
byte *CBC_Buffer() {return m_buffer+REQUIRED_BLOCKSIZE;}
|
||||
|
||||
enum {REQUIRED_BLOCKSIZE = 16};
|
||||
int m_digestSize, m_L;
|
||||
word64 m_messageLength, m_aadLength;
|
||||
CTR_Mode_ExternalCipher::Encryption m_ctr;
|
||||
};
|
||||
|
||||
/// \brief CCM block cipher final implementation
|
||||
/// \tparam T_BlockCipher block cipher
|
||||
/// \tparam T_DefaultDigestSize default digest size, in bytes
|
||||
/// \tparam T_IsEncryption direction in which to operate the cipher
|
||||
/// \since Crypto++ 5.6.0
|
||||
template <class T_BlockCipher, int T_DefaultDigestSize, bool T_IsEncryption>
|
||||
class CCM_Final : public CCM_Base
|
||||
{
|
||||
public:
|
||||
static std::string StaticAlgorithmName()
|
||||
{return T_BlockCipher::StaticAlgorithmName() + std::string("/CCM");}
|
||||
bool IsForwardTransformation() const
|
||||
{return T_IsEncryption;}
|
||||
|
||||
private:
|
||||
BlockCipher & AccessBlockCipher() {return m_cipher;}
|
||||
int DefaultDigestSize() const {return T_DefaultDigestSize;}
|
||||
typename T_BlockCipher::Encryption m_cipher;
|
||||
};
|
||||
|
||||
/// \brief CCM block cipher mode of operation
|
||||
/// \tparam T_BlockCipher block cipher
|
||||
/// \tparam T_DefaultDigestSize default digest size, in bytes
|
||||
/// \details \p CCM provides the \p Encryption and \p Decryption typedef. See GCM_Base
|
||||
/// and GCM_Final for the AuthenticatedSymmetricCipher implementation.
|
||||
/// \sa <a href="http://www.cryptopp.com/wiki/CCM_Mode">CCM Mode</a> and
|
||||
/// <A HREF="http://www.cryptopp.com/wiki/Modes_of_Operation">Modes of Operation</A>
|
||||
/// on the Crypto++ wiki.
|
||||
/// \since Crypto++ 5.6.0
|
||||
template <class T_BlockCipher, int T_DefaultDigestSize = 16>
|
||||
struct CCM : public AuthenticatedSymmetricCipherDocumentation
|
||||
{
|
||||
typedef CCM_Final<T_BlockCipher, T_DefaultDigestSize, true> Encryption;
|
||||
typedef CCM_Final<T_BlockCipher, T_DefaultDigestSize, false> Decryption;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif
|
||||
+568
@@ -0,0 +1,568 @@
|
||||
// chacha.cpp - written and placed in the public domain by Jeffrey Walton.
|
||||
// Based on Wei Dai's Salsa20, Botan's SSE2 implementation,
|
||||
// and Bernstein's reference ChaCha family implementation at
|
||||
// http://cr.yp.to/chacha.html.
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
#include "chacha.h"
|
||||
#include "argnames.h"
|
||||
#include "misc.h"
|
||||
#include "cpu.h"
|
||||
|
||||
// Internal compiler error in GCC 3.3 and below
|
||||
#if defined(__GNUC__) && (__GNUC__ < 4)
|
||||
# undef CRYPTOPP_SSE2_INTRIN_AVAILABLE
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
extern void ChaCha_OperateKeystream_NEON(const word32 *state, const byte* input, byte *output, unsigned int rounds);
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
extern void ChaCha_OperateKeystream_AVX2(const word32 *state, const byte* input, byte *output, unsigned int rounds);
|
||||
#endif
|
||||
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
|
||||
extern void ChaCha_OperateKeystream_SSE2(const word32 *state, const byte* input, byte *output, unsigned int rounds);
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
extern void ChaCha_OperateKeystream_ALTIVEC(const word32 *state, const byte* input, byte *output, unsigned int rounds);
|
||||
#endif
|
||||
|
||||
#if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
|
||||
void ChaCha_TestInstantiations()
|
||||
{
|
||||
ChaCha::Encryption x;
|
||||
ChaChaTLS::Encryption y;
|
||||
XChaCha20::Encryption z;
|
||||
}
|
||||
#endif
|
||||
|
||||
NAMESPACE_END // CryptoPP
|
||||
|
||||
////////////////////////////// ChaCha Core //////////////////////////////
|
||||
|
||||
#define CHACHA_QUARTER_ROUND(a,b,c,d) \
|
||||
a += b; d ^= a; d = rotlConstant<16,word32>(d); \
|
||||
c += d; b ^= c; b = rotlConstant<12,word32>(b); \
|
||||
a += b; d ^= a; d = rotlConstant<8,word32>(d); \
|
||||
c += d; b ^= c; b = rotlConstant<7,word32>(b);
|
||||
|
||||
#define CHACHA_OUTPUT(x){\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 0, x0 + state[0]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 1, x1 + state[1]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 2, x2 + state[2]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 3, x3 + state[3]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 4, x4 + state[4]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 5, x5 + state[5]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 6, x6 + state[6]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 7, x7 + state[7]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 8, x8 + state[8]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 9, x9 + state[9]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 10, x10 + state[10]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 11, x11 + state[11]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 12, x12 + state[12]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 13, x13 + state[13]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 14, x14 + state[14]);\
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 15, x15 + state[15]);}
|
||||
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
// Hacks... Bring in all symbols, and supply
|
||||
// the stuff the templates normally provide.
|
||||
using namespace CryptoPP;
|
||||
typedef word32 WordType;
|
||||
enum {BYTES_PER_ITERATION=64};
|
||||
|
||||
// MultiBlockSafe detects a condition that can arise in the SIMD
|
||||
// implementations where we overflow one of the 32-bit state words during
|
||||
// addition in an intermediate result. Preconditions for the issue include
|
||||
// a user seeks to around 2^32 blocks (256 GB of data) for ChaCha; or a
|
||||
// user specifies an arbitrarily large initial counter block for ChaChaTLS.
|
||||
// Also see https://github.com/weidai11/cryptopp/issues/732.
|
||||
inline bool MultiBlockSafe(unsigned int ctrLow, unsigned int blocks)
|
||||
{
|
||||
return 0xffffffff - ctrLow > blocks;
|
||||
}
|
||||
|
||||
// OperateKeystream always produces a key stream. The key stream is written
|
||||
// to output. Optionally a message may be supplied to xor with the key stream.
|
||||
// The message is input, and output = output ^ input.
|
||||
void ChaCha_OperateKeystream(KeystreamOperation operation,
|
||||
word32 state[16], word32& ctrLow, word32& ctrHigh, word32 rounds,
|
||||
byte *output, const byte *input, size_t iterationCount)
|
||||
{
|
||||
do
|
||||
{
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
if (HasAVX2())
|
||||
{
|
||||
while (iterationCount >= 8 && MultiBlockSafe(state[12], 8))
|
||||
{
|
||||
const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
|
||||
ChaCha_OperateKeystream_AVX2(state, xorInput ? input : NULLPTR, output, rounds);
|
||||
|
||||
// MultiBlockSafe avoids overflow on the counter words
|
||||
state[12] += 8;
|
||||
|
||||
input += (!!xorInput) * 8 * BYTES_PER_ITERATION;
|
||||
output += 8 * BYTES_PER_ITERATION;
|
||||
iterationCount -= 8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
|
||||
if (HasSSE2())
|
||||
{
|
||||
while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
|
||||
{
|
||||
const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
|
||||
ChaCha_OperateKeystream_SSE2(state, xorInput ? input : NULLPTR, output, rounds);
|
||||
|
||||
// MultiBlockSafe avoids overflow on the counter words
|
||||
state[12] += 4;
|
||||
|
||||
input += (!!xorInput)*4*BYTES_PER_ITERATION;
|
||||
output += 4*BYTES_PER_ITERATION;
|
||||
iterationCount -= 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
{
|
||||
while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
|
||||
{
|
||||
const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
|
||||
ChaCha_OperateKeystream_NEON(state, xorInput ? input : NULLPTR, output, rounds);
|
||||
|
||||
// MultiBlockSafe avoids overflow on the counter words
|
||||
state[12] += 4;
|
||||
|
||||
input += (!!xorInput)*4*BYTES_PER_ITERATION;
|
||||
output += 4*BYTES_PER_ITERATION;
|
||||
iterationCount -= 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
if (HasAltivec())
|
||||
{
|
||||
while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
|
||||
{
|
||||
const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
|
||||
ChaCha_OperateKeystream_ALTIVEC(state, xorInput ? input : NULLPTR, output, rounds);
|
||||
|
||||
// MultiBlockSafe avoids overflow on the counter words
|
||||
state[12] += 4;
|
||||
|
||||
input += (!!xorInput)*4*BYTES_PER_ITERATION;
|
||||
output += 4*BYTES_PER_ITERATION;
|
||||
iterationCount -= 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (iterationCount)
|
||||
{
|
||||
word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||
|
||||
x0 = state[0]; x1 = state[1]; x2 = state[2]; x3 = state[3];
|
||||
x4 = state[4]; x5 = state[5]; x6 = state[6]; x7 = state[7];
|
||||
x8 = state[8]; x9 = state[9]; x10 = state[10]; x11 = state[11];
|
||||
x12 = state[12]; x13 = state[13]; x14 = state[14]; x15 = state[15];
|
||||
|
||||
for (int i = static_cast<int>(rounds); i > 0; i -= 2)
|
||||
{
|
||||
CHACHA_QUARTER_ROUND(x0, x4, x8, x12);
|
||||
CHACHA_QUARTER_ROUND(x1, x5, x9, x13);
|
||||
CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
|
||||
CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
|
||||
|
||||
CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
|
||||
CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
|
||||
CHACHA_QUARTER_ROUND(x2, x7, x8, x13);
|
||||
CHACHA_QUARTER_ROUND(x3, x4, x9, x14);
|
||||
}
|
||||
|
||||
CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(CHACHA_OUTPUT, BYTES_PER_ITERATION);
|
||||
|
||||
// This is state[12] and state[13] from ChaCha. In the case of
|
||||
// ChaChaTLS ctrHigh is a reference to a discard value.
|
||||
if (++ctrLow == 0)
|
||||
ctrHigh++;
|
||||
}
|
||||
|
||||
// We may re-enter a SIMD keystream operation from here.
|
||||
} while (iterationCount--);
|
||||
}
|
||||
|
||||
// XChaCha key derivation
|
||||
void HChaCha_OperateKeystream(const word32 state[16], word32 output[8])
|
||||
{
|
||||
word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||
|
||||
x0 = state[0]; x1 = state[1]; x2 = state[2]; x3 = state[3];
|
||||
x4 = state[4]; x5 = state[5]; x6 = state[6]; x7 = state[7];
|
||||
x8 = state[8]; x9 = state[9]; x10 = state[10]; x11 = state[11];
|
||||
x12 = state[12]; x13 = state[13]; x14 = state[14]; x15 = state[15];
|
||||
|
||||
for (int i = 20; i > 0; i -= 2)
|
||||
{
|
||||
CHACHA_QUARTER_ROUND(x0, x4, x8, x12);
|
||||
CHACHA_QUARTER_ROUND(x1, x5, x9, x13);
|
||||
CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
|
||||
CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
|
||||
|
||||
CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
|
||||
CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
|
||||
CHACHA_QUARTER_ROUND(x2, x7, x8, x13);
|
||||
CHACHA_QUARTER_ROUND(x3, x4, x9, x14);
|
||||
}
|
||||
|
||||
output[0] = x0; output[1] = x1;
|
||||
output[2] = x2; output[3] = x3;
|
||||
output[4] = x12; output[5] = x13;
|
||||
output[6] = x14; output[7] = x15;
|
||||
}
|
||||
|
||||
std::string ChaCha_AlgorithmProvider()
|
||||
{
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
if (HasAVX2())
|
||||
return "AVX2";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
|
||||
if (HasSSE2())
|
||||
return "SSE2";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
return "NEON";
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
if (HasAltivec())
|
||||
return "Altivec";
|
||||
else
|
||||
#endif
|
||||
return "C++";
|
||||
}
|
||||
|
||||
unsigned int ChaCha_GetAlignment()
|
||||
{
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
if (HasAVX2())
|
||||
return 16;
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
|
||||
if (HasSSE2())
|
||||
return 16;
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
if (HasAltivec())
|
||||
return 16;
|
||||
else
|
||||
#endif
|
||||
return GetAlignmentOf<word32>();
|
||||
}
|
||||
|
||||
unsigned int ChaCha_GetOptimalBlockSize()
|
||||
{
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
if (HasAVX2())
|
||||
return 8 * BYTES_PER_ITERATION;
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
|
||||
if (HasSSE2())
|
||||
return 4*BYTES_PER_ITERATION;
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ARM_NEON_AVAILABLE)
|
||||
if (HasNEON())
|
||||
return 4*BYTES_PER_ITERATION;
|
||||
else
|
||||
#endif
|
||||
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
|
||||
if (HasAltivec())
|
||||
return 4*BYTES_PER_ITERATION;
|
||||
else
|
||||
#endif
|
||||
return BYTES_PER_ITERATION;
|
||||
}
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
////////////////////////////// Bernstein ChaCha //////////////////////////////
|
||||
|
||||
std::string ChaCha_Policy::AlgorithmName() const
|
||||
{
|
||||
return std::string("ChaCha")+IntToString(m_rounds);
|
||||
}
|
||||
|
||||
std::string ChaCha_Policy::AlgorithmProvider() const
|
||||
{
|
||||
return ChaCha_AlgorithmProvider();
|
||||
}
|
||||
|
||||
void ChaCha_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 16 || length == 32);
|
||||
CRYPTOPP_UNUSED(key); CRYPTOPP_UNUSED(length);
|
||||
|
||||
// Use previous rounds as the default value
|
||||
int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);
|
||||
if (rounds != 20 && rounds != 12 && rounds != 8)
|
||||
throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);
|
||||
|
||||
// Latch a good value
|
||||
m_rounds = rounds;
|
||||
|
||||
// "expand 16-byte k" or "expand 32-byte k"
|
||||
m_state[0] = 0x61707865;
|
||||
m_state[1] = (length == 16) ? 0x3120646e : 0x3320646e;
|
||||
m_state[2] = (length == 16) ? 0x79622d36 : 0x79622d32;
|
||||
m_state[3] = 0x6b206574;
|
||||
|
||||
GetBlock<word32, LittleEndian> get1(key);
|
||||
get1(m_state[4])(m_state[5])(m_state[6])(m_state[7]);
|
||||
|
||||
GetBlock<word32, LittleEndian> get2(key + ((length == 32) ? 16 : 0));
|
||||
get2(m_state[8])(m_state[9])(m_state[10])(m_state[11]);
|
||||
}
|
||||
|
||||
void ChaCha_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
|
||||
{
|
||||
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
|
||||
CRYPTOPP_ASSERT(length==8); CRYPTOPP_UNUSED(length);
|
||||
|
||||
GetBlock<word32, LittleEndian> get(IV);
|
||||
m_state[12] = m_state[13] = 0;
|
||||
get(m_state[14])(m_state[15]);
|
||||
}
|
||||
|
||||
void ChaCha_Policy::SeekToIteration(lword iterationCount)
|
||||
{
|
||||
m_state[12] = (word32)iterationCount; // low word
|
||||
m_state[13] = (word32)SafeRightShift<32>(iterationCount);
|
||||
}
|
||||
|
||||
unsigned int ChaCha_Policy::GetAlignment() const
|
||||
{
|
||||
return ChaCha_GetAlignment();
|
||||
}
|
||||
|
||||
unsigned int ChaCha_Policy::GetOptimalBlockSize() const
|
||||
{
|
||||
return ChaCha_GetOptimalBlockSize();
|
||||
}
|
||||
|
||||
void ChaCha_Policy::OperateKeystream(KeystreamOperation operation,
|
||||
byte *output, const byte *input, size_t iterationCount)
|
||||
{
|
||||
ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
|
||||
m_rounds, output, input, iterationCount);
|
||||
}
|
||||
|
||||
////////////////////////////// IETF ChaChaTLS //////////////////////////////
|
||||
|
||||
std::string ChaChaTLS_Policy::AlgorithmName() const
|
||||
{
|
||||
return std::string("ChaChaTLS");
|
||||
}
|
||||
|
||||
std::string ChaChaTLS_Policy::AlgorithmProvider() const
|
||||
{
|
||||
return ChaCha_AlgorithmProvider();
|
||||
}
|
||||
|
||||
void ChaChaTLS_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
|
||||
CRYPTOPP_UNUSED(length);
|
||||
|
||||
// ChaChaTLS is always 20 rounds. Fetch Rounds() to avoid a spurious failure.
|
||||
int rounds = params.GetIntValueWithDefault(Name::Rounds(), ROUNDS);
|
||||
if (rounds != 20)
|
||||
throw InvalidRounds(ChaChaTLS::StaticAlgorithmName(), rounds);
|
||||
|
||||
// RFC 8439 test vectors use an initial block counter. However, the counter
|
||||
// can be an arbitrary value per RFC 8439 Section 2.4. We stash the counter
|
||||
// away in state[16] and use it for a Resynchronize() operation. I think
|
||||
// the initial counter is used more like a Tweak when non-0, and it should
|
||||
// be provided in Resynchronize() (light-weight re-keying). However,
|
||||
// Resynchronize() does not have an overload that allows us to pass it into
|
||||
// the function, so we have to use the heavier-weight SetKey to change it.
|
||||
word64 block;
|
||||
if (params.GetValue("InitialBlock", block))
|
||||
m_counter = static_cast<word32>(block);
|
||||
else
|
||||
m_counter = 0;
|
||||
|
||||
// State words are defined in RFC 8439, Section 2.3. Key is 32-bytes.
|
||||
GetBlock<word32, LittleEndian> get(key);
|
||||
get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
|
||||
(m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
|
||||
}
|
||||
|
||||
void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
|
||||
{
|
||||
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
|
||||
CRYPTOPP_ASSERT(length==12);
|
||||
|
||||
// State words are defined in RFC 8439, Section 2.3.
|
||||
m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
|
||||
m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
|
||||
|
||||
// Copy saved key into state
|
||||
std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
|
||||
|
||||
// State words are defined in RFC 8439, Section 2.3
|
||||
GetBlock<word32, LittleEndian> get(IV);
|
||||
m_state[12] = m_counter;
|
||||
get(m_state[13])(m_state[14])(m_state[15]);
|
||||
}
|
||||
|
||||
void ChaChaTLS_Policy::SeekToIteration(lword iterationCount)
|
||||
{
|
||||
// Should we throw here??? If the initial block counter is
|
||||
// large then we can wrap and process more data as long as
|
||||
// data processed in the security context does not exceed
|
||||
// 2^32 blocks or approximately 256 GB of data.
|
||||
CRYPTOPP_ASSERT(iterationCount <= std::numeric_limits<word32>::max());
|
||||
m_state[12] = (word32)iterationCount; // low word
|
||||
}
|
||||
|
||||
unsigned int ChaChaTLS_Policy::GetAlignment() const
|
||||
{
|
||||
return ChaCha_GetAlignment();
|
||||
}
|
||||
|
||||
unsigned int ChaChaTLS_Policy::GetOptimalBlockSize() const
|
||||
{
|
||||
return ChaCha_GetOptimalBlockSize();
|
||||
}
|
||||
|
||||
void ChaChaTLS_Policy::OperateKeystream(KeystreamOperation operation,
|
||||
byte *output, const byte *input, size_t iterationCount)
|
||||
{
|
||||
word32 discard=0;
|
||||
ChaCha_OperateKeystream(operation, m_state, m_state[12], discard,
|
||||
ROUNDS, output, input, iterationCount);
|
||||
|
||||
// If this fires it means ChaCha_OperateKeystream generated a counter
|
||||
// block carry that was discarded. The problem is, the RFC does not
|
||||
// specify what should happen when the counter block wraps. All we can
|
||||
// do is inform the user that something bad may happen because we don't
|
||||
// know what we should do.
|
||||
// Also see https://github.com/weidai11/cryptopp/issues/790 and
|
||||
// https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU
|
||||
// CRYPTOPP_ASSERT(discard==0);
|
||||
}
|
||||
|
||||
////////////////////////////// IETF XChaCha20 //////////////////////////////
|
||||
|
||||
std::string XChaCha20_Policy::AlgorithmName() const
|
||||
{
|
||||
return std::string("XChaCha20");
|
||||
}
|
||||
|
||||
std::string XChaCha20_Policy::AlgorithmProvider() const
|
||||
{
|
||||
return ChaCha_AlgorithmProvider();
|
||||
}
|
||||
|
||||
void XChaCha20_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length)
|
||||
{
|
||||
CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
|
||||
CRYPTOPP_UNUSED(length);
|
||||
|
||||
// Use previous rounds as the default value
|
||||
int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);
|
||||
if (rounds != 20 && rounds != 12)
|
||||
throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);
|
||||
|
||||
// Latch a good value
|
||||
m_rounds = rounds;
|
||||
|
||||
word64 block;
|
||||
if (params.GetValue("InitialBlock", block))
|
||||
m_counter = static_cast<word32>(block);
|
||||
else
|
||||
m_counter = 1;
|
||||
|
||||
// Stash key away for use in CipherResynchronize
|
||||
GetBlock<word32, LittleEndian> get(key);
|
||||
get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
|
||||
(m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
|
||||
}
|
||||
|
||||
void XChaCha20_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
|
||||
{
|
||||
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
|
||||
CRYPTOPP_ASSERT(length==24);
|
||||
|
||||
// HChaCha derivation
|
||||
m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
|
||||
m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
|
||||
|
||||
// Copy saved key into state
|
||||
std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
|
||||
|
||||
GetBlock<word32, LittleEndian> get(iv);
|
||||
get(m_state[12])(m_state[13])(m_state[14])(m_state[15]);
|
||||
|
||||
// Operate the keystream without adding state back in.
|
||||
// This function also gathers the key words into a
|
||||
// contiguous 8-word block.
|
||||
HChaCha_OperateKeystream(m_state, m_state+4);
|
||||
|
||||
// XChaCha state
|
||||
m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
|
||||
m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
|
||||
|
||||
// Setup new IV
|
||||
m_state[12] = m_counter;
|
||||
m_state[13] = 0;
|
||||
m_state[14] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+16);
|
||||
m_state[15] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+20);
|
||||
}
|
||||
|
||||
void XChaCha20_Policy::SeekToIteration(lword iterationCount)
|
||||
{
|
||||
// Should we throw here??? XChaCha does not have a block
|
||||
// counter, so I'm not sure how to seek on it.
|
||||
CRYPTOPP_ASSERT(0); CRYPTOPP_UNUSED(iterationCount);
|
||||
}
|
||||
|
||||
unsigned int XChaCha20_Policy::GetAlignment() const
|
||||
{
|
||||
return ChaCha_GetAlignment();
|
||||
}
|
||||
|
||||
unsigned int XChaCha20_Policy::GetOptimalBlockSize() const
|
||||
{
|
||||
return ChaCha_GetOptimalBlockSize();
|
||||
}
|
||||
|
||||
void XChaCha20_Policy::OperateKeystream(KeystreamOperation operation,
|
||||
byte *output, const byte *input, size_t iterationCount)
|
||||
{
|
||||
ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
|
||||
m_rounds, output, input, iterationCount);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
// chacha.h - written and placed in the public domain by Jeffrey Walton.
|
||||
// Based on Wei Dai's Salsa20, Botan's SSE2 implementation,
|
||||
// and Bernstein's reference ChaCha family implementation at
|
||||
// http://cr.yp.to/chacha.html.
|
||||
|
||||
// The library added Bernstein's ChaCha classses at Crypto++ 5.6.4. The IETF
|
||||
// uses a slightly different implementation than Bernstein, and the IETF
|
||||
// ChaCha and XChaCha classes were added at Crypto++ 8.1. We wanted to maintain
|
||||
// ABI compatibility at the 8.1 release so the original ChaCha classes were not
|
||||
// disturbed. Instead new classes were added for IETF ChaCha. The back-end
|
||||
// implementation shares code as expected, however.
|
||||
|
||||
/// \file chacha.h
|
||||
/// \brief Classes for ChaCha8, ChaCha12 and ChaCha20 stream ciphers
|
||||
/// \details Crypto++ provides Bernstein and ECRYPT's ChaCha from <a
|
||||
/// href="http://cr.yp.to/chacha/chacha-20080128.pdf">ChaCha, a
|
||||
/// variant of Salsa20</a> (2008.01.28). Crypto++ also provides the
|
||||
/// IETF implementation of ChaCha using the ChaChaTLS name. Bernstein's
|
||||
/// implementation is _slightly_ different from the TLS working group's
|
||||
/// implementation for cipher suites
|
||||
/// <tt>TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256</tt>,
|
||||
/// <tt>TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256</tt>,
|
||||
/// and <tt>TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256</tt>. Finally,
|
||||
/// the library provides <a
|
||||
/// href="https://tools.ietf.org/html/draft-arciszewski-xchacha">XChaCha:
|
||||
/// eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 (rev. 03)</a>.
|
||||
/// \since ChaCha since Crypto++ 5.6.4, ChaChaTLS and XChaCha20 since Crypto++ 8.1
|
||||
|
||||
#ifndef CRYPTOPP_CHACHA_H
|
||||
#define CRYPTOPP_CHACHA_H
|
||||
|
||||
#include "strciphr.h"
|
||||
#include "secblock.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
////////////////////////////// Bernstein ChaCha //////////////////////////////
|
||||
|
||||
/// \brief ChaCha stream cipher information
|
||||
/// \since Crypto++ 5.6.4
|
||||
struct ChaCha_Info : public VariableKeyLength<32, 16, 32, 16, SimpleKeyingInterface::UNIQUE_IV, 8>
|
||||
{
|
||||
/// \brief The algorithm name
|
||||
/// \return the algorithm name
|
||||
/// \details StaticAlgorithmName returns the algorithm's name as a static
|
||||
/// member function.
|
||||
/// \details Bernstein named the cipher variants ChaCha8, ChaCha12 and
|
||||
/// ChaCha20. More generally, Bernstein called the family ChaCha{r}.
|
||||
/// AlgorithmName() provides the exact name once rounds are set.
|
||||
static const char* StaticAlgorithmName() {
|
||||
return "ChaCha";
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief ChaCha stream cipher implementation
|
||||
/// \since Crypto++ 5.6.4
|
||||
class CRYPTOPP_NO_VTABLE ChaCha_Policy : public AdditiveCipherConcretePolicy<word32, 16>
|
||||
{
|
||||
public:
|
||||
virtual ~ChaCha_Policy() {}
|
||||
ChaCha_Policy() : m_rounds(ROUNDS) {}
|
||||
|
||||
protected:
|
||||
void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length);
|
||||
void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount);
|
||||
void CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length);
|
||||
bool CipherIsRandomAccess() const {return true;}
|
||||
void SeekToIteration(lword iterationCount);
|
||||
unsigned int GetAlignment() const;
|
||||
unsigned int GetOptimalBlockSize() const;
|
||||
|
||||
std::string AlgorithmName() const;
|
||||
std::string AlgorithmProvider() const;
|
||||
|
||||
CRYPTOPP_CONSTANT(ROUNDS = 20); // Default rounds
|
||||
FixedSizeAlignedSecBlock<word32, 16> m_state;
|
||||
unsigned int m_rounds;
|
||||
};
|
||||
|
||||
/// \brief ChaCha stream cipher
|
||||
/// \details This is Bernstein and ECRYPT's ChaCha. It is _slightly_ different
|
||||
/// from the IETF's version of ChaCha called ChaChaTLS.
|
||||
/// \sa <a href="http://cr.yp.to/chacha/chacha-20080208.pdf">ChaCha, a variant
|
||||
/// of Salsa20</a> (2008.01.28).
|
||||
/// \since Crypto++ 5.6.4
|
||||
struct ChaCha : public ChaCha_Info, public SymmetricCipherDocumentation
|
||||
{
|
||||
/// \brief ChaCha Encryption
|
||||
typedef SymmetricCipherFinal<ConcretePolicyHolder<ChaCha_Policy, AdditiveCipherTemplate<> >, ChaCha_Info > Encryption;
|
||||
/// \brief ChaCha Decryption
|
||||
typedef Encryption Decryption;
|
||||
};
|
||||
|
||||
////////////////////////////// IETF ChaChaTLS //////////////////////////////
|
||||
|
||||
/// \brief IETF ChaCha20 stream cipher information
|
||||
/// \since Crypto++ 8.1
|
||||
struct ChaChaTLS_Info : public FixedKeyLength<32, SimpleKeyingInterface::UNIQUE_IV, 12>, FixedRounds<20>
|
||||
{
|
||||
/// \brief The algorithm name
|
||||
/// \return the algorithm name
|
||||
/// \details StaticAlgorithmName returns the algorithm's name as a static
|
||||
/// member function.
|
||||
/// \details This is the IETF's variant of Bernstein's ChaCha from RFC
|
||||
/// 8439. IETF ChaCha is called ChaChaTLS in the Crypto++ library. It
|
||||
/// is _slightly_ different from Bernstein's implementation.
|
||||
static const char* StaticAlgorithmName() {
|
||||
return "ChaChaTLS";
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief IETF ChaCha20 stream cipher implementation
|
||||
/// \since Crypto++ 8.1
|
||||
class CRYPTOPP_NO_VTABLE ChaChaTLS_Policy : public AdditiveCipherConcretePolicy<word32, 16>
|
||||
{
|
||||
public:
|
||||
virtual ~ChaChaTLS_Policy() {}
|
||||
ChaChaTLS_Policy() : m_counter(0) {}
|
||||
|
||||
protected:
|
||||
void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length);
|
||||
void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount);
|
||||
void CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length);
|
||||
bool CipherIsRandomAccess() const {return true;}
|
||||
void SeekToIteration(lword iterationCount);
|
||||
unsigned int GetAlignment() const;
|
||||
unsigned int GetOptimalBlockSize() const;
|
||||
|
||||
std::string AlgorithmName() const;
|
||||
std::string AlgorithmProvider() const;
|
||||
|
||||
FixedSizeAlignedSecBlock<word32, 16+8> m_state;
|
||||
unsigned int m_counter;
|
||||
CRYPTOPP_CONSTANT(ROUNDS = ChaChaTLS_Info::ROUNDS);
|
||||
CRYPTOPP_CONSTANT(KEY = 16); // Index into m_state
|
||||
CRYPTOPP_CONSTANT(CTR = 24); // Index into m_state
|
||||
};
|
||||
|
||||
/// \brief IETF ChaCha20 stream cipher
|
||||
/// \details This is the IETF's variant of Bernstein's ChaCha from RFC 8439.
|
||||
/// IETF ChaCha is called ChaChaTLS in the Crypto++ library. It is
|
||||
/// _slightly_ different from the Bernstein implementation. ChaCha-TLS
|
||||
/// can be used for cipher suites
|
||||
/// <tt>TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256</tt>,
|
||||
/// <tt>TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256</tt>, and
|
||||
/// <tt>TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256</tt>.
|
||||
/// \sa <a href="https://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and
|
||||
/// Poly1305 for IETF Protocols</a>, <A
|
||||
/// HREF="https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU">How
|
||||
/// to handle block counter wrap in IETF's ChaCha algorithm?</A> and
|
||||
/// <A HREF="https://github.com/weidai11/cryptopp/issues/790">Issue
|
||||
/// 790, ChaChaTLS results when counter block wraps</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
struct ChaChaTLS : public ChaChaTLS_Info, public SymmetricCipherDocumentation
|
||||
{
|
||||
/// \brief ChaCha-TLS Encryption
|
||||
typedef SymmetricCipherFinal<ConcretePolicyHolder<ChaChaTLS_Policy, AdditiveCipherTemplate<> >, ChaChaTLS_Info > Encryption;
|
||||
/// \brief ChaCha-TLS Decryption
|
||||
typedef Encryption Decryption;
|
||||
};
|
||||
|
||||
////////////////////////////// IETF XChaCha20 draft //////////////////////////////
|
||||
|
||||
/// \brief IETF XChaCha20 stream cipher information
|
||||
/// \since Crypto++ 8.1
|
||||
struct XChaCha20_Info : public FixedKeyLength<32, SimpleKeyingInterface::UNIQUE_IV, 24>
|
||||
{
|
||||
/// \brief The algorithm name
|
||||
/// \return the algorithm name
|
||||
/// \details StaticAlgorithmName returns the algorithm's name as a static
|
||||
/// member function.
|
||||
/// \details This is the IETF's XChaCha from draft-arciszewski-xchacha.
|
||||
static const char* StaticAlgorithmName() {
|
||||
return "XChaCha20";
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief IETF XChaCha20 stream cipher implementation
|
||||
/// \since Crypto++ 8.1
|
||||
class CRYPTOPP_NO_VTABLE XChaCha20_Policy : public AdditiveCipherConcretePolicy<word32, 16>
|
||||
{
|
||||
public:
|
||||
virtual ~XChaCha20_Policy() {}
|
||||
XChaCha20_Policy() : m_counter(0), m_rounds(ROUNDS) {}
|
||||
|
||||
protected:
|
||||
void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length);
|
||||
void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount);
|
||||
void CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length);
|
||||
bool CipherIsRandomAccess() const {return false;}
|
||||
void SeekToIteration(lword iterationCount);
|
||||
unsigned int GetAlignment() const;
|
||||
unsigned int GetOptimalBlockSize() const;
|
||||
|
||||
std::string AlgorithmName() const;
|
||||
std::string AlgorithmProvider() const;
|
||||
|
||||
FixedSizeAlignedSecBlock<word32, 16+8> m_state;
|
||||
unsigned int m_counter, m_rounds;
|
||||
CRYPTOPP_CONSTANT(ROUNDS = 20); // Default rounds
|
||||
CRYPTOPP_CONSTANT(KEY = 16); // Index into m_state
|
||||
};
|
||||
|
||||
/// \brief IETF XChaCha20 stream cipher
|
||||
/// \details This is the IETF's XChaCha from draft-arciszewski-xchacha.
|
||||
/// \sa <a href="https://tools.ietf.org/html/draft-arciszewski-xchacha">XChaCha:
|
||||
/// eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 (rev. 03)</a>, <A
|
||||
/// HREF="https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU">How
|
||||
/// to handle block counter wrap in IETF's ChaCha algorithm?</A> and
|
||||
/// <A HREF="https://github.com/weidai11/cryptopp/issues/790">Issue
|
||||
/// 790, ChaCha20 results when counter block wraps</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
struct XChaCha20 : public XChaCha20_Info, public SymmetricCipherDocumentation
|
||||
{
|
||||
/// \brief XChaCha Encryption
|
||||
typedef SymmetricCipherFinal<ConcretePolicyHolder<XChaCha20_Policy, AdditiveCipherTemplate<> >, XChaCha20_Info > Encryption;
|
||||
/// \brief XChaCha Decryption
|
||||
typedef Encryption Decryption;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif // CRYPTOPP_CHACHA_H
|
||||
+421
@@ -0,0 +1,421 @@
|
||||
// chacha_avx.cpp - written and placed in the public domain by
|
||||
// Jack Lloyd and Jeffrey Walton
|
||||
//
|
||||
// This source file uses intrinsics and built-ins to gain access to
|
||||
// AVX2 instructions. A separate source file is needed because
|
||||
// additional CXXFLAGS are required to enable the appropriate
|
||||
// instructions sets in some build configurations.
|
||||
//
|
||||
// AVX2 implementation based on Botan's chacha_avx.cpp. Many thanks
|
||||
// to Jack Lloyd and the Botan team for allowing us to use it.
|
||||
//
|
||||
// Here are some relative numbers for ChaCha8:
|
||||
// * Intel Skylake, 3.0 GHz: AVX2 at 4411 MB/s; 0.57 cpb.
|
||||
// * Intel Broadwell, 2.3 GHz: AVX2 at 3828 MB/s; 0.58 cpb.
|
||||
// * AMD Bulldozer, 3.3 GHz: AVX2 at 1680 MB/s; 1.47 cpb.
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "chacha.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if defined(CRYPTOPP_AVX2_AVAILABLE)
|
||||
# include <xmmintrin.h>
|
||||
# include <emmintrin.h>
|
||||
# include <immintrin.h>
|
||||
#endif
|
||||
|
||||
// Squash MS LNK4221 and libtool warnings
|
||||
extern const char CHACHA_AVX_FNAME[] = __FILE__;
|
||||
|
||||
// Sun Studio 12.4 OK, 12.5 and 12.6 compile error.
|
||||
#if (__SUNPRO_CC >= 0x5140) && (__SUNPRO_CC <= 0x5150)
|
||||
# define MAYBE_CONST
|
||||
#else
|
||||
# define MAYBE_CONST const
|
||||
#endif
|
||||
|
||||
// VS2017 and global optimization bug. TODO, figure out when
|
||||
// we can re-enable full optimizations for VS2017. Also see
|
||||
// https://github.com/weidai11/cryptopp/issues/649 and
|
||||
// https://github.com/weidai11/cryptopp/issues/735. The
|
||||
// 649 issue affects AES but it is the same here. The 735
|
||||
// issue is ChaCha AVX2 cut-in where it surfaced again.
|
||||
#if (_MSC_VER >= 1910)
|
||||
# ifndef CRYPTOPP_DEBUG
|
||||
# pragma optimize("", off)
|
||||
# pragma optimize("ts", on)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// The data is aligned, but Clang issues warning based on type
|
||||
// and not the actual alignment of the variable and data.
|
||||
#if CRYPTOPP_GCC_DIAGNOSTIC_AVAILABLE
|
||||
# pragma GCC diagnostic ignored "-Wcast-align"
|
||||
#endif
|
||||
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
|
||||
template <unsigned int R>
|
||||
inline __m256i RotateLeft(const __m256i val)
|
||||
{
|
||||
return _mm256_or_si256(_mm256_slli_epi32(val, R), _mm256_srli_epi32(val, 32-R));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline __m256i RotateLeft<8>(const __m256i val)
|
||||
{
|
||||
const __m256i mask = _mm256_set_epi8(14,13,12,15, 10,9,8,11, 6,5,4,7, 2,1,0,3,
|
||||
14,13,12,15, 10,9,8,11, 6,5,4,7, 2,1,0,3);
|
||||
return _mm256_shuffle_epi8(val, mask);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline __m256i RotateLeft<16>(const __m256i val)
|
||||
{
|
||||
const __m256i mask = _mm256_set_epi8(13,12,15,14, 9,8,11,10, 5,4,7,6, 1,0,3,2,
|
||||
13,12,15,14, 9,8,11,10, 5,4,7,6, 1,0,3,2);
|
||||
return _mm256_shuffle_epi8(val, mask);
|
||||
}
|
||||
|
||||
#endif // CRYPTOPP_AVX2_AVAILABLE
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
#if (CRYPTOPP_AVX2_AVAILABLE)
|
||||
|
||||
void ChaCha_OperateKeystream_AVX2(const word32 *state, const byte* input, byte *output, unsigned int rounds)
|
||||
{
|
||||
const __m256i state0 = _mm256_broadcastsi128_si256(
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(state+0*4)));
|
||||
const __m256i state1 = _mm256_broadcastsi128_si256(
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(state+1*4)));
|
||||
const __m256i state2 = _mm256_broadcastsi128_si256(
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(state+2*4)));
|
||||
const __m256i state3 = _mm256_broadcastsi128_si256(
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(state+3*4)));
|
||||
|
||||
const __m256i CTR0 = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, 4);
|
||||
const __m256i CTR1 = _mm256_set_epi32(0, 0, 0, 1, 0, 0, 0, 5);
|
||||
const __m256i CTR2 = _mm256_set_epi32(0, 0, 0, 2, 0, 0, 0, 6);
|
||||
const __m256i CTR3 = _mm256_set_epi32(0, 0, 0, 3, 0, 0, 0, 7);
|
||||
|
||||
__m256i X0_0 = state0;
|
||||
__m256i X0_1 = state1;
|
||||
__m256i X0_2 = state2;
|
||||
__m256i X0_3 = _mm256_add_epi64(state3, CTR0);
|
||||
|
||||
__m256i X1_0 = state0;
|
||||
__m256i X1_1 = state1;
|
||||
__m256i X1_2 = state2;
|
||||
__m256i X1_3 = _mm256_add_epi64(state3, CTR1);
|
||||
|
||||
__m256i X2_0 = state0;
|
||||
__m256i X2_1 = state1;
|
||||
__m256i X2_2 = state2;
|
||||
__m256i X2_3 = _mm256_add_epi64(state3, CTR2);
|
||||
|
||||
__m256i X3_0 = state0;
|
||||
__m256i X3_1 = state1;
|
||||
__m256i X3_2 = state2;
|
||||
__m256i X3_3 = _mm256_add_epi64(state3, CTR3);
|
||||
|
||||
for (int i = static_cast<int>(rounds); i > 0; i -= 2)
|
||||
{
|
||||
X0_0 = _mm256_add_epi32(X0_0, X0_1);
|
||||
X1_0 = _mm256_add_epi32(X1_0, X1_1);
|
||||
X2_0 = _mm256_add_epi32(X2_0, X2_1);
|
||||
X3_0 = _mm256_add_epi32(X3_0, X3_1);
|
||||
|
||||
X0_3 = _mm256_xor_si256(X0_3, X0_0);
|
||||
X1_3 = _mm256_xor_si256(X1_3, X1_0);
|
||||
X2_3 = _mm256_xor_si256(X2_3, X2_0);
|
||||
X3_3 = _mm256_xor_si256(X3_3, X3_0);
|
||||
|
||||
X0_3 = RotateLeft<16>(X0_3);
|
||||
X1_3 = RotateLeft<16>(X1_3);
|
||||
X2_3 = RotateLeft<16>(X2_3);
|
||||
X3_3 = RotateLeft<16>(X3_3);
|
||||
|
||||
X0_2 = _mm256_add_epi32(X0_2, X0_3);
|
||||
X1_2 = _mm256_add_epi32(X1_2, X1_3);
|
||||
X2_2 = _mm256_add_epi32(X2_2, X2_3);
|
||||
X3_2 = _mm256_add_epi32(X3_2, X3_3);
|
||||
|
||||
X0_1 = _mm256_xor_si256(X0_1, X0_2);
|
||||
X1_1 = _mm256_xor_si256(X1_1, X1_2);
|
||||
X2_1 = _mm256_xor_si256(X2_1, X2_2);
|
||||
X3_1 = _mm256_xor_si256(X3_1, X3_2);
|
||||
|
||||
X0_1 = RotateLeft<12>(X0_1);
|
||||
X1_1 = RotateLeft<12>(X1_1);
|
||||
X2_1 = RotateLeft<12>(X2_1);
|
||||
X3_1 = RotateLeft<12>(X3_1);
|
||||
|
||||
X0_0 = _mm256_add_epi32(X0_0, X0_1);
|
||||
X1_0 = _mm256_add_epi32(X1_0, X1_1);
|
||||
X2_0 = _mm256_add_epi32(X2_0, X2_1);
|
||||
X3_0 = _mm256_add_epi32(X3_0, X3_1);
|
||||
|
||||
X0_3 = _mm256_xor_si256(X0_3, X0_0);
|
||||
X1_3 = _mm256_xor_si256(X1_3, X1_0);
|
||||
X2_3 = _mm256_xor_si256(X2_3, X2_0);
|
||||
X3_3 = _mm256_xor_si256(X3_3, X3_0);
|
||||
|
||||
X0_3 = RotateLeft<8>(X0_3);
|
||||
X1_3 = RotateLeft<8>(X1_3);
|
||||
X2_3 = RotateLeft<8>(X2_3);
|
||||
X3_3 = RotateLeft<8>(X3_3);
|
||||
|
||||
X0_2 = _mm256_add_epi32(X0_2, X0_3);
|
||||
X1_2 = _mm256_add_epi32(X1_2, X1_3);
|
||||
X2_2 = _mm256_add_epi32(X2_2, X2_3);
|
||||
X3_2 = _mm256_add_epi32(X3_2, X3_3);
|
||||
|
||||
X0_1 = _mm256_xor_si256(X0_1, X0_2);
|
||||
X1_1 = _mm256_xor_si256(X1_1, X1_2);
|
||||
X2_1 = _mm256_xor_si256(X2_1, X2_2);
|
||||
X3_1 = _mm256_xor_si256(X3_1, X3_2);
|
||||
|
||||
X0_1 = RotateLeft<7>(X0_1);
|
||||
X1_1 = RotateLeft<7>(X1_1);
|
||||
X2_1 = RotateLeft<7>(X2_1);
|
||||
X3_1 = RotateLeft<7>(X3_1);
|
||||
|
||||
X0_1 = _mm256_shuffle_epi32(X0_1, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
X0_2 = _mm256_shuffle_epi32(X0_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X0_3 = _mm256_shuffle_epi32(X0_3, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
|
||||
X1_1 = _mm256_shuffle_epi32(X1_1, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
X1_2 = _mm256_shuffle_epi32(X1_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X1_3 = _mm256_shuffle_epi32(X1_3, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
|
||||
X2_1 = _mm256_shuffle_epi32(X2_1, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
X2_2 = _mm256_shuffle_epi32(X2_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X2_3 = _mm256_shuffle_epi32(X2_3, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
|
||||
X3_1 = _mm256_shuffle_epi32(X3_1, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
X3_2 = _mm256_shuffle_epi32(X3_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X3_3 = _mm256_shuffle_epi32(X3_3, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
|
||||
X0_0 = _mm256_add_epi32(X0_0, X0_1);
|
||||
X1_0 = _mm256_add_epi32(X1_0, X1_1);
|
||||
X2_0 = _mm256_add_epi32(X2_0, X2_1);
|
||||
X3_0 = _mm256_add_epi32(X3_0, X3_1);
|
||||
|
||||
X0_3 = _mm256_xor_si256(X0_3, X0_0);
|
||||
X1_3 = _mm256_xor_si256(X1_3, X1_0);
|
||||
X2_3 = _mm256_xor_si256(X2_3, X2_0);
|
||||
X3_3 = _mm256_xor_si256(X3_3, X3_0);
|
||||
|
||||
X0_3 = RotateLeft<16>(X0_3);
|
||||
X1_3 = RotateLeft<16>(X1_3);
|
||||
X2_3 = RotateLeft<16>(X2_3);
|
||||
X3_3 = RotateLeft<16>(X3_3);
|
||||
|
||||
X0_2 = _mm256_add_epi32(X0_2, X0_3);
|
||||
X1_2 = _mm256_add_epi32(X1_2, X1_3);
|
||||
X2_2 = _mm256_add_epi32(X2_2, X2_3);
|
||||
X3_2 = _mm256_add_epi32(X3_2, X3_3);
|
||||
|
||||
X0_1 = _mm256_xor_si256(X0_1, X0_2);
|
||||
X1_1 = _mm256_xor_si256(X1_1, X1_2);
|
||||
X2_1 = _mm256_xor_si256(X2_1, X2_2);
|
||||
X3_1 = _mm256_xor_si256(X3_1, X3_2);
|
||||
|
||||
X0_1 = RotateLeft<12>(X0_1);
|
||||
X1_1 = RotateLeft<12>(X1_1);
|
||||
X2_1 = RotateLeft<12>(X2_1);
|
||||
X3_1 = RotateLeft<12>(X3_1);
|
||||
|
||||
X0_0 = _mm256_add_epi32(X0_0, X0_1);
|
||||
X1_0 = _mm256_add_epi32(X1_0, X1_1);
|
||||
X2_0 = _mm256_add_epi32(X2_0, X2_1);
|
||||
X3_0 = _mm256_add_epi32(X3_0, X3_1);
|
||||
|
||||
X0_3 = _mm256_xor_si256(X0_3, X0_0);
|
||||
X1_3 = _mm256_xor_si256(X1_3, X1_0);
|
||||
X2_3 = _mm256_xor_si256(X2_3, X2_0);
|
||||
X3_3 = _mm256_xor_si256(X3_3, X3_0);
|
||||
|
||||
X0_3 = RotateLeft<8>(X0_3);
|
||||
X1_3 = RotateLeft<8>(X1_3);
|
||||
X2_3 = RotateLeft<8>(X2_3);
|
||||
X3_3 = RotateLeft<8>(X3_3);
|
||||
|
||||
X0_2 = _mm256_add_epi32(X0_2, X0_3);
|
||||
X1_2 = _mm256_add_epi32(X1_2, X1_3);
|
||||
X2_2 = _mm256_add_epi32(X2_2, X2_3);
|
||||
X3_2 = _mm256_add_epi32(X3_2, X3_3);
|
||||
|
||||
X0_1 = _mm256_xor_si256(X0_1, X0_2);
|
||||
X1_1 = _mm256_xor_si256(X1_1, X1_2);
|
||||
X2_1 = _mm256_xor_si256(X2_1, X2_2);
|
||||
X3_1 = _mm256_xor_si256(X3_1, X3_2);
|
||||
|
||||
X0_1 = RotateLeft<7>(X0_1);
|
||||
X1_1 = RotateLeft<7>(X1_1);
|
||||
X2_1 = RotateLeft<7>(X2_1);
|
||||
X3_1 = RotateLeft<7>(X3_1);
|
||||
|
||||
X0_1 = _mm256_shuffle_epi32(X0_1, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
X0_2 = _mm256_shuffle_epi32(X0_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X0_3 = _mm256_shuffle_epi32(X0_3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
|
||||
X1_1 = _mm256_shuffle_epi32(X1_1, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
X1_2 = _mm256_shuffle_epi32(X1_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X1_3 = _mm256_shuffle_epi32(X1_3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
|
||||
X2_1 = _mm256_shuffle_epi32(X2_1, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
X2_2 = _mm256_shuffle_epi32(X2_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X2_3 = _mm256_shuffle_epi32(X2_3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
|
||||
X3_1 = _mm256_shuffle_epi32(X3_1, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
X3_2 = _mm256_shuffle_epi32(X3_2, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
X3_3 = _mm256_shuffle_epi32(X3_3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
}
|
||||
|
||||
X0_0 = _mm256_add_epi32(X0_0, state0);
|
||||
X0_1 = _mm256_add_epi32(X0_1, state1);
|
||||
X0_2 = _mm256_add_epi32(X0_2, state2);
|
||||
X0_3 = _mm256_add_epi32(X0_3, state3);
|
||||
X0_3 = _mm256_add_epi64(X0_3, CTR0);
|
||||
|
||||
X1_0 = _mm256_add_epi32(X1_0, state0);
|
||||
X1_1 = _mm256_add_epi32(X1_1, state1);
|
||||
X1_2 = _mm256_add_epi32(X1_2, state2);
|
||||
X1_3 = _mm256_add_epi32(X1_3, state3);
|
||||
X1_3 = _mm256_add_epi64(X1_3, CTR1);
|
||||
|
||||
X2_0 = _mm256_add_epi32(X2_0, state0);
|
||||
X2_1 = _mm256_add_epi32(X2_1, state1);
|
||||
X2_2 = _mm256_add_epi32(X2_2, state2);
|
||||
X2_3 = _mm256_add_epi32(X2_3, state3);
|
||||
X2_3 = _mm256_add_epi64(X2_3, CTR2);
|
||||
|
||||
X3_0 = _mm256_add_epi32(X3_0, state0);
|
||||
X3_1 = _mm256_add_epi32(X3_1, state1);
|
||||
X3_2 = _mm256_add_epi32(X3_2, state2);
|
||||
X3_3 = _mm256_add_epi32(X3_3, state3);
|
||||
X3_3 = _mm256_add_epi64(X3_3, CTR3);
|
||||
|
||||
if (input)
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+0*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X0_0, X0_1, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+0*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+1*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X0_2, X0_3, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+1*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+2*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X1_0, X1_1, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+2*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+3*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X1_2, X1_3, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+3*32)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+0*32),
|
||||
_mm256_permute2x128_si256(X0_0, X0_1, 1 + (3 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+1*32),
|
||||
_mm256_permute2x128_si256(X0_2, X0_3, 1 + (3 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+2*32),
|
||||
_mm256_permute2x128_si256(X1_0, X1_1, 1 + (3 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+3*32),
|
||||
_mm256_permute2x128_si256(X1_2, X1_3, 1 + (3 << 4)));
|
||||
}
|
||||
|
||||
if (input)
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+4*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X2_0, X2_1, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+4*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+5*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X2_2, X2_3, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+5*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+6*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X3_0, X3_1, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+6*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+7*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X3_2, X3_3, 1 + (3 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+7*32)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+4*32),
|
||||
_mm256_permute2x128_si256(X2_0, X2_1, 1 + (3 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+5*32),
|
||||
_mm256_permute2x128_si256(X2_2, X2_3, 1 + (3 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+6*32),
|
||||
_mm256_permute2x128_si256(X3_0, X3_1, 1 + (3 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+7*32),
|
||||
_mm256_permute2x128_si256(X3_2, X3_3, 1 + (3 << 4)));
|
||||
}
|
||||
|
||||
if (input)
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+ 8*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X0_0, X0_1, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+8*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+ 9*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X0_2, X0_3, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+9*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+10*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X1_0, X1_1, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+10*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+11*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X1_2, X1_3, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+11*32)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+ 8*32),
|
||||
_mm256_permute2x128_si256(X0_0, X0_1, 0 + (2 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+ 9*32),
|
||||
_mm256_permute2x128_si256(X0_2, X0_3, 0 + (2 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+10*32),
|
||||
_mm256_permute2x128_si256(X1_0, X1_1, 0 + (2 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+11*32),
|
||||
_mm256_permute2x128_si256(X1_2, X1_3, 0 + (2 << 4)));
|
||||
}
|
||||
|
||||
if (input)
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+12*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X2_0, X2_1, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+12*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+13*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X2_2, X2_3, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+13*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+14*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X3_0, X3_1, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+14*32)))));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+15*32),
|
||||
_mm256_xor_si256(_mm256_permute2x128_si256(X3_2, X3_3, 0 + (2 << 4)),
|
||||
_mm256_loadu_si256(const_cast<MAYBE_CONST __m256i*>(reinterpret_cast<const __m256i*>(input+15*32)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+12*32),
|
||||
_mm256_permute2x128_si256(X2_0, X2_1, 0 + (2 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+13*32),
|
||||
_mm256_permute2x128_si256(X2_2, X2_3, 0 + (2 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+14*32),
|
||||
_mm256_permute2x128_si256(X3_0, X3_1, 0 + (2 << 4)));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+15*32),
|
||||
_mm256_permute2x128_si256(X3_2, X3_3, 0 + (2 << 4)));
|
||||
}
|
||||
|
||||
// https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
#endif // CRYPTOPP_AVX2_AVAILABLE
|
||||
|
||||
NAMESPACE_END
|
||||
+1109
File diff suppressed because it is too large
Load Diff
+211
@@ -0,0 +1,211 @@
|
||||
// chachapoly.cpp - written and placed in the public domain by Jeffrey Walton
|
||||
// RFC 8439, Section 2.8, AEAD Construction, http://tools.ietf.org/html/rfc8439
|
||||
|
||||
#include "pch.h"
|
||||
#include "chachapoly.h"
|
||||
#include "algparam.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if CRYPTOPP_MSC_VERSION
|
||||
# pragma warning(disable: 4244)
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
////////////////////////////// IETF ChaChaTLS //////////////////////////////
|
||||
|
||||
// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was
|
||||
// predicated on BlockCiphers, where the key and key schedule could be
|
||||
// calculated independent of the IV being used. However, the ChaCha and
|
||||
// ChaCha20Poly1305 construction combines key setup and IV. That is, both are
|
||||
// needed to key or rekey the cipher. Even a simple Resync() requires us to
|
||||
// regenerate the initial state for both ChaCha20 and Poly1305.
|
||||
void ChaCha20Poly1305_Base::RekeyCipherAndMac(const byte *userKey, size_t keylength, const NameValuePairs ¶ms)
|
||||
{
|
||||
// Derive MAC key
|
||||
AlgorithmParameters block0 = MakeParameters("InitialBlock", (word64)0, true);
|
||||
AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block0));
|
||||
|
||||
// Only the first 256-bits are used to key the MAC
|
||||
SecByteBlock derived(NULLPTR, 32);
|
||||
AccessSymmetricCipher().ProcessString(derived, derived.size());
|
||||
|
||||
// Key the Poly1305 MAC
|
||||
AccessMAC().SetKey(derived, derived.size(), params);
|
||||
|
||||
// Key the ChaCha20 cipher
|
||||
AlgorithmParameters block1 = MakeParameters("InitialBlock", (word64)1, true);
|
||||
AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block1));
|
||||
}
|
||||
|
||||
void ChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t userKeyLength, const NameValuePairs ¶ms)
|
||||
{
|
||||
CRYPTOPP_ASSERT(userKey && userKeyLength == 32);
|
||||
m_userKey.Assign(userKey, userKeyLength);
|
||||
|
||||
// ChaCha/Poly1305 initial state depends on both the key and IV. The
|
||||
// IV may or may not be present during the call to SetKeyWithoutResync.
|
||||
// If the IV is present, the framework will call SetKeyWithoutResync
|
||||
// followed by Resynchronize which calls Resync. In this case we defer
|
||||
// calculating the initial state until the call to Resynchronize.
|
||||
// If the IV is not present, it avoids calling ChaCha's SetKey without
|
||||
// an IV, which results in an exception. In this case the user will need
|
||||
// to call Resynchronize to key ChaCha and Poly1305.
|
||||
// RekeyCipherAndMac(userKey, userKeyLength, params);
|
||||
CRYPTOPP_UNUSED(params);
|
||||
}
|
||||
|
||||
void ChaCha20Poly1305_Base::Resync(const byte *iv, size_t len)
|
||||
{
|
||||
CRYPTOPP_ASSERT(iv && len == 12);
|
||||
RekeyCipherAndMac(m_userKey, m_userKey.SizeInBytes(),
|
||||
MakeParameters(Name::IV(), ConstByteArrayParameter(iv,len)));
|
||||
}
|
||||
|
||||
size_t ChaCha20Poly1305_Base::AuthenticateBlocks(const byte *data, size_t len)
|
||||
{
|
||||
AccessMAC().Update(data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ChaCha20Poly1305_Base::AuthenticateLastHeaderBlock()
|
||||
{
|
||||
// Pad to a multiple of 16 or 0
|
||||
const byte zero[16] = {0};
|
||||
size_t pad = (16U - (m_totalHeaderLength % 16)) % 16;
|
||||
AccessMAC().Update(zero, pad);
|
||||
}
|
||||
|
||||
void ChaCha20Poly1305_Base::AuthenticateLastConfidentialBlock()
|
||||
{
|
||||
// Pad to a multiple of 16 or 0
|
||||
const byte zero[16] = {0};
|
||||
size_t pad = (16U - (m_totalMessageLength % 16)) % 16;
|
||||
AccessMAC().Update(zero, pad);
|
||||
}
|
||||
|
||||
void ChaCha20Poly1305_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
|
||||
{
|
||||
CRYPTOPP_ALIGN_DATA(8) byte length[2*sizeof(word64)];
|
||||
PutWord(true, LITTLE_ENDIAN_ORDER, length+0, m_totalHeaderLength);
|
||||
PutWord(true, LITTLE_ENDIAN_ORDER, length+8, m_totalMessageLength);
|
||||
AccessMAC().Update(length, sizeof(length));
|
||||
AccessMAC().TruncatedFinal(mac, macSize);
|
||||
m_state = State_KeySet;
|
||||
}
|
||||
|
||||
void ChaCha20Poly1305_Base::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength)
|
||||
{
|
||||
Resynchronize(iv, ivLength);
|
||||
Update(aad, aadLength);
|
||||
ProcessString(ciphertext, message, messageLength);
|
||||
TruncatedFinal(mac, macSize);
|
||||
}
|
||||
|
||||
bool ChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength)
|
||||
{
|
||||
Resynchronize(iv, ivLength);
|
||||
Update(aad, aadLength);
|
||||
ProcessString(message, ciphertext, ciphertextLength);
|
||||
return TruncatedVerify(mac, macLength);
|
||||
}
|
||||
|
||||
////////////////////////////// IETF XChaCha20 draft //////////////////////////////
|
||||
|
||||
// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was
|
||||
// predicated on BlockCiphers, where the key and key schedule could be
|
||||
// calculated independent of the IV being used. However, the ChaCha and
|
||||
// ChaCha20Poly1305 construction combines key setup and IV. That is, both are
|
||||
// needed to key or rekey the cipher. Even a simple Resync() requires us to
|
||||
// regenerate the initial state for both ChaCha20 and Poly1305.
|
||||
void XChaCha20Poly1305_Base::RekeyCipherAndMac(const byte *userKey, size_t keylength, const NameValuePairs ¶ms)
|
||||
{
|
||||
// Derive MAC key
|
||||
AlgorithmParameters block0 = MakeParameters("InitialBlock", (word64)0, true);
|
||||
AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block0));
|
||||
|
||||
// Only the first 256-bits are used to key the MAC
|
||||
SecByteBlock derived(NULLPTR, 32);
|
||||
AccessSymmetricCipher().ProcessString(derived, derived.size());
|
||||
|
||||
// Key the Poly1305 MAC
|
||||
AccessMAC().SetKey(derived, derived.size(), params);
|
||||
|
||||
// Key the ChaCha20 cipher
|
||||
AlgorithmParameters block1 = MakeParameters("InitialBlock", (word64)1, true);
|
||||
AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block1));
|
||||
}
|
||||
|
||||
void XChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t userKeyLength, const NameValuePairs ¶ms)
|
||||
{
|
||||
CRYPTOPP_ASSERT(userKey && userKeyLength == 32);
|
||||
m_userKey.Assign(userKey, userKeyLength);
|
||||
|
||||
// XChaCha20/Poly1305 initial state depends on both the key and IV. The
|
||||
// IV may or may not be present during the call to SetKeyWithoutResync.
|
||||
// If the IV is present, the framework will call SetKeyWithoutResync
|
||||
// followed by Resynchronize which calls Resync. In this case we defer
|
||||
// calculating the initial state until the call to Resynchronize.
|
||||
// If the IV is not present, it avoids calling ChaCha's SetKey without
|
||||
// an IV, which results in an exception. In this case the user will need
|
||||
// to call Resynchronize to key ChaCha and Poly1305.
|
||||
// RekeyCipherAndMac(userKey, userKeyLength, params);
|
||||
CRYPTOPP_UNUSED(params);
|
||||
}
|
||||
|
||||
void XChaCha20Poly1305_Base::Resync(const byte *iv, size_t len)
|
||||
{
|
||||
CRYPTOPP_ASSERT(iv && len == 24);
|
||||
RekeyCipherAndMac(m_userKey, m_userKey.SizeInBytes(),
|
||||
MakeParameters(Name::IV(), ConstByteArrayParameter(iv,len)));
|
||||
}
|
||||
|
||||
size_t XChaCha20Poly1305_Base::AuthenticateBlocks(const byte *data, size_t len)
|
||||
{
|
||||
AccessMAC().Update(data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void XChaCha20Poly1305_Base::AuthenticateLastHeaderBlock()
|
||||
{
|
||||
// Pad to a multiple of 16 or 0
|
||||
const byte zero[16] = {0};
|
||||
size_t pad = (16 - (m_totalHeaderLength % 16)) % 16;
|
||||
AccessMAC().Update(zero, pad);
|
||||
}
|
||||
|
||||
void XChaCha20Poly1305_Base::AuthenticateLastConfidentialBlock()
|
||||
{
|
||||
// Pad to a multiple of 16 or 0
|
||||
const byte zero[16] = {0};
|
||||
size_t pad = (16 - (m_totalMessageLength % 16)) % 16;
|
||||
AccessMAC().Update(zero, pad);
|
||||
}
|
||||
|
||||
void XChaCha20Poly1305_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
|
||||
{
|
||||
CRYPTOPP_ALIGN_DATA(8) byte length[2*sizeof(word64)];
|
||||
PutWord(true, LITTLE_ENDIAN_ORDER, length+0, m_totalHeaderLength);
|
||||
PutWord(true, LITTLE_ENDIAN_ORDER, length+8, m_totalMessageLength);
|
||||
AccessMAC().Update(length, sizeof(length));
|
||||
AccessMAC().TruncatedFinal(mac, macSize);
|
||||
m_state = State_KeySet;
|
||||
}
|
||||
|
||||
void XChaCha20Poly1305_Base::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength)
|
||||
{
|
||||
Resynchronize(iv, ivLength);
|
||||
Update(aad, aadLength);
|
||||
ProcessString(ciphertext, message, messageLength);
|
||||
TruncatedFinal(mac, macSize);
|
||||
}
|
||||
|
||||
bool XChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength)
|
||||
{
|
||||
Resynchronize(iv, ivLength);
|
||||
Update(aad, aadLength);
|
||||
ProcessString(message, ciphertext, ciphertextLength);
|
||||
return TruncatedVerify(mac, macLength);
|
||||
}
|
||||
|
||||
NAMESPACE_END
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
// chachapoly.h - written and placed in the public domain by Jeffrey Walton
|
||||
// RFC 8439, Section 2.8, AEAD Construction, http://tools.ietf.org/html/rfc8439
|
||||
|
||||
/// \file chachapoly.h
|
||||
/// \brief IETF ChaCha20/Poly1305 AEAD scheme
|
||||
/// \details ChaCha20Poly1305 is an authenticated encryption scheme that combines
|
||||
/// ChaCha20TLS and Poly1305TLS. The scheme is defined in RFC 8439, section 2.8,
|
||||
/// AEAD_CHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha20
|
||||
/// and Poly1305.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
|
||||
/// for IETF Protocols</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
|
||||
#ifndef CRYPTOPP_CHACHA_POLY1305_H
|
||||
#define CRYPTOPP_CHACHA_POLY1305_H
|
||||
|
||||
#include "cryptlib.h"
|
||||
#include "authenc.h"
|
||||
#include "chacha.h"
|
||||
#include "poly1305.h"
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
////////////////////////////// IETF ChaChaTLS //////////////////////////////
|
||||
|
||||
/// \brief IETF ChaCha20Poly1305 cipher base implementation
|
||||
/// \details Base implementation of the AuthenticatedSymmetricCipher interface
|
||||
/// \since Crypto++ 8.1
|
||||
class ChaCha20Poly1305_Base : public AuthenticatedSymmetricCipherBase
|
||||
{
|
||||
public:
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName()
|
||||
{return "ChaCha20/Poly1305";}
|
||||
|
||||
virtual ~ChaCha20Poly1305_Base() {}
|
||||
|
||||
// AuthenticatedSymmetricCipher
|
||||
std::string AlgorithmName() const
|
||||
{return std::string("ChaCha20/Poly1305");}
|
||||
std::string AlgorithmProvider() const
|
||||
{return GetSymmetricCipher().AlgorithmProvider();}
|
||||
size_t MinKeyLength() const
|
||||
{return 32;}
|
||||
size_t MaxKeyLength() const
|
||||
{return 32;}
|
||||
size_t DefaultKeyLength() const
|
||||
{return 32;}
|
||||
size_t GetValidKeyLength(size_t n) const
|
||||
{CRYPTOPP_UNUSED(n); return 32;}
|
||||
bool IsValidKeyLength(size_t n) const
|
||||
{return n==32;}
|
||||
unsigned int OptimalDataAlignment() const
|
||||
{return GetSymmetricCipher().OptimalDataAlignment();}
|
||||
IV_Requirement IVRequirement() const
|
||||
{return UNIQUE_IV;}
|
||||
unsigned int IVSize() const
|
||||
{return 12;}
|
||||
unsigned int MinIVLength() const
|
||||
{return 12;}
|
||||
unsigned int MaxIVLength() const
|
||||
{return 12;}
|
||||
unsigned int DigestSize() const
|
||||
{return 16;}
|
||||
lword MaxHeaderLength() const
|
||||
{return LWORD_MAX;} // 2^64-1 bytes
|
||||
lword MaxMessageLength() const
|
||||
{return W64LIT(274877906880);} // 2^38-1 blocks
|
||||
lword MaxFooterLength() const
|
||||
{return 0;}
|
||||
|
||||
/// \brief Encrypts and calculates a MAC in one call
|
||||
/// \param ciphertext the encryption buffer
|
||||
/// \param mac the mac buffer
|
||||
/// \param macSize the size of the MAC buffer, in bytes
|
||||
/// \param iv the iv buffer
|
||||
/// \param ivLength the size of the IV buffer, in bytes
|
||||
/// \param aad the AAD buffer
|
||||
/// \param aadLength the size of the AAD buffer, in bytes
|
||||
/// \param message the message buffer
|
||||
/// \param messageLength the size of the messagetext buffer, in bytes
|
||||
/// \details EncryptAndAuthenticate() encrypts and generates the MAC in one call. The function
|
||||
/// truncates the MAC if <tt>macSize < TagSize()</tt>.
|
||||
virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength);
|
||||
|
||||
/// \brief Decrypts and verifies a MAC in one call
|
||||
/// \param message the decryption buffer
|
||||
/// \param mac the mac buffer
|
||||
/// \param macSize the size of the MAC buffer, in bytes
|
||||
/// \param iv the iv buffer
|
||||
/// \param ivLength the size of the IV buffer, in bytes
|
||||
/// \param aad the AAD buffer
|
||||
/// \param aadLength the size of the AAD buffer, in bytes
|
||||
/// \param ciphertext the cipher buffer
|
||||
/// \param ciphertextLength the size of the ciphertext buffer, in bytes
|
||||
/// \return true if the MAC is valid and the decoding succeeded, false otherwise
|
||||
/// \details DecryptAndVerify() decrypts and verifies the MAC in one call.
|
||||
/// <tt>message</tt> is a decryption buffer and should be at least as large as the ciphertext buffer.
|
||||
/// \details The function returns true iff MAC is valid. DecryptAndVerify() assumes the MAC
|
||||
/// is truncated if <tt>macLength < TagSize()</tt>.
|
||||
virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength);
|
||||
|
||||
protected:
|
||||
// AuthenticatedSymmetricCipherBase
|
||||
bool AuthenticationIsOnPlaintext() const {return false;}
|
||||
unsigned int AuthenticationBlockSize() const {return 1;}
|
||||
void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms);
|
||||
void Resync(const byte *iv, size_t len);
|
||||
size_t AuthenticateBlocks(const byte *data, size_t len);
|
||||
void AuthenticateLastHeaderBlock();
|
||||
void AuthenticateLastConfidentialBlock();
|
||||
void AuthenticateLastFooterBlock(byte *mac, size_t macSize);
|
||||
|
||||
// See comments in chachapoly.cpp
|
||||
void RekeyCipherAndMac(const byte *userKey, size_t userKeyLength, const NameValuePairs ¶ms);
|
||||
|
||||
virtual const MessageAuthenticationCode & GetMAC() const = 0;
|
||||
virtual MessageAuthenticationCode & AccessMAC() = 0;
|
||||
|
||||
private:
|
||||
SecByteBlock m_userKey;
|
||||
};
|
||||
|
||||
/// \brief IETF ChaCha20Poly1305 cipher final implementation
|
||||
/// \tparam T_IsEncryption flag indicating cipher direction
|
||||
/// \details ChaCha20Poly1305 is an authenticated encryption scheme that combines
|
||||
/// ChaCha20TLS and Poly1305TLS. The scheme is defined in RFC 8439, section 2.8,
|
||||
/// AEAD_CHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha20
|
||||
/// and Poly1305.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
|
||||
/// for IETF Protocols</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
template <bool T_IsEncryption>
|
||||
class ChaCha20Poly1305_Final : public ChaCha20Poly1305_Base
|
||||
{
|
||||
public:
|
||||
virtual ~ChaCha20Poly1305_Final() {}
|
||||
|
||||
protected:
|
||||
const SymmetricCipher & GetSymmetricCipher()
|
||||
{return const_cast<ChaCha20Poly1305_Final *>(this)->AccessSymmetricCipher();}
|
||||
SymmetricCipher & AccessSymmetricCipher()
|
||||
{return m_cipher;}
|
||||
bool IsForwardTransformation() const
|
||||
{return T_IsEncryption;}
|
||||
|
||||
const MessageAuthenticationCode & GetMAC() const
|
||||
{return const_cast<ChaCha20Poly1305_Final *>(this)->AccessMAC();}
|
||||
MessageAuthenticationCode & AccessMAC()
|
||||
{return m_mac;}
|
||||
|
||||
private:
|
||||
ChaChaTLS::Encryption m_cipher;
|
||||
Poly1305TLS m_mac;
|
||||
};
|
||||
|
||||
/// \brief IETF ChaCha20/Poly1305 AEAD scheme
|
||||
/// \details ChaCha20Poly1305 is an authenticated encryption scheme that combines
|
||||
/// ChaCha20TLS and Poly1305TLS. The scheme is defined in RFC 8439, section 2.8,
|
||||
/// AEAD_CHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha20
|
||||
/// and Poly1305.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
|
||||
/// for IETF Protocols</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
struct ChaCha20Poly1305 : public AuthenticatedSymmetricCipherDocumentation
|
||||
{
|
||||
/// \brief ChaCha20Poly1305 encryption
|
||||
typedef ChaCha20Poly1305_Final<true> Encryption;
|
||||
/// \brief ChaCha20Poly1305 decryption
|
||||
typedef ChaCha20Poly1305_Final<false> Decryption;
|
||||
};
|
||||
|
||||
////////////////////////////// IETF XChaCha20 draft //////////////////////////////
|
||||
|
||||
/// \brief IETF XChaCha20Poly1305 cipher base implementation
|
||||
/// \details Base implementation of the AuthenticatedSymmetricCipher interface
|
||||
/// \since Crypto++ 8.1
|
||||
class XChaCha20Poly1305_Base : public AuthenticatedSymmetricCipherBase
|
||||
{
|
||||
public:
|
||||
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName()
|
||||
{return "XChaCha20/Poly1305";}
|
||||
|
||||
virtual ~XChaCha20Poly1305_Base() {}
|
||||
|
||||
// AuthenticatedSymmetricCipher
|
||||
std::string AlgorithmName() const
|
||||
{return std::string("XChaCha20/Poly1305");}
|
||||
std::string AlgorithmProvider() const
|
||||
{return GetSymmetricCipher().AlgorithmProvider();}
|
||||
size_t MinKeyLength() const
|
||||
{return 32;}
|
||||
size_t MaxKeyLength() const
|
||||
{return 32;}
|
||||
size_t DefaultKeyLength() const
|
||||
{return 32;}
|
||||
size_t GetValidKeyLength(size_t n) const
|
||||
{CRYPTOPP_UNUSED(n); return 32;}
|
||||
bool IsValidKeyLength(size_t n) const
|
||||
{return n==32;}
|
||||
unsigned int OptimalDataAlignment() const
|
||||
{return GetSymmetricCipher().OptimalDataAlignment();}
|
||||
IV_Requirement IVRequirement() const
|
||||
{return UNIQUE_IV;}
|
||||
unsigned int IVSize() const
|
||||
{return 24;}
|
||||
unsigned int MinIVLength() const
|
||||
{return 24;}
|
||||
unsigned int MaxIVLength() const
|
||||
{return 24;}
|
||||
unsigned int DigestSize() const
|
||||
{return 16;}
|
||||
lword MaxHeaderLength() const
|
||||
{return LWORD_MAX;} // 2^64-1 bytes
|
||||
lword MaxMessageLength() const
|
||||
{return W64LIT(274877906880);} // 2^38-1 blocks
|
||||
lword MaxFooterLength() const
|
||||
{return 0;}
|
||||
|
||||
/// \brief Encrypts and calculates a MAC in one call
|
||||
/// \param ciphertext the encryption buffer
|
||||
/// \param mac the mac buffer
|
||||
/// \param macSize the size of the MAC buffer, in bytes
|
||||
/// \param iv the iv buffer
|
||||
/// \param ivLength the size of the IV buffer, in bytes
|
||||
/// \param aad the AAD buffer
|
||||
/// \param aadLength the size of the AAD buffer, in bytes
|
||||
/// \param message the message buffer
|
||||
/// \param messageLength the size of the messagetext buffer, in bytes
|
||||
/// \details EncryptAndAuthenticate() encrypts and generates the MAC in one call. The function
|
||||
/// truncates the MAC if <tt>macSize < TagSize()</tt>.
|
||||
virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength);
|
||||
|
||||
/// \brief Decrypts and verifies a MAC in one call
|
||||
/// \param message the decryption buffer
|
||||
/// \param mac the mac buffer
|
||||
/// \param macSize the size of the MAC buffer, in bytes
|
||||
/// \param iv the iv buffer
|
||||
/// \param ivLength the size of the IV buffer, in bytes
|
||||
/// \param aad the AAD buffer
|
||||
/// \param aadLength the size of the AAD buffer, in bytes
|
||||
/// \param ciphertext the cipher buffer
|
||||
/// \param ciphertextLength the size of the ciphertext buffer, in bytes
|
||||
/// \return true if the MAC is valid and the decoding succeeded, false otherwise
|
||||
/// \details DecryptAndVerify() decrypts and verifies the MAC in one call.
|
||||
/// <tt>message</tt> is a decryption buffer and should be at least as large as the ciphertext buffer.
|
||||
/// \details The function returns true iff MAC is valid. DecryptAndVerify() assumes the MAC
|
||||
/// is truncated if <tt>macLength < TagSize()</tt>.
|
||||
virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength);
|
||||
|
||||
protected:
|
||||
// AuthenticatedSymmetricCipherBase
|
||||
bool AuthenticationIsOnPlaintext() const {return false;}
|
||||
unsigned int AuthenticationBlockSize() const {return 1;}
|
||||
void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs ¶ms);
|
||||
void Resync(const byte *iv, size_t len);
|
||||
size_t AuthenticateBlocks(const byte *data, size_t len);
|
||||
void AuthenticateLastHeaderBlock();
|
||||
void AuthenticateLastConfidentialBlock();
|
||||
void AuthenticateLastFooterBlock(byte *mac, size_t macSize);
|
||||
|
||||
// See comments in chachapoly.cpp
|
||||
void RekeyCipherAndMac(const byte *userKey, size_t userKeyLength, const NameValuePairs ¶ms);
|
||||
|
||||
virtual const MessageAuthenticationCode & GetMAC() const = 0;
|
||||
virtual MessageAuthenticationCode & AccessMAC() = 0;
|
||||
|
||||
private:
|
||||
SecByteBlock m_userKey;
|
||||
};
|
||||
|
||||
/// \brief IETF XChaCha20Poly1305 cipher final implementation
|
||||
/// \tparam T_IsEncryption flag indicating cipher direction
|
||||
/// \details XChaCha20Poly1305 is an authenticated encryption scheme that combines
|
||||
/// XChaCha20 and Poly1305-TLS. The scheme is defined in RFC 8439, section 2.8,
|
||||
/// AEAD_CHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha20
|
||||
/// and Poly1305.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
|
||||
/// for IETF Protocols</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
template <bool T_IsEncryption>
|
||||
class XChaCha20Poly1305_Final : public XChaCha20Poly1305_Base
|
||||
{
|
||||
public:
|
||||
virtual ~XChaCha20Poly1305_Final() {}
|
||||
|
||||
protected:
|
||||
const SymmetricCipher & GetSymmetricCipher()
|
||||
{return const_cast<XChaCha20Poly1305_Final *>(this)->AccessSymmetricCipher();}
|
||||
SymmetricCipher & AccessSymmetricCipher()
|
||||
{return m_cipher;}
|
||||
bool IsForwardTransformation() const
|
||||
{return T_IsEncryption;}
|
||||
|
||||
const MessageAuthenticationCode & GetMAC() const
|
||||
{return const_cast<XChaCha20Poly1305_Final *>(this)->AccessMAC();}
|
||||
MessageAuthenticationCode & AccessMAC()
|
||||
{return m_mac;}
|
||||
|
||||
private:
|
||||
XChaCha20::Encryption m_cipher;
|
||||
Poly1305TLS m_mac;
|
||||
};
|
||||
|
||||
/// \brief IETF XChaCha20/Poly1305 AEAD scheme
|
||||
/// \details XChaCha20Poly1305 is an authenticated encryption scheme that combines
|
||||
/// XChaCha20 and Poly1305-TLS. The scheme is defined in RFC 8439, section 2.8,
|
||||
/// AEAD_XCHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha20
|
||||
/// and Poly1305.
|
||||
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
|
||||
/// for IETF Protocols</A>.
|
||||
/// \since Crypto++ 8.1
|
||||
struct XChaCha20Poly1305 : public AuthenticatedSymmetricCipherDocumentation
|
||||
{
|
||||
/// \brief XChaCha20Poly1305 encryption
|
||||
typedef XChaCha20Poly1305_Final<true> Encryption;
|
||||
/// \brief XChaCha20Poly1305 decryption
|
||||
typedef XChaCha20Poly1305_Final<false> Decryption;
|
||||
};
|
||||
|
||||
NAMESPACE_END
|
||||
|
||||
#endif // CRYPTOPP_CHACHA_POLY1305_H
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
// cham.cpp - written and placed in the public domain by Kim Sung Hee and Jeffrey Walton
|
||||
// Based on "CHAM: A Family of Lightweight Block Ciphers for
|
||||
// Resource-Constrained Devices" by Bonwook Koo, Dongyoung Roh,
|
||||
// Hyeonjin Kim, Younghoon Jung, Dong-Geon Lee, and Daesung Kwon
|
||||
|
||||
#include "pch.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "cham.h"
|
||||
#include "misc.h"
|
||||
#include "cpu.h"
|
||||
|
||||
// CHAM table of parameters
|
||||
// +-------------------------------------------------
|
||||
// +cipher n k r w k/w
|
||||
// +-------------------------------------------------
|
||||
// +CHAM-64/128 64 128 80 16 8
|
||||
// +CHAM-128/128 128 128 80 32 4
|
||||
// +CHAM-128/256 128 256 96 32 8
|
||||
// +-------------------------------------------------
|
||||
|
||||
ANONYMOUS_NAMESPACE_BEGIN
|
||||
|
||||
using CryptoPP::rotlConstant;
|
||||
using CryptoPP::rotrConstant;
|
||||
|
||||
/// \brief CHAM encryption round
|
||||
/// \tparam RR the round number residue
|
||||
/// \tparam KW the number of key words
|
||||
/// \tparam T words type
|
||||
/// \param x the state array
|
||||
/// \param k the subkey table
|
||||
/// \param i the round number
|
||||
/// \details CHAM_EncRound applies the encryption round to the plain text.
|
||||
/// RR is the "round residue" and it is used modulo 4. ProcessAndXorBlock
|
||||
/// may provide a fully unrolled encryption transformation, or provide
|
||||
/// a transformation that loops using multiples of 4 encryption rounds.
|
||||
/// \details CHAM_EncRound calculates indexes into the x[] array based
|
||||
/// on the round number residue. There is no need for the assignments
|
||||
/// that shift values in preparations for the next round.
|
||||
/// \details CHAM_EncRound depends on the round number. The actual round
|
||||
/// being executed is passed through the parameter <tt>i</tt>. If
|
||||
/// ProcessAndXorBlock fully unrolled the loop then the parameter
|
||||
/// <tt>i</tt> would be unnecessary.
|
||||
template <unsigned int RR, unsigned int KW, class T>
|
||||
inline void CHAM_EncRound(T x[4], const T k[KW], unsigned int i)
|
||||
{
|
||||
CRYPTOPP_CONSTANT(IDX0 = (RR+0) % 4);
|
||||
CRYPTOPP_CONSTANT(IDX1 = (RR+1) % 4);
|
||||
CRYPTOPP_CONSTANT(IDX3 = (RR+3+1) % 4);
|
||||
CRYPTOPP_CONSTANT(R1 = (RR % 2 == 0) ? 1 : 8);
|
||||
CRYPTOPP_CONSTANT(R2 = (RR % 2 == 0) ? 8 : 1);
|
||||
|
||||
// Follows conventions in the ref impl
|
||||
const T kk = k[i % KW];
|
||||
const T aa = x[IDX0] ^ static_cast<T>(i);
|
||||
const T bb = rotlConstant<R1>(x[IDX1]) ^ kk;
|
||||
x[IDX3] = rotlConstant<R2>(static_cast<T>(aa + bb));
|
||||
}
|
||||
|
||||
/// \brief CHAM decryption round
|
||||
/// \tparam RR the round number residue
|
||||
/// \tparam KW the number of key words
|
||||
/// \tparam T words type
|
||||
/// \param x the state array
|
||||
/// \param k the subkey table
|
||||
/// \param i the round number
|
||||
/// \details CHAM_DecRound applies the decryption round to the cipher text.
|
||||
/// RR is the "round residue" and it is used modulo 4. ProcessAndXorBlock
|
||||
/// may provide a fully unrolled decryption transformation, or provide
|
||||
/// a transformation that loops using multiples of 4 decryption rounds.
|
||||
/// \details CHAM_DecRound calculates indexes into the x[] array based
|
||||
/// on the round number residue. There is no need for the assignments
|
||||
/// that shift values in preparations for the next round.
|
||||
/// \details CHAM_DecRound depends on the round number. The actual round
|
||||
/// being executed is passed through the parameter <tt>i</tt>. If
|
||||
/// ProcessAndXorBlock fully unrolled the loop then the parameter
|
||||
/// <tt>i</tt> would be unnecessary.
|
||||
template <unsigned int RR, unsigned int KW, class T>
|
||||
inline void CHAM_DecRound(T x[4], const T k[KW], unsigned int i)
|
||||
{
|
||||
CRYPTOPP_CONSTANT(IDX0 = (RR+0) % 4);
|
||||
CRYPTOPP_CONSTANT(IDX1 = (RR+1) % 4);
|
||||
CRYPTOPP_CONSTANT(IDX3 = (RR+3+1) % 4);
|
||||
CRYPTOPP_CONSTANT(R1 = (RR % 2 == 0) ? 8 : 1);
|
||||
CRYPTOPP_CONSTANT(R2 = (RR % 2 == 0) ? 1 : 8);
|
||||
|
||||
// Follows conventions in the ref impl
|
||||
const T kk = k[i % KW];
|
||||
const T aa = rotrConstant<R1>(x[IDX3]);
|
||||
const T bb = rotlConstant<R2>(x[IDX1]) ^ kk;
|
||||
x[IDX0] = static_cast<T>(aa - bb) ^ static_cast<T>(i);
|
||||
}
|
||||
|
||||
ANONYMOUS_NAMESPACE_END
|
||||
|
||||
NAMESPACE_BEGIN(CryptoPP)
|
||||
|
||||
#if CRYPTOPP_CHAM128_ADVANCED_PROCESS_BLOCKS
|
||||
# if (CRYPTOPP_SSSE3_AVAILABLE)
|
||||
extern size_t CHAM64_Enc_AdvancedProcessBlocks_SSSE3(const word16* subKeys, size_t rounds,
|
||||
const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
|
||||
|
||||
extern size_t CHAM64_Dec_AdvancedProcessBlocks_SSSE3(const word16* subKeys, size_t rounds,
|
||||
const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
|
||||
|
||||
extern size_t CHAM128_Enc_AdvancedProcessBlocks_SSSE3(const word32* subKeys, size_t rounds,
|
||||
const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
|
||||
|
||||
extern size_t CHAM128_Dec_AdvancedProcessBlocks_SSSE3(const word32* subKeys, size_t rounds,
|
||||
const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
|
||||
# endif // CRYPTOPP_SSSE3_AVAILABLE
|
||||
#endif // CRYPTOPP_CHAM128_ADVANCED_PROCESS_BLOCKS
|
||||
|
||||
void CHAM64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs ¶ms)
|
||||
{
|
||||
CRYPTOPP_UNUSED(params);
|
||||
m_kw = keyLength/sizeof(word16);
|
||||
m_rk.New(2*m_kw);
|
||||
|
||||
for (size_t i = 0; i < m_kw; userKey += sizeof(word32))
|
||||
{
|
||||
// Do not cast the buffer. It will SIGBUS on some ARM and SPARC.
|
||||
const word32 rk = GetWord<word32>(false, BIG_ENDIAN_ORDER, userKey);
|
||||
|
||||
const word16 rk1 = rk >> 16;
|
||||
m_rk[i] = rk1 ^ rotlConstant<1>(rk1) ^ rotlConstant<8>(rk1);
|
||||
m_rk[(i + m_kw) ^ 1] = rk1 ^ rotlConstant<1>(rk1) ^ rotlConstant<11>(rk1);
|
||||
i++;
|
||||
|
||||
const word16 rk2 = rk & 0xffff;
|
||||
m_rk[i] = rk2 ^ rotlConstant<1>(rk2) ^ rotlConstant<8>(rk2);
|
||||
m_rk[(i + m_kw) ^ 1] = rk2 ^ rotlConstant<1>(rk2) ^ rotlConstant<11>(rk2);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void CHAM64::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
// Do not cast the buffer. It will SIGBUS on some ARM and SPARC.
|
||||
GetBlock<word16, BigEndian> iblock(inBlock);
|
||||
iblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
|
||||
const int R = 80;
|
||||
for (int i = 0; i < R; i+=16)
|
||||
{
|
||||
CHAM_EncRound< 0, 16>(m_x.begin(), m_rk.begin(), i+0);
|
||||
CHAM_EncRound< 1, 16>(m_x.begin(), m_rk.begin(), i+1);
|
||||
CHAM_EncRound< 2, 16>(m_x.begin(), m_rk.begin(), i+2);
|
||||
CHAM_EncRound< 3, 16>(m_x.begin(), m_rk.begin(), i+3);
|
||||
CHAM_EncRound< 4, 16>(m_x.begin(), m_rk.begin(), i+4);
|
||||
CHAM_EncRound< 5, 16>(m_x.begin(), m_rk.begin(), i+5);
|
||||
CHAM_EncRound< 6, 16>(m_x.begin(), m_rk.begin(), i+6);
|
||||
CHAM_EncRound< 7, 16>(m_x.begin(), m_rk.begin(), i+7);
|
||||
CHAM_EncRound< 8, 16>(m_x.begin(), m_rk.begin(), i+8);
|
||||
CHAM_EncRound< 9, 16>(m_x.begin(), m_rk.begin(), i+9);
|
||||
CHAM_EncRound<10, 16>(m_x.begin(), m_rk.begin(), i+10);
|
||||
CHAM_EncRound<11, 16>(m_x.begin(), m_rk.begin(), i+11);
|
||||
CHAM_EncRound<12, 16>(m_x.begin(), m_rk.begin(), i+12);
|
||||
CHAM_EncRound<13, 16>(m_x.begin(), m_rk.begin(), i+13);
|
||||
CHAM_EncRound<14, 16>(m_x.begin(), m_rk.begin(), i+14);
|
||||
CHAM_EncRound<15, 16>(m_x.begin(), m_rk.begin(), i+15);
|
||||
}
|
||||
|
||||
PutBlock<word16, BigEndian> oblock(xorBlock, outBlock);
|
||||
oblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
}
|
||||
|
||||
void CHAM64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
// Do not cast the buffer. It will SIGBUS on some ARM and SPARC.
|
||||
GetBlock<word16, BigEndian> iblock(inBlock);
|
||||
iblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
|
||||
const int R = 80;
|
||||
for (int i = R-1; i >=0 ; i-=16)
|
||||
{
|
||||
CHAM_DecRound<15, 16>(m_x.begin(), m_rk.begin(), i-0);
|
||||
CHAM_DecRound<14, 16>(m_x.begin(), m_rk.begin(), i-1);
|
||||
CHAM_DecRound<13, 16>(m_x.begin(), m_rk.begin(), i-2);
|
||||
CHAM_DecRound<12, 16>(m_x.begin(), m_rk.begin(), i-3);
|
||||
CHAM_DecRound<11, 16>(m_x.begin(), m_rk.begin(), i-4);
|
||||
CHAM_DecRound<10, 16>(m_x.begin(), m_rk.begin(), i-5);
|
||||
CHAM_DecRound< 9, 16>(m_x.begin(), m_rk.begin(), i-6);
|
||||
CHAM_DecRound< 8, 16>(m_x.begin(), m_rk.begin(), i-7);
|
||||
CHAM_DecRound< 7, 16>(m_x.begin(), m_rk.begin(), i-8);
|
||||
CHAM_DecRound< 6, 16>(m_x.begin(), m_rk.begin(), i-9);
|
||||
CHAM_DecRound< 5, 16>(m_x.begin(), m_rk.begin(), i-10);
|
||||
CHAM_DecRound< 4, 16>(m_x.begin(), m_rk.begin(), i-11);
|
||||
CHAM_DecRound< 3, 16>(m_x.begin(), m_rk.begin(), i-12);
|
||||
CHAM_DecRound< 2, 16>(m_x.begin(), m_rk.begin(), i-13);
|
||||
CHAM_DecRound< 1, 16>(m_x.begin(), m_rk.begin(), i-14);
|
||||
CHAM_DecRound< 0, 16>(m_x.begin(), m_rk.begin(), i-15);
|
||||
}
|
||||
|
||||
PutBlock<word16, BigEndian> oblock(xorBlock, outBlock);
|
||||
oblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
}
|
||||
|
||||
std::string CHAM128::Base::AlgorithmProvider() const
|
||||
{
|
||||
#if defined(CRYPTOPP_SSSE3_AVAILABLE)
|
||||
if (HasSSSE3())
|
||||
return "SSSE3";
|
||||
#endif
|
||||
return "C++";
|
||||
}
|
||||
|
||||
void CHAM128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs ¶ms)
|
||||
{
|
||||
CRYPTOPP_UNUSED(params);
|
||||
m_kw = keyLength/sizeof(word32);
|
||||
m_rk.New(2*m_kw);
|
||||
|
||||
for (size_t i = 0; i < m_kw; userKey += sizeof(word32))
|
||||
{
|
||||
// Do not cast the buffer. It will SIGBUS on some ARM and SPARC.
|
||||
const word32 rk = GetWord<word32>(false, BIG_ENDIAN_ORDER, userKey);
|
||||
m_rk[i] = rk ^ rotlConstant<1>(rk) ^ rotlConstant<8>(rk);
|
||||
m_rk[(i + m_kw) ^ 1] = rk ^ rotlConstant<1>(rk) ^ rotlConstant<11>(rk);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void CHAM128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
// Do not cast the buffer. It will SIGBUS on some ARM and SPARC.
|
||||
GetBlock<word32, BigEndian> iblock(inBlock);
|
||||
iblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
|
||||
switch (m_kw)
|
||||
{
|
||||
case 4: // 128-bit key
|
||||
{
|
||||
const int R = 80;
|
||||
for (int i = 0; i < R; i+=8)
|
||||
{
|
||||
CHAM_EncRound<0, 8>(m_x.begin(), m_rk.begin(), i+0);
|
||||
CHAM_EncRound<1, 8>(m_x.begin(), m_rk.begin(), i+1);
|
||||
CHAM_EncRound<2, 8>(m_x.begin(), m_rk.begin(), i+2);
|
||||
CHAM_EncRound<3, 8>(m_x.begin(), m_rk.begin(), i+3);
|
||||
CHAM_EncRound<4, 8>(m_x.begin(), m_rk.begin(), i+4);
|
||||
CHAM_EncRound<5, 8>(m_x.begin(), m_rk.begin(), i+5);
|
||||
CHAM_EncRound<6, 8>(m_x.begin(), m_rk.begin(), i+6);
|
||||
CHAM_EncRound<7, 8>(m_x.begin(), m_rk.begin(), i+7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: // 256-bit key
|
||||
{
|
||||
const int R = 96;
|
||||
for (int i = 0; i < R; i+=16)
|
||||
{
|
||||
CHAM_EncRound< 0, 16>(m_x.begin(), m_rk.begin(), i+0);
|
||||
CHAM_EncRound< 1, 16>(m_x.begin(), m_rk.begin(), i+1);
|
||||
CHAM_EncRound< 2, 16>(m_x.begin(), m_rk.begin(), i+2);
|
||||
CHAM_EncRound< 3, 16>(m_x.begin(), m_rk.begin(), i+3);
|
||||
CHAM_EncRound< 4, 16>(m_x.begin(), m_rk.begin(), i+4);
|
||||
CHAM_EncRound< 5, 16>(m_x.begin(), m_rk.begin(), i+5);
|
||||
CHAM_EncRound< 6, 16>(m_x.begin(), m_rk.begin(), i+6);
|
||||
CHAM_EncRound< 7, 16>(m_x.begin(), m_rk.begin(), i+7);
|
||||
CHAM_EncRound< 8, 16>(m_x.begin(), m_rk.begin(), i+8);
|
||||
CHAM_EncRound< 9, 16>(m_x.begin(), m_rk.begin(), i+9);
|
||||
CHAM_EncRound<10, 16>(m_x.begin(), m_rk.begin(), i+10);
|
||||
CHAM_EncRound<11, 16>(m_x.begin(), m_rk.begin(), i+11);
|
||||
CHAM_EncRound<12, 16>(m_x.begin(), m_rk.begin(), i+12);
|
||||
CHAM_EncRound<13, 16>(m_x.begin(), m_rk.begin(), i+13);
|
||||
CHAM_EncRound<14, 16>(m_x.begin(), m_rk.begin(), i+14);
|
||||
CHAM_EncRound<15, 16>(m_x.begin(), m_rk.begin(), i+15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CRYPTOPP_ASSERT(0);
|
||||
}
|
||||
|
||||
PutBlock<word32, BigEndian> oblock(xorBlock, outBlock);
|
||||
oblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
}
|
||||
|
||||
void CHAM128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
|
||||
{
|
||||
// Do not cast the buffer. It will SIGBUS on some ARM and SPARC.
|
||||
GetBlock<word32, BigEndian> iblock(inBlock);
|
||||
iblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
|
||||
switch (m_kw)
|
||||
{
|
||||
case 4: // 128-bit key
|
||||
{
|
||||
const int R = 80;
|
||||
for (int i = R-1; i >= 0; i-=8)
|
||||
{
|
||||
CHAM_DecRound<7, 8>(m_x.begin(), m_rk.begin(), i-0);
|
||||
CHAM_DecRound<6, 8>(m_x.begin(), m_rk.begin(), i-1);
|
||||
CHAM_DecRound<5, 8>(m_x.begin(), m_rk.begin(), i-2);
|
||||
CHAM_DecRound<4, 8>(m_x.begin(), m_rk.begin(), i-3);
|
||||
CHAM_DecRound<3, 8>(m_x.begin(), m_rk.begin(), i-4);
|
||||
CHAM_DecRound<2, 8>(m_x.begin(), m_rk.begin(), i-5);
|
||||
CHAM_DecRound<1, 8>(m_x.begin(), m_rk.begin(), i-6);
|
||||
CHAM_DecRound<0, 8>(m_x.begin(), m_rk.begin(), i-7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: // 256-bit key
|
||||
{
|
||||
const int R = 96;
|
||||
for (int i = R-1; i >= 0; i-=16)
|
||||
{
|
||||
CHAM_DecRound<15, 16>(m_x.begin(), m_rk.begin(), i-0);
|
||||
CHAM_DecRound<14, 16>(m_x.begin(), m_rk.begin(), i-1);
|
||||
CHAM_DecRound<13, 16>(m_x.begin(), m_rk.begin(), i-2);
|
||||
CHAM_DecRound<12, 16>(m_x.begin(), m_rk.begin(), i-3);
|
||||
CHAM_DecRound<11, 16>(m_x.begin(), m_rk.begin(), i-4);
|
||||
CHAM_DecRound<10, 16>(m_x.begin(), m_rk.begin(), i-5);
|
||||
CHAM_DecRound< 9, 16>(m_x.begin(), m_rk.begin(), i-6);
|
||||
CHAM_DecRound< 8, 16>(m_x.begin(), m_rk.begin(), i-7);
|
||||
CHAM_DecRound< 7, 16>(m_x.begin(), m_rk.begin(), i-8);
|
||||
CHAM_DecRound< 6, 16>(m_x.begin(), m_rk.begin(), i-9);
|
||||
CHAM_DecRound< 5, 16>(m_x.begin(), m_rk.begin(), i-10);
|
||||
CHAM_DecRound< 4, 16>(m_x.begin(), m_rk.begin(), i-11);
|
||||
CHAM_DecRound< 3, 16>(m_x.begin(), m_rk.begin(), i-12);
|
||||
CHAM_DecRound< 2, 16>(m_x.begin(), m_rk.begin(), i-13);
|
||||
CHAM_DecRound< 1, 16>(m_x.begin(), m_rk.begin(), i-14);
|
||||
CHAM_DecRound< 0, 16>(m_x.begin(), m_rk.begin(), i-15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CRYPTOPP_ASSERT(0);
|
||||
}
|
||||
|
||||
PutBlock<word32, BigEndian> oblock(xorBlock, outBlock);
|
||||
oblock(m_x[0])(m_x[1])(m_x[2])(m_x[3]);
|
||||
}
|
||||
|
||||
#if CRYPTOPP_CHAM128_ADVANCED_PROCESS_BLOCKS
|
||||
size_t CHAM128::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
|
||||
byte *outBlocks, size_t length, word32 flags) const
|
||||
{
|
||||
# if (CRYPTOPP_SSSE3_AVAILABLE)
|
||||
if (HasSSSE3()) {
|
||||
const size_t rounds = (m_kw == 4 ? 80 : 96);
|
||||
return CHAM128_Enc_AdvancedProcessBlocks_SSSE3(m_rk, rounds,
|
||||
inBlocks, xorBlocks, outBlocks, length, flags);
|
||||
}
|
||||
# endif // CRYPTOPP_SSSE3_AVAILABLE
|
||||
return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
|
||||
}
|
||||
|
||||
size_t CHAM128::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
|
||||
byte *outBlocks, size_t length, word32 flags) const
|
||||
{
|
||||
# if (CRYPTOPP_SSSE3_AVAILABLE)
|
||||
if (HasSSSE3()) {
|
||||
const size_t rounds = (m_kw == 4 ? 80 : 96);
|
||||
return CHAM128_Dec_AdvancedProcessBlocks_SSSE3(m_rk, rounds,
|
||||
inBlocks, xorBlocks, outBlocks, length, flags);
|
||||
}
|
||||
# endif // CRYPTOPP_SSSE3_AVAILABLE
|
||||
return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
|
||||
}
|
||||
#endif // CRYPTOPP_CHAM128_ADVANCED_PROCESS_BLOCKS
|
||||
|
||||
NAMESPACE_END
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user