Commit bb41108b authored by jan.koester's avatar jan.koester
Browse files

test

parent 0aba6955
Loading
Loading
Loading
Loading
+164 −0
Original line number Diff line number Diff line
@@ -440,4 +440,168 @@ u256 fp_inv(const u256& a_mont) {
    return fp_pow(a_mont, exp);
}

// ============================================================================
// P-256 group order n for scalar field arithmetic (ECDSA)
// n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
// ============================================================================
static const u256 SCALAR_N = {{
    0xFC632551, // w[0] LSW
    0xF3B9CAC2,
    0xA7179E84,
    0xBCE6FAAD,
    0xFFFFFFFF,
    0xFFFFFFFF,
    0x00000000,
    0xFFFFFFFF  // w[7] MSW
}};

// Reduction mod n (group order) - used for ECDSA scalar arithmetic
// Uses schoolbook division since n doesn't have a special form like p
u256 mod_n256(const u512& in) {
    // For a 512-bit value, we do trial subtraction of n*2^i for i from 256 down to 0
    // This is slow but correct. For production, use Barrett reduction.
    
    // Work with extended precision
    uint64_t t[16];
    for (int i = 0; i < 16; i++) t[i] = in.w[i];
    
    // Reduce: while value >= n * 2^256, subtract n * 2^256
    // Then while value >= n * 2^224, subtract n * 2^224, etc.
    
    // Simple approach: convert to u256, handle overflow with subtraction
    // First, check if high 256 bits are non-zero
    bool hasHigh = false;
    for (int i = 8; i < 16; i++) if (t[i] != 0) { hasHigh = true; break; }
    
    if (!hasHigh) {
        // Just low 256 bits - simple reduction
        u256 r;
        for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i];
        while (u256_cmp(r, SCALAR_N) >= 0) {
            uint32_t br = 0;
            r = u256_sub_raw(r, SCALAR_N, br);
        }
        return r;
    }
    
    // Full 512-bit reduction using repeated subtraction
    // This is O(n) in the worst case but works for random inputs
    
    // Compute q ≈ floor(in / n) using high bits estimation
    // Then r = in - q*n and adjust
    
    // For simplicity, use a loop that processes word by word from high to low
    // Each iteration reduces the value by subtracting n shifted appropriately
    
    for (int shift = 256; shift >= 0; shift -= 32) {
        // Check if we can subtract n << shift
        int wordShift = shift / 32;
        
        while (true) {
            // Check if t >= n << shift
            bool canSubtract = false;
            
            // Compare t with n << wordShift
            int topWord = wordShift + 7;
            if (topWord < 16) {
                // Check from top
                bool greater = false;
                bool equal = true;
                for (int i = 15; i >= 0; i--) {
                    uint64_t nShifted = 0;
                    int ni = i - wordShift;
                    if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni];
                    
                    if (t[i] > nShifted) { greater = true; equal = false; break; }
                    if (t[i] < nShifted) { greater = false; equal = false; break; }
                }
                canSubtract = greater || equal;
            }
            
            if (!canSubtract) break;
            
            // Subtract n << wordShift from t
            int64_t borrow = 0;
            for (int i = 0; i < 16; i++) {
                uint64_t nShifted = 0;
                int ni = i - wordShift;
                if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni];
                
                int64_t diff = (int64_t)t[i] - (int64_t)nShifted - borrow;
                if (diff < 0) {
                    diff += 0x100000000LL;
                    borrow = 1;
                } else {
                    borrow = 0;
                }
                t[i] = (uint64_t)diff;
            }
        }
    }
    
    // Result is now in t[0..7]
    u256 r;
    for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i];
    
    // Final normalization
    while (u256_cmp(r, SCALAR_N) >= 0) {
        uint32_t br = 0;
        r = u256_sub_raw(r, SCALAR_N, br);
    }
    
    return r;
}

// Scalar field multiplication: (a * b) mod n
u256 scalar_mul_mod_n(const u256& a, const u256& b) {
    u512 prod = u256_mul_raw(a, b);
    return mod_n256(prod);
}

// Scalar field addition: (a + b) mod n
u256 scalar_add_mod_n(const u256& a, const u256& b) {
    uint32_t carry = 0;
    u256 r = u256_add_raw(a, b, carry);
    
    // If carry or r >= n, subtract n
    if (carry || u256_cmp(r, SCALAR_N) >= 0) {
        uint32_t br = 0;
        r = u256_sub_raw(r, SCALAR_N, br);
    }
    return r;
}

