Cryptography
DeroAuth performs signature verification using DERO's native cryptographic primitives — entirely in TypeScript.
Schnorr Signatures on BN256
DERO uses Schnorr signatures on the BN256 (Barreto-Naehrig 254-bit) elliptic curve. This is fundamentally different from Bitcoin/Ethereum which use ECDSA on secp256k1.
Signature Scheme
A Schnorr signature consists of (s, e) where:
e = H(R || P || M)— hash of the nonce point, public key, and messages = r - e * x— the scalar response (wherexis the private key,ris the nonce)
Verification checks: s*G + e*P == R
Hash Function
DERO uses Keccak-256 (not SHA-256) for signature hashing. The hash output is reduced modulo the curve order to produce a scalar.
import { reducedHash } from "dero-auth/crypto";
// Keccak-256 of the input, reduced mod curve order
const scalar = reducedHash(messageBytes);DERO's Custom Generator Point
Critical detail: DERO does not use the standard BN256 generator point (1, 2). It uses a custom generator derived from hashing. This was a critical discovery during development — using the standard generator produces invalid verification results.
The custom generator is defined in dero-auth/crypto and was reverse-engineered from the DERO Go source code. This is the single most important implementation detail for anyone attempting DERO signature verification.
Address Format
DERO addresses use Bech32 encoding with a 33-byte compressed G1 public key:
dero1qy[...rest of bech32...]The dero-auth/crypto module provides encoding and decoding:
import { decodeAddress, encodeAddress, isValidAddress } from "dero-auth/crypto";
// Decode address to get the public key bytes
const publicKeyBytes = decodeAddress("dero1qy...");
// Returns: Uint8Array (33 bytes, compressed G1 point)
// Encode a public key back to an address
const address = encodeAddress(publicKeyBytes);
// Validate format
const isValid = isValidAddress("dero1qy...");PEM Signature Format
DERO wallets produce signatures in PEM format — a base64-encoded block wrapped in header/footer lines:
-----BEGIN DERO SIGNED MESSAGE-----
[base64 encoded signature data]
-----END DERO SIGNED MESSAGE-----import { parsePEM } from "dero-auth/crypto";
const { signature, publicKey } = parsePEM(pemString);Verification Pipeline
The full verification process:
import { verifySignature } from "dero-auth/crypto";
const result = verifySignature({
message: challengeMessage,
signature: pemSignature,
address: "dero1qy...",
});
if (result.valid) {
// Signature is mathematically valid
// result.address contains the verified DERO address
}Under the hood:
- Parse PEM to extract raw signature bytes
- Decode the DERO address to a compressed G1 public key
- Decompress the public key to a full curve point
- Hash the message with Keccak-256, reduce mod curve order
- Verify the Schnorr equation:
s*G + e*P == R
Dependencies
All cryptographic operations use audited libraries:
@noble/curves— BN256 curve operations (audited by Cure53)@noble/hashes— Keccak-256 hashing (audited by Cure53)
No custom cryptographic implementations.