Skip to content

jindada1/Directly-Verifiable-Signcryption

Repository files navigation

Public Verifiable Signcryption

原文

A Directly Public Verifiable Signcryption Scheme based on Elliptic Curves

image-20211031104618738

image-20211031104053037

初始化

一个椭圆曲线 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

Alice -> Bob 签名

签名过程分以下几个步骤

  1. 检查 Bob 的证书和公钥:Cert_BW_B
  2. 选择一个随机数 r,r ∈ [1, n-1]
  3. 计算该随机数 r 在椭圆曲线 G 上对应的公钥 R,写成坐标形式 (x_R, y_R)
  4. image-20211031110701230
    • f 为 n 的 bit length,f / 2 为一半的 bit length,如果在 x_R 左侧填充 0 直到 x_Rn 的二进制串等
    • x_R mod 2 ^ [f / 2] 就是 x_R 二进制串的后半段;加上 2 ^ [f / 2] 意味着在左边加一个 1
    • 即得到一个这样的二进制串 1 [x_R的后半段] 记为 x_RR,对应论文中的
    • 计算 K = (r + x_RR * w_A) * W_B,写成坐标形式 (x_K, y_K)
  5. image-20211031112914956
    • 选择一个 one-way 哈希函数 H,计算 k = H ( x_K || ID_A || y_K || ID_B )
  6. 选择一个 对称加密算法 E,以上一步得到的 k 为密钥,加密消息 M 得到密文 C = E ( k, M )
  7. 计算哈希 t = H ( C || x_R || ID_A || y_R || ID_B )
  8. 计算签名 s = t * w_A - r (mod n)
  9. 发送 R, C, s

Bob 验证

以下几个步骤:

  1. 检查 Alice 的证书和公钥:Cert_AW_A
  2. image-20211031114318372
    • 计算 K = (R + x_RR * W_A) * w_B,写成坐标形式 (x_K, y_K)
  3. image-20211031114602603
    • 计算解密密钥 k = H ( x_K || ID_A || y_K || ID_B )
  4. 用密钥 k 去解密密文 C 得到原文 M
  5. 计算哈希 t = H ( C || x_R || ID_A || y_R || ID_B )
  6. 判断 s * G + R 是否等于 t * W_A

原理

签名 验证
image-20211031115044680 image-20211031115057663

签名和验证过程中的两个 K 是相等的

由于双方已知的信息不同,在计算 K 的时候,一方把 G 放在括号里面计算,另一方放在外面计算

实现 Secp256k1

跳过验证证书和公钥这一步骤

选择

  • 椭圆曲线:Secp256k1
  • 哈希函数:keccak256
  • 对称加密算法:【暂未找到 Solidity 实现】,使用 xor

Alice 签名加密( sol )

函数: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;
    }
    
    ...
}

Alice 签名加密( js )

函数: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
    }
}

Bob 验签解密( sol )

函数: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;
    }
    
    ...
}

Bob 验签( js )

函数: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

签名加密验签解密 设计了六组测试:

  1. js 签名加密 + sol 验签解密:signCryption-js & unSignCryption-sol
  2. js 签名加密 + sol 验签:signCryption-js & verifySignature-sol
  3. js 签名加密 + js 验签:signCryption-js & verifySignature-js
  4. sol 签名加密 + sol 验签解密:signCryption-sol & unSignCryption-sol
  5. sol 签名加密 + sol 验签:signCryption-sol & verifySignature-sol
  6. sol 签名加密 + js 验签:signCryption-sol & verifySignature-js

Gas 消耗

测试脚本在 ./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

跳过验证证书和公钥这一步骤

选择

  • 椭圆曲线:bn128
  • 哈希函数:keccak256
  • 对称加密算法:【暂未找到 Solidity 实现】,使用 xor

Alice 签名加密( sol )

函数: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);
    }
        
    ...
}

Bob 验签解密( sol )

函数: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

签名加密验签解密 设计了两组测试:

  1. sol 签名加密 + sol 验签解密:signCryption-sol & unSignCryption-sol
  2. sol 签名加密 + sol 验签:signCryption-sol & verifySignature-sol

Gas 消耗

测试脚本在 ./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

About

A Directly Public Verifiable Signcryption Scheme based on Elliptic Curves

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published