// Scalar field inversion using Fermat's little theorem: a^-1 = a^(n-2) mod n
u256 scalar_inv_mod_n(const u256& a) {
    // n-2 for SCALAR_N
    static const u256 N_MINUS_2 = {{
        0xFC63254F, // w[0] = 0xFC632551 - 2
        0xF3B9CAC2,
        0xA7179E84,
        0xBCE6FAAD,
        0xFFFFFFFF,
        0xFFFFFFFF,
        0x00000000,
        0xFFFFFFFF
    }};
    
    // Square-and-multiply: result = a^(n-2) mod n
    u256 result;
    u256_zero(result);
    result.w[0] = 1;
    
    u256 base = a;
    
    for (int i = 0; i < 8; i++) {
        for (int bit = 0; bit < 32; bit++) {
            if ((N_MINUS_2.w[i] >> bit) & 1) {
                result = scalar_mul_mod_n(result, base);
            }
            base = scalar_mul_mod_n(base, base);
        }
    }
    
    return result;
}

} // namespace netplus
+6 −0
Original line number Diff line number Diff line
@@ -66,4 +66,10 @@ uint32_t get_bit_be(const uint8_t priv[32], int bit_index);
// reduction (512 -> 256 mod P)
u256 mod_p256(const u512& in);

// scalar field arithmetic (mod n, group order) for ECDSA
u256 mod_n256(const u512& in);
u256 scalar_mul_mod_n(const u256& a, const u256& b);
u256 scalar_add_mod_n(const u256& a, const u256& b);
u256 scalar_inv_mod_n(const u256& a);

} // namespace netplus
+88 −89
Original line number Diff line number Diff line
@@ -3862,20 +3862,35 @@ bool netplus::ssl::loadServerPrivateKeyDer(const std::string& keyDerPath) {
             root.children[2].data != nullptr &&
             root.children[2].len > 0);

        std::cerr << "[SSL] loadServerPrivateKeyDer: looksPkcs8=" << looksPkcs8 
                  << " children=" << root.children.size() << std::endl;
        
        bool ok = false;
        
        // Check if this is an EC key (PKCS#8 with ecPublicKey OID)
        bool isEcKey = false;
        if (looksPkcs8 && root.children.size() >= 2 && root.children[1].tag == 0x30) {
            const auto& algId = root.children[1];
            std::cerr << "[SSL] algId.children.size()=" << algId.children.size() << std::endl;
            if (algId.children.size() >= 1 && algId.children[0].tag == 0x06) {
                // Check algorithm OID
                const auto& oid = algId.children[0];
                std::cerr << "[SSL] Algorithm OID len=" << oid.len << " bytes: ";
                for (size_t i = 0; i < oid.len && i < 16; i++) {
                    std::cerr << std::hex << (int)oid.data[i] << " ";
                }
                std::cerr << std::dec << std::endl;
                isEcKey = isOidEcPublicKey(oid.data, oid.len);
                std::cerr << "[SSL] isEcKey after OID check: " << isEcKey << std::endl;
                
                // Also check for P-256 curve OID in parameters
                if (isEcKey && algId.children.size() >= 2 && algId.children[1].tag == 0x06) {
                    const auto& curveOid = algId.children[1];
                    std::cerr << "[SSL] Curve OID len=" << curveOid.len << " bytes: ";
                    for (size_t i = 0; i < curveOid.len && i < 16; i++) {
                        std::cerr << std::hex << (int)curveOid.data[i] << " ";
                    }
                    std::cerr << std::dec << std::endl;
                    if (!isOidP256(curveOid.data, curveOid.len)) {
                        // Not P-256, we only support P-256 for now
                        isEcKey = false;
@@ -4302,141 +4317,125 @@ std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_
}

// ECDSA-SHA256 signature using P-256 curve
// Now uses proper scalar field arithmetic from ecc_u256.cpp

std::vector<uint8_t> netplus::ssl::_ecdsa_sha256_sign(const std::vector<uint8_t>& in){
    if (!_has_ec_key) throwSSL(NetException::Error, "TLS1.3: no EC private key");
    
    std::cerr << "[ECDSA] Starting signature generation" << std::endl;
    
    // 1. Hash the message with SHA-256
    std::vector<uint8_t> hash = sha256_hash(in);
    
    // 2. Generate ephemeral k (must be random per RFC 6979, simplified here)
    uint8_t k[32];
    fillRandom(std::vector<uint8_t>(k, k+32));
    // Ensure k is in valid range [1, n-1]
    // 2. Generate ephemeral k (must be random, in range [1, n-1])
    uint8_t k_bytes[32];
    u256 k_val;
    
    for (int retry = 0; retry < 100; ++retry) {
        fillRandom(std::vector<uint8_t>(k, k+32));
        // Check k < n and k != 0
        bool kZero = true;
        for (int i = 0; i < 32; ++i) if (k[i] != 0) { kZero = false; break; }
        if (kZero) continue;
        if (u256_cmp(*(const u256*)k, P256_N) < 0) break;
        std::vector<uint8_t> rnd(32);
        fillRandom(rnd);
        std::memcpy(k_bytes, rnd.data(), 32);
        
        // Convert to u256 using the proper conversion function
        k_val = u256_from_be(k_bytes);
        
        // Check k != 0 and k < n
        if (u256_is_zero(k_val)) continue;
        if (u256_cmp(k_val, P256_N) < 0) break;
    }
    
    // 3. Compute R = k*G, get r = R.x mod n
    P256Point R = scalar_mul_G(k);
    std::cerr << "[ECDSA] Generated k" << std::endl;
    
    // 3. Compute R = k*G
    P256Point R = scalar_mul_G(k_bytes);
    if (R.inf) throwSSL(NetException::Error, "ECDSA: R at infinity");
    
    // Convert R.x to big-endian bytes
    std::cerr << "[ECDSA] Computed R = k*G" << std::endl;
    
    // 4. Get r = R.x mod n
    std::vector<uint8_t> rx_be = encode_tls_point(R);
    // rx_be is 65 bytes: 04 || X(32) || Y(32), extract X
    std::vector<uint8_t> r_bytes(rx_be.begin() + 1, rx_be.begin() + 33);
    
    // 4. Compute s = k^-1 * (hash + r * privkey) mod n
    // This requires modular arithmetic in the scalar field
    // For simplicity, we use a direct bignum approach
    uint8_t r_bytes[32];
    std::memcpy(r_bytes, rx_be.data() + 1, 32);
    
    // Convert values to u256 for arithmetic
    u256 r_val, s_val, k_val, d_val, z_val;
    std::memset(&r_val, 0, sizeof(r_val));
    std::memset(&k_val, 0, sizeof(k_val));
    std::memset(&d_val, 0, sizeof(d_val));
    std::memset(&z_val, 0, sizeof(z_val));
    u256 r_val = u256_from_be(r_bytes);
    
    // Load big-endian bytes into u256 (little-endian words)
    for (int i = 0; i < 32; ++i) {
        ((uint8_t*)&r_val)[31-i] = r_bytes[i];
        ((uint8_t*)&k_val)[31-i] = k[i];
        ((uint8_t*)&d_val)[31-i] = _ec_priv[i];
        ((uint8_t*)&z_val)[31-i] = hash[i];
    }
    
    // r = r mod n (R.x is already < p, but we need mod n)
    // Reduce r mod n (R.x is < p, but we need mod n which is slightly smaller)
    while (u256_cmp(r_val, P256_N) >= 0) {
        u256 tmp;
        u256_sub(tmp, r_val, P256_N);
        r_val = tmp;
        uint32_t br = 0;
        r_val = u256_sub_raw(r_val, P256_N, br);
    }
    
    // Compute s = k^-1 * (z + r*d) mod n
    // We need modular inverse and multiplication in the scalar field
    // Using extended Euclidean algorithm for k^-1 mod n
    // Check r != 0
    if (u256_is_zero(r_val)) throwSSL(NetException::Error, "ECDSA: r = 0");
    
    // For now, use a simplified approach with the existing fp_* functions
    // adapted for the scalar field n instead of prime p
    std::cerr << "[ECDSA] r computed" << std::endl;
    
    // s = (z + r*d) * k^-1 mod n
    // This is complex to implement correctly. Let's use a workaround:
    // Export to external bignum or implement scalar field arithmetic.
    // 5. Convert hash (z) and private key (d) to u256
    u256 z_val = u256_from_be(hash.data());
    u256 d_val = u256_from_be(_ec_priv);
    
    // Simplified: just produce a valid DER-encoded signature structure
    // with r and placeholder s (THIS IS NOT CRYPTOGRAPHICALLY CORRECT)
    // We need proper scalar field arithmetic.
    // 6. Compute s = k^-1 * (z + r*d) mod n using proper scalar field arithmetic
    
    // Actually, let's compute it properly using the existing u256 operations
    // adapted for mod n arithmetic
    // First: rd = r * d mod n
    u256 rd = scalar_mul_mod_n(r_val, d_val);
    
    // Compute r*d mod n
    u512 rd_wide;
    u256_mul_wide(rd_wide, r_val, d_val);
    u256 rd_mod;
    // Reduce mod n - this needs Barrett reduction or similar
    // For now, do repeated subtraction (slow but correct for small values)
    std::memcpy(&rd_mod, &rd_wide, sizeof(u256)); // Take low 256 bits
    // This is incorrect for values >= n, but a proper implementation would use Barrett
    while (u256_cmp(rd_mod, P256_N) >= 0) {
        u256 tmp;
        u256_sub(tmp, rd_mod, P256_N);
        rd_mod = tmp;
    }
    // Second: sum = z + rd mod n
    u256 sum = scalar_add_mod_n(z_val, rd);
    
    // Compute z + rd mod n  
    u256 sum;
    u256_add(sum, z_val, rd_mod);
    while (u256_cmp(sum, P256_N) >= 0) {
        u256 tmp;
        u256_sub(tmp, sum, P256_N);
        sum = tmp;
    }
    // Third: k_inv = k^-1 mod n
    u256 k_inv = scalar_inv_mod_n(k_val);
    
    // Compute k^-1 mod n using Fermat's little theorem: k^-1 = k^(n-2) mod n
    // This requires modular exponentiation which is expensive
    // For production, use extended Euclidean algorithm
    std::cerr << "[ECDSA] Computed k^-1" << std::endl;
    
    // Placeholder: set s = sum (incorrect, but will be fixed)
    s_val = sum;
    // Fourth: s = k_inv * sum mod n
    u256 s_val = scalar_mul_mod_n(k_inv, sum);
    
    // Convert r and s back to big-endian bytes
    std::vector<uint8_t> s_bytes(32);
    for (int i = 0; i < 32; ++i) {
        s_bytes[i] = ((uint8_t*)&s_val)[31-i];
    }
    // Check s != 0
    if (u256_is_zero(s_val)) throwSSL(NetException::Error, "ECDSA: s = 0");
    
    // Encode as DER: SEQUENCE { INTEGER r, INTEGER s }
    auto encodeInteger = [](const std::vector<uint8_t>& val) -> std::vector<uint8_t> {
    std::cerr << "[ECDSA] s computed" << std::endl;
    
    // 7. Convert r and s back to big-endian bytes
    uint8_t r_be[32], s_be[32];
    u256_to_be(r_be, r_val);
    u256_to_be(s_be, s_val);
    
    // 8. Encode as DER: SEQUENCE { INTEGER r, INTEGER s }
    auto encodeInteger = [](const uint8_t* val, size_t len) -> std::vector<uint8_t> {
        std::vector<uint8_t> out;
        out.push_back(0x02); // INTEGER tag
        
        // Skip leading zeros but keep one if high bit set
        size_t start = 0;
        while (start < val.size() - 1 && val[start] == 0) ++start;
        while (start < len - 1 && val[start] == 0) ++start;
        
        bool needPad = (val[start] & 0x80) != 0;
        size_t len = val.size() - start + (needPad ? 1 : 0);
        size_t outLen = len - start + (needPad ? 1 : 0);
        
        out.push_back((uint8_t)len);
        out.push_back((uint8_t)outLen);
        if (needPad) out.push_back(0x00);
        out.insert(out.end(), val.begin() + start, val.end());
        out.insert(out.end(), val + start, val + len);
        return out;
    };
    
    std::vector<uint8_t> r_der = encodeInteger(r_bytes);
    std::vector<uint8_t> s_der = encodeInteger(s_bytes);
    std::vector<uint8_t> r_der = encodeInteger(r_be, 32);
    std::vector<uint8_t> s_der = encodeInteger(s_be, 32);
    
    std::vector<uint8_t> sig;
    sig.push_back(0x30); // SEQUENCE tag
    sig.push_back((uint8_t)(r_der.size() + s_der.size()));
    size_t inner_len = r_der.size() + s_der.size();
    if (inner_len >= 128) {
        sig.push_back(0x81);
        sig.push_back((uint8_t)inner_len);
    } else {
        sig.push_back((uint8_t)inner_len);
    }
    sig.insert(sig.end(), r_der.begin(), r_der.end());
    sig.insert(sig.end(), s_der.begin(), s_der.end());
    
    std::cerr << "[ECDSA] Signature generated, size=" << sig.size() << std::endl;
    
    return sig;
}