samuelthomas2774 / saltpack
Requires
- ext-gmp: *
- ext-hash: *
- ext-sodium: *
- rybakit/msgpack: ^0.7.0
Requires (Dev)
- phpunit/phpunit: ^9.2
- react/stream: ^1.1
Suggests
- react/stream: Required for streaming
This package is auto-updated.
Last update: 2024-09-23 03:09:50 UTC
README
PHP实现Keybase的Saltpack加密/签名消息格式。
php-saltpack实现了Saltpack的2.0版本。支持所有消息类型(加密、附加签名、分离签名和签密)。
安装
php-saltpack发布到Packagist。需要GMP和Sodium。
composer require samuelthomas2774/saltpack
需要React来实现流式传输。
composer require react/stream
加密
Encryption::encryptAndArmor
加密字符串并返回ASCII加密数据。
Encryption::encrypt
接受与Encryption::encryptAndArmor
相同的参数,但返回无 armor 的字符串。
use Saltpack\Encryption;
$plaintext = '...';
$sender_keypair = sodium_crypto_box_keypair();
$recipients_keys = [
sodium_crypto_box_publickey(sodium_crypto_box_keypair()),
];
$encrypted = Encryption::encryptAndArmor($plaintext, $sender_keypair, $recipients_keys);
// $encrypted === 'BEGIN SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeD305h3lDop TELGyPzBAAawRfZ rss3XwjQHK0irv7 rNIcmnvmn5YlTtK 7O1fFPePZGpx46P ...
php-saltpack还支持使用Encryption::encryptAndArmorStream
(或Encryption::encryptStream
用于无 armor 加密)的流式加密。
use Saltpack\Encryption;
$sender_keypair = sodium_crypto_box_keypair();
$recipients_keys = [
sodium_crypto_box_publickey(sodium_crypto_box_keypair()),
];
$stream = Encryption::encryptAndArmorStream($sender_keypair, $recipients_keys);
// Write the encrypted and armored data to stdout
$stdout = new WritableResourceStream(STDOUT, $loop);
$stream->pipe($stdout);
$stream->end('...');
可以使用Encryption::dearmorAndDecrypt
(或Encryption::decrypt
如果消息未 armor)解密消息。
use Saltpack\Encryption;
$encrypted = 'BEGIN SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeD305h3lDop TELGyPzBAAawRfZ rss3XwjQHK0irv7 rNIcmnvmn5YlTtK 7O1fFPePZGpx46P ...';
$recipient_keypair = sodium_crypto_box_keypair();
// If you know the sender's public key you can pass it to Encryption::dearmorAndDecrypt and it will throw if it doesn't match
$sender_key = sodium_crypto_box_publickey(sodium_crypto_box_keypair());
try {
// $sender_key is passed by reference - if it is set this will throw if it doesn't match, otherwise it will be set to the sender's public key
$decrypted = Encryption::dearmorAndDecrypt($encrypted, $recipient_keypair, $sender_key);
// If you didn't pass the sender's public key you should check it now
if ($sender_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
// $decrypted === '...'
} catch (\Saltpack\Exceptions\DecryptionError $err) {
// Message could not be decrypted
} catch (\Saltpack\Exceptions\VerifyError $err) {
// Message could not be verified
} catch (\Throwable $err) {
//
}
解密也支持使用Encryption::dearmorAndDecryptStream
或Encryption::decryptStream
的流式传输。
use Saltpack\Encryption;
$recipient_keypair = sodium_crypto_box_keypair();
// If you know the sender's public key you can pass it to Encryption::dearmorAndDecryptStream and it will emit an error if it doesn't match
$sender_key = sodium_crypto_box_publickey(sodium_crypto_box_keypair());
$stream = Encryption::dearmorAndDecryptStream($recipient_keypair, $sender_key);
$stream->on('end', () => {
// If you didn't pass the sender's public key you should check it now
if ($stream->sender_public_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
});
$stream->on('error', function (\Exception $err) {
//
});
// Write the encrypted and armored data to stdout
$stdout = new WritableResourceStream(STDOUT, $loop);
$stream->pipe($stdout);
$stream->end('BEGIN SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeD305h3lDop TELGyPzBAAawRfZ rss3XwjQHK0irv7 rNIcmnvmn5YlTtK 7O1fFPePZGpx46P ...');
签名
Signing::signAndArmor
对字符串进行签名并返回ASCII签名数据。
Signing::sign
接受与Signing::signAndArmor
相同的参数,但返回无 armor 的字符串。
use Saltpack\Signing;
$plaintext = '...';
$signing_keypair = sodium_crypto_sign_keypair();
$signed = Signing::signAndArmor($plaintext, $signing_keypair);
// $signed === 'BEGIN SALTPACK SIGNED MESSAGE. kYM5h1pg6qz9UMn j6G9T0lmMjkYOsZ Kn4Acw58u39dn3B kmdpuvqpO3t2QdM CnBX5wO1ZIO8LTd knNlCR0WSEC0000 ...
支持使用Signing::signAndArmorStream
或Signing::signStream
进行流式签名。
use Saltpack\Signing;
$signing_keypair = sodium_crypto_sign_keypair();
$stream = Signing::signAndArmorStream($signing_keypair);
// Write the encrypted and armored data to stdout
$stdout = new WritableResourceStream(STDOUT, $loop);
$stream->pipe($stdout);
$stream->end('...');
可以使用Signing::dearmorAndVerify
或Signing::verify
验证和读取签名消息。
use Saltpack\Signing;
$signed = 'BEGIN SALTPACK SIGNED MESSAGE. kYM5h1pg6qz9UMn j6G9T0lmMjkYOsZ Kn4Acw58u39dn3B kmdpuvqpO3t2QdM CnBX5wO1ZIO8LTd knNlCR0WSEC0000 ...';
// If you know the sender's public key you can pass it to Signing::dearmorAndVerify and it will throw if it doesn't match
$sender_key = sodium_crypto_sign_publickey(sodium_crypto_sign_keypair());
try {
// $sender_key is passed by reference - if it is set this will throw if it doesn't match, otherwise it will be set to the sender's public key
$verified = Signing::dearmorAndVerify($signed, $sender_key);
// If you didn't pass the sender's public key you should check it now
if ($sender_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
// $verified === '...'
} catch (\Saltpack\Exceptions\VerifyError $err) {
// Message could not be verified
} catch (\Throwable $err) {
//
}
读取签名消息也支持使用Signing::dearmorAndVerifyStream
或Signing::verifyStream
的流式传输。
use Saltpack\Signing;
// If you know the sender's public key you can pass it to Signing::dearmorAndVerifyStream and it will throw if it doesn't match
$sender_key = sodium_crypto_sign_publickey(sodium_crypto_sign_keypair());
$stream = Signing::dearmorAndVerifyStream($sender_key);
$stream->on('end', () => {
// If you didn't pass the sender's public key you should check it now
if ($stream->public_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
});
$stream->on('error', function (\Exception $err) {
//
});
// Write the verified data to stdout
$stdout = new WritableResourceStream(STDOUT, $loop);
$stream->pipe($stdout);
$stream->end('BEGIN SALTPACK SIGNED MESSAGE. kYM5h1pg6qz9UMn j6G9T0lmMjkYOsZ Kn4Acw58u39dn3B kmdpuvqpO3t2QdM CnBX5wO1ZIO8LTd knNlCR0WSEC0000 ...');
分离签名
Signing::signDetachedAndArmor
对字符串进行签名并返回ASCII签名。
Signing::signDetached
接受与Signing::signDetachedAndArmor
相同的参数,但返回无 armor 的字符串。
分离签名/验证目前不支持流式传输。
use Saltpack\Signing;
$plaintext = '...';
$signing_keypair = sodium_crypto_sign_keypair();
$signed = Signing::signDetachedAndArmor($plaintext, $signing_keypair);
// $signed === 'BEGIN SALTPACK DETACHED SIGNATURE. kYM5h1pg6qz9UMn j6G9T0tZQlxoky3 0YoKQ4s21IrFv3B kmdpuvqpO3t2QdM CnBX5wO1ZIO8LTd knNlCR0WSEC0000 ...
可以使用Signing::dearmorAndVerifyDetached
或Signing::verifyDetached
验证分离签名。
use Saltpack\Signing;
$signed = 'BEGIN SALTPACK SIGNED MESSAGE. kYM5h1pg6qz9UMn j6G9T0lmMjkYOsZ Kn4Acw58u39dn3B kmdpuvqpO3t2QdM CnBX5wO1ZIO8LTd knNlCR0WSEC0000 ...';
$plaintext = '...';
// If you know the sender's public key you can pass it to dearmorAndVerifyDetached and it will throw if it doesn't match
$sender_key = sodium_crypto_sign_publickey(sodium_crypto_sign_keypair());
try {
// $sender_key is passed by reference - if it is set this will throw if it doesn't match, otherwise it will be set to the sender's public key
Signing::dearmorAndVerifyDetached($signature, $plaintext, $sender_key);
// If you didn't pass the sender's public key you should check it now
if ($sender_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
} catch (\Saltpack\Exceptions\VerifyError $err) {
// Message could not be verified
} catch (\Throwable $err) {
//
}
签密
签密与Saltpack通常的加密格式非常相似,但
- 发送者使用Ed25519签名密钥而不是X25519加密密钥,
- 可以为一组接收者提供一个对称密钥,而不是每个接收者都有自己的加密密钥(尽管php-saltpack尚未实现,但内部API已经存在),
- 并且消息是不可抵赖的,这意味着任何拥有消息副本和解密密钥的人都可以验证其真实性,而不仅仅是预期的接收者。
Signcryption::encryptAndArmor
加密字符串并返回ASCII签密数据。
Signcryption::encrypt
接受与Signcryption::encryptAndArmor
相同的参数,但返回无 armor 的字符串。
use Saltpack\Signcryption;
$plaintext = '...';
$sender_keypair = sodium_crypto_sign_keypair();
$recipients_keys = [
// TODO: how can a recipient identifier and symmetric key be provided?
sodium_crypto_box_publickey(sodium_crypto_box_keypair()),
];
$signcrypted = Signcryption::encryptAndArmor($plaintext, $sender_keypair, $recipients_keys);
// $signcrypted === 'BEGIN SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeDQNHnhYI5G UXZkLqLqVvhmpfZ rss3XwjQHK0irv7 rNIcmnvmn5RTzTR OPZLLRr1s0DEZtS ...
支持使用Signcryption::encryptAndArmorStream
或(Signcryption::encryptStream
用于无 armor 加密)的流式传输。
use Saltpack\Signcryption;
$sender_keypair = sodium_crypto_sign_keypair();
$recipients_keys = [
// TODO: how can a recipient identifier and symmetric key be provided?
sodium_crypto_box_publickey(sodium_crypto_box_keypair()),
];
$stream = Signcryption::encryptAndArmorStream($sender_keypair, $recipients_keys);
// Write the verified data to stdout
$stdout = new WritableResourceStream(STDOUT, $loop);
$stream->pipe($stdout);
$stream->end('...');
可以使用Signcryption::dearmorAndDecrypt
(或Signcryption::decrypt
如果消息未 armor)解密消息。
use Saltpack\Signcryption;
$encrypted = 'BEGIN SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeDQNHnhYI5G UXZkLqLqVvhmpfZ rss3XwjQHK0irv7 rNIcmnvmn5RTzTR OPZLLRr1s0DEZtS ...';
// TODO: how can a recipient identifier and symmetric key be provided?
// How can multiple keys be provided (as a recipient may have multiple shared symmetric keys that may be used for this message)
$recipient_keypair = sodium_crypto_box_keypair();
// If you know the sender's public key you can pass it to Signcryption::dearmorAndDecrypt and it will throw if it doesn't match
$sender_key = sodium_crypto_sign_publickey(sodium_crypto_sign_keypair());
try {
// $sender_key is passed by reference - if it is set this will throw if it doesn't match, otherwise it will be set to the sender's public key
$decrypted = Signcryption::dearmorAndDecrypt($encrypted, $recipient_keypair, $sender_key);
// If you didn't pass the sender's public key you should check it now
if ($sender_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
// $decrypted === '...'
} catch (\Saltpack\Exceptions\DecryptionError $err) {
// Message could not be decrypted
} catch (\Saltpack\Exceptions\VerifyError $err) {
// Message could not be verified
} catch (\Throwable $err) {
//
}
解密也支持使用Signcryption::dearmorAndDecryptStream
或Signcryption::decryptStream
的流式传输。
use Saltpack\Signcryption;
// TODO: how can a recipient identifier and symmetric key be provided?
// How can multiple keys be provided (as a recipient may have multiple shared symmetric keys that may be used for this message)
$recipient_keypair = sodium_crypto_box_keypair();
// If you know the sender's public key you can pass it to Signcryption::dearmorAndDecryptStream and it will emit an error if it doesn't match
$sender_key = sodium_crypto_sign_publickey(sodium_crypto_sign_keypair());
$stream = Signcryption::dearmorAndDecryptStream($recipient_keypair, $sender_key);
$stream->on('end', () => {
// If you didn't pass the sender's public key you should check it now
if ($stream->sender_public_key !== hex2bin('...')) {
throw new Exception('Sender public key doesn\'t match');
}
});
$stream->on('error', function (\Exception $err) {
//
});
// Write the encrypted and armored data to stdout
$stdout = new WritableResourceStream(STDOUT, $loop);
$stream->pipe($stdout);
$stream->end('BEGIN SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeDQNHnhYI5G UXZkLqLqVvhmpfZ rss3XwjQHK0irv7 rNIcmnvmn5RTzTR OPZLLRr1s0DEZtS ...');
附加说明
- php-saltpack始终将输入数据分块为1 MB的有效负载。
- php-saltpack已与node-saltpack完全测试。
- php-saltpack已与Keybase部分测试。
- 由node-saltpack和php-saltpack创建的加密消息可以用Keybase解密。
- 由node-saltpack和php-saltpack创建的Signcrypted消息可以用Keybase解密。
- 由Keybase创建的签名消息可以用node-saltpack和php-saltpack验证。
- 由node-saltpack和php-saltpack创建的签名消息可以用Keybase阅读。
许可协议
php-saltpack遵循MIT许可协议发布。Saltpack由Keybase开发者设计,使用NaCl进行加密和MessagePack进行二进制编码。node-saltpack和php-saltpack的防护实现基于saltpack-ruby。