此包最新版本(v0.2.1)没有提供许可证信息。

v0.2.1 2020-07-22 17:51 UTC

This package is auto-updated.

Last update: 2024-09-23 03:09:50 UTC


README

PHP实现KeybaseSaltpack加密/签名消息格式。

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::dearmorAndDecryptStreamEncryption::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::signAndArmorStreamSigning::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::dearmorAndVerifySigning::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::dearmorAndVerifyStreamSigning::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::dearmorAndVerifyDetachedSigning::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::dearmorAndDecryptStreamSigncryption::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