> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Verification

> Signature verification for SIWE messages

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=primitives/siwe.ts">
  Run SIWE examples in the interactive playground
</Card>

# Verification

Signature verification for SIWE messages.

## verify

Verify SIWE message signature matches claimed address.

<Tabs>
  <Tab title="Wrapper API">
    ### Signature

    ```typescript theme={null}
    function verify(
      message: BrandedMessage,
      signature: Signature
    ): boolean
    ```

    ### Parameters

    * `message` - BrandedMessage that was signed
    * `signature` - 65-byte signature (r + s + v)

    ### Returns

    `true` if signature valid and matches `message.address`
    `false` if signature invalid or address mismatch

    ### Signature Format

    ```
    Bytes 0-31:  r (32 bytes) - ECDSA signature component
    Bytes 32-63: s (32 bytes) - ECDSA signature component
    Byte 64:     v (1 byte)   - Recovery ID (0, 1, 27, or 28)
    ```

    **Total:** 65 bytes

    **V normalization:**

    * If v >= 27: `recoveryId = v - 27`
    * If v \< 27: `recoveryId = v`
    * Valid recovery IDs: 0 or 1

    ### Process

    1. **Validate Message:** Check structure with `validate(message)`
    2. **Get Hash:** Compute EIP-191 hash with `getMessageHash(message)`
    3. **Extract Components:** Parse r, s, v from signature
    4. **Normalize V:** Convert v to recovery ID (0 or 1)
    5. **Recover Public Key:** Use secp256k1 recovery
    6. **Derive Address:** Keccak-256(publicKey)\[12:32]
    7. **Compare:** Check recovered address === message.address

    ### Example

    ```typescript theme={null}
    const message = Siwe.create({
      domain: "example.com",
      address: userAddress,
      uri: "https://example.com",
      chainId: 1,
    });

    const text = Siwe.format(message);
    const signature = await wallet.signMessage(text);

    const valid = Siwe.verify(message, signature);
    if (valid) {
      console.log("Signature verified, user authenticated");
    } else {
      console.log("Invalid signature");
    }
    ```
  </Tab>
</Tabs>

### Common Patterns

#### Backend Verification

```typescript theme={null}
app.post('/auth/verify', async (req, res) => {
  try {
    const message = Siwe.parse(req.body.message);
    const signature = hexToBytes(req.body.signature);

    const valid = Siwe.verify(message, signature);
    if (valid) {
      createSession(message.address);
      res.json({ success: true });
    } else {
      res.status(401).json({ error: "Invalid signature" });
    }
  } catch (err) {
    res.status(400).json({ error: "Invalid request" });
  }
});
```

#### Instance Method

```typescript theme={null}
const message = Siwe.create({ ... });
const valid = message.verify(signature);
// Same as Siwe.verify(message, signature)
```

#### With Validation

```typescript theme={null}
// Validate before expensive verification
const validationResult = Siwe.validate(message);
if (!validationResult.valid) {
  return error("Invalid message structure");
}

const signatureValid = Siwe.verify(message, signature);
if (!signatureValid) {
  return error("Signature mismatch");
}
```

### Error Cases

Returns `false` for:

* Invalid message structure
* Signature length !== 65 bytes
* Invalid v value (not 0, 1, 27, or 28)
* Failed public key recovery
* Address mismatch
* Any exception during verification

### Security Considerations

**Constant-Time Comparison:**

* Address comparison loops through all 20 bytes
* Prevents timing attacks

**V Normalization:**

* Accepts both raw (0, 1) and EIP-155 (27, 28) formats
* Rejects invalid v values

**Public Key Recovery:**

* Uses secp256k1 curve (same as Ethereum)
* Validates recovered point on curve
* Rejects invalid signatures

**No Malleability:**

* Signature uniqueness enforced by secp256k1
* Low-s values recommended but not required

***

## verifyMessage

Combined validation and signature verification.

### Signature

```typescript theme={null}
function verifyMessage(
  message: BrandedMessage,
  signature: Signature,
  options?: { now?: Date }
): ValidationResult
```

### Parameters

* `message` - BrandedMessage to verify
* `signature` - 65-byte signature
* `options.now` - Current time for timestamp checks

### Returns

`ValidationResult`:

* `{ valid: true }` - Structure valid AND signature verified
* `{ valid: false, error: ValidationError }` - Structure invalid OR signature mismatch

### Process

1. **Validate Structure:** Call `validate(message, options)`
2. **Return if Invalid:** Early return with validation error
3. **Verify Signature:** Call `verify(message, signature)`
4. **Return Result:** `{ valid: true }` or signature mismatch error

### Example

```typescript theme={null}
const message = Siwe.parse(req.body.message);
const signature = hexToBytes(req.body.signature);

const result = Siwe.verifyMessage(message, signature);

if (result.valid) {
  // Message structure valid AND signature verified
  createSession(message.address);
} else {
  // Either structure invalid or signature mismatch
  console.error(result.error.type);
  console.error(result.error.message);
}
```

### Error Types

All validation errors plus:

* `signature_mismatch` - Signature does not match address or verification failed

