Loading src/crypto/curve25519.cpp +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 src/crypto/curve25519.h +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 } test/x25519.cpp +43 −20 Original line number Diff line number Diff line #include <iostream> #include <array> #include <iomanip> #include <vector> #include <algorithm> Loading @@ -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); Loading @@ -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 Loading Loading
src/crypto/curve25519.cpp +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
src/crypto/curve25519.h +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 }
test/x25519.cpp +43 −20 Original line number Diff line number Diff line #include <iostream> #include <array> #include <iomanip> #include <vector> #include <algorithm> Loading @@ -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); Loading @@ -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 Loading