Skip to content

The signature and verification of SM2 return incorrect results in crypto.createSign and crypto.createVerify #53761

Closed as not planned

Description

@xicilion

Version

v22.0.0

Platform

Darwin air.lan 23.5.0 Darwin Kernel Version 23.5.0: Wed May  1 20:19:05 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8112 arm64

Subsystem

crypto

What steps will reproduce the bug?

When using crypto.createSign to sign SM2, I got an incorrect signature. The same issue occurs with crypto.createVerify.

const crypto = require('crypto');

const data = "AABB";

var keys = {
    privateKey: crypto.createPrivateKey(`-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbjCNHopgvyGVfLaP
PamI9E9lf6jXT+xm1Pns1t/xQTihRANCAATV+I7HUGF2gC+miVl3JfjpoZaU2hrZ
QqHwKUNtIDE/uxxWNLBbYKaiLOWrbYA8skrWQWl3RkbXW4ZI28afRw9g
-----END PRIVATE KEY-----
`),
    publicKey: crypto.createPublicKey(`-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE1fiOx1BhdoAvpolZdyX46aGWlNoa
2UKh8ClDbSAxP7scVjSwW2Cmoizlq22APLJK1kFpd0ZG11uGSNvGn0cPYA==
-----END PUBLIC KEY-----`)
};

var sig = crypto.createSign('sm3').update(data).sign(keys.privateKey);
// var sig = crypto.sign('sm3', data, keys.privateKey);
var ok = crypto.verify('sm3', data, keys.publicKey, sig);
console.log("verify: ", ok);

How often does it reproduce? Is there a required condition?

always.

What is the expected behavior? Why is that the expected behavior?

verify: true

What do you see instead?

verify: false

Additional information

No response

