2

I'm trying to figure out why my pallet UI produces wrong signatures when using Polkadot.js extension. UI is similar to https://polkadot.js.org/apps/#/signing: it calculates the signature over data and also sends the (data, account public key and calculated signature) as a signed transaction to a backend. The extrinsic at the backend uses: sp_io::crypto::sr25519_verify function to validate the signature over the data and ensure_signed to validate the signature over the transaction.

sp_io::crypto::sr25519_verify works fine when I use the data signed by default accounts like Alice, Bob etc or with accounts created from browser but fails with accounts created in Polkadot.js extension.

I've also tried to figure out how the page https://polkadot.js.org/apps/#/signing works, but I failed to validate any of signatures created by the https://polkadot.js.org/apps/#/signing page with sp_io::crypto::sr25519_verify.

Could you explain?

  • A. Why I have the problem with data signed by the extension but don't have a problem with local accounts?
  • B. Why signature that is generated by https://polkadot.js.org/apps/#/signing doesn't pass sp_io::crypto::sr25519_verify checks?

Here are examples: 1A. GOOD

fn right_polkadot_js_local_referee_sign() {
    new_test_ext().execute_with(|| {
        //--------
        let letter_id: u32 = 0;
        let ask_price_u128: u128 = 1000000000000000;
        let referee_id_bytes: [u8; 32] = [
            212, 53, 147, 199,  21, 253, 211,  28,
             97, 20,  26, 189,   4, 169, 159, 214,
            130, 44, 133,  88, 133,  76, 205, 227,
            154, 86, 132, 231, 165, 109, 162, 125
          ];
        let worker_id_bytes: [u8; 32] = [
            142, 175,   4,  21,  22, 135, 115,  99,
             38, 201, 254, 161, 126,  37, 252,  82,
            135,  97,  54, 147, 201,  18, 144, 156,
            178,  38, 170,  71, 148, 242, 106,  72
          ];
        let referee_sign_bytes: [u8; 64] = [
            182,  73, 190, 228, 168,  27,  40,  93, 241,   1, 189,
            213, 181,  17,  37,   5,  40, 185, 154, 183,  57, 178,
             69, 102, 202, 243, 230, 192,  75,  40, 124,  16, 254,
             95,   7, 125,  50, 157, 254,   1, 204,  32,  22,   2,
            176,  45, 232,   5, 152,  71, 213, 175, 102, 235, 177,
            237, 199, 213,  84, 125, 226, 142, 206, 137
          ];
        //--------
        let ask_price_bytes = &ask_price_u128.to_be_bytes();
        let letter_id_bytes = &letter_id.to_be_bytes();
        let mut skill_receipt_data = Vec::new();
        skill_receipt_data.extend_from_slice(letter_id_bytes);
        skill_receipt_data.extend_from_slice(&referee_id_bytes);
        skill_receipt_data.extend_from_slice(&worker_id_bytes);
        skill_receipt_data.extend_from_slice(ask_price_bytes);

        assert_eq!(
            LettersModule::signature_is_valid(
                H512::from(referee_sign_bytes),
                skill_receipt_data,
                H256::from(referee_id_bytes)
            ),
            true
        );
    });
}
------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
        sp_io::crypto::sr25519_verify(
            &Signature::from_raw(*signature.as_fixed_bytes()),
            &message,
            &Public::from_h256(pubkey),
        )
    }

2B. BAD (fails)

fn right_polkadot_js_extension_referee_sign() {
    new_test_ext().execute_with(|| {
        //--------
        let letter_id: u32 = 23;
        let ask_price_u128: u128 = 1000000000000000;
        let referee_id_bytes: [u8; 32] = [
            202, 112, 158, 97, 34, 240, 209, 219, 93, 46, 189, 180, 28, 113, 25, 197, 205, 6, 81,
            50, 184, 168, 77, 159, 24, 205, 125, 9, 110, 129, 98, 22,
        ];
        let worker_id_bytes: [u8; 32] = [
            142, 175, 4, 21, 22, 135, 115, 99, 38, 201, 254, 161, 126, 37, 252, 82, 135, 97, 54,
            147, 201, 18, 144, 156, 178, 38, 170, 71, 148, 242, 106, 72,
        ];
        let referee_sign_bytes: [u8; 64] = [
            46, 98, 203, 32, 13, 16, 69, 158, 4, 224, 203, 206, 205, 18, 44, 113, 74, 154, 131, 90,
            154, 30, 71, 181, 186, 130, 120, 30, 8, 253, 177, 25, 26, 56, 200, 13, 48, 180, 5, 9,
            30, 190, 171, 221, 146, 79, 231, 151, 59, 47, 1, 177, 117, 99, 119, 23, 69, 68, 27,
            219, 112, 27, 245, 132,
        ];
        //--------
        let ask_price_bytes = &ask_price_u128.to_be_bytes();
        let letter_id_bytes = &letter_id.to_be_bytes();
        let mut skill_receipt_data = Vec::new();
        skill_receipt_data.extend_from_slice(letter_id_bytes);
        skill_receipt_data.extend_from_slice(&referee_id_bytes);
        skill_receipt_data.extend_from_slice(&worker_id_bytes);
        skill_receipt_data.extend_from_slice(ask_price_bytes);

        assert_eq!(
            LettersModule::signature_is_valid(
                H512::from(referee_sign_bytes),
                skill_receipt_data,
                H256::from(referee_id_bytes)
            ),
            true
        );
    });
}
------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
        sp_io::crypto::sr25519_verify(
            &Signature::from_raw(*signature.as_fixed_bytes()),
            &message,
            &Public::from_h256(pubkey),
        )
    }

