Commit 39e4cd7b authored by jan.koester's avatar jan.koester
Browse files

test

parent 2121e6fe
Loading
Loading
Loading
Loading
+168 −185
Original line number Diff line number Diff line
#include <cstdio>
#include <iostream>
#include <algorithm>

#include "curve25519.h"
#include "../src/exception.h"

namespace netplus {
#include <array>
#include <cstdint>
#include <vector>
#include <algorithm>

FieldElement::FieldElement() {
    limbs.fill(0);
}
namespace netplus {

FieldElement::FieldElement(int v) {
    limbs.fill(0);
    limbs[0] = v;
}
// ============================================================================
// FieldElement: gf[16] like TweetNaCl (internal only)
// ============================================================================

FieldElement::FieldElement(const std::vector<int32_t>& v) {
    limbs.fill(0);
    for (size_t i = 0; i < 16 && i < v.size(); ++i)
        limbs[i] = v[i];
}
class FieldElement {
public:
    std::array<int64_t, 16> limbs;

FieldElement::FieldElement(const FieldElement& src) : limbs(src.limbs) {}
    FieldElement() { limbs.fill(0); }
    explicit FieldElement(int v) { limbs.fill(0); limbs[0] = v; }

FieldElement& FieldElement::operator=(const std::vector<int32_t>& v) {
    limbs.fill(0);
    for (size_t i = 0; i < 16 && i < v.size(); ++i)
        limbs[i] = v[i];
    return *this;
}
    void zero() { limbs.fill(0); }
    void one()  { limbs.fill(0); limbs[0] = 1; }

FieldElement FieldElement::operator+(const FieldElement& other) const {
    // A(o,a,b): o=a+b   (NO carry, TweetNaCl style)
    FieldElement operator+(const FieldElement& other) const {
        FieldElement r;
        for (int i = 0; i < 16; ++i)
            r.limbs[i] = limbs[i] + other.limbs[i];
        return r;
    }

FieldElement FieldElement::operator-(const FieldElement& other) const {
    // Z(o,a,b): o=a-b   (NO carry, TweetNaCl style)
    FieldElement operator-(const FieldElement& other) const {
        FieldElement r;
        for (int i = 0; i < 16; ++i)
            r.limbs[i] = limbs[i] - other.limbs[i];
        return r;
    }

void FieldElement::carry25519(){
    int64_t cry;
    for(size_t i=0; i<16; ++i){
        cry =limbs[i] >> 16;
        limbs[i]-=cry << 16;
        i < 15 ? limbs[i+1]+=cry : limbs[0] += 38 * cry;
    }
    return;
};

FieldElement FieldElement::operator*(const FieldElement& g) const {
    std::array<int64_t,31> prod;
    std::fill(prod.begin(),prod.end(),0);
    FieldElement out;
    // car25519(o)  --- EXACT TweetNaCl
    void carry25519() {
        for (int i = 0; i < 16; ++i) {
            limbs[i] += (1LL << 16);
            int64_t c = limbs[i] >> 16;
            limbs[i] -= c << 16;

    for(size_t i=0; i< 16; ++i){
        for(size_t j=0; j<16; ++j){
            prod[i+j]+=this->limbs[i] * g.limbs[j];
            if (i < 15) {
                limbs[i + 1] += c - 1;
            } else {
                limbs[0] += 38 * (c - 1);   // <-- WICHTIG: 38*(c-1), nicht 37*(c-1)
            }
        }
    for(size_t i=0; i< 15; ++i){
        prod[i] += 38 * prod[i+16];
    }
    for(size_t i=0; i <16; ++i){
        out.limbs[i]=prod[i];
    }
    out.carry25519();
    out.carry25519();
    return out;
    }

FieldElement FieldElement::operator*(int32_t scalar) const {
    FieldElement r;
    int64_t sc = scalar;
    for (int i = 0; i < 16; ++i)
        r.limbs[i] = limbs[i] * sc;
    return r;
}

FieldElement operator*(int32_t scalar, const FieldElement& fe) {
    return fe * scalar;
}

bool FieldElement::operator==(const FieldElement& other) const {

    auto ct_is_zero_u64=[](uint64_t x) {
        // returns 1 if x == 0 else 0 (constant-time)
        return (uint32_t)(1 ^ ((x | (0ULL - x)) >> 63));
    };

    uint64_t diff = 0;
    for (size_t i = 0; i < 16; ++i) {
        diff |= (uint64_t)(limbs[i] ^ other.limbs[i]);
    // sel25519(p,q,b) : swap if b=1  --- EXACT TweetNaCl
    void sel25519(FieldElement& q, int b) {
        int64_t t;
        int64_t c = ~((int64_t)b - 1); // b=0 => 0, b=1 => -1
        for (int i = 0; i < 16; ++i) {
            t = c & (limbs[i] ^ q.limbs[i]);
            limbs[i] ^= t;
            q.limbs[i] ^= t;
        }
    return ct_is_zero_u64(diff);
    }

    // M(o,a,b): multiplication + reduction  --- EXACT TweetNaCl
    FieldElement operator*(const FieldElement& g) const {
        int64_t t[31];
        for (int i = 0; i < 31; ++i) t[i] = 0;

void FieldElement::zero() { std::fill(limbs.begin(), limbs.end(), 0); }
        for (int i = 0; i < 16; ++i)
            for (int j = 0; j < 16; ++j)
                t[i + j] += limbs[i] * g.limbs[j];

void FieldElement::one() { zero(); limbs[0] = 1; }
        for (int i = 0; i < 15; ++i)
            t[i] += 38 * t[i + 16];

// Add to FieldElement class:
FieldElement FieldElement::invert() const {
        FieldElement out;
    for(size_t i =0; i< limbs.size(); ++i){
        out.limbs[i]=limbs[i];
    }
    for(size_t i = 253; i!=std::string::npos; i--){
        out=out * out;
        if(i!=2 && i!=4){
            out=out*(*this);
        }
    }
        for (int i = 0; i < 16; ++i)
            out.limbs[i] = t[i];

        out.carry25519();
        out.carry25519();
        return out;
    }

void FieldElement::from_bytes(std::vector<uint8_t> in){
    for(size_t i=0; i< 16; ++i){
        limbs[i]=in[2*i]+static_cast<int64_t>(in[2*i+1] << 8);
    }
    limbs[15] &=0x7FFF;
    // S(o,a): square (TweetNaCl: S(o,a) = M(o,a,a))
    FieldElement square() const {
        return (*this) * (*this);
    }

std::vector<uint8_t> FieldElement::to_bytes() const {
       std::vector <uint8_t> out(32,0);
        int carry;
        FieldElement m,t=*this;
        t.carry25519(); t.carry25519(); t.carry25519();
        for(size_t j=0; j<2; ++j){
            m.limbs[0]=t.limbs[0]-0xFFED;
            for(size_t i=1; i< 15; i++){
                m.limbs[i]=t.limbs[i]-0xFFFF-((m.limbs[i-1]>>16) & 1);
                m.limbs[i-1]&=0xFFFF;
            }
            m.limbs[15]=t.limbs[15]-0x7FFF-((m.limbs[14]>>16) &1);
            carry=(m.limbs[15]>>16) & 1;
            m.limbs[14] &=0xFFFF;
            t.cswap(m,1-carry);
        }
        for(size_t i = 0; i <16; ++i){
            out[2*i] = t.limbs[i] & 0xFF;
            out[2*i+1] = t.limbs[i] >> 8;
    // unpack25519
    void from_bytes(const std::vector<uint8_t>& in) {
        for (int i = 0; i < 16; ++i) {
            limbs[i] = (int64_t)in[2*i] + ((int64_t)in[2*i+1] << 8);
        }
        return out;
        limbs[15] &= 0x7fff;
    }

void FieldElement::cswap(FieldElement& other, int b) {
    int64_t t;
    int64_t c = ~( (int64_t)b - 1 );   // b=0 => c=0, b=1 => c=-1 (all bits 1)
    // pack25519  --- EXACT TweetNaCl
    std::vector<uint8_t> to_bytes() const {
        std::vector<uint8_t> out(32, 0);
        FieldElement t = *this;
        FieldElement m;

    for (size_t i = 0; i < 16; ++i) {
        t = c & (limbs[i] ^ other.limbs[i]);
        limbs[i] ^= t;
        other.limbs[i] ^= t;
        t.carry25519();
        t.carry25519();
        t.carry25519();

        for (int j = 0; j < 2; ++j) {
            m.limbs[0] = t.limbs[0] - 0xffed;
            for (int i = 1; i < 15; ++i) {
                m.limbs[i] = t.limbs[i] - 0xffff - ((m.limbs[i-1] >> 16) & 1);
                m.limbs[i-1] &= 0xffff;
            }
            m.limbs[15] = t.limbs[15] - 0x7fff - ((m.limbs[14] >> 16) & 1);
            int b = (m.limbs[15] >> 16) & 1;
            m.limbs[14] &= 0xffff;
            t.sel25519(m, 1 - b);
        }

void FieldElement::sel25519(FieldElement& q, int b) {
    b &= 1; // safety: ensure b is 0 or 1

    int64_t t;
    int64_t c = ~((int64_t)b - 1); // b=0 => 0x0, b=1 => 0xFFFF...FFFF
        for (int i = 0; i < 16; ++i) {
            out[2*i]     = (uint8_t)(t.limbs[i] & 0xff);
            out[2*i + 1] = (uint8_t)(t.limbs[i] >> 8);
        }
        return out;
    }

    for (size_t i = 0; i < 16; ++i) {
        t = c & (limbs[i] ^ q.limbs[i]);
        limbs[i] ^= t;
        q.limbs[i] ^= t;
    // inv25519(o,i)  --- EXACT TweetNaCl
    FieldElement invert() const {
        FieldElement c = *this;
        for (int a = 253; a >= 0; --a) {
            c = c.square();
            if (a != 2 && a != 4)
                c = c * (*this);
        }
        return c;
    }
};

// ============================================================================
// X25519 scalar multiplication (TweetNaCl crypto_scalarmult)
// ============================================================================

bool scalarmult_curve25519(std::vector<uint8_t>& out,
                           const std::vector<uint8_t>& scalar,
                           const std::vector<uint8_t> &point_u) {
                           const std::vector<uint8_t>& point_u)
{
    if (scalar.size() != 32 || point_u.size() != 32) return false;

    std::array<uint8_t,32> clamped;
    FieldElement _12665;
    _12665.limbs={0xDB41,1};
    FieldElement a,b,c,d,e,f,x;
    for(size_t i=0; i<32; ++i){
        clamped[i]=scalar[i];
    }
    clamped[0] &=0xF8;
    clamped[31] = (clamped[31] & 0x7F) | 0x40;
    // Clamp scalar
    std::array<uint8_t, 32> z{};
    for (int i = 0; i < 32; ++i) z[i] = scalar[i];
    z[31] = (z[31] & 127) | 64;
    z[0] &= 248;


    // Decode u
    FieldElement x;
    x.from_bytes(point_u);
    for(size_t i=0; i<16; ++i){
        b.limbs[i]=x.limbs[i];
        d.limbs[i]=a.limbs[i] = c.limbs[i] =0;
    }
    a.limbs[0]=d.limbs[0] = 1;
    int swapbit = 0;

    // Ladder state
    FieldElement a, b, c, d, e, f;
    b = x;
    a.zero();
    c.zero();
    d.zero();
    a.limbs[0] = 1;
    d.limbs[0] = 1;

    FieldElement _121665;
    _121665.zero();
    _121665.limbs[0] = 0xDB41;
    _121665.limbs[1] = 1;

    // Main ladder loop (exact TweetNaCl)
    for (int i = 254; i >= 0; --i) {
        int bit = (clamped[i >> 3] >> (i & 7)) & 1;
        int r = (z[i >> 3] >> (i & 7)) & 1;

        swapbit ^= bit;
        a.sel25519(b, swapbit);
        c.sel25519(d, swapbit);
        swapbit = bit;
        a.sel25519(b, r);
        c.sel25519(d, r);

        e = a + c;
        a = a - c;
        c = b + d;
        b = b - d;
        d = e * e;
        f = a * a;

        d = e.square();
        f = a.square();

        a = c * a;
        c = b * e;

        e = a + c;
        a = a - c;
        b = a * a;

        b = a.square();
        c = d - f;
        a = c * _12665;

        a = c * _121665;
        a = a + d;

        c = c * a;
        a = d * f;

        d = b * x;
        b = e * e;
    }
        b = e.square();

    a.sel25519(b, swapbit);
    c.sel25519(d, swapbit);
        a.sel25519(b, r);
        c.sel25519(d, r);
    }

    // Convert: a / c
    c = c.invert();
    a = a * c;

    out = a.to_bytes();
    return true;
}


bool scalarmult_curve25519_base(std::vector<uint8_t>& out,
                                const std::vector<uint8_t> &scalar) {
    // The Curve25519 base point (u = 9, little-endian 32 bytes)
    std::vector<uint8_t> base_point(32,0);
    base_point[0]=9;
    // Delegate to the main scalar multiplication function
    return scalarmult_curve25519(out, scalar, base_point);
}
                                const std::vector<uint8_t>& scalar)
{
    if (scalar.size() != 32) return false;

    std::vector<uint8_t> base(32, 0);
    base[0] = 9;
    return scalarmult_curve25519(out, scalar, base);
}

} // namespace netplus
+6 −43
Original line number Diff line number Diff line
#pragma once

#include <array>
#include <cstdint>
#include <vector>
#include <algorithm>

#define ALIMBS 16

namespace netplus {

class FieldElement {
public:
    std::array<int64_t, 16> limbs;

    FieldElement();
    FieldElement(int v);
    FieldElement(const std::vector<int32_t>& v);
    FieldElement(const FieldElement& src);

    FieldElement& operator=(const std::vector<int32_t>& v);

    FieldElement operator+(const FieldElement& other) const;
    FieldElement operator-(const FieldElement& other) const;
    FieldElement operator*(const FieldElement& other) const;
    FieldElement operator*(int32_t scalar) const;

    bool operator==(const FieldElement& other) const;

    void carry25519();
    void zero();
    void one();
    FieldElement invert() const;
    void cswap(FieldElement& other, int b);
    void sel25519(FieldElement& q, int b);

    std::vector<uint8_t> to_bytes() const;
    void from_bytes(std::vector<uint8_t> in);
};

FieldElement operator*(int32_t scalar, const FieldElement& fe);

    bool scalarmult_curve25519(std::vector<uint8_t>& out,
                            const std::vector<uint8_t>& scalar,
                            const std::vector<uint8_t>& point_u);

    bool scalarmult_curve25519_base(std::vector<uint8_t>& out,
                                    const std::vector<uint8_t>& scalar);

} // namespace netplus
}
+43 −20
Original line number Diff line number Diff line
#include <iostream>
#include <array>
#include <iomanip>
#include <vector>
#include <algorithm>
@@ -18,16 +19,6 @@ bool equal32(const std::vector<uint8_t>& x, const std::vector<uint8_t>& y) {
}

int main() {
    netplus::FieldElement a, b;
a.limbs[0] = 1;
b.limbs[0] = 2;

a.sel25519(b, 0);
assert(a.limbs[0] == 1 && b.limbs[0] == 2);

a.sel25519(b, 1);
assert(a.limbs[0] == 2 && b.limbs[0] == 1);

    std::cout << "Running X25519 RFC7748 vectors + 1000-iteration KAT...\n";

    std::vector<uint8_t> out(32,0);
@@ -37,32 +28,64 @@ assert(a.limbs[0] == 2 && b.limbs[0] == 1);
    // RFC7748 vector #1: scalar1 * base(9)
    // ---------------------------------------------------------

    // RFC7748 vector #1: scalar1 * u1 (NOT basepoint!)
    const std::vector<uint8_t> scalar1 = {
        0x1c,0x1d,0x95,0x59,0xa7,0x4c,0x1d,0x32,
        0x0b,0x8d,0x5b,0x39,0xa0,0x74,0x04,0x2a,
        0x71,0x1c,0x1f,0x79,0x1d,0x2d,0x76,0xc9,
        0xbf,0x6a,0x6c,0xf2,0x6b,0xe3,0x46,0xa5
        0xa5,0x46,0xe3,0x6b,0xf0,0x52,0x7c,0x9d,
        0x3b,0x16,0x15,0x4b,0x82,0x46,0x5e,0xdd,
        0x62,0x14,0x4c,0x0a,0xc1,0xfc,0x5a,0x18,
        0x50,0x6a,0x22,0x44,0xba,0x44,0x9a,0xc4
    };


    const std::vector<uint8_t> expect1 = {
    const std::vector<uint8_t> u1 = {
        0xe6,0xdb,0x68,0x67,0x58,0x30,0x30,0xdb,
        0x35,0x94,0xc1,0xa4,0x24,0xb1,0x5f,0x7c,
        0x72,0x66,0x24,0xec,0x26,0xb3,0x35,0x3b,
        0x10,0xa9,0x03,0xa6,0xd0,0xab,0x1c,0x4c
    };

    netplus::scalarmult_curve25519_base(out, scalar1);
    if (!equal32(out, expect1)) {
    const std::vector<uint8_t> expect1 = {
        0xc3,0xda,0x55,0x37,0x9d,0xe9,0xc6,0x90,
        0x8e,0x94,0xea,0x4d,0xf2,0x8d,0x08,0x4f,
        0x32,0xec,0xcf,0x03,0x49,0x1c,0x71,0xf7,
        0x54,0xb4,0x07,0x55,0x77,0xa2,0x85,0x52
    };

    if (!netplus::scalarmult_curve25519(out, scalar1, u1) || !equal32(out, expect1)) {
        ok = 0;
        std::cerr << "FAIL: RFC7748 vector #1 (scalar1*base)\n";
        std::cerr << "FAIL: RFC7748 vector #1 (scalar1*u1)\n";
        std::cerr << "  got: "; print_hex(out, 32, std::cerr); std::cerr << "\n";
        std::cerr << "  exp: "; print_hex(expect1, 32, std::cerr); std::cerr << "\n";
    } else {
        std::cout << "PASS: RFC7748 vector #1\n";
    }

    out.clear();
    // ---------------------------------------------------------
    // Basepoint test: X25519(k=9, basepoint=9)
    // ---------------------------------------------------------
    {
        std::vector<uint8_t> scalar_base(32, 0);
        scalar_base[0] = 9;

        const std::vector<uint8_t> expect_base = {
            0x42,0x2c,0x8e,0x7a,0x62,0x27,0xd7,0xbc,
            0xa1,0x35,0x0b,0x3e,0x2b,0xb7,0x27,0x9f,
            0x78,0x97,0xb8,0x7b,0xb6,0x85,0x4b,0x78,
            0x3c,0x60,0xe8,0x03,0x11,0xae,0x30,0x79
        };

        std::vector<uint8_t> out_base(32,0);
        netplus::scalarmult_curve25519_base(out_base, scalar_base);

        if (!equal32(out_base, expect_base)) {
            ok = 0;
            std::cerr << "FAIL: basepoint test (k=9)\n";
            std::cerr << "  got: "; print_hex(out_base, 32, std::cerr); std::cerr << "\n";
            std::cerr << "  exp: "; print_hex(expect_base, 32, std::cerr); std::cerr << "\n";
        } else {
            std::cout << "PASS: basepoint test (k=9)\n";
        }
    }


    // ---------------------------------------------------------
    // RFC7748 vector #2: scalar2 * point2