Activity

  1. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member

    (+ repro-exists)

    I've made a few changes to the code for better logging, and here is my output:

    const crypto = require('crypto');
    
    const data = "AABB";
    
    // Define the private and public keys
    const privateKeyPEM = `-----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbjCNHopgvyGVfLaP
    PamI9E9lf6jXT+xm1Pns1t/xQTihRANCAATV+I7HUGF2gC+miVl3JfjpoZaU2hrZ
    QqHwKUNtIDE/uxxWNLBbYKaiLOWrbYA8skrWQWl3RkbXW4ZI28afRw9g
    -----END PRIVATE KEY-----
    `;
    
    const publicKeyPEM = `-----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE1fiOx1BhdoAvpolZdyX46aGWlNoa
    2UKh8ClDbSAxP7scVjSwW2Cmoizlq22APLJK1kFpd0ZG11uGSNvGn0cPYA==
    -----END PUBLIC KEY-----
    `;
    
    // Create the private and public key objects
    const privateKey = crypto.createPrivateKey(privateKeyPEM);
    const publicKey = crypto.createPublicKey(publicKeyPEM);
    
    // Log the private and public keys
    console.log("Private Key:");
    console.log(privateKey.export({ type: 'pkcs8', format: 'pem' }));
    
    console.log("Public Key:");
    console.log(publicKey.export({ type: 'spki', format: 'pem' }));
    
    // Create the signature using the private key
    const sign = crypto.createSign('sm3');
    sign.update(data);
    const signature = sign.sign(privateKey);
    
    // Log the signature
    console.log("Signature:");
    console.log(signature.toString('hex'));
    
    // Verify the signature using the public key via crypto.createVerify
    const verify = crypto.createVerify('sm3');
    verify.update(data);
    const isVerified = verify.verify(publicKey, signature);
    
    // Log the verification result
    console.log("Signature verified (via crypto.createVerify):", isVerified);
    
    // Verify the signature using the public key via crypto.verify
    const verify2 = crypto.verify('sm3', data, publicKey, signature);
    console.log("Signature verified (via crypto.verify):", verify2);
    Private Key:
    -----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbjCNHopgvyGVfLaP
    PamI9E9lf6jXT+xm1Pns1t/xQTihRANCAATV+I7HUGF2gC+miVl3JfjpoZaU2hrZ
    QqHwKUNtIDE/uxxWNLBbYKaiLOWrbYA8skrWQWl3RkbXW4ZI28afRw9g
    -----END PRIVATE KEY-----
    
    Public Key:
    -----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE1fiOx1BhdoAvpolZdyX46aGWlNoa
    2UKh8ClDbSAxP7scVjSwW2Cmoizlq22APLJK1kFpd0ZG11uGSNvGn0cPYA==
    -----END PUBLIC KEY-----
    
    Signature:
    3044022056cc3567aa4842c4b1c5f9de75059dbaf63efab5aa34097e5a8547419aa3175e02206deb865046f12b25f111922da6858c7e97e475b656604679bcd4a0b05d8c76f1
    Signature verified (via crypto.createVerify): true
    Signature verified (via crypto.verify): false
    

    This seems to only affect crypto.verify, CC @nodejs/crypto

  2. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member

    That being said, I believe the issue title is misleading. crypto.createVerify appears to be fine, it's crypto.verify that is affected, right?

  3. tniessen commented on Jul 8, 2024

    @tniessen
    Member

    Related: #37066

  4. xicilion commented on Jul 8, 2024

    @xicilion
    ContributorAuthor

    My test results are as follows:

    1. The signature result of cresteSign can be verified using createVerify;
    2. The signature result of crypto.sign can be verified using crypto.verify;
    3. crypto.sign/crypto.verify and cresteSign/createVerify cannot verify each other;
    4. openssl command line can verify with crypto.sign/crypto.verify.

    So the results of crypto.createSign and crypto.createVerify on SM2 are both incorrect.

  5. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member

    crypto.sign/crypto.verify and cresteSign/createVerify cannot verify each other;

    Can you provide example code with createVerify? I'm unable to reproduce that part.

  6. xicilion commented on Jul 8, 2024

    @xicilion
    ContributorAuthor
    const crypto = require('crypto');
    
    const data = "AABB";
    
    var keys = {
        privateKey: crypto.createPrivateKey(`-----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbjCNHopgvyGVfLaP
    PamI9E9lf6jXT+xm1Pns1t/xQTihRANCAATV+I7HUGF2gC+miVl3JfjpoZaU2hrZ
    QqHwKUNtIDE/uxxWNLBbYKaiLOWrbYA8skrWQWl3RkbXW4ZI28afRw9g
    -----END PRIVATE KEY-----
    `),
        publicKey: crypto.createPublicKey(`-----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE1fiOx1BhdoAvpolZdyX46aGWlNoa
    2UKh8ClDbSAxP7scVjSwW2Cmoizlq22APLJK1kFpd0ZG11uGSNvGn0cPYA==
    -----END PUBLIC KEY-----`)
    };
    
    // var sig = crypto.createSign('sm3').update(data).sign(keys.privateKey);
    var sig = crypto.sign('sm3', data, keys.privateKey);
    var ok = crypto.createVerify('sm3').update(data).verify(keys.publicKey, sig);
    console.log("verify: ", ok);
  7. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member

    Thanks! I understand now. My reproduction used createSign, which worked, this is weird indeed. @tniessen confirmed-bug?

  8. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member
    const crypto = require('crypto');
    
    // Data to be signed and verified
    const data = "AABB";
    
    // Define the private and public keys
    const privateKeyPEM = `
    -----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbjCNHopgvyGVfLaP
    PamI9E9lf6jXT+xm1Pns1t/xQTihRANCAATV+I7HUGF2gC+miVl3JfjpoZaU2hrZ
    QqHwKUNtIDE/uxxWNLBbYKaiLOWrbYA8skrWQWl3RkbXW4ZI28afRw9g
    -----END PRIVATE KEY-----
    `;
    
    const publicKeyPEM = `
    -----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE1fiOx1BhdoAvpolZdyX46aGWlNoa
    2UKh8ClDbSAxP7scVjSwW2Cmoizlq22APLJK1kFpd0ZG11uGSNvGn0cPYA==
    -----END PUBLIC KEY-----
    `;
    
    // Function to create private key object
    function createPrivateKey(privateKeyPEM) {
        return crypto.createPrivateKey(privateKeyPEM);
    }
    
    // Function to create public key object
    function createPublicKey(publicKeyPEM) {
        return crypto.createPublicKey(publicKeyPEM);
    }
    
    // Array of signer functions
    const SIGNERS = [
        // Via crypto.createSign
        (data, privateKey) => {
            const sign = crypto.createSign('sm3');
            sign.update(data);
            return sign.sign(privateKey);
        },
        // Via crypto.sign
        (data, privateKey) => {
            return crypto.sign('sm3', Buffer.from(data), { key: privateKey });
        }
    ];
    
    // Array of verifier functions
    const VERIFIERS = [
        // Via crypto.createVerify
        (data, signature, publicKey) => {
            const verify = crypto.createVerify('sm3');
            verify.update(data);
            return verify.verify(publicKey, signature);
        },
        // Via crypto.verify
        (data, signature, publicKey) => {
            return crypto.verify('sm3', Buffer.from(data), publicKey, signature);
        }
    ];
    
    // Function to sign data using all signers and verify using all verifiers
    function signAndVerifyAll(data, privateKey, publicKey) {
        const results = SIGNERS.flatMap(signer => {
            const signature = signer(data, privateKey);
            return VERIFIERS.map(verifier => {
                return {
                    "Sign Method": SIGNERS.indexOf(signer) === 0 ? 'crypto.createSign' : 'crypto.sign',
                    "Verify Method": VERIFIERS.indexOf(verifier) === 0 ? 'crypto.createVerify' : 'crypto.verify',
                    "Signature": signature.toString('hex'),
                    "Result": verifier(data, signature, publicKey)
                };
            });
        });
        return results;
    }
    
    
    // Create private and public key objects
    const privateKey = createPrivateKey(privateKeyPEM);
    const publicKey = createPublicKey(publicKeyPEM);
    
    // Sign and verify data using all methods
    const results = signAndVerifyAll(data, privateKey, publicKey);
    
    // Output results in a table
    console.table(results);
    ┌─────────┬─────────────────────┬───────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────┐
    │ (index) │ Sign Method         │ Verify Method         │ Signature                                                                                                                                        │ Result │
    ├─────────┼─────────────────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┤
    │ 0       │ 'crypto.createSign' │ 'crypto.createVerify' │ '3045022046fa9c7b15e1ff55aa7f5dfcc2092614e25b2b321c82e0e1c09b03d21bd48fc9022100e45cddee885a1c075691f00b577c617ff584a89c65a19896600d5569d752d9f4' │ true   │
    │ 1       │ 'crypto.createSign' │ 'crypto.verify'       │ '3045022046fa9c7b15e1ff55aa7f5dfcc2092614e25b2b321c82e0e1c09b03d21bd48fc9022100e45cddee885a1c075691f00b577c617ff584a89c65a19896600d5569d752d9f4' │ false  │
    │ 2       │ 'crypto.sign'       │ 'crypto.createVerify' │ '3045022100cf800e13a0ead14be0ec9d614257fb6f409187642404dccb3a4374ace9fc162602206bf6820b8902ae9ce4cd10708f188fddd5bc2a41db2c47c003ca8265a00396ae' │ false  │
    │ 3       │ 'crypto.sign'       │ 'crypto.verify'       │ '3045022100cf800e13a0ead14be0ec9d614257fb6f409187642404dccb3a4374ace9fc162602206bf6820b8902ae9ce4cd10708f188fddd5bc2a41db2c47c003ca8265a00396ae' │ true   │
    └─────────┴─────────────────────┴───────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴────────┘
    

    I've generated the table above to demonstrate which methods are incompatible.

  9. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member

    openssl command line can verify with crypto.sign/crypto.verify.

    So crypto.createXXX seems to have the wrong algorithm, IIUC

  10. xicilion commented on Jul 8, 2024

    @xicilion
    ContributorAuthor

    crypto.sign and crypto.verify are correct.

  11. avivkeller commented on Jul 8, 2024

    @avivkeller
    Member

    Thanks for the excellent info! When the @nodejs/crypto team gets a chance to look at this issue, they will be able to help even more!

  12. github-actions commented on May 2, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 210 days of inactivity.
    It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.

  13. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on May 2, 2026
  14. github-actions commented on Jun 1, 2026

    @github-actions
    Contributor

    This issue has been automatically closed after 30 days of inactivity following its stale status (no activity for a total of 240 days).
    If this is still relevant, feel free to reopen it or leave a comment with additional details so we can continue the discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cryptoIssues and PRs related to the crypto subsystem.staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions