/* * Secucore * * Copyright (C) 2023 Trevor Hall * All rights reserved. * * This software may be modified and distributed under the terms * of the MIT license. * */ using System; using System.Security.Cryptography; namespace SecuCore.Curves { public class Curve25519 { public const int KeySize = 32; static readonly byte[] Order = { 237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }; public static void ClampPrivateKeyInline(byte[] key) { if (key == null) throw new ArgumentNullException("key"); if (key.Length != 32) throw new ArgumentException(String.Format("key must be 32 bytes long (but was {0} bytes long)", key.Length)); key[31] &= 0x7F; key[31] |= 0x40; key[0] &= 0xF8; } public static byte[] ClampPrivateKey(byte[] rawKey) { if (rawKey == null) throw new ArgumentNullException("rawKey"); if (rawKey.Length != 32) throw new ArgumentException(String.Format("rawKey must be 32 bytes long (but was {0} bytes long)", rawKey.Length), "rawKey"); var res = new byte[32]; Array.Copy(rawKey, res, 32); res[31] &= 0x7F; res[31] |= 0x40; res[0] &= 0xF8; return res; } public static byte[] CreateRandomPrivateKey() { var privateKey = new byte[32]; privateKey = RandomNumberGenerator.GetBytes(32); ClampPrivateKeyInline(privateKey); return privateKey; } public static void KeyGenInline(byte[] publicKey, byte[] signingKey, byte[] privateKey) { if (publicKey == null) throw new ArgumentNullException("publicKey"); if (publicKey.Length != 32) throw new ArgumentException(String.Format("publicKey must be 32 bytes long (but was {0} bytes long)", publicKey.Length), "publicKey"); if (signingKey == null) throw new ArgumentNullException("signingKey"); if (signingKey.Length != 32) throw new ArgumentException(String.Format("signingKey must be 32 bytes long (but was {0} bytes long)", signingKey.Length), "signingKey"); if (privateKey == null) throw new ArgumentNullException("privateKey"); if (privateKey.Length != 32) throw new ArgumentException(String.Format("privateKey must be 32 bytes long (but was {0} bytes long)", privateKey.Length), "privateKey"); privateKey = RandomNumberGenerator.GetBytes(32); ClampPrivateKeyInline(privateKey); Core(publicKey, signingKey, privateKey, null); } public static byte[] GetPublicKey(byte[] privateKey) { var publicKey = new byte[32]; Core(publicKey, null, privateKey, null); return publicKey; } public static byte[] GetSigningKey(byte[] privateKey) { var signingKey = new byte[32]; var publicKey = new byte[32]; Core(publicKey, signingKey, privateKey, null); return signingKey; } public static byte[] GetSharedSecret(byte[] privateKey, byte[] peerPublicKey) { var sharedSecret = new byte[32]; Core(sharedSecret, null, privateKey, peerPublicKey); return sharedSecret; } private sealed class Long10 { public Long10() { } public Long10(long n0, long n1, long n2, long n3, long n4, long n5, long n6, long n7, long n8, long n9) { N0 = n0; N1 = n1; N2 = n2; N3 = n3; N4 = n4; N5 = n5; N6 = n6; N7 = n7; N8 = n8; N9 = n9; } public long N0, N1, N2, N3, N4, N5, N6, N7, N8, N9; } static void Copy32(byte[] source, byte[] destination) { Array.Copy(source, 0, destination, 0, 32); } static int MultiplyArraySmall(byte[] p, byte[] q, int m, byte[] x, int n, int z) { int v = 0; for (int i = 0; i < n; ++i) { v += (q[i + m] & 0xFF) + z * (x[i] & 0xFF); p[i + m] = (byte)v; v >>= 8; } return v; } static void MultiplyArray32(byte[] p, byte[] x, byte[] y, int t, int z) { const int n = 31; int w = 0; int i = 0; for (; i < t; i++) { int zy = z * (y[i] & 0xFF); w += MultiplyArraySmall(p, p, i, x, n, zy) + (p[i + n] & 0xFF) + zy * (x[n] & 0xFF); p[i + n] = (byte)w; w >>= 8; } p[i + n] = (byte)(w + (p[i + n] & 0xFF)); } static void DivMod(byte[] q, byte[] r, int n, byte[] d, int t) { int rn = 0; int dt = ((d[t - 1] & 0xFF) << 8); if (t > 1) { dt |= (d[t - 2] & 0xFF); } while (n-- >= t) { int z = (rn << 16) | ((r[n] & 0xFF) << 8); if (n > 0) { z |= (r[n - 1] & 0xFF); } z /= dt; rn += MultiplyArraySmall(r, r, n - t + 1, d, t, -z); q[n - t + 1] = (byte)((z + rn) & 0xFF); MultiplyArraySmall(r, r, n - t + 1, d, t, -rn); rn = (r[n] & 0xFF); r[n] = 0; } r[t - 1] = (byte)rn; } static int GetNumSize(byte[] num, int maxSize) { for (int i = maxSize; i >= 0; i++) { if (num[i] == 0) return i + 1; } return 0; } static byte[] Egcd32(byte[] x, byte[] y, byte[] a, byte[] b) { int bn = 32; int i; for (i = 0; i < 32; i++) x[i] = y[i] = 0; x[0] = 1; int an = GetNumSize(a, 32); if (an == 0) return y; var temp = new byte[32]; while (true) { int qn = bn - an + 1; DivMod(temp, b, bn, a, an); bn = GetNumSize(b, bn); if (bn == 0) return x; MultiplyArray32(y, x, temp, qn, -1); qn = an - bn + 1; DivMod(temp, a, an, b, bn); an = GetNumSize(a, an); if (an == 0) return y; MultiplyArray32(x, y, temp, qn, -1); } } private const int P25 = 33554431; private const int P26 = 67108863; static void Unpack(Long10 x, byte[] m) { x.N0 = ((m[0] & 0xFF)) | ((m[1] & 0xFF)) << 8 | (m[2] & 0xFF) << 16 | ((m[3] & 0xFF) & 3) << 24; x.N1 = ((m[3] & 0xFF) & ~3) >> 2 | (m[4] & 0xFF) << 6 | (m[5] & 0xFF) << 14 | ((m[6] & 0xFF) & 7) << 22; x.N2 = ((m[6] & 0xFF) & ~7) >> 3 | (m[7] & 0xFF) << 5 | (m[8] & 0xFF) << 13 | ((m[9] & 0xFF) & 31) << 21; x.N3 = ((m[9] & 0xFF) & ~31) >> 5 | (m[10] & 0xFF) << 3 | (m[11] & 0xFF) << 11 | ((m[12] & 0xFF) & 63) << 19; x.N4 = ((m[12] & 0xFF) & ~63) >> 6 | (m[13] & 0xFF) << 2 | (m[14] & 0xFF) << 10 | (m[15] & 0xFF) << 18; x.N5 = (m[16] & 0xFF) | (m[17] & 0xFF) << 8 | (m[18] & 0xFF) << 16 | ((m[19] & 0xFF) & 1) << 24; x.N6 = ((m[19] & 0xFF) & ~1) >> 1 | (m[20] & 0xFF) << 7 | (m[21] & 0xFF) << 15 | ((m[22] & 0xFF) & 7) << 23; x.N7 = ((m[22] & 0xFF) & ~7) >> 3 | (m[23] & 0xFF) << 5 | (m[24] & 0xFF) << 13 | ((m[25] & 0xFF) & 15) << 21; x.N8 = ((m[25] & 0xFF) & ~15) >> 4 | (m[26] & 0xFF) << 4 | (m[27] & 0xFF) << 12 | ((m[28] & 0xFF) & 63) << 20; x.N9 = ((m[28] & 0xFF) & ~63) >> 6 | (m[29] & 0xFF) << 2 | (m[30] & 0xFF) << 10 | (m[31] & 0xFF) << 18; } static bool IsOverflow(Long10 x) { return (((x.N0 > P26 - 19)) & ((x.N1 & x.N3 & x.N5 & x.N7 & x.N9) == P25) & ((x.N2 & x.N4 & x.N6 & x.N8) == P26)) || (x.N9 > P25); } static void Pack(Long10 x, byte[] m) { int ld = (IsOverflow(x) ? 1 : 0) - ((x.N9 < 0) ? 1 : 0); int ud = ld * -(P25 + 1); ld *= 19; long t = ld + x.N0 + (x.N1 << 26); m[0] = (byte)t; m[1] = (byte)(t >> 8); m[2] = (byte)(t >> 16); m[3] = (byte)(t >> 24); t = (t >> 32) + (x.N2 << 19); m[4] = (byte)t; m[5] = (byte)(t >> 8); m[6] = (byte)(t >> 16); m[7] = (byte)(t >> 24); t = (t >> 32) + (x.N3 << 13); m[8] = (byte)t; m[9] = (byte)(t >> 8); m[10] = (byte)(t >> 16); m[11] = (byte)(t >> 24); t = (t >> 32) + (x.N4 << 6); m[12] = (byte)t; m[13] = (byte)(t >> 8); m[14] = (byte)(t >> 16); m[15] = (byte)(t >> 24); t = (t >> 32) + x.N5 + (x.N6 << 25); m[16] = (byte)t; m[17] = (byte)(t >> 8); m[18] = (byte)(t >> 16); m[19] = (byte)(t >> 24); t = (t >> 32) + (x.N7 << 19); m[20] = (byte)t; m[21] = (byte)(t >> 8); m[22] = (byte)(t >> 16); m[23] = (byte)(t >> 24); t = (t >> 32) + (x.N8 << 12); m[24] = (byte)t; m[25] = (byte)(t >> 8); m[26] = (byte)(t >> 16); m[27] = (byte)(t >> 24); t = (t >> 32) + ((x.N9 + ud) << 6); m[28] = (byte)t; m[29] = (byte)(t >> 8); m[30] = (byte)(t >> 16); m[31] = (byte)(t >> 24); } static void Copy(Long10 numOut, Long10 numIn) { numOut.N0 = numIn.N0; numOut.N1 = numIn.N1; numOut.N2 = numIn.N2; numOut.N3 = numIn.N3; numOut.N4 = numIn.N4; numOut.N5 = numIn.N5; numOut.N6 = numIn.N6; numOut.N7 = numIn.N7; numOut.N8 = numIn.N8; numOut.N9 = numIn.N9; } static void Set(Long10 numOut, int numIn) { numOut.N0 = numIn; numOut.N1 = 0; numOut.N2 = 0; numOut.N3 = 0; numOut.N4 = 0; numOut.N5 = 0; numOut.N6 = 0; numOut.N7 = 0; numOut.N8 = 0; numOut.N9 = 0; } static void Add(Long10 xy, Long10 x, Long10 y) { xy.N0 = x.N0 + y.N0; xy.N1 = x.N1 + y.N1; xy.N2 = x.N2 + y.N2; xy.N3 = x.N3 + y.N3; xy.N4 = x.N4 + y.N4; xy.N5 = x.N5 + y.N5; xy.N6 = x.N6 + y.N6; xy.N7 = x.N7 + y.N7; xy.N8 = x.N8 + y.N8; xy.N9 = x.N9 + y.N9; } static void Sub(Long10 xy, Long10 x, Long10 y) { xy.N0 = x.N0 - y.N0; xy.N1 = x.N1 - y.N1; xy.N2 = x.N2 - y.N2; xy.N3 = x.N3 - y.N3; xy.N4 = x.N4 - y.N4; xy.N5 = x.N5 - y.N5; xy.N6 = x.N6 - y.N6; xy.N7 = x.N7 - y.N7; xy.N8 = x.N8 - y.N8; xy.N9 = x.N9 - y.N9; } static void MulSmall(Long10 xy, Long10 x, long y) { long temp = (x.N8 * y); xy.N8 = (temp & ((1 << 26) - 1)); temp = (temp >> 26) + (x.N9 * y); xy.N9 = (temp & ((1 << 25) - 1)); temp = 19 * (temp >> 25) + (x.N0 * y); xy.N0 = (temp & ((1 << 26) - 1)); temp = (temp >> 26) + (x.N1 * y); xy.N1 = (temp & ((1 << 25) - 1)); temp = (temp >> 25) + (x.N2 * y); xy.N2 = (temp & ((1 << 26) - 1)); temp = (temp >> 26) + (x.N3 * y); xy.N3 = (temp & ((1 << 25) - 1)); temp = (temp >> 25) + (x.N4 * y); xy.N4 = (temp & ((1 << 26) - 1)); temp = (temp >> 26) + (x.N5 * y); xy.N5 = (temp & ((1 << 25) - 1)); temp = (temp >> 25) + (x.N6 * y); xy.N6 = (temp & ((1 << 26) - 1)); temp = (temp >> 26) + (x.N7 * y); xy.N7 = (temp & ((1 << 25) - 1)); temp = (temp >> 25) + xy.N8; xy.N8 = (temp & ((1 << 26) - 1)); xy.N9 += (temp >> 26); } static void Multiply(Long10 xy, Long10 x, Long10 y) { long x0 = x.N0, x1 = x.N1, x2 = x.N2, x3 = x.N3, x4 = x.N4, x5 = x.N5, x6 = x.N6, x7 = x.N7, x8 = x.N8, x9 = x.N9; long y0 = y.N0, y1 = y.N1, y2 = y.N2, y3 = y.N3, y4 = y.N4, y5 = y.N5, y6 = y.N6, y7 = y.N7, y8 = y.N8, y9 = y.N9; long t = (x0 * y8) + (x2 * y6) + (x4 * y4) + (x6 * y2) + (x8 * y0) + 2 * ((x1 * y7) + (x3 * y5) + (x5 * y3) + (x7 * y1)) + 38 * (x9 * y9); xy.N8 = (t & ((1 << 26) - 1)); t = (t >> 26) + (x0 * y9) + (x1 * y8) + (x2 * y7) + (x3 * y6) + (x4 * y5) + (x5 * y4) + (x6 * y3) + (x7 * y2) + (x8 * y1) + (x9 * y0); xy.N9 = (t & ((1 << 25) - 1)); t = (x0 * y0) + 19 * ((t >> 25) + (x2 * y8) + (x4 * y6) + (x6 * y4) + (x8 * y2)) + 38 * ((x1 * y9) + (x3 * y7) + (x5 * y5) + (x7 * y3) + (x9 * y1)); xy.N0 = (t & ((1 << 26) - 1)); t = (t >> 26) + (x0 * y1) + (x1 * y0) + 19 * ((x2 * y9) + (x3 * y8) + (x4 * y7) + (x5 * y6) + (x6 * y5) + (x7 * y4) + (x8 * y3) + (x9 * y2)); xy.N1 = (t & ((1 << 25) - 1)); t = (t >> 25) + (x0 * y2) + (x2 * y0) + 19 * ((x4 * y8) + (x6 * y6) + (x8 * y4)) + 2 * (x1 * y1) + 38 * ((x3 * y9) + (x5 * y7) + (x7 * y5) + (x9 * y3)); xy.N2 = (t & ((1 << 26) - 1)); t = (t >> 26) + (x0 * y3) + (x1 * y2) + (x2 * y1) + (x3 * y0) + 19 * ((x4 * y9) + (x5 * y8) + (x6 * y7) + (x7 * y6) + (x8 * y5) + (x9 * y4)); xy.N3 = (t & ((1 << 25) - 1)); t = (t >> 25) + (x0 * y4) + (x2 * y2) + (x4 * y0) + 19 * ((x6 * y8) + (x8 * y6)) + 2 * ((x1 * y3) + (x3 * y1)) + 38 * ((x5 * y9) + (x7 * y7) + (x9 * y5)); xy.N4 = (t & ((1 << 26) - 1)); t = (t >> 26) + (x0 * y5) + (x1 * y4) + (x2 * y3) + (x3 * y2) + (x4 * y1) + (x5 * y0) + 19 * ((x6 * y9) + (x7 * y8) + (x8 * y7) + (x9 * y6)); xy.N5 = (t & ((1 << 25) - 1)); t = (t >> 25) + (x0 * y6) + (x2 * y4) + (x4 * y2) + (x6 * y0) + 19 * (x8 * y8) + 2 * ((x1 * y5) + (x3 * y3) + (x5 * y1)) + 38 * ((x7 * y9) + (x9 * y7)); xy.N6 = (t & ((1 << 26) - 1)); t = (t >> 26) + (x0 * y7) + (x1 * y6) + (x2 * y5) + (x3 * y4) + (x4 * y3) + (x5 * y2) + (x6 * y1) + (x7 * y0) + 19 * ((x8 * y9) + (x9 * y8)); xy.N7 = (t & ((1 << 25) - 1)); t = (t >> 25) + xy.N8; xy.N8 = (t & ((1 << 26) - 1)); xy.N9 += (t >> 26); } static void Square(Long10 xsqr, Long10 x) { long x0 = x.N0, x1 = x.N1, x2 = x.N2, x3 = x.N3, x4 = x.N4, x5 = x.N5, x6 = x.N6, x7 = x.N7, x8 = x.N8, x9 = x.N9; long t = (x4 * x4) + 2 * ((x0 * x8) + (x2 * x6)) + 38 * (x9 * x9) + 4 * ((x1 * x7) + (x3 * x5)); xsqr.N8 = (t & ((1 << 26) - 1)); t = (t >> 26) + 2 * ((x0 * x9) + (x1 * x8) + (x2 * x7) + (x3 * x6) + (x4 * x5)); xsqr.N9 = (t & ((1 << 25) - 1)); t = 19 * (t >> 25) + (x0 * x0) + 38 * ((x2 * x8) + (x4 * x6) + (x5 * x5)) + 76 * ((x1 * x9) + (x3 * x7)); xsqr.N0 = (t & ((1 << 26) - 1)); t = (t >> 26) + 2 * (x0 * x1) + 38 * ((x2 * x9) + (x3 * x8) + (x4 * x7) + (x5 * x6)); xsqr.N1 = (t & ((1 << 25) - 1)); t = (t >> 25) + 19 * (x6 * x6) + 2 * ((x0 * x2) + (x1 * x1)) + 38 * (x4 * x8) + 76 * ((x3 * x9) + (x5 * x7)); xsqr.N2 = (t & ((1 << 26) - 1)); t = (t >> 26) + 2 * ((x0 * x3) + (x1 * x2)) + 38 * ((x4 * x9) + (x5 * x8) + (x6 * x7)); xsqr.N3 = (t & ((1 << 25) - 1)); t = (t >> 25) + (x2 * x2) + 2 * (x0 * x4) + 38 * ((x6 * x8) + (x7 * x7)) + 4 * (x1 * x3) + 76 * (x5 * x9); xsqr.N4 = (t & ((1 << 26) - 1)); t = (t >> 26) + 2 * ((x0 * x5) + (x1 * x4) + (x2 * x3)) + 38 * ((x6 * x9) + (x7 * x8)); xsqr.N5 = (t & ((1 << 25) - 1)); t = (t >> 25) + 19 * (x8 * x8) + 2 * ((x0 * x6) + (x2 * x4) + (x3 * x3)) + 4 * (x1 * x5) + 76 * (x7 * x9); xsqr.N6 = (t & ((1 << 26) - 1)); t = (t >> 26) + 2 * ((x0 * x7) + (x1 * x6) + (x2 * x5) + (x3 * x4)) + 38 * (x8 * x9); xsqr.N7 = (t & ((1 << 25) - 1)); t = (t >> 25) + xsqr.N8; xsqr.N8 = (t & ((1 << 26) - 1)); xsqr.N9 += (t >> 26); } static void Reciprocal(Long10 y, Long10 x, bool sqrtAssist) { Long10 t0 = new Long10(), t1 = new Long10(), t2 = new Long10(), t3 = new Long10(), t4 = new Long10(); int i; Square(t1, x); Square(t2, t1); Square(t0, t2); Multiply(t2, t0, x); Multiply(t0, t2, t1); Square(t1, t0); Multiply(t3, t1, t2); Square(t1, t3); Square(t2, t1); Square(t1, t2); Square(t2, t1); Square(t1, t2); Multiply(t2, t1, t3); Square(t1, t2); Square(t3, t1); for (i = 1; i < 5; i++) { Square(t1, t3); Square(t3, t1); } Multiply(t1, t3, t2); Square(t3, t1); Square(t4, t3); for (i = 1; i < 10; i++) { Square(t3, t4); Square(t4, t3); } Multiply(t3, t4, t1); for (i = 0; i < 5; i++) { Square(t1, t3); Square(t3, t1); } Multiply(t1, t3, t2); Square(t2, t1); Square(t3, t2); for (i = 1; i < 25; i++) { Square(t2, t3); Square(t3, t2); } Multiply(t2, t3, t1); Square(t3, t2); Square(t4, t3); for (i = 1; i < 50; i++) { Square(t3, t4); Square(t4, t3); } Multiply(t3, t4, t2); for (i = 0; i < 25; i++) { Square(t4, t3); Square(t3, t4); } Multiply(t2, t3, t1); Square(t1, t2); Square(t2, t1); if (sqrtAssist) { Multiply(y, x, t2); } else { Square(t1, t2); Square(t2, t1); Square(t1, t2); Multiply(y, t1, t0); } } static int IsNegative(Long10 x) { return (int)(((IsOverflow(x) | (x.N9 < 0)) ? 1 : 0) ^ (x.N0 & 1)); } static void MontyPrepare(Long10 t1, Long10 t2, Long10 ax, Long10 az) { Add(t1, ax, az); Sub(t2, ax, az); } static void MontyAdd(Long10 t1, Long10 t2, Long10 t3, Long10 t4, Long10 ax, Long10 az, Long10 dx) { Multiply(ax, t2, t3); Multiply(az, t1, t4); Add(t1, ax, az); Sub(t2, ax, az); Square(ax, t1); Square(t1, t2); Multiply(az, t1, dx); } static void MontyDouble(Long10 t1, Long10 t2, Long10 t3, Long10 t4, Long10 bx, Long10 bz) { Square(t1, t3); Square(t2, t4); Multiply(bx, t1, t2); Sub(t2, t1, t2); MulSmall(bz, t2, 121665); Add(t1, t1, bz); Multiply(bz, t1, t2); } static void CurveEquationInline(Long10 y2, Long10 x, Long10 temp) { Square(temp, x); MulSmall(y2, x, 486662); Add(temp, temp, y2); temp.N0++; Multiply(y2, temp, x); } static void Core(byte[] publicKey, byte[] signingKey, byte[] privateKey, byte[] peerPublicKey) { if (publicKey == null) throw new ArgumentNullException("publicKey"); if (publicKey.Length != 32) throw new ArgumentException(String.Format("publicKey must be 32 bytes long (but was {0} bytes long)", publicKey.Length), "publicKey"); if (signingKey != null && signingKey.Length != 32) throw new ArgumentException(String.Format("signingKey must be null or 32 bytes long (but was {0} bytes long)", signingKey.Length), "signingKey"); if (privateKey == null) throw new ArgumentNullException("privateKey"); if (privateKey.Length != 32) throw new ArgumentException(String.Format("privateKey must be 32 bytes long (but was {0} bytes long)", privateKey.Length), "privateKey"); if (peerPublicKey != null && peerPublicKey.Length != 32) throw new ArgumentException(String.Format("peerPublicKey must be null or 32 bytes long (but was {0} bytes long)", peerPublicKey.Length), "peerPublicKey"); Long10 dx = new Long10(), t1 = new Long10(), t2 = new Long10(), t3 = new Long10(), t4 = new Long10(); Long10[] x = { new Long10(), new Long10() }, z = { new Long10(), new Long10() }; if (peerPublicKey != null) Unpack(dx, peerPublicKey); else Set(dx, 9); Set(x[0], 1); Set(z[0], 0); Copy(x[1], dx); Set(z[1], 1); for (int i = 32; i-- != 0;) { for (int j = 8; j-- != 0;) { int bit1 = (privateKey[i] & 0xFF) >> j & 1; int bit0 = ~(privateKey[i] & 0xFF) >> j & 1; Long10 ax = x[bit0]; Long10 az = z[bit0]; Long10 bx = x[bit1]; Long10 bz = z[bit1]; MontyPrepare(t1, t2, ax, az); MontyPrepare(t3, t4, bx, bz); MontyAdd(t1, t2, t3, t4, ax, az, dx); MontyDouble(t1, t2, t3, t4, bx, bz); } } Reciprocal(t1, z[0], false); Multiply(dx, x[0], t1); Pack(dx, publicKey); if (signingKey != null) { CurveEquationInline(t1, dx, t2); Reciprocal(t3, z[1], false); Multiply(t2, x[1], t3); Add(t2, t2, dx); t2.N0 += 9 + 486662; dx.N0 -= 9; Square(t3, dx); Multiply(dx, t2, t3); Sub(dx, dx, t1); dx.N0 -= 39420360; Multiply(t1, dx, BaseR2Y); if (IsNegative(t1) != 0) Copy32(privateKey, signingKey); else MultiplyArraySmall(signingKey, OrderTimes8, 0, privateKey, 32, -1); var temp1 = new byte[32]; var temp2 = new byte[64]; var temp3 = new byte[64]; Copy32(Order, temp1); Copy32(Egcd32(temp2, temp3, signingKey, temp1), signingKey); if ((signingKey[31] & 0x80) != 0) MultiplyArraySmall(signingKey, signingKey, 0, Order, 32, 1); } } static readonly byte[] OrderTimes8 = { 104, 159, 174, 231, 210, 24, 147, 192, 178, 230, 188, 23, 245, 206, 247, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 }; static readonly Long10 BaseR2Y = new Long10(5744, 8160848, 4790893, 13779497, 35730846, 12541209, 49101323, 30047407, 40071253, 6226132); } }