
Часто при разработке на языке Swift мы сталкиваемся с отсутствием необходимых инструментов, которые давно стали привычными в других языках. В частности, чувствуется нехватка криптографических инструментов с хорошей историей и репутацией. В таком случае нам приходит на помощь возможность подключать и использовать в Swift библиотеки, написанные на языках Си и С++.
Библиотека bee2
является референсной имплементацией криптографических алгоритмов, стандартизированных в Республике Беларусь. Она написана на Си и распространяется под свободной лицензией Apache License, Version 2.0. B сообществе bcrypto
разрабатывается набор инструментов криптографической инфрастуктуры, в основе которых лежит библиотека bee2
. Там же возникла идея сделать доступным и удобным использование этой библиотеки на максимально возможном количестве платформ и языков программирования. Начало было положено в 2017 году с разработки библиотеки bee2j
, которая позволяет не только вызывать функции bee2
, но и встраивает их в криптографическую подсистему JCA (Java Cryptography Architecture) языка Java. Ниже я покажу, как подключить и использовать библиотеку bee2
в программах на языке Swift.
Поскольку одной из целей является покрытие максимального количества платформ, я не буду описывать привычную для Swift-программистов среду разработки XCode. Для создания примера вполне достаточно встроенной в язык системы управления пакетами, доступной везде, где поддерживается язык Swift (Linux, Windows и платформы Apple). Также предполагается, что библиотека bee2
установлена в систему, как описано в руководстве.
Для начала создадим пустой проект:
mkdir bee2swift
cd bee2swift
swift package init --type=executable
Далее в каталоге Sources
проекта создадим каталог для модуля библиотеки:
cd Sources
mkdir bee2
Также Swift требует отдельных каталогов для каждого модуля, если их больше одного. Поэтому нужно переместить основной файл программы в отдельный каталог:
mkdir bee2swift
mv main.swift bee2swift/
Затем в каталоге bee2
создаём два файла: заголовочный файл библиотеки bee2swift.h
и описание модуля module.modulemap
. В заголовочный файл включаем минимальное количество заголовочных файлов, необходимых для реализации примера выработки электронной цифровой подписи (ЭЦП).
#include "bee2/core/safe.h"
#include "bee2/core/mem.h"
#include "bee2/core/hex.h"
#include "bee2/crypto/belt.h"
#include "bee2/crypto/bign.h"
В описании модуля указываем созданный заголовочный файл и ссылку на библиотеку:
module bee2 {
umbrella header "bee2swift.h"
link "/usr/local/lib/libbee2_static.a"
}
Для подключения библиотеки в проект нужно добавить в файл Package.swift
объявление библиотеки и подключение зависимости для исполняемого файла:
targets: [
.systemLibrary(name: "bee2"),
.executableTarget(
name: "bee2swift", dependencies: ["bee2"]),
]
Подготовка закончена, теперь можно перейти к программе на языке Swift. Для этого добавим в программу импорт модуля и вызов функции из него:
import bee2
print(beltHash_keep())
Теперь можно проверить, что библиотека собирается и программа запускается. Для этого в каталоге проекта выполняем команды:
swift build
swift run
Для выработки ЭЦП необходимо выделить неуправляемую память под все данные, которые используются в функциях библиотеки:
let privkey = UnsafeMutableRawPointer.allocate(byteCount: 64, alignment: 4)
let pubkey = UnsafeMutableRawPointer.allocate(byteCount: 128, alignment: 4)
let hash = UnsafeMutableRawPointer.allocate(byteCount: 64, alignment: 4)
let der = UnsafeMutableRawPointer.allocate(byteCount: 512, alignment: 4)
let sig = UnsafeMutableRawPointer.allocate(byteCount: 64+32, alignment: 4)
Также понадобится строковый буфер для вывода информации на экран. Для него мы делаем привязку к массиву символов. Подробнее об этом я писал в посте.
let buf = UnsafeMutableRawPointer.allocate(byteCount: 128, alignment: 4)
let sbuf = buf.bindMemory(to:CChar.self, capacity:128)
Для структур языка Си Свифт создаёт классы. Ниже мы создаем экземпляр класса параметров и заполняем его значениями параметров стандартизованной кривой
bign-curve128v1
:
var params = bign_params()
var err = bignParamsStd(¶ms, "1.2.112.0.2.0.34.101.45.3.1")
print(params.l)
Для вывода обычного целого числа мы воспользовались функцией print
. Для вывода больших чисел мы должны их представить в шестнадцатеричной форме и вывести через строковый буфер:
hexFrom(buf, ¶ms.q, 32)
print(String(cString:sbuf))
Далее мы загружаем в переменную тестовое значение личного ключа и для проверки выводим соответствующее ему значение открытого ключа:
hexTo(privkey, "1F66B5B84B7339674533F0329C74F21834281FED0732429E0C79235FC273E269")
err = bignPubkeyCalc(pubkey, ¶ms, privkey)
hexFrom(buf, pubkey, 64)
print(String(cString:sbuf))
Для тестирования ЭЦП используется встроенный в библиотеку набор данных, указатель на которые возвращает функция beltH
. От этих данных вырабатывается хэш-значение, которое и будет подписываться.
let src = beltH()
err = beltHash(hash, src, 13)
hexFrom(buf, hash, 64)
print(String(cString:sbuf))
Для выработки ЭЦП также необходимо передать значение идентификатора использованного алгоритма хэширования в DER-кодировке:
var count: Int = 512
bignOidToDER(der, &count, "1.2.112.0.2.0.34.101.31.81")
print(count)
В предыдущей функции длина DER-кода запишется в переменную count
. Для целочисленных переменных, которые передаются в функции языка Си как параметры, необходимо явно указывать тип переменной.
Теперь всё готово к выработке ЭЦП. Здесь для простоты мы используем детерминированную ЭЦП из СТБ 34.101.45:
err = bignSign2(sig, ¶ms, der, count, hash, privkey, nil, 0)
hexFrom(buf, sig, 16*3)
print(String(cString:sbuf))
Для проверки подписи нужно использовать аналогичный набор параметров, но вместо личного ключа используется открытый. В результате выведется 0, если подпись корректная, или код ошибки.
err = bignVerify(¶ms, der, count, hash, sig, pubkey)
print(err)
В конце остается только освободить использованную пямять:
buf.deallocate()
privkey.deallocate()
pubkey.deallocate()
hash.deallocate()
der.deallocate()
sig.deallocate()
Полный код проекта доступен в репозитории. Надеюсь, что этот пример не только поможет подключать библиотеки в Swift, но и расширить область применения криптографии для увеличения безопасности ваших программ и данных.