تقني › التشفير

منحنى Ed25519

الأعداد القياسية والنقاط والعملية الوحيدة التي يقوم عليها Monero كله

باختصار

يتعامل معظم تشفير Monero مع نوعين من الكائنات: الأعداد القياسية (أعداد كبيرة، سرية عادةً) والنقاط على المنحنى الإهليلجي Ed25519 (علنية عادةً). العملية الرئيسية هي ضرب نقطة في عدد: P = x·G. حساب P من x سهل؛ أما إيجاد x من P فمستحيل عمليًا.

لماذا منحنى

نحتاج إلى "دالة أحادية الاتجاه بقفل": من السر يسهل الحصول على شيء علني، لكن ليس العكس. ونريد إجراء عمليات حسابية على الأشياء العلنية: جمعها وضربها في أعداد. والمنحنى الإهليلجي يقدّم ذلك بالضبط.

يستخدم Monero المنحنى نفسه الذي تستخدمه تواقيع Ed25519 (Daniel J. Bernstein وآخرون): منحنى Edwards ملتوٍ

−x² + y² = 1 + d·x²·y²   (mod p) حيث p = 2²⁵⁵ − 19 و d = −121665/121666

النقطة زوج من الأعداد (x, y) يحقق المعادلة. وكل الحسابات تجري بترديد العدد الأولي p، لذا فـ "المنحنى" في الحقيقة سحابة من نحو 2²⁵⁵ نقطة منفصلة.

جمع النقاط

يمكن جمع النقاط: P + Q = R، حيث R أيضًا على المنحنى. وعلى منحنيات Edwards تغطي صيغة واحدة جميع الحالات (وهذا مريح وآمن):

(x₁, y₁) + (x₂, y₂) = ( (x₁y₂ + y₁x₂) / (1 + d·x₁x₂y₁y₂) , (y₁y₂ + x₁x₂) / (1 − d·x₁x₂y₁y₂) )

يتصرف هذا الجمع كالجمع العادي: فهو تبديلي وتجميعي، وله "صفر" — النقطة (0, 1) — ولكل نقطة نظير (−x, y).

الضرب في عدد

ضرب نقطة في x يعني جمعها مع نفسها x مرة. وبالطبع لا أحد يجمع 2²⁵² مرة: طريقة "المضاعفة والجمع" لا تحتاج إلا إلى نحو 256 مضاعفة ونحو 128 عملية جمع.

العدد القياسي x سري، 32 بايت x·G — سريع (أجزاء من الثانية) بالعكس — أطول من عمر الكون النقطة P علنية، 32 بايت
إيجاد x من P يُسمى مسألة اللوغاريتم المتقطع على المنحنيات الإهليلجية (ECDLP).

نقطة الأساس G والرتبة l

يستخدم الجميع نقطة الأساس نفسها G المتفق عليها مسبقًا. اضرب G في 1، 2، 3، … وبعد l خطوة تعود إلى الصفر:

l = 2²⁵² + 27742317777372353535851937790883648493 l·G = 0، لذا تؤخذ الأعداد القياسية دائمًا بترديد l

l هي رتبة الزمرة الجزئية المولَّدة بـ G. وعلى المنحنى ثمانية أضعاف هذا العدد من النقاط إجمالًا (8·l)؛ ويُسمى 8 العامل المرافق (cofactor). وهذا العامل مصدر عدة دقائق في Monero: سترى العامل 8 في صيغ العناوين لمرة واحدة، وفي 2017 كان من الممكن أن تنتج "النقاط ذات الرتبة الصغيرة" عدة key images لمخرج واحد (أُصلح بالتحقق من l·I = 0).

لماذا يصلح هذا للمفاتيح

خذ x عشوائيًا — هذا مفتاح خاص. واحسب P = x·G — هذا مفتاح عام. نشر P آمن. والأهم أن النقاط تدعم الجبر:

(a + b)·G = a·G + b·G     a·(b·G) = b·(a·G)

المتطابقة الثانية أساس تبادل مفاتيح Diffie–Hellman: أليس تعرف a وترى B = b·G؛ وبوب يعرف b ويرى A = a·G. كلاهما يستطيع حساب النقطة نفسها a·B = b·A = ab·G، أما الغريب فلا. وعلى هذا تُبنى العناوين لمرة واحدة.

والمتطابقة الأولى أساس التزامات Pedersen: يمكن جمع المبالغ المخفية في النقاط.

ترميز النقطة في 32 بايتًا

تُخزَّن النقطة بشكل مضغوط: يُكتب الإحداثي y (255 بتًا، بترتيب little-endian) وتوضع زوجية x في البت الأعلى من البايت الأخير. ويُستعاد x من y عبر معادلة المنحنى (حتى الإشارة — التي يعطيها البت). هذه هي البايتات الـ32 التي تراها بالنظام الست عشري حين تنظر إلى مفتاح عام في Monero.

على سبيل المثال، تُكتب نقطة الأساس G هكذا:

5866666666666666666666666666666666666666666666666666666666666666

(إحداثيها y = 4/5 mod p، وx زوجي).

الشيفرة

تنفيذ مصغّر بلغة JavaScript باستخدام BigInt — من xmrcrypto.js، الذي تستخدمه كل أدوات هذا الموقع:

const P = (1n << 255n) - 19n;
const L = (1n << 252n) + 27742317777372353535851937790883648493n;
const D = mod(-121665n * inv(121666n));

// النقاط بالإحداثيات الموسّعة (X:Y:Z:T)، x = X/Z، y = Y/Z
function add(p, q) {
  const A = mod((p.Y - p.X) * (q.Y - q.X));
  const B = mod((p.Y + p.X) * (q.Y + q.X));
  const C = mod(2n * D * p.T * q.T);
  const Dd = mod(2n * p.Z * q.Z);
  const E = B - A, F = Dd - C, G = Dd + C, H = B + A;
  return { X: mod(E * F), Y: mod(G * H), Z: mod(F * G), T: mod(E * H) };
}

// الضرب القياسي: المضاعفة والجمع
function mul(k, p) {
  let r = ZERO, q = p;
  while (k > 0n) { if (k & 1n) r = add(r, q); q = add(q, q); k >>= 1n; }
  return r;
}
ليست للاستخدام الإنتاجي

تستغرق هذه الشيفرة وقتًا مختلفًا بحسب بتات السر (هجوم التوقيت). أما التنفيذات الحقيقية (ref10 في Monero، وcurve25519-dalek) فتعمل في زمن ثابت وأسرع بمئات المرات.