add crypto library

This commit is contained in:
dagger 2026-08-22 10:50:08 +07:00 • committed by dagger
parent 2c6cffcf19
commit a23c4d4159
17 changed files with 2839 additions and 7 deletions

View file

@ -14,7 +14,8 @@ Subsections:
- [base64](scripting/builtins/libbase64.md)
- [bjson, json, toml, yaml](scripting/filesystem.md)
- [block](scripting/builtins/libblock.md)
- [byteutil](scripting/builtins/libbyteutil.md)
- [byteutil](scripting/builtins/libbyteutil.md)
- [crypto](scripting/builtins/libcrypto.md)
- [cameras](scripting/builtins/libcameras.md)
- [ctypes](scripting/builtins/libctypes.md)
- [entities](scripting/builtins/libentities.md)

View file

@ -0,0 +1,140 @@
# *crypto* library
The library provides common cryptographic functions for mods
TLS and HTTP are not part of this library. They can use crypto for expensive
operations
Keys, signatures, hashes and encrypted data are passed as regular Lua strings.
Strings may contain zero bytes
The current API version is available in `crypto.API_VERSION`
## Hashing
```lua
crypto.sha256(data: str) -> str
crypto.sha384(data: str) -> str
crypto.sha512(data: str) -> str
crypto.md5(data: str) -> str
crypto.hash(hash: str, data: str) -> str
crypto.hmac(hash: str, key: str, data: str) -> str
```
Supported names are `SHA256`, `SHA384`, `SHA512` and `MD5`
MD5 is provided for old formats. SHA256 should be used for new data
Large data can be hashed in parts
```lua
local hash = crypto.hash_new("SHA256")
hash:update(part1)
hash:update(part2)
local result = hash:final()
```
Call `reset` before using the context again
## Signatures
```lua
crypto.ed25519_keypair() -> private_key, public_key
crypto.ed25519_public(private_key: str) -> str
crypto.ed25519_sign(private_key: str, message: str) -> str
crypto.ed25519_verify(public_key: str, message: str, signature: str)
-> true
-> false, error
crypto.ecdsa_keypair(curve: str) -> private_key, public_key
crypto.ecdsa_public(curve: str, private_key: str) -> str
crypto.ecdsa_sign(curve: str, private_key: str, message: str, hash: str) -> str
crypto.ecdsa_verify(curve: str, public_key: str, message: str,
signature: str, hash: str)
-> true
-> false, error
crypto.rsa_pkcs1_verify(hash: str, modulus: str, exponent: str,
message: str, signature: str)
-> true
-> false, error
crypto.rsa_pss_verify(hash: str, modulus: str, exponent: str,
message: str, signature: str, salt_length: int)
-> true
-> false, error
```
Ed25519 keys are 32 bytes and signatures are 64 bytes
ECDSA supports `P-256`, `P-384` and `P-521`. Public keys use the uncompressed
SEC1 format and signatures use DER
RSA modulus and exponent use big endian. A `salt_length` of `-1` uses the hash
size. RSA key generation is not provided, Ed25519 is easier for new keys
## Key exchange
```lua
crypto.x25519_keypair() -> private_key, public_key
crypto.x25519_public(private_key: str) -> str
crypto.x25519(private_key: str, peer_public_key: str) -> str
crypto.x25519_shared(private_key: str, peer_public_key: str) -> str
crypto.p256_keypair() -> private_key, public_key
crypto.p256_public(private_key: str) -> str
crypto.p256_shared(private_key: str, peer_public_key: str) -> str
```
`x25519` and `x25519_shared` do the same operation
Pass the shared secret through HKDF before using it as a key
## Encryption
```lua
crypto.aes_gcm_encrypt(key: str, nonce: str, aad: str, plaintext: str) -> str
crypto.aes_gcm_decrypt(key: str, nonce: str, aad: str, ciphertext: str)
-> plaintext
-> nil, error
crypto.chacha20_poly1305_encrypt(key: str, nonce: str, aad: str,
plaintext: str) -> str
crypto.chacha20_poly1305_decrypt(key: str, nonce: str, aad: str,
ciphertext: str)
-> plaintext
-> nil, error
```
AES GCM accepts 16 and 32 byte keys
ChaCha20 Poly1305 uses a 32 byte key and a 12 byte nonce
A 16 byte tag is appended to ciphertext. Decryption returns nothing when the
tag is invalid
Never use the same nonce twice with the same key
## Other functions
```lua
crypto.random_bytes(length: int) -> str
crypto.constant_time_equal(left: str, right: str) -> bool
crypto.hkdf_extract(hash: str, salt: str, ikm: str) -> str
crypto.hkdf_expand(hash: str, prk: str, info: str, length: int) -> str
crypto.pbkdf2(hash: str, password: str, salt: str,
iterations: int, length: int) -> str
crypto.scrypt(password: str, salt: str, n: int, r: int, p: int,
length: int, [optional]max_memory: int=0) -> str
crypto.features() -> table
```
Use scrypt or PBKDF2 with a random salt for passwords. A regular SHA256 hash is
not suitable for password storage
`features` returns the OpenSSL version and available functions
An invalid signature returns `false, "invalid_signature"`
An invalid encryption tag returns `nil, "authentication_failed"`
Invalid arguments raise a regular Lua error

View file

