Stark signatures

Payloads to stark signing are a result of a Pedersen hash.

Depending on your implementation of the Pedersen hash, the result of this hash might be an integer, or encoded in 32-byte representation, according to Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA).

Fordefi supports these formats:

  • hash_integer: This is the integer representation, encoded as a hex string, starting with 0x and followed by an even number of hex-digits.
  • hash_binary: This is the 32-byte encoded format.

Once the payload is signed, under the details field in the Get Transaction response, the signature object will hold the R- and S-values of the signature.

Example

This example demonstrates how to use the new stark vault for creating and signing stark signatures. The request should be inside the body, as demonstrated here.

import requests
import base64
import json
import datetime
import ecdsa
import hashlib

# https://github.com/starkware-libs/cairo-lang
from starkware.crypto.signature.signature import verify, pedersen_hash

# Obtain the hash of the transaction
payload_int = pedersen_hash(0xdeadbeaf)

# Build the signature request
request_json = {
    "vault_id": vault_id,
    "note":" string",
    "signer_type": "api_signer",
    "type": "black_box_signature",
    "details": {
        "format": "hash_integer",
        "hash_integer": "0x{:064x}".format(payload_int),
    },
}

# Alternatively, you can use the binary hash
# this is useful if pedersen_hash implementation you are using returns bytes according to https://www.rfc-editor.org/rfc/rfc6979#section-2.3.2
payload_bytes = b"0" * 32
payload_encoded = base64.b64encode(payload_bytes).decode()

request_json = {
    "vault_id": vault_id,
    "note":" string",
    "signer_type": "api_signer",
    "type": "black_box_signature",
    "details": {
        "format": "hash_binary",
        "hash_binary": payload_encoded,
    },
}

request_body = json.dumps(request_json)
private_key_file = "private.pem"
timestamp = datetime.datetime.now().strftime("%s")
payload = f"/api/v1/transactions|{timestamp}|{request_body}"

with open(private_key_file, "r") as f:
    signing_key = ecdsa.SigningKey.from_pem(f.read())

signature = signing_key.sign(data=payload.encode(), hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der)

resp_tx = requests.post(
    f"https://{gateway_host}/api/v1/transactions",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-signature": base64.b64encode(signature),
        "x-timestamp": timestamp.encode(),
    },
    data=request_body,
).json()

# Obtain the signature result
transaction_id = resp_tx["id"]
signed_transaction = requests.get(
    f"https://{gateway_host}/api/v1/transactions/{transaction_id}",
    headers={
        "Authorization": f"Bearer {access_token}",
    }
).json()

r = int(signed_transaction["details"]["signature"]["r"], 16)
s = int(signed_transaction["details"]["signature"]["s"], 16)

# Obtain your vault's public key
vault = requests.get(
    f"https://{gateway_host}/api/v1/vaults/{vault_id}",
    headers={
        "Authorization": f"Bearer {access_token}",
    }
).json()

stark_key = int(vault["details"]["stark_key"], 16)

# Verify the signature
assert verify(payload_int, r, s, stark_key)

# You can also use the public key in the compressed format
compressed_public_key = base64.b64decode(vault["public_key_compressed"])

# This is the relation between the compressed public key and the stark key
assert int.from_bytes(compressed_public_key[1:], byteorder="big") == stark_key