Как проверить с помощью Python (PyNaCl) сообщение, подписанное адаптером кошелька Solana (javascript)

Я подписал сообщение, используя пример адаптера кошелька Solana :

      import { useWallet } from '@solana/wallet-adapter-react';
import bs58 from 'bs58';
import React, { FC, useCallback } from 'react';
import { sign } from 'tweetnacl';

export const SignMessageButton: FC = () => {
    const { publicKey, signMessage } = useWallet();

    const onClick = useCallback(async () => {
        try {
            // `publicKey` will be null if the wallet isn't connected
            if (!publicKey) throw new Error('Wallet not connected!');
            // `signMessage` will be undefined if the wallet doesn't support it
            if (!signMessage) throw new Error('Wallet does not support message signing!');

            // Encode anything as bytes
            const message = new TextEncoder().encode('Hello, world!');
            // Sign the bytes using the wallet
            const signature = await signMessage(message);
            // Verify that the bytes were signed using the private key that matches the known public key
            if (!sign.detached.verify(message, signature, publicKey.toBytes())) throw new Error('Invalid signature!');

            alert(`Message signature: ${bs58.encode(signature)}`);
        } catch (error: any) {
            alert(`Signing failed: ${error?.message}`);
        }
    }, [publicKey, signMessage]);

    return signMessage ? (<button onClick={onClick} disabled={!publicKey}>Sign Message</button>) : null;
};

Но я не могу проверить подписанное сообщение с помощью PyNaCl и Solana-py. Я пробовал следующее:

      from nacl.signing import VerifyKey
from solana.publickey import PublicKey

result = VerifyKey(bytes(PublicKey("HERE_THE_PUB_KEY"))
  ).verify(
    smessage=bytes("HERE_THE_MESSAGE", "utf8"),
    signature=bytes("HERE_THE_SIGNATURE", "utf8"),
)

Но проверка возвращается: nacl.exceptions.BadSignatureError: Signature was forged or corrupt.

Кто-нибудь знает, что не так? Может быть проблема с кодировкой? Похоже, что JS использует следующие типы байтов:

      Pubkey:  Uint8Array(32) [144, 188, 240, ...
Message: Uint8Array(44) [57,   85,  65, ...
Signed:  Uint8Array(64) [82,  220, 242, ...

И в питоне:

      Pubkey:  <class 'bytes'> b'\x90\xbc\xf...'
Message: <class 'bytes'> b'9UA...'
Signed:  <class 'bytes'> b'2f68j5...'

Нужно ли мне использовать другую кодировку на Python?

1 ответ

Спасибо за конкретный пример, вы очень близки! Кодировка здесь является абсолютной проблемой - публичный ключ правильно закодирован в Python как байты. Этот первый байт \x90, закодированный как два шестнадцатеричных значения, 144в JS, и вы можете проверить это в Python с помощью: int('90', 16) = 144.

Таким образом, чтобы проверить свой ключ, вы можете вместо этого использовать base58пакет https://github.com/keis/base58 и выполните:

      from nacl.signing import VerifyKey
from solana.publickey import PublicKey
import base58

pubkey = bytes(PublicKey("DKpHyR1WjWE23E3xizPUhefZKmpMrMXNBVfoxQ7WXCRR"))
msg = bytes("hello", 'utf8')
signed = bytes("3EWDdtU1w8pWkr6fg8faJvKn1wBZmNjgf5kUx4Pn5gw4HeBPYVDm7cTHNqpRVMami6yX36jdaeZacv9GXR19Jzye", 'utf8')

result = VerifyKey(
    pubkey
).verify(
    smessage=msg,  
    signature=base58.b58decode(signed)
)

Примечание . В smessage вам не нужно использовать b58, потому что он был закодирован с помощью new TextEncoder().encode("hello").

Второй вариант: если у вас уже есть UInt8Array из JS, вы можете сделать:

      result = VerifyKey(bytes(PublicKey("HERE_THE_PUB_KEY"))
  ).verify(
    smessage=bytes([byte1, byte2, byte3, ...])
    signature=bytes([byte1, byte2, byte3, ...])
)
Другие вопросы по тегам