@ -15,6 +15,7 @@
- [bjson, json, toml, yaml](scripting/filesystem.md)
- [block](scripting/builtins/libblock.md)
- [byteutil](scripting/builtins/libbyteutil.md)
- [crypto](scripting/builtins/libcrypto.md)
- [cameras](scripting/builtins/libcameras.md)
- [ctypes](scripting/builtins/libctypes.md)
- [entities](scripting/builtins/libentities.md)

View file

@ -0,0 +1,159 @@
# Библиотека *crypto*
Библиотека предоставляет основные криптографические функции для модов
TLS и HTTP в нее не входят. Эти протоколы используют crypto для тяжелых
вычислений
Все бинарные значения передаются обычными Lua строками. Это касается ключей,
подписей, хешей и зашифрованных данных. Строки могут содержать нулевые байты
Текущая версия API находится в `crypto.API_VERSION`
## Хеширование
```lua
crypto.sha256(data: str) -> str
crypto.sha384(data: str) -> str
crypto.sha512(data: str) -> str
crypto.md5(data: str) -> str
crypto.hash(hash: str, data: str) -> str
crypto.hmac(hash: str, key: str, data: str) -> str
```
Поддерживаются `SHA256`, `SHA384`, `SHA512` и `MD5`
MD5 нужен для совместимости со старыми форматами. Для новых данных лучше
использовать SHA256
Большой файл можно передавать частями
```lua
local hash = crypto.hash_new("SHA256")
hash:update(part1)
hash:update(part2)
local result = hash:final()
```
После `final` контекст можно очистить через `reset`
## Подписи
```lua
crypto.ed25519_keypair() -> private_key, public_key
crypto.ed25519_public(private_key: str) -> str
crypto.ed25519_sign(private_key: str, message: str) -> str
crypto.ed25519_verify(public_key: str, message: str, signature: str)
-> true
-> false, error
```
Ключи Ed25519 имеют длину 32 байта. Подпись имеет длину 64 байта
```lua
crypto.ecdsa_keypair(curve: str) -> private_key, public_key
crypto.ecdsa_public(curve: str, private_key: str) -> str
crypto.ecdsa_sign(curve: str, private_key: str, message: str, hash: str) -> str
crypto.ecdsa_verify(curve: str, public_key: str, message: str,
signature: str, hash: str)
-> true
-> false, error
```
Доступны кривые `P-256`, `P-384` и `P-521`
Публичный ключ ECDSA записывается как несжатая SEC1 точка. Подпись хранится в
DER формате
```lua
crypto.rsa_pkcs1_verify(hash: str, modulus: str, exponent: str,
message: str, signature: str)
-> true
-> false, error
crypto.rsa_pss_verify(hash: str, modulus: str, exponent: str,
message: str, signature: str, salt_length: int)
-> true
-> false, error
```
Модуль и экспонента RSA передаются в big endian. Значение `salt_length = -1`
использует размер хеша
Генерации RSA ключей в библиотеке нет. Для новых ключей проще использовать
Ed25519
## Обмен ключами
```lua
crypto.x25519_keypair() -> private_key, public_key
crypto.x25519_public(private_key: str) -> str
crypto.x25519(private_key: str, peer_public_key: str) -> str
crypto.x25519_shared(private_key: str, peer_public_key: str) -> str
crypto.p256_keypair() -> private_key, public_key
crypto.p256_public(private_key: str) -> str
crypto.p256_shared(private_key: str, peer_public_key: str) -> str
```
`x25519` и `x25519_shared` выполняют одну и ту же операцию
Общий секрет не стоит использовать как готовый ключ. Сначала его нужно
обработать через HKDF
## Шифрование
```lua
crypto.aes_gcm_encrypt(key: str, nonce: str, aad: str, plaintext: str) -> str
crypto.aes_gcm_decrypt(key: str, nonce: str, aad: str, ciphertext: str)
-> plaintext
-> nil, error
crypto.chacha20_poly1305_encrypt(key: str, nonce: str, aad: str,
plaintext: str) -> str
crypto.chacha20_poly1305_decrypt(key: str, nonce: str, aad: str,
ciphertext: str)
-> plaintext
-> nil, error
```
AES GCM принимает ключи размером 16 или 32 байта
ChaCha20 Poly1305 использует ключ размером 32 байта и nonce размером 12 байт
В конец зашифрованных данных добавляется тег размером 16 байт. При неверном
теге функция расшифровки ничего не возвращает
Нельзя повторно использовать один nonce с тем же ключом
## Остальные функции
```lua
crypto.random_bytes(length: int) -> str
crypto.constant_time_equal(left: str, right: str) -> bool
crypto.hkdf_extract(hash: str, salt: str, ikm: str) -> str
crypto.hkdf_expand(hash: str, prk: str, info: str, length: int) -> str
crypto.pbkdf2(hash: str, password: str, salt: str,
iterations: int, length: int) -> str
crypto.scrypt(password: str, salt: str, n: int, r: int, p: int,
length: int, [опционально]max_memory: int=0) -> str
crypto.features() -> table
```
Для хранения паролей следует использовать scrypt или PBKDF2 со случайной
солью. Обычный SHA256 для паролей не подходит
`features` возвращает версию OpenSSL и список доступных возможностей. Это
можно использовать если мод запускается на разных сборках движка
Неверная подпись возвращает `false, "invalid_signature"`
Неверный тег при расшифровке возвращает `nil, "authentication_failed"`
Ошибки в аргументах вызывают обычную Lua ошибку