module std.crypto.ecdsa

// Generic ECDSA helpers.
//
// This module provides curve-agnostic ECDSA utilities: signature
// representation, range validation, and input reading. It works
// with any curve whose order fits in 256 bits.
use std.crypto.bigint

use vm.core.convert

use vm.io.io

// An ECDSA signature consisting of (r, s), each a 256-bit scalar.
pub struct Signature {
    r: bigint.U256,
    s: bigint.U256,
}

// ---------------------------------------------------------------------------
// Input reading
// ---------------------------------------------------------------------------
// Read a single U256 from public input (8 field elements, each holding one U32 limb).
// Limbs are read in little-endian order (least significant first).
fn read_u256() -> bigint.U256 {
    let f0: Field = io.read()
    let f1: Field = io.read()
    let f2: Field = io.read()
    let f3: Field = io.read()
    let f4: Field = io.read()
    let f5: Field = io.read()
    let f6: Field = io.read()
    let f7: Field = io.read()
    bigint.U256 { l0: convert.as_u32(f0), l1: convert.as_u32(f1), l2: convert.as_u32(f2), l3: convert.as_u32(f3), l4: convert.as_u32(f4), l5: convert.as_u32(f5), l6: convert.as_u32(f6), l7: convert.as_u32(f7) }
}

// Read a single U256 from divine (secret) input.
fn divine_u256() -> bigint.U256 {
    let f0: Field = io.divine()
    let f1: Field = io.divine()
    let f2: Field = io.divine()
    let f3: Field = io.divine()
    let f4: Field = io.divine()
    let f5: Field = io.divine()
    let f6: Field = io.divine()
    let f7: Field = io.divine()
    bigint.U256 { l0: convert.as_u32(f0), l1: convert.as_u32(f1), l2: convert.as_u32(f2), l3: convert.as_u32(f3), l4: convert.as_u32(f4), l5: convert.as_u32(f5), l6: convert.as_u32(f6), l7: convert.as_u32(f7) }
}

// Decompose a signature from public input.
// Reads r (8 field elements) then s (8 field elements), 16 total.
pub fn read_signature() -> Signature {
    let r: bigint.U256 = read_u256()
    let s: bigint.U256 = read_u256()
    Signature { r: r, s: s }
}

// Read a signature from divine (secret/witness) input.
pub fn divine_signature() -> Signature {
    let r: bigint.U256 = divine_u256()
    let s: bigint.U256 = divine_u256()
    Signature { r: r, s: s }
}

// ---------------------------------------------------------------------------
// Range validation
// ---------------------------------------------------------------------------
// Check that r and s are in valid range [1, n-1] for a given curve order n.
// Both r and s must be nonzero and strictly less than n.
pub fn valid_range(sig: Signature, order: bigint.U256) -> Bool {
    // r must not be zero
    let r_zero: Bool = bigint.is_zero(sig.r)
    if r_zero {
        false
    } else {
        // s must not be zero
        let s_zero: Bool = bigint.is_zero(sig.s)
        if s_zero {
            false
        } else {
            // r must be < order
            let r_lt: Bool = bigint.lt256(sig.r, order)
            if r_lt {
                // s must be < order
                bigint.lt256(sig.s, order)
            } else {
                false
            }
        }
    }
}

// Low-S normalization: 1 <= s <= floor(order / 2). Range-checking r is
// the separate valid_range operation; this helper does not verify a signature.
fn half_limb(low: U32, high: U32) -> U32 {
    let (q, _rem): (U32, U32) = low /% convert.as_u32(2)
    let carry: U32 = high & convert.as_u32(1)
    convert.as_u32(convert.as_field(q) + convert.as_field(carry) * 2147483648)
}

pub fn is_low_s(sig: Signature, order: bigint.U256) -> Bool {
    let half: bigint.U256 = bigint.U256 {
        l0: half_limb(order.l0, order.l1),
        l1: half_limb(order.l1, order.l2),
        l2: half_limb(order.l2, order.l3),
        l3: half_limb(order.l3, order.l4),
        l4: half_limb(order.l4, order.l5),
        l5: half_limb(order.l5, order.l6),
        l6: half_limb(order.l6, order.l7),
        l7: half_limb(order.l7, convert.as_u32(0)),
    }
    if bigint.is_zero(sig.s) { false }
    else { bigint.lt256(half, sig.s) == false }
}

// ---------------------------------------------------------------------------
// Signature encoding helpers
// ---------------------------------------------------------------------------
// Write a U256 to public output (8 field elements, little-endian limb order).
pub fn write_u256(val: bigint.U256) {
    io.write(convert.as_field(val.l0))
    io.write(convert.as_field(val.l1))
    io.write(convert.as_field(val.l2))
    io.write(convert.as_field(val.l3))
    io.write(convert.as_field(val.l4))
    io.write(convert.as_field(val.l5))
    io.write(convert.as_field(val.l6))
    io.write(convert.as_field(val.l7))
}

// Write a signature to public output (r then s, 16 field elements total).
pub fn write_signature(sig: Signature) {
    write_u256(sig.r)
    write_u256(sig.s)
}

Graph