Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify p2 delegated conditions signatures #17054

Merged
merged 12 commits into from Dec 18, 2023
15 changes: 11 additions & 4 deletions chia/rpc/wallet_rpc_api.py
Expand Up @@ -79,6 +79,7 @@
from chia.wallet.outer_puzzles import AssetType
from chia.wallet.payment import Payment
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.puzzles import p2_delegated_conditions
from chia.wallet.puzzles.clawback.metadata import AutoClaimSettings, ClawbackMetadata
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_synthetic_public_key
from chia.wallet.singleton import (
Expand Down Expand Up @@ -1482,7 +1483,7 @@ async def verify_signature(self, request: Dict[str, Any]) -> EndpointResult:
except ValueError:
raise ValueError(f"Invalid signing mode: {signing_mode_str!r}")

if signing_mode == SigningMode.CHIP_0002:
if signing_mode == SigningMode.CHIP_0002 or signing_mode == SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS:
# CHIP-0002 message signatures are made over the tree hash of:
# ("Chia Signed Message", message)
message_to_verify: bytes = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, input_message)).get_tree_hash()
Expand All @@ -1506,9 +1507,15 @@ async def verify_signature(self, request: Dict[str, Any]) -> EndpointResult:
# endpoints, the "address" field should contain the p2_address of the NFT/DID
# that was used to sign the message.
puzzle_hash: bytes32 = decode_puzzle_hash(request["address"])
if puzzle_hash != puzzle_hash_for_synthetic_public_key(
G1Element.from_bytes(hexstr_to_bytes(request["pubkey"]))
):
expected_puzzle_hash: Optional[bytes32] = None
if signing_mode == SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS:
puzzle = p2_delegated_conditions.puzzle_for_pk(Program.to(hexstr_to_bytes(request["pubkey"])))
expected_puzzle_hash = bytes32(puzzle.get_tree_hash())
else:
expected_puzzle_hash = puzzle_hash_for_synthetic_public_key(
G1Element.from_bytes(hexstr_to_bytes(request["pubkey"]))
)
if puzzle_hash != expected_puzzle_hash:
return {"isValid": False, "error": "Public key doesn't match the address"}
if is_valid:
return {"isValid": is_valid}
Expand Down
3 changes: 3 additions & 0 deletions chia/types/signing_mode.py
Expand Up @@ -21,6 +21,9 @@ class SigningMode(Enum):
# Same as above but with the message specified as a string of hex characters
BLS_MESSAGE_AUGMENTATION_HEX_INPUT = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG:hexinput_"

# Use for verifying signatures made with Tangem cards
CHIP_0002_P2_DELEGATED_CONDITIONS = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG:CHIP-0002_P2_DELEGATED_PUZZLE"


# https://github.com/Chia-Network/chips/blob/80e4611fe52b174bf1a0382b9dff73805b18b8c6/CHIPs/chip-0002.md#signmessage
CHIP_0002_SIGN_MESSAGE_PREFIX = "Chia Signed Message"
33 changes: 33 additions & 0 deletions tests/wallet/rpc/test_wallet_rpc.py
Expand Up @@ -2151,6 +2151,23 @@ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironmen
},
{"isValid": True},
),
(
{
"message": "4f7a6f6e65", # Ozone
"pubkey": (
"8fba5482e6c798a06ee1fd95deaaa83f11c46da06006ab35"
"24e917f4e116c2bdec69d6098043ca568290ac366e5e2dc5"
),
"signature": (
"92a5124d53b74e4197d075277d0b31eda1571353415c4a87952035aa392d4e9206b35e4af959e7135e45db1"
"c884b8b970f9cbffd42291edc1acdb124554f04608b8d842c19e1404d306f881fa79c0e287bdfcf36a6e5da"
"334981b974a6cebfd0"
),
"signing_mode": SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS.value,
"address": "xch1hh9phcc8tt703dla70qthlhrxswy88va04zvc7vd8cx2v6a5ywyst8mgul",
},
{"isValid": True},
),
# Negative tests
(
# Message was modified
Expand Down Expand Up @@ -2186,6 +2203,22 @@ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironmen
},
{"isValid": False, "error": "Public key doesn't match the address"},
),
(
{
"message": "4f7a6f6e65", # Ozone
"pubkey": (
"8fba5482e6c798a06ee1fd95deaaa83f11c46da06006ab35"
"24e917f4e116c2bdec69d6098043ca568290ac366e5e2dc5"
),
"signature": (
"92a5124d53b74e4197d075277d0b31eda1571353415c4a87952035aa392d4e9206b35e4af959e7135e45db1"
"c884b8b970f9cbffd42291edc1acdb124554f04608b8d842c19e1404d306f881fa79c0e287bdfcf36a6e5da"
"334981b974a6cebfd0"
),
"address": "xch1hh9phcc8tt703dla70qthlhrxswy88va04zvc7vd8cx2v6a5ywyst8mgul",
},
{"isValid": False, "error": "Public key doesn't match the address"},
),
],
)
@pytest.mark.parametrize("prefix_hex_strings", [True, False], ids=["with 0x", "no 0x"])
Expand Down