# Ed25519 Signature Some endpoints require Ed25519 signatures for security (indicated by the `X-Signature` header parameter). ## Generating Your Ed25519 Keypair Before you can sign requests, you need to generate an Ed25519 keypair. Here's how to generate one: ```js import nacl from "tweetnacl"; // Generate a new random Ed25519 keypair const keyPair = nacl.sign.keyPair(); // Encode keys as base64 strings const publicKey = Buffer.from(keyPair.publicKey).toString("base64"); const privateKey = Buffer.from(keyPair.secretKey).toString("base64"); // Output as JSON for easier parsing console.log("publicKey:", publicKey); console.log("privateKey:", privateKey); ``` > **Important:** You need to register your public key with the ORO Bank platform before you can use it to sign requests. Keep your private key secure and never share it. ## Overview The signature generation process involves: 1. **Create Hash Payload** - Construct a JSON object with specific fields 2. **Stringify with Sorted Keys** - JSON.stringify with keys in alphabetical order 3. **SHA-256 Hash** - Hash the stringified JSON 4. **Sign** - Sign the hash with your Ed25519 private key 5. **Base64 Encode** - Encode the signature in base64 6. **Include in Header** - Add to the `X-Signature` header ## Hash Payload Structure ### For Transfer Orders (`POST /accounts/{accountId}/orders`) ```json { "method": "POST", "path": "/accounts/123/orders", "nonce": "1704528000000", "amount": "100000", "recipientID": 368 } ``` **Important:** Only include fields that are in the request body (amount, recipientID) plus method, path, and nonce. ## Code Examples ### JavaScript ```js import crypto from "crypto"; import nacl from "tweetnacl"; function generateSignature(payload, privateKeyBase64) { // 1. Sort keys and stringify const payloadString = JSON.stringify(payload); // 2. SHA-256 hash const hash = crypto.createHash("sha256").update(payloadString).digest(); // 3. Sign with Ed25519 private key const privateKey = Buffer.from(privateKeyBase64, "base64"); const signature = nacl.sign.detached(hash, privateKey); // 4. Base64 encode return Buffer.from(signature).toString("base64"); } // Usage example const payload = { method: "POST", path: "/accounts/123/orders", nonce: Date.now().toString(), amount: "100000", recipientID: 368, }; const signature = generateSignature(payload, yourPrivateKey); // Make API call await fetch("https://bank.bank.place/accounts/123/orders", { method: "POST", headers: { Authorization: "Bearer " + accessToken, "Content-Type": "application/json", "X-Nonce": payload.nonce, "X-Signature": signature, "Workspace-ID": "456", }, body: JSON.stringify({ amount: "100000", type: "instant", recipientID: 368, }), }); ``` ### Go ```go package main import ( "crypto/ed25519" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "time" ) type NewOrderHashPayload struct { Method string `json:"method"` Path string `json:"path"` Nonce string `json:"nonce"` Amount string `json:"amount"` RecipientID int `json:"recipientID"` } func generateSignature(payload NewOrderHashPayload, privateKeyBase64 string) (string, error) { payloadBytes, err := json.Marshal(payload) if err != nil { return "", err } // 2. SHA-256 hash hash := sha256.Sum256(payloadBytes) // 3. Decode private key privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64) if err != nil { return "", err } // 4. Sign with Ed25519 signature := ed25519.Sign(ed25519.PrivateKey(privateKeyBytes), hash[:]) // 5. Base64 encode return base64.StdEncoding.EncodeToString(signature), nil } // Usage example func main() { // Create struct instance with the same data as the original map payload := NewOrderHashPayload{ Method: "POST", Path: "/accounts/123/orders", Nonce: fmt.Sprintf("%d", time.Now().UnixMilli()), Amount: "100000", RecipientID: 368, } // Replace 'yourPrivateKey' with actual base64-encoded private key signature, err := generateSignature(payload, "yourPrivateKey") if err != nil { fmt.Printf("Error generating signature: %v\n", err) return } // Make API call with signature in X-Signature header fmt.Printf("Generated signature: %s\n", signature) } ``` ## Important Notes > **Warning:** Never include your private key in client-side code or commit it to version control. > **Info:** The nonce must be a Unix timestamp in milliseconds. It's used for deduplication and replay attack prevention. > **Info:** The hash payload must include ALL request body fields that will be sent, plus method, path, and nonce. > **Success:** Keys in the JSON payload are automatically sorted alphabetically by most JSON libraries (Go's json.Marshal, Python's json.dumps with sort_keys=True, JavaScript's JSON.stringify with sorted keys).