2A. GOOD: sp_io::crypto::sr25519_verify validates signature generated by sign function from Keyring (@polkadot/keyring)

------ tests.rs ------
// //Alice
// account: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
// account_hex: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
// dataHex: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
// signatureHex: 0x803453ffa845ff410d66fadef8fcbccf5131e117f9e9dd92ff0dc6fff5c6051fda5e3a4dd291d2093172cabada00c0bbe9fb5ab481ee65cc431928cd09cd1c81
// true: signature is valid by signatureVerify from @polkadot/util-crypto

let account_bytes: [u8; 32] = [212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125];
let data_bytes: [u8; 32] = [142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72];
let signature_bytes: [u8; 64] = [128,52,83,255,168,69,255,65,13,102,250,222,248,252,188,207,81,49,225,23,249,233,221,146,255,13,198,255,245,198,5,31,218,94,58,77,210,145,210,9,49,114,202,186,218,0,192,187,233,251,90,180,129,238,101,204,67,25,40,205,9,205,28,129];

let mut data_to_sign = Vec::new();
        data_to_sign.extend_from_slice(&data_bytes);

        assert_eq!(
            LettersModule::signature_is_valid(
                H512::from(signature_bytes),
                data_to_sign,
                H256::from(account_bytes)
            ),
            true
        );

------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
        sp_io::crypto::sr25519_verify(
            &Signature::from_raw(*signature.as_fixed_bytes()),
            &message,
            &Public::from_h256(pubkey),
        )
    }

2B. BAD: sp_io::crypto::sr25519_verify marks signature as INVALID. The signature was generated by https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/signing

// --------- for Rust tests ------------
// Test-local (extension) (https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/signing)
// account: 15aS8Pih7iv6ZUVBL4AXkhihuZEaMbXdXe6CpN1MDE8LXGkm
// account_hex: 0xca709e6122f0d1db5d2ebdb41c7119c5cd065132b8a84d9f18cd7d096e816216
// dataHex: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
// signatureHex: 0xc043d89ebc0046edda36715cc60b5bc1b433d95aa445ae40b3d7264802b5976582c47d2750ec18ae16194b8d75e46d3f3fa6028625717aa6c86741864dad0086
// true: signature is valid by signatureVerify from @polkadot/util-crypto

let account_bytes: [u8; 32] = [202,112,158,97,34,240,209,219,93,46,189,180,28,113,25,197,205,6,81,50,184,168,77,159,24,205,125,9,110,129,98,22];
let data_bytes: [u8; 32] = [142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72];
let signature_bytes: [u8; 64] = [192,67,216,158,188,0,70,237,218,54,113,92,198,11,91,193,180,51,217,90,164,69,174,64,179,215,38,72,2,181,151,101,130,196,125,39,80,236,24,174,22,25,75,141,117,228,109,63,63,166,2,134,37,113,122,166,200,103,65,134,77,173,0,134];

let mut data_to_sign = Vec::new();
        data_to_sign.extend_from_slice(&data_bytes);

        assert_eq!(
            LettersModule::signature_is_valid(
                H512::from(signature_bytes),
                data_to_sign,
                H256::from(account_bytes)
            ),
            true
        );

------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
        sp_io::crypto::sr25519_verify(
            &Signature::from_raw(*signature.as_fixed_bytes()),
            &message,
            &Public::from_h256(pubkey),
        )
    }

1 Answer 1

1

The extension wraps all data inside <Bytes>...</Bytes> for the raw-data signing operation.

Here is some relevant Rust tests as used via JS <-> WASM.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.