### Common Patterns

#### Complete Verification

```typescript theme={null}
const result = Siwe.verifyMessage(message, signature);

if (!result.valid) {
  switch (result.error.type) {
    case "signature_mismatch":
      logSecurityEvent("Invalid signature", { address: message.address });
      return unauthorized();
    case "expired":
      return reauth("Session expired");
    case "not_yet_valid":
      return error("Authentication not yet valid");
    case "invalid_nonce":
      return error("Invalid nonce");
    default:
      return error(result.error.message);
  }
}

// Authenticated
createSession(message.address);
```

#### With Timestamp Check

```typescript theme={null}
const result = Siwe.verifyMessage(message, signature, {
  now: new Date(),
});

if (result.valid) {
  // Message not expired, not before satisfied, signature valid
  authenticateUser(message.address);
}
```

#### Complete Auth Flow

```typescript theme={null}
async function authenticateWithSiwe(
  messageText: string,
  signatureHex: string
): Promise<{ success: boolean; error?: string }> {
  try {
    // Parse message
    const message = Siwe.parse(messageText);

    // Verify nonce
    if (!await verifyNonce(message.nonce)) {
      return { success: false, error: "Invalid or reused nonce" };
    }

    // Verify domain
    if (message.domain !== expectedDomain) {
      return { success: false, error: "Domain mismatch" };
    }

    // Convert signature
    const signature = hexToBytes(signatureHex);

    // Verify message and signature
    const result = Siwe.verifyMessage(message, signature);

    if (!result.valid) {
      return { success: false, error: result.error.message };
    }

    // Create session
    await createSession(message.address, message.chainId);
    return { success: true };

  } catch (err) {
    return { success: false, error: "Authentication failed" };
  }
}
```

### Advantages Over Separate Calls

**Convenience:**

* Single function call for complete verification
* Structured error handling
* Consistent return type

**Efficiency:**

* Early return on validation failure
* Skips expensive signature verification if structure invalid

**Correctness:**

* Ensures validation always happens first
* Prevents verification of malformed messages

### Performance

**Validation:** O(1) - Fast structure checks
**Signature Verification:** O(1) - secp256k1 recovery (expensive)

**Optimization:** Always validates first to avoid wasting cycles on invalid messages

***

## Factory API

Tree-shakeable factory pattern with explicit crypto dependencies.

### Verify Factory

```typescript theme={null}
function Verify({
  keccak256,
  secp256k1RecoverPublicKey,
  addressFromPublicKey
}): (message: BrandedMessage, signature: Signature) => boolean
```

**Dependencies:**

* `keccak256: (data: Uint8Array) => Uint8Array` - Keccak256 hash function
* `secp256k1RecoverPublicKey: (sig: {r, s, v}, hash: Uint8Array) => Uint8Array` - secp256k1 public key recovery
* `addressFromPublicKey: (x: bigint, y: bigint) => Uint8Array` - Address derivation from public key

**Example:**

```typescript theme={null}
import { Verify } from 'tevm/Siwe'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'
import { recoverPublicKey as secp256k1RecoverPublicKey } from 'tevm/crypto/Secp256k1'
import { FromPublicKey } from 'tevm/Address'

const addressFromPublicKey = FromPublicKey({ keccak256 })
const verify = Verify({ keccak256, secp256k1RecoverPublicKey, addressFromPublicKey })

const valid = verify(message, signature)
```

**Bundle size:** Crypto only included if you import it.

### VerifyMessage Factory

```typescript theme={null}
function VerifyMessage({
  keccak256,
  secp256k1RecoverPublicKey,
  addressFromPublicKey
}): (message: BrandedMessage, signature: Signature, options?: {now?: Date}) => ValidationResult
```

Same dependencies as `Verify`.

**Example:**

```typescript theme={null}
import { VerifyMessage } from 'tevm/Siwe'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'
import { recoverPublicKey as secp256k1RecoverPublicKey } from 'tevm/crypto/Secp256k1'
import { FromPublicKey } from 'tevm/Address'

const addressFromPublicKey = FromPublicKey({ keccak256 })
const verifyMessage = VerifyMessage({ keccak256, secp256k1RecoverPublicKey, addressFromPublicKey })

const result = verifyMessage(message, signature, { now: new Date() })
```

### GetMessageHash Factory

```typescript theme={null}
function GetMessageHash({
  keccak256
}): (message: BrandedMessage) => Uint8Array
```

**Dependencies:**

* `keccak256: (data: Uint8Array) => Uint8Array` - Keccak256 hash function

**Example:**

```typescript theme={null}
import { GetMessageHash } from 'tevm/Siwe'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'

const getMessageHash = GetMessageHash({ keccak256 })
const hash = getMessageHash(message)
```

***

## See Also

* [Siwe.validate](/primitives/siwe/validation) - Message validation
* [Siwe.getMessageHash](/primitives/siwe/signing) - Hash generation
* [Usage Patterns](/primitives/siwe/usage-patterns) - Authentication flows
* [EIP-191](https://eips.ethereum.org/EIPS/eip-191) - Signed data standard
