A Directly Public Verifiable Signcryption Scheme based on Elliptic Curves
一个椭圆曲线 G
| Alice | Bob | |
|---|---|---|
| 私钥 | w_A | w_B |
| 公钥 | W_A = w_A * G | W_B = w_B * G |
| 身份标识 | ID_A | ID_B |
| 证书 | Cert_A | Cert_B |
私钥 w ∈ [1, n-1],n > 2^160,即 n 至少 160 bits
签名过程分以下几个步骤
- 检查 Bob 的证书和公钥:Cert_B,W_B
- 选择一个随机数 r,r ∈ [1, n-1]
- 计算该随机数 r 在椭圆曲线 G 上对应的公钥 R,写成坐标形式 (x_R, y_R)
- 选择一个
one-way 哈希函数 H,计算 k = H ( x_K || ID_A || y_K || ID_B )
- 选择一个
- 选择一个
对称加密算法 E,以上一步得到的 k 为密钥,加密消息 M 得到密文 C = E ( k, M ) - 计算哈希 t = H ( C || x_R || ID_A || y_R || ID_B )
- 计算签名 s = t * w_A - r (mod n)
- 发送 R, C, s
以下几个步骤:
- 检查 Alice 的证书和公钥:Cert_A,W_A
- 计算 K = (R + x_RR * W_A) * w_B,写成坐标形式 (x_K, y_K)
- 计算解密密钥 k = H ( x_K || ID_A || y_K || ID_B )
- 用密钥 k 去解密密文 C 得到原文 M
- 计算哈希 t = H ( C || x_R || ID_A || y_R || ID_B )
- 判断 s * G + R 是否等于 t * W_A
| 签名 | 验证 |
|---|---|
![]() |
![]() |
由于双方已知的信息不同,在计算 K 的时候,一方把 G 放在括号里面计算,另一方放在外面计算
跳过验证证书和公钥这一步骤
选择
- 椭圆曲线:Secp256k1
- 哈希函数:keccak256
- 对称加密算法:【暂未找到 Solidity 实现】,使用 xor
函数:signCryption 返回签名加密结果:R、C、s
代码见 contracts/MainSecp.sol
...
contract MainSecp {
/**
* @dev AliceK
* @notice this function should be invoked by Alice
* @param r - random number generated by Alice
* @param w_A - Alice's private key
* @param W_B - Bob's public key
* @return K - point K
* @return x_RR - x_RR
*/
function AliceK(
uint256 r,
uint256 w_A,
uint256[2] memory W_B
) public view returns (uint256[2] memory K)
{
// 计算该随机数 r 在椭圆曲线 G 上对应的公钥 R,写成坐标形式 (x_R, y_R)
Secp256k1.G1Point memory point_R = pMul(curve.P1(), r);
// 计算 x_RR
uint256 x_RR = oneAndRightHalf(point_R.X);
// 计算 K,坐标形式为(x_K, y_K)
uint256 X_RR_x_A = mulmod(x_RR, w_A, GEN_ORDER);
Secp256k1.G1Point memory point_K = pMul(point(W_B), addmod(r, X_RR_x_A, GEN_ORDER));
K = [point_K.X, point_K.Y];
}
/**
* @dev signCryption
* Alice generates the signcrypted text (R,C,s) to Bob.
* @notice this function should be invoked by Alice
* @param r - random number generated by Alice
* @param w_A - Alice's private key
* @param ID_A - Alice's unique identifiers
* @param M - message to be signcrypted
* @param W_B - Bob's public key
* @param ID_B - Bob's unique identifiers
* @return R - r * G, G is base of Elliptic Curve (Secp256k1)
* @return C - ciphertext of M
* @return s - signature
*/
function signCryption(
uint256 r,
uint256 w_A,
uint256 ID_A,
uint256 M,
uint256[2] memory W_B,
uint256 ID_B
)
public
view
returns (
uint256[2] memory R,
uint256 C,
uint256 s
)
{
// 计算该随机数 r 在椭圆曲线 G 上对应的公钥 R,写成坐标形式 (x_R, y_R)
Secp256k1.G1Point memory point_R = pMul(curve.P1(), r);
R = [point_R.X, point_R.Y];
// // 计算 K,坐标形式为(x_K, y_K
uint256[2] memory K = AliceK(r, w_A, W_B);
// 使用 keccak256 哈希函数计算密钥 k
uint256 k = uint256(keccak256(abi.encodePacked(K[0], ID_A, K[1], ID_B)));
// 使用 k 对 M 进行加密,用 xor
C = k ^ M;
// 计算 t
uint256 t = uint256(keccak256(abi.encodePacked(C, point_R.X, ID_A, point_R.Y, ID_B)));
// 计算签名
uint256 t_w_A = mulmod(t, w_A, GEN_ORDER);
s = t_w_A - r;
}
...
}函数:signCryption 返回签名加密结果:R、C、s
代码见 signencrypt.js
function Signer(curve) {
function AliceK(r, w_A, W_B) {
const R = curve.drivePub(r);
const x_RR = utils.oneAndRightHalf(R[0])
const K = curve.mulPoint(W_B, curve.eccAddHex(r, curve.eccMulHex(x_RR, w_A)))
return [x_RR, K]
}
/**
* Public Verifiable Signcryption, invoked by Alice
*
* @method signCryption
* @param {String} r - random number generated by Alice
* @param {String} w_A - Alice's private key
* @param {String} ID_A - Alice's unique identifiers
* @param {String} M - message to be signcrypted
* @param {Array} W_B - Bob's public key
* @param {String} ID_B - Bob's unique identifiers
* @returns {Array} R - r * G, G is base of Elliptic Curve (Secp256k1)
* @returns {String} C - ciphertext of M
* @returns {String} s - signature
*/
function signCryption(r, w_A, ID_A, M, W_B, ID_B) {
const R = curve.drivePub(r);
const [x_RR, K] = AliceK(r, w_A, W_B)
const k = utils.keccak256Hash([K[0], ID_A, K[1], ID_B])
const C = utils.encrypt(M, k);
const t = utils.keccak256Hash([C, R[0], ID_A, R[1], ID_B])
const s = curve.eccSubHex(curve.eccMulHex(t, w_A), r)
return { R, C, s }
}
return {
AliceK,
signCryption
}
}函数:unSignCryption 返回签名的真假并解密密文:v、M
函数:verifySignature 返回签名的真假:v
代码见 contracts/MainSecp.sol
...
contract MainSecp {
/**
* @dev BobK
* @notice this function should be invoked by Bob
* @param R - signature's R
* @param W_A - Alice's public key
* @param w_B - Bob's private key
* @return K - point K
* @return x_RR - x_RR
*/
function BobK(
uint256[2] memory R,
uint256[2] memory W_A,
uint256 w_B
) public view returns (uint256[2] memory K) {
// 计算 x_RR
uint256 x_RR = oneAndRightHalf(R[0]);
// 计算 K,坐标形式为(x_K, y_K)
Secp256k1.G1Point memory point_K = pMul(pAdd(point(R), pMul(point(W_A), x_RR)), w_B);
K = [point_K.X, point_K.Y];
}
/**
* @dev unSignCryption
* Bob verify signature and decrypt C (R,C,s) from Alice.
* @notice this function should be invoked by Bob
* @param R - signature
* @param C - ciphertext
* @param s - signature
* @param W_A - Alice's public key
* @param ID_A - Alice's unique identifiers
* @param w_B - Bob's private key
* @param ID_B - Bob's unique identifiers
* @return M - Decrypts the ciphertext C
* @return v - result of verification(True/False).
*/
function unSignCryption(
uint256[2] memory R,
uint256 C,
uint256 s,
uint256[2] memory W_A,
uint256 ID_A,
uint256 w_B,
uint256 ID_B
) public view returns (uint256 M, bool v) {
// 计算 x_RR
uint256 x_RR = oneAndRightHalf(R[0]);
// 计算 K,坐标形式为(x_K, y_K)
Secp256k1.G1Point memory point_K = pMul(pAdd(point(R), pMul(point(W_A), x_RR)), w_B);
// 使用 keccak256 哈希函数计算密钥 k
uint256 k = uint256(keccak256(abi.encodePacked(point_K.X, ID_A, point_K.Y, ID_B)));
// 使用 k 对 M 进行解密,用 xor
M = k ^ C;
// 验证签名
v = verifySignature(R, C, s, W_A, ID_A, ID_B);
}
/**
* @dev verifySignature
* Bob verify signature (R,C,s) from Alice.
* @notice this function should be invoked by Bob
* @param R - signature
* @param C - ciphertext
* @param s - signature
* @param W_A - Alice's public key
* @param ID_A - Alice's unique identifiers
* @param ID_B - Bob's unique identifiers
* @return v - result of verification(True/False).
*/
function verifySignature(
uint256[2] memory R,
uint256 C,
uint256 s,
uint256[2] memory W_A,
uint256 ID_A,
uint256 ID_B
) public view returns (bool v) {
// 计算 t
uint256 t = uint256(keccak256(abi.encodePacked(C, R[0], ID_A, R[1], ID_B)));
// 验证签名
v = pointEqual(
pAdd(pMul(curve.P1(), s), point(R)),
pMul(point(W_A), t)
);
}
// 判断曲线上的两个点是否相等
function pointEqual(
Secp256k1.G1Point memory p1,
Secp256k1.G1Point memory p2
) internal pure returns (bool) {
return p1.Y == p2.Y;
}
...
}函数:verifySignature 返回签名的真假:v
代码见 signencrypt.js
function Signer(curve) {
/**
* Bob verify signature (R,C,s) from Alice.
*
* @method verifySignature
* @param R - signature
* @param C - ciphertext
* @param s - signature
* @param W_A - Alice's public key
* @param ID_A - Alice's unique identifiers
* @param ID_B - Bob's unique identifiers
* @return v - result of verification(True/False).
*/
function verifySignature(R, C, s, W_A, ID_A, ID_B) {
// 计算 t
let t = utils.keccak256Hash([C, R[0], ID_A, R[1], ID_B]);
// 验证签名
const S = curve.drivePub(s);
const S_R = curve.addPoints(S, R);
const W_At = curve.mulPoint(W_A, t);
return BigInt(S_R[1]) === BigInt(W_At[1])
}
return {
BobK,
verifySignature
}
}测试脚本在 ./test/ 目录下,智能合约的测试脚本为 ./test/mainSecpContractTest.js
为 签名加密 和 验签解密 设计了六组测试:
- js 签名加密 + sol 验签解密:
signCryption-js & unSignCryption-sol - js 签名加密 + sol 验签:
signCryption-js & verifySignature-sol - js 签名加密 + js 验签:
signCryption-js & verifySignature-js - sol 签名加密 + sol 验签解密:
signCryption-sol & unSignCryption-sol - sol 签名加密 + sol 验签:
signCryption-sol & verifySignature-sol - sol 签名加密 + js 验签:
signCryption-sol & verifySignature-js
测试脚本在 ./test/ 目录下,智能合约的测试脚本为 ./test/estimateSecpGas.js
样例代码
$ truffle test .\test\estimateSecpGas.js结果
748047 drivePubkey (2947ms)
22276 oneAndRightHalf (154ms)
65459 ECCMul (290ms)
64640 ECCAdd (265ms)
1414806 AliceK (4928ms)
1060954 BobK (3962ms)
2081899 signCryption-sol (7368ms)
1383409 sverifySignature-sol (7078ms)
2410737 unSignCryption-sol (11009ms)
跳过验证证书和公钥这一步骤
选择
- 椭圆曲线:bn128
- 哈希函数:keccak256
- 对称加密算法:【暂未找到 Solidity 实现】,使用 xor
函数:signCryption 返回签名加密结果:R、C、s
代码见 contracts/MainBN.sol
...
contract MainBN {
/**
* @dev AliceK
* @notice this function should be invoked by Alice
* @param r - random number generated by Alice
* @param w_A - Alice's private key
* @param W_B - Bob's public key
* @return K - point K
* @return x_RR - x_RR
*/
function AliceK(
uint256 r,
uint256 w_A,
uint256[2] memory W_B
) public view returns (uint256[2] memory K)
{
// 计算该随机数 r 在椭圆曲线 G 上对应的公钥 R,写成坐标形式 (x_R, y_R)
BN128.G1Point memory point_R = pMul(BN128.P1(), r);
// 计算 x_RR
uint256 x_RR = oneAndRightHalf(point_R.X);
// 计算 K,坐标形式为(x_K, y_K)
uint256 X_RR_x_A = mulmod(x_RR, w_A, GEN_ORDER);
BN128.G1Point memory point_K = pMul(point(W_B), addmod(r, X_RR_x_A, GEN_ORDER));
K = [point_K.X, point_K.Y];
}
/**
* @dev signCryption
* Alice generates the signcrypted text (R,C,s) to Bob.
* @notice this function should be invoked by Alice
* @param r - random number generated by Alice
* @param w_A - Alice's private key
* @param ID_A - Alice's unique identifiers
* @param M - message to be signcrypted
* @param W_B - Bob's public key
* @param ID_B - Bob's unique identifiers
* @return R - r * G, G is base of Elliptic Curve (Secp256k1)
* @return C - ciphertext of M
* @return s - signature
*/
function signCryption(
uint256 r,
uint256 w_A,
uint256 ID_A,
uint256 M,
uint256[2] memory W_B,
uint256 ID_B
)
public
view
returns (
uint256[2] memory R,
uint256 C,
uint256 s,
uint256 t
)
{
// 计算该随机数 r 在椭圆曲线 G 上对应的公钥 R,写成坐标形式 (x_R, y_R)
BN128.G1Point memory point_R = pMul(BN128.P1(), r);
R = [point_R.X, point_R.Y];
// // 计算 K,坐标形式为(x_K, y_K
uint256[2] memory K = AliceK(r, w_A, W_B);
// 使用 keccak256 哈希函数计算密钥 k
uint256 k = uint256(keccak256(abi.encodePacked(K[0], ID_A, K[1], ID_B)));
// 使用 k 对 M 进行加密,用 xor
C = k ^ M;
// 计算 t
t = uint256(keccak256(abi.encodePacked(C, point_R.X, ID_A, point_R.Y, ID_B)));
// 计算签名
uint256 t_w_A = mulmod(t, w_A, GEN_ORDER);
s = submod(t_w_A, r, GEN_ORDER);
}
...
}函数:unSignCryption 返回签名的真假并解密密文:v、M
函数:verifySignature 返回签名的真假:v
代码见 contracts/MainBN.sol
...
contract MainBN {
/**
* @dev BobK
* @notice this function should be invoked by Bob
* @param R - signature's R
* @param W_A - Alice's public key
* @param w_B - Bob's private key
* @return K - point K
* @return x_RR - x_RR
*/
function BobK(
uint256[2] memory R,
uint256[2] memory W_A,
uint256 w_B
) public view returns (uint256[2] memory K) {
// 计算 x_RR
uint256 x_RR = oneAndRightHalf(R[0]);
// 计算 K,坐标形式为(x_K, y_K)
BN128.G1Point memory point_K = pMul(pAdd(point(R), pMul(point(W_A), x_RR)), w_B);
K = [point_K.X, point_K.Y];
}
/**
* @dev unSignCryption
* Bob verify signature and decrypt C (R,C,s) from Alice.
* @notice this function should be invoked by Bob
* @param R - signature
* @param C - ciphertext
* @param s - signature
* @param W_A - Alice's public key
* @param ID_A - Alice's unique identifiers
* @param w_B - Bob's private key
* @param ID_B - Bob's unique identifiers
* @return M - Decrypts the ciphertext C
* @return v - result of verification(True/False).
*/
function unSignCryption(
uint256[2] memory R,
uint256 C,
uint256 s,
uint256[2] memory W_A,
uint256 ID_A,
uint256 w_B,
uint256 ID_B
) public view returns (uint256 M, bool v, uint256 t) {
// 计算 x_RR
uint256 x_RR = oneAndRightHalf(R[0]);
// 计算 K,坐标形式为(x_K, y_K)
BN128.G1Point memory point_K = pMul(pAdd(point(R), pMul(point(W_A), x_RR)), w_B);
// 使用 keccak256 哈希函数计算密钥 k
uint256 k = uint256(keccak256(abi.encodePacked(point_K.X, ID_A, point_K.Y, ID_B)));
// 使用 k 对 M 进行解密,用 xor
M = k ^ C;
// 验证签名
(v, t) = verifySignature(R, C, s, W_A, ID_A, ID_B);
}
/**
* @dev verifySignature
* Bob verify signature (R,C,s) from Alice.
* @notice this function should be invoked by Bob
* @param R - signature
* @param C - ciphertext
* @param s - signature
* @param W_A - Alice's public key
* @param ID_A - Alice's unique identifiers
* @param ID_B - Bob's unique identifiers
* @return v - result of verification(True/False).
*/
function verifySignature(
uint256[2] memory R,
uint256 C,
uint256 s,
uint256[2] memory W_A,
uint256 ID_A,
uint256 ID_B
) public view returns (bool v, uint256 t) {
// 计算 t
t = uint256(keccak256(abi.encodePacked(C, R[0], ID_A, R[1], ID_B)));
// 验证签名
v = pointEqual(
pAdd(pMul(BN128.P1(), s), point(R)),
pMul(point(W_A), t)
);
}
// 判断曲线上的两个点是否相等
function pointEqual(
BN128.G1Point memory p1,
BN128.G1Point memory p2
) internal pure returns (bool) {
return p1.Y == p2.Y;
}
...
}测试脚本在 ./test/ 目录下,智能合约的测试脚本为 ./test/mainBNContractTest.js
为 签名加密 和 验签解密 设计了两组测试:
- sol 签名加密 + sol 验签解密:
signCryption-sol & unSignCryption-sol - sol 签名加密 + sol 验签:
signCryption-sol & verifySignature-sol
测试脚本在 ./test/ 目录下,智能合约的测试脚本为 ./test/estimateBNGas.js
样例代码
$ truffle test .\test\estimateBNGas.js结果
31932 drivePubkey (230ms)
22231 oneAndRightHalf (191ms)
33626 ECCMul (212ms)
29565 ECCAdd (225ms)
42404 AliceK (285ms)
45116 BobK (280ms)
53261 signCryption-sol (301ms)
47956 verifySignature-sol (442ms)
66542 unSignCryption-sol (475ms)
$ npm run test或者
$ truffle test指令规则 npm run test [path to test script] 示例如下
$ npm run test test/mainContractBNTest.js或者
$ truffle test test/mainContractBNTest.js


