该系统提供了多种组件,用于将QKD集成到安全系统中。它使用C++11编写,但采用GRPC接口,因此可以与多种不同语言进行集成。
要在本地运行该软件,请:
要克隆包含子模块的源代码:```bash git clone --recurse-submodules [email protected]:QComms/cqptoolkit.git
> 如果你没有使用 `--recurse-submodules` 进行克隆,可以通过在源码文件夹中运行 `git submodule update --init` 来更新子模块。
以下是编译该项目所需的依赖列表(请阅读下文以了解更详细的安装说明):```bash
sudo apt install pkg-config ca-certificates file build-essential cmake ninja-build libusb-1.0-0-dev libcurl4-openssl-dev \
libcrypto++-dev libcap-dev uuid-dev libssl-dev libsqlite3-dev libprotobuf-dev libgrpc++-dev \
libssl-dev protobuf-compiler protobuf-compiler-grpc checkinstall
mkdir build-cqptoolkit
cd build-cqptoolkit
cmake -G Ninja ../cqptoolkit && ninja
快速测试
从构建文件夹中,要运行两个站点(在同一台本地计算机上),每个站点都配备一个 QKD 设备,首先启动站点 "A",方法是启动一个站点代理并将一个 Alice “虚拟驱动程序” 连接到它: (如果已安装二进制文件,则从说明中省略命令的路径。)```bash ./src/Tools/SiteAgentRunner/SiteAgentRunner -p 8000 & ./src/Drivers/DummyQKDDriver/DummyQKDDriver -r localhost:8000 -a
(无输入内容,故输出空白)```bash
./src/Tools/SiteAgentRunner/SiteAgentRunner -p 8001 &
./src/Drivers/DummyQKDDriver/DummyQKDDriver -r localhost:8001 -b
这不会立即开始生成密钥,因为该系统设计为由管理系统控制,需要使用命令 SiteAgentCtl 建立连接。
默认情况下,如果 *SiteAgentRunner* 和 *DummyQKDDriver* 在未指定配置文件 JSON 字符串参数的情况下启动,应该会产生类似以下的结果:```json
{
"url": "<hostname>:8000",
"devices": [
{
"config": {
"id": "dummyqkd__0__16_alice",
"kind": "dummyqkd"
},
"controlAddress": "<hostname>:34219"
}
]
}
并且端口 8001 应该产生类似的结果```json
{
"url": ":8001",
"devices": [
{
"config": {
"id": "dummyqkd__0__16_bob",
"side": "Bob",
"kind": "dummyqkd"
},
"controlAddress": ":38367"
}
]
}
- 现在,可以通过调用以下代码来建立连接:
```py```bash
./src/Tools/SiteAgentCtl/SiteAgentCtl -c localhost:8000 -j localhost:8001
This will create a single hop from one site to the next, again more complex routes can be defined by using the -a option with a JSON string specifying the path.
After a few seconds there should be key available which can be tested by requesting a key.
NOTE: The -k parameter must be the url shown in the details of the second site, not "localhost:8001"```bash ./src/Tools/SiteAgentCtl/SiteAgentCtl -c localhost:8000 -k
hostname:8001
链接可以通过 unjoin 命令停止:```bash
./src/Tools/SiteAgentCtl/SiteAgentCtl -c localhost:8000 -u localhost:8001
请注意,即使密钥生成已停止,只要站点代理仍在运行,该密钥仍然可用。 可以使用上述相同的密钥请求命令来请求它。
加密示例
如上所述,在启动同一本地计算机上的站点代理和驱动程序后,并开始用于密钥交换的链接后,还可以测试加密功能。
hostname:8001现在启动 VPN 的 Alice 端,定义要创建的隧道。将打开两个端口,分别对应两侧的 9000 和 9001,任何进入这些端口的数据都将被加密、传输到另一端、解密并输出到另一个端口。```
./src/Tools/QTunnelServer/QTunnelServer --keystore-url=`hostname`:8000 --remote=localhost:9010 --start-node=tcpsrv://0.0.0.0:9000 --end-node=tcpsrv://0.0.0.0:9001
任何使用 tcp 通信的程序都可以使用这个端口,netcat 是一个简单的程序,它可以通过端口发送数据,在一端启动一个:``` nc localhost 9000
以及另一个方向上的:```
nc localhost 9001
在一侧输入的任何内容在按下回车后都会显示在另一侧。使用诸如 Wireshark 之类的工具检查通过端口 9000 和 9001 传输的数据包,将显示加密的数据以及使用的密钥 ID。
除了 tcpserv 之外,还可以创建其他形式的连接:
| 示例 | 描述 | | ============================= | ===================================================== | | tcpserv://0.0.0.0:1234 | 在端口 1234 上创建监听端口 | | tcp://127.0.01:1234 | 建立到本地主机 TCP 端口 1234 的连接 | | udp://0.0.0.0:1234 | 从此端口发送 UDP 数据包 | | tun://192.168.101.1/?netmask=255.255.255.0 | 创建带有 IP 地址的 IP 级隧道设备 | | tap://192.168.101.1/?netmask=255.255.255.0 | 创建以太网级 tap 设备 | | eth://eth0/?level=tcp | 创建原始套接字,级别可以是 tcp、ip 或 eth。 |
计划及已完成功能
希望这个项目能对科学研究和大型项目都有用。关于该项目的更多细节可在本文中找到。
若要为该项目做出贡献,请参阅贡献文件。
该系统目前适用于 Linux——未来计划支持 Windows。目前最简单的方式是从源码构建
在 [gitlab 镜像仓库][] 中有一个可直接运行的 Docker 镜像。你可以使用 sudo docker run -it --rm registry.gitlab.com/qcomms/cqptoolkit/runtime 运行。在末尾添加命令可直接运行某些内容,例如使用 QKDSim 运行 QKD 密钥生成模拟:```bash
sudo docker run -it --rm registry.gitlab.com/qcomms/cqptoolkit/runtime AlignmentTests
### Ubuntu 18.04+
你可以从 [Gitlab](https://gitlab.com/QComms/cqptoolkit/-/jobs/artifacts/master/download?job=package%3Adeb) 安装二进制包。解压 zip 文件并使用 `dpkg` 安装工具,它会抱怨缺少依赖项,但不用担心,第二行将会修复它们。```bash
sudo dpkg -i setup/*.deb build/gcc/CQP-*-Linux-{Algorithms,Networking,CQPToolkit,KeyManagement,QKDInterfaces,CQPUI,Simulate,Tools,UI,Drivers,IDQDevices}.deb
sudo apt install -fy
要安装开发文件(头文件和静态库),请运行 sudo dpkg -i build/gcc/CQP-*-Linux-*-dev.deb ; sudo apt install -fy 替代。
当然,如果你不想修改系统库和依赖版本,你可以在一个已安装所有依赖的 docker 容器 中构建,该容器已经包含了所有依赖:```bash sudo docker run -it registry.gitlab.com/qcomms/cqptoolkit/buildenv
否则,从源代码构建需要安装 `setup/setupbuild.sh` 中列出的依赖项,目前它适用于 Ubuntu 和 Arch Linux。
使用 [git][] 从 [gitlab](https://gitlab.com/QComms/cqptoolkit.git) 克隆源代码,然后使用 [CMake][] 和 [gnu make](https://www.gnu.org/software/make/) 构建:
`--recurse-submodules` 添加了可选附加组件——其中一些组件的访问权限仅限于 UoB 及其合作伙伴,但即使没有它们,构建也能正常工作。```bash
git clone --recurse-submodules https://gitlab.com/QComms/cqptoolkit.git
如果你想获取所有子模块,并且拥有适当的登录凭据,运行:```bash git submodule update --checkout
现在,你可以进入你的本地仓库,并使用脚本安装依赖项(可能需要更改文件权限):```bash
cd cqptoolkit/setup
./setupbuild.sh
然后,你可以将项目构建到一个新文件夹中:```bash mkdir build-cqptoolkit cd build-cqptoolkit cmake ../cqptoolkit && nice make -s -j
sudo make install
构建使用 [CMake][] 为不同平台生成 makefiles/解决方案等,并从空构建文件夹中调用,该文件夹将包含所有输出文件。调试构建会生成带有 "D" 后缀的包。
可以通过向 cmake 传递选项来控制构建,例如 `-DBUILD_TESTING=OFF`。运行 cmake 时使用 `-LH` 选项可列出可用开关。
要进行更改并开发库,建议安装 [QT Creator](http://doc.qt.io/qtcreator/),并通过 [选择 CMakeLists.txt 文件](https://codeyarns.com/2016/01/26/how-to-import-cmake-project-in-qt-creator/) 打开项目。建议启用并行构建,方法是进入 Projects->Build Steps->Details,并在工具参数中添加 `-j<number>`,参见 [so](https://stackoverflow.com/questions/8860712/setting-default-make-options-for-qt-creator)。
构建完成后,默认情况下,文件位于与项目文件夹同级的 `build-<项目名称>-<平台>-<目标>` 文件夹中。
> **关于 Ubuntu 上 protobuf + QT 的说明**
> 库 `qt5-gtk-platformtheme` 链接到旧版本的 protobuf,这将阻止我们的 QT 程序运行。
> 可以通过 `apt-get remove qt5-gtk-platformtheme` 移除这个可选依赖。
## 探索库
@startuml
title Applicaiton Overview
component "QKD Device Drivers" as drv
interface IDevice as idev
drv - idev
component "Device contol and\n Key storage" as sa
interface IKey as ikey
sa - ikey
sa ..> idev
package "Key consumers" as kc {
component "Custom VPN" as tun
component "Custom\nWeb Server" as nginx
component "HSM bridge" as hsm
}
tun ..> ikey
nginx ..> ikey
hsm ..> ikey
package Utilities {
component "Config/Control GUI" as gui
component "Simulators/Testing" as sim
component "Data extraction" as stats
gui -[hidden]down- sim
sim -[hidden]down- stats
}
@enduml
下面是一个流程图,帮助您找到与自己相关的领域,因为该项目涵盖了 QKD 和密钥管理的许多不同方面——欢迎贡献者推动该项目变得更加专业化。
QKD 需要某种形式的 [不可克隆](https://en.wikipedia.org/wiki/No-cloning_theorem) 通信,通常利用光纤电缆中的单光子实现。它们可以点对点或一对多运行,但本质上有物理位置(光纤终端所在处)——它们无法虚拟化!光子被传输或检测的点是安全系统的边界——几乎类似于网络的 [防火墙](https://en.wikipedia.org/wiki/Firewall_(computing))。一旦不可分割的光子被转换成一串比特以形成 [对称密钥](https://en.wikipedia.org/wiki/Key_(cryptography)),计算机安全的标准规则(如认证、访问控制等)便适用。不同之处在于,一旦这些密钥被生成,每个 QKD 设备都拥有一个 [无人知晓的数字](https://en.wikipedia.org/wiki/Shared_secret),且 [经过科学证明](https://arxiv.org/pdf/quant-ph/0003004.pdf)。
这种“防火墙”效应的本质在于,控制 QKD 设备的系统必须安全且被视为可信——也称为“可信节点”——划定界限的方式和范围可以从武装警卫到仅仅 [锁上服务器机房的门](https://www.youtube.com/watch?v=rnmcRTnTNC8)。
如果您看不到下面的图示,请访问 [在线文档](https://qcomms.gitlab.io/cqptoolkit/),也可以通过 `doc` 目标构建。
@startuml
title Where to start \n
skinparam activity {
StartColor #EF476F
BarColor #FFD166
EndColor #EF476F
BackgroundColor #06D6A0
BorderColor #118AB2
}
start
if (Do you have a QKD Device?) then (Yes)
if (Does your QKD device have a driver?) then (No)
:You will need to [[./index.html#CreatingDrivers create a driver]] which implements the
<b>IDriver</b> and <b>IReporting</b> interfaces.;
if (Use the library?) then (Yes)
:See the [[./index.html#RunningDummyQKDDriver DummyQKDDriver]] for an example.;
else (No)
endif
else (Yes)
endif
else (No)
:Check out how to run the
[[./index.html#RunningDummyQKDDriver DummyQKDDriver]];
endif
if (Do you want keys for multiple
locations in a network?) then (Yes: Sites)
:[[./index.html#Registering Register your driver]] with the site agent
using the <b>ISiteAgent</b> Interface
Keys can be obtained by using the [[./index.html#IKeyInterface IKey]] interface;
if(Do you want keys to be stored/persist between restarts?) then (Yes)
if (Do the keys need to be secured?) then (Yes)
:Use the [[./index.html#HSMs HSM storage]] options;
if (Is your HSM supported?) then (No)
:Create a driver that implements
the <b>IBackingStore</b> interface.
Add the driver creation to the
<b>BackingStoreFactory</b>;
else (Yes)
endif
else (No)
: When running SiteAgentRunner,
use the option ""-b file"" or
set ""backingStoreUrl"" to
""file:///filename.db"";
endif
else (No)
endif
else (No: Point-to-Point)
:Use the [[./index.html#IDeviceInterface IDevice interface]] to
control the driver for your system.
As keys become available they will
be sent via the call to <b>WaitForSession</b>.;
endif
if (Do you want to use the key for anything?) then (Yes)
:See the [[./index.html#Encryption encryption section]]
for an example of using the [[./index.html#IKeyInterface IKey interface]];
else (No)
endif
stop
@enduml
### 运行 DummyQKDDriver <a name="RunningDummyQKDDriver" />
本节仅对 DummyQKDDriver 模拟 QKD 设备输出的有用功能进行评论。请参考下一节中关于使用它们设置简单链接的引导示例。
与大多数程序一样,向其传递 `-h` 会显示可用选项。DummyQKDDriver 使用 cqp::DummyQKD 类对模拟的光子探测运行一套标准的后处理步骤。需要运行该程序的两个实例,一个用于 Alice,一个用于 Bob。
首先在端口 8000 上运行 Bob,通过调用:```bash
DummyQKDDriver -b -k 0.0.0.0:8000
现在运行 Alice,告诉她连接到 Bob 并开始交换密钥(手动模式):```bash DummyQKDDriver -a -m localhost:8000
如果一切正常,你会看到大量类似这样的错误信息:`ERROR: OnKeyGeneration No listener for generated key`。这是因为虽然以这种方式运行系统能显示它在工作,但实际用处不大——生成的密钥无处存放。驱动程序设计为供其他组件使用。
该项目有一套名为 [Site Agents](#SiteAgents) 的密钥管理系统,你也可以通过 [IDevice interface](#IDeviceInterface) 直接与驱动程序交互。
### 配置 Site Agents <a name="SiteAgents" />
Site agents、驱动程序和 VPN 隧道需要为网络中的每个节点进行参数化配置,使用 JSON 字符串文件。因为默认的不带参数命令并未指定所有必要字段,仅适用于同一台本地机器上的测试。
Site agents 可以通过 `SiteAgentRunner` 运行,我们可以运行两个站点,使用:```bash
SiteAgentRunner -c site-a.json
以及:```bash SiteAgentRunner -c site-b.json
每个 JSON 文件都相应地指定了 Alice 和 Bob 的不同端口(例如 9000/9001)。JSON 字符串看起来像这样:```json
{
"name":"",
"id":"",
"netManUri":"",
"bindAddress":"0.0.0.0",
"listenPort":9000,
"connectionAddress":"",
"credentials": {},
"useAutoDiscover":false,
"backingStoreUrl":"",
"fallbackKey":""
}
现在我们可以将我们的 DummyQKDDriver 附加到每个站点:```bash DummyQKDDriver -c driver_config-a.json # run as Alice, register with site agent
以及:```bash
DummyQKDDriver -c driver_config-b.json # run as Bob, register with site agent
每个 JSON 文件适当指示 Alice 和 Bob 的不同端口(例如 9000/9001)。JSON 字符串如下所示:```json
{
"controlParams": {
"config": {
"id": "dummyqkd__0__16_alice",
"side": "Alice",
"switchName": "",
"switchPort": "",
"kind": "dummyqkd",
"bytesPerKey": 0
},
"controlAddress": "'hostnameIP':4423",
"siteAgentAddress": "127.0.0.1:9000"
},
}
> 如果没有防火墙需要处理,控制地址字段可以为 0.0.0.0:0。如果是这样,则需要指明主机的实际IP,并选择一个防火墙不会拦截的合适端口。
目前还不会发生任何事情,因为站点代理不知道如何处理这些设备。我们可以通过发送 cqp::ISiteAgent::StartNode 命令来指示它们开始生成密钥(-b 表示开始):```bash
SiteAgentCtl -c localhost:9000 -b '{"hops":[{"first":{"site":"'hostname':9000","deviceId":"dummyqkd__0__16_alice"},"second":{"site":"'hostname':9001","deviceId":"dummyqkd__0__16_bob"}}]}'
计算机必须能够解析主机名,并且这里应使用主机名而非硬编码的IP地址。因此,如果尚未配置,应在两台机器上修改/etc/hostnames文件。
这是一种冗长的方式来描述将A连接到B,但它非常强大,可以通过指定多个跳转,从设备链中生成端到端的安全密钥。可以在站点代理的"staticHops"配置字段中指定此JSON字符串,以便在所有设备可用时自动完成此操作。
然后可以通过以下方式停止该链接:```bash SiteAgentCtl -c localhost:9000 -e '{"hops":[{"first":{"site":"'hostname':9000","deviceId":"dummyqkd__0__16_alice"},"second":{"site":"'hostname':9001","deviceId":"dummyqkd__0__16_bob"}}]}'
> 注意使用 `-e` 而不是 `-b` 来*结束*链接而不是*开始*链接。
更复杂的设置可以通过自己实现 cqp::remote::INetworkManager 接口来发出命令。来自设备的反馈通过同一套接字上的 cqp::remote::IReporting 接口传递,这样你就可以对系统变化做出反应。在[这里](#Reporting)你可以看到如何使用 StatsDump 工具从报告接口中提取信息。
### 创建驱动程序 <a name="CreatingDrivers" />
驱动应用程序是内部设备接口(cqp::IQKDDevice)和外部 cqp::remote::IDevice 接口之间的桥梁。cqp::RemoteQKDDevice 类为你处理大部分工作,应用程序必须处理设备的配置和创建。
真正的工作在于创建驱动程序来设置设备和读取密钥。如果你的设备只产生原始检测结果,那么你需要配置一个[处理管道](#ProcessingPipelines),例如 cqp::DummyQKD 或 cqp::PhotonDetectorMk1 和 cqp::LEDAliceMk1。如果你的设备生成可直接使用的密钥(如 cqp::Clavis3Device),你需要读取它并通过 cqp::IKeyCallback 接口发布它(使用 cqp::KeyPublisher)。
这两种方法都需要某种形式的会话管理,由 cqp::session::SessionController 和 cqp::session::AliceSessionController 提供,它们实现了 cqp::ISessionController 和 cqp::remote::ISession 接口,被 cqp::RemoteQKDDevice 用来启动和停止设备及其对端。这些通常就足够了,但在某些情况下,它们需要专门化以满足设备需求。
@startuml Readme_Drivers
title Anatomy of a driver
package Application {
namespace cqp #DDDDDD {
class RemoteQKD
interface IQKDDevice {
GetSessionController()
}
class "SessionController" as session
interface "IDetector::Service" as detServ {
StartDetecting()
StopDetecting()
}
class "Provider<IDetectionEventCallback>" as provider {
Attach()
Dettach()
Emit()
}
RemoteQKD .r.> IQKDDevice : uses
IQKDDevice -r[hidden]-> session
}
class Main {
main()
}
class MyDriver
class Detector
MyDriver .u.|> cqp.IQKDDevice
MyDriver o-u-> cqp.session
Detector .u.|> cqp.detServ
Detector -u-|> cqp.provider
Main o-> MyDriver
MyDriver o-> Detector
Main o-u-> cqp.RemoteQKD
note bottom of MyDriver
In this case the driver is a simple detector
which produces detection. Post processing detail not shown.
MyDriver pull together all the parts to run the driver.
end note
note bottom of Detector
The detector controls the device
and outputs the data using the Provider
end note
}
@enduml
### 注册驱动程序 <a name="Registering" />
在编写本文时,所有驱动程序都可以使用 `-r` 选项向站点代理注册。这会导致 cqp::RemoteQKDDevice 在 cqp::SiteAgent 上调用 cqp::remote::ISiteAgent::RegisterDevice,然后它将使用 cqp::remote::ISession 接口来启动/停止设备。
### IDevice 接口 <a name="IDeviceInterface" />
此接口允许比通过站点代理更直接地访问设备。设备生成的密钥会立即反馈给调用者。首先调用 cqp::remote::IDevice::WaitForSession,然后调用 cqp::remote::IDevice::RunSession。调用 cqp::remote::IDevice::EndSession 停止生成密钥。
### HSMs <a name="HSMs" />
[HSMs](https://en.wikipedia.org/wiki/Hardware_security_module) 是物理安全的数字保险库存储设备。它们有一个标准接口叫 [PKCS#11](https://en.wikipedia.org/wiki/PKCS_11)。每个制造商都有自己的接口,对 PKCS#11 的支持参差不齐,不过有一个我们用作参考的软件实现叫 [SoftHSM2](https://www.opendnssec.org/softhsm/)。cqp::keygen::HSMStore 类提供了一个实现,将 cqp::SiteAgent 与 cqp::IBackingStore 接口连接起来。
### IKey 接口 <a name="IKeyInterface" />
站点向多个端点提供密钥(可通过调用 cqp::remote::IKey::GetKeyStores 检索可用的密钥存储),可以通过 cqp::remote::IKey 接口请求密钥。一旦请求了新密钥,另一方只能将其作为现有密钥检索——这可以防止密钥冲突和误用。
### 加密 <a name="Encryption" />
有关具体实现的更多细节,请参见:
* [Tunnels](https://gitlab.com/qcomms/cqptoolkit/-/blob/master/Tunnels.md)
### 报告 <a name="Reporting" />
系统值的变更通过 cqp::remote::IReporting 接口对外发布,并在内部通过 cqp::stats::Stat 类发布。你可以通过设置 cqp::remote::ReportingFilter::listIsExclude 字段为 `true` 来注册所有统计信息,或者指定特定的过滤器。
### 处理管道 <a name="ProcessingPipelines" />
标准 [BB84 QKD 协议](https://en.wikipedia.org/wiki/BB84) 需要后处理步骤将原始检测结果转换为可用密钥。
- **对齐** 找到真正传输的开始和结束。
+ 调整时序差异/漂移等。
+ 补偿发送方和接收方之间的相位或其他差异等。
- **筛分** 丢弃无效检测
- **纠错** 纠正错误检测——不泄露值
- **隐私放大** 对密钥进行哈希,使任何泄露的比特无效
处理管道的一个示例可以在 cqp::DummyQKD::ProcessingChain 中看到。
## 构建
构建由 gitlab 的[持续集成系统](https://about.gitlab.com/product/continuous-integration/)管理,定义在 `.gitlab-ci.yml` 文件中。Docker 镜像可以通过运行 `setup/makeDocker.sh` 手动构建。
### Windows
这是正在进行的工作。
该设置已在 Window 10 和 Windows Server 2016 上使用 Visual Studio 15 (2016) 进行了测试。
支持以下配置
| OS | VS 2017 | QT Creator | Codeblocks |
|:----------|-----------|---------------|---------------|
| Linux | | gcc | gcc |
| Windows | MSVC | MSVC / MSYS2 | MSYS2-Mingw |
#### IDE
- [Visual Studio 2016][]
+ 运行一次 [InstallVcPkg.bat](https://gitlab.com/qcomms/cqptoolkit/-/blob/master/build/vs2017_x64/InstallVcPkg.bat) 脚本以在 C:\vcpkg 中安装 vcpkg
+ 运行 [SetupMSBuild.bat](https://gitlab.com/qcomms/cqptoolkit/-/blob/master/build/vs2017_x64/SetupMSBuild.bat) 脚本
+ 打开 [cqp.sln](https://gitlab.com/qcomms/cqptoolkit/-/blob/master/build/vs2017_x64/cqp.sln) 解决方案
+ 选择 Build->Solution
- [QT Creator for windows][]
+ QT Creator 可以使用原生的 Microsoft 编译器或 MSYS2。单独安装 MSYS2,你不需要安装 QTCreator 安装程序自带的 minGW 编译器。或者安装 [Windows 10 SDK][]
+ 在组件菜单下,选择你使用的编译器对应的组件。例如 msvc2017
+ 使用"打开项目",选择源代码树根目录的 `CMakeLists.txt` 文件。
- [MSYS2][] 和 [Codeblocks][]
+ 安装 [MSYS2][] 包。
+ 运行 [installMSYS2Dependencies.bat](https://gitlab.com/qcomms/cqptoolkit/-/blob/master/build/installMSYS2Dependencies.bat) 脚本
+ 在 Settings->Debugger 下 -> 创建一个名为 "MSYS2 GDB" 的配置
- 将调试器指向 gdb.exe(例如 C:\\msys64\\mingw64\\bin\\gdb.exe)
+ 在 Settings->Compiler 下
+ 复制 GCC 编译器设置,将其命名为 "GCC - Old"
+ 将工具链安装目录更改为 MSYS2 的 mingw64 安装位置(例如 C:\\msys64\\mingw64)
+ 在 "Program Files" 下的每个字段中,除了 "make" 程序,删除 `mingw32-` 前缀。
+ 在 make 字段中,添加 -j 开关以支持多核构建:`mingw32-make.exe -j`
+ 选择之前创建的 "MSYS2 GDB" 调试器
+ 如果你更喜欢编译器输出可读,将 "Other Settings"->"Compiler Logging" 设置为 "Task description"
+ 在 CodeBlock 构建目录 (\\build\\CodeBlocks) 中
+ 运行 `SetupCodeBlocks-MSYS2.bat` 文件
+ 打开 "CQP.cbp" CodeBlocks 项目文件。
如果你的项目文件有深文件夹结构(这是一个 bug),可以通过右键单击工作区并取消选中"显示文件夹如同在磁盘上"并选中"隐藏文件夹名称"来改善。
## Citing this software```
@Manual{,
title = {CQPToolkit: A QKD toolkit library},
author = {{Richard Collins, University of Bristol, UK}},
organization = {University of Bristol},
address = {Bristol, UK},
year = 2018,
url = {https://gitlab.com/QComms}
}