Skip to content

Commit e51edc3

Browse files
committed
Elliptic curve implementation (WIP)
1 parent 23784e4 commit e51edc3

File tree

4 files changed

+203
-14
lines changed

4 files changed

+203
-14
lines changed

Classes/FCryptoEC_Prime.uc

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ struct Jacobian
8282
var _Monty C[3];
8383
};
8484

85+
// TODO: not needed?
86+
// const SIZEOF_JACOBIAN = 222;
87+
88+
var const Jacobian ZERO_JACOBIAN;
89+
8590
/*
8691
* We use a custom interpreter that uses a dozen registers, and
8792
* only six operations:
@@ -511,6 +516,7 @@ static final function SetOne(
511516

512517
PLen = (P[0] + 31) >>> 4;
513518
// memset(x, 0, plen * sizeof *x);
519+
class'FCryptoMemory'.static.MemSet_UInt16_Static37(X, 0, PLen * SIZEOF_UINT16_T);
514520
X[0] = P[0];
515521
X[1] = 0x0001;
516522
}
@@ -521,6 +527,9 @@ static final function PointZero(
521527
)
522528
{
523529
// memset(P, 0, sizeof *P);
530+
// `ZERO_JACOBIAN(P); <-- this is much slower than assignment.
531+
P = default.ZERO_JACOBIAN;
532+
524533
P.C[0].X[0] = Cc.P[0];
525534
P.C[1].X[0] = Cc.P[0];
526535
P.C[2].X[0] = Cc.P[0];
@@ -554,6 +563,7 @@ static final function PointMul(
554563
local int K;
555564
local int Bits;
556565
local int Bnz;
566+
local int XOffset;
557567
local Jacobian P2;
558568
local Jacobian P3;
559569
local Jacobian Q;
@@ -582,22 +592,97 @@ static final function PointMul(
582592

583593
PointZero(Q, Cc);
584594
Qz = 1;
595+
XOffset = 0;
585596
while (XLen-- > 0)
586597
{
587598
for (K = 6; K >= 0; K -= 2)
588599
{
589600
PointDouble(Q, Cc);
590601
PointDouble(Q, Cc);
591602
// memcpy(&T, P, sizeof T);
592-
// memcpy(&U, &Q, sizeof U);
603+
// memcpy(&U, &Q, sizeof U);
593604
// TODO: offset parameter needed for X?
594-
Bits = (X[0] >>> K) & 3;
605+
Bits = (X[XOffset] >>> K) & 3;
595606
Bnz = class'FCryptoBigInt'.static.NEQ(Bits, 0);
596607
// TODO:
597608
// class'FCryptoBigInt'.static.CCOPY(class'FCryptoBigInt'.static.EQ(Bits, 2), T, P2, 0 /* sizeof T */);
598609
// class'FCryptoBigInt'.static.CCOPY(class'FCryptoBigInt'.static.EQ(Bits, 3), T, P3, 0 /* sizeof T */);
610+
PointAdd(U, T, Cc);
611+
// CCOPY(bnz & qz, &Q, &T, sizeof Q);
612+
// CCOPY(bnz & ~qz, &Q, &U, sizeof Q);
613+
Qz = Qz & (~Bnz);
599614
}
615+
++XOffset;
600616
}
617+
// memcpy(P, &Q, sizeof Q);
618+
}
619+
620+
static final function int PointDecode(
621+
out Jacobian P,
622+
const out array<byte> Src,
623+
int Len,
624+
const out CurveParams Cc
625+
)
626+
{
627+
local int PLen;
628+
local int ZLen;
629+
local int R;
630+
local Jacobian Q;
631+
632+
/*
633+
* Points must use uncompressed format:
634+
* -- first byte is 0x04;
635+
* -- coordinates X and Y use unsigned big-endian, with the same
636+
* length as the field modulus.
637+
*
638+
* We don't support hybrid format (uncompressed, but first byte
639+
* has value 0x06 or 0x07, depending on the least significant bit
640+
* of Y) because it is rather useless, and explicitly forbidden
641+
* by PKIX (RFC 5480, section 2.2).
642+
*
643+
* We don't support compressed format either, because it is not
644+
* much used in practice (there are or were patent-related
645+
* concerns about point compression, which explains the lack of
646+
* generalised support). Also, point compression support would
647+
* need a bit more code.
648+
*/
649+
650+
PointZero(P, Cc);
651+
PLen = (CC.P[0] - (CC.P[0] >>> 4) + 7) >>> 3;
652+
if (Len != 1 + (PLen << 1))
653+
{
654+
return 0;
655+
}
656+
// R = class'FCryptoBigInt'.static.DecodeMod(P.C[0], Buf + 1, PLen, CC.P);
657+
// R = R & class'FCryptoBigInt'.static.DecodeMod(P.C[0], Buf + 1 + PLen, PLen, Cc.P);
658+
659+
/*
660+
* Check first byte.
661+
*/
662+
R = R & class'FCryptoBigInt'.static.EQ(Src[0], 0x04);
663+
664+
/*
665+
* Convert coordinates and check that the point is valid.
666+
*/
667+
// ZLen = ((Cc.P[0] + 31) >>> 4) * SIZEOF_UINT16_T;
668+
// memcpy(Q.c[0], cc->R2, zlen);
669+
// memcpy(Q.c[1], cc->b, zlen);
670+
// SetOne(Q.C[2], Cc.P); TODO: need another variant for this.
671+
R = R & ~RunCode(P, Q, Cc, default.CodeCheck);
672+
return R;
673+
}
674+
675+
/*
676+
* Encode a point. This method assumes that the point is correct and is
677+
* not the point at infinity. Encoded size is always 1+2*plen, where
678+
* plen is the field modulus length, in bytes.
679+
*/
680+
static final function PointEncode(
681+
out array<byte> Dst,
682+
const out Jacobian P,
683+
const out CurveParams Cc
684+
)
685+
{
601686
}
602687

603688
// Differs from C version: const out param for performance.
@@ -667,15 +752,15 @@ static function int MulAdd(
667752
DefaultProperties
668753
{
669754
// TODO: are these needed?
670-
P256_P={(`P256_P_VALUES)}
671-
P256_R2={(`P256_R2_VALUES)}
672-
P256_B={(`P256_B_VALUES)}
673-
P384_P={(`P384_P_VALUES)}
674-
P384_R2={(`P384_R2_VALUES)}
675-
P384_B={(`P384_B_VALUES)}
676-
P521_P={(`P521_P_VALUES)}
677-
P521_R2={(`P521_R2_VALUES)}
678-
P521_B={(`P521_B_VALUES)}
755+
P256_P = {(`P256_P_VALUES)}
756+
P256_R2 = {(`P256_R2_VALUES)}
757+
P256_B = {(`P256_B_VALUES)}
758+
P384_P = {(`P384_P_VALUES)}
759+
P384_R2 = {(`P384_R2_VALUES)}
760+
P384_B = {(`P384_B_VALUES)}
761+
P521_P = {(`P521_P_VALUES)}
762+
P521_R2 = {(`P521_R2_VALUES)}
763+
P521_B = {(`P521_B_VALUES)}
679764

680765
_PP(0)={(P=(`P256_P_VALUES), B=(`P256_B_VALUES), R2=(`P256_R2_VALUES), P0i=0x001, PointLen=65)}
681766
_PP(1)={(P=(`P384_P_VALUES), B=(`P384_B_VALUES), R2=(`P384_R2_VALUES), P0i=0x001, PointLen=97)}
@@ -762,9 +847,11 @@ DefaultProperties
762847

763848
CodeAdd={(
764849

850+
`ENCODE
765851
)}
766852

767853
CodeCheck={(
768854

855+
`ENCODE
769856
)}
770857
}

Classes/FCryptoEllipticCurveMacros.uci

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,42 @@
6262
0x6193, 0x3C2A, 0x3C42, 0x48C7, 0x3489, 0x6771, 0x4C57, 0x5CCD, \
6363
0x2725, 0x545B, 0x503B, 0x5B42, 0x21A0, 0x2534, 0x687E, 0x70E4, \
6464
0x1618, 0x27D7, 0x0465
65+
66+
`define ZERO_JACOBIAN(P) \
67+
`P.C[0].X[ 0] = 0; `P.C[1].X[ 0] = 0; `P.C[2].X[ 0] = 0; \
68+
`P.C[0].X[ 1] = 0; `P.C[1].X[ 1] = 0; `P.C[2].X[ 1] = 0; \
69+
`P.C[0].X[ 2] = 0; `P.C[1].X[ 2] = 0; `P.C[2].X[ 2] = 0; \
70+
`P.C[0].X[ 3] = 0; `P.C[1].X[ 3] = 0; `P.C[2].X[ 3] = 0; \
71+
`P.C[0].X[ 4] = 0; `P.C[1].X[ 4] = 0; `P.C[2].X[ 4] = 0; \
72+
`P.C[0].X[ 5] = 0; `P.C[1].X[ 5] = 0; `P.C[2].X[ 5] = 0; \
73+
`P.C[0].X[ 6] = 0; `P.C[1].X[ 6] = 0; `P.C[2].X[ 6] = 0; \
74+
`P.C[0].X[ 7] = 0; `P.C[1].X[ 7] = 0; `P.C[2].X[ 7] = 0; \
75+
`P.C[0].X[ 8] = 0; `P.C[1].X[ 8] = 0; `P.C[2].X[ 8] = 0; \
76+
`P.C[0].X[ 9] = 0; `P.C[1].X[ 9] = 0; `P.C[2].X[ 9] = 0; \
77+
`P.C[0].X[10] = 0; `P.C[1].X[10] = 0; `P.C[2].X[10] = 0; \
78+
`P.C[0].X[11] = 0; `P.C[1].X[11] = 0; `P.C[2].X[11] = 0; \
79+
`P.C[0].X[12] = 0; `P.C[1].X[12] = 0; `P.C[2].X[12] = 0; \
80+
`P.C[0].X[13] = 0; `P.C[1].X[13] = 0; `P.C[2].X[13] = 0; \
81+
`P.C[0].X[14] = 0; `P.C[1].X[14] = 0; `P.C[2].X[14] = 0; \
82+
`P.C[0].X[15] = 0; `P.C[1].X[15] = 0; `P.C[2].X[15] = 0; \
83+
`P.C[0].X[16] = 0; `P.C[1].X[16] = 0; `P.C[2].X[16] = 0; \
84+
`P.C[0].X[17] = 0; `P.C[1].X[17] = 0; `P.C[2].X[17] = 0; \
85+
`P.C[0].X[18] = 0; `P.C[1].X[18] = 0; `P.C[2].X[18] = 0; \
86+
`P.C[0].X[19] = 0; `P.C[1].X[19] = 0; `P.C[2].X[19] = 0; \
87+
`P.C[0].X[20] = 0; `P.C[1].X[20] = 0; `P.C[2].X[20] = 0; \
88+
`P.C[0].X[21] = 0; `P.C[1].X[21] = 0; `P.C[2].X[21] = 0; \
89+
`P.C[0].X[22] = 0; `P.C[1].X[22] = 0; `P.C[2].X[22] = 0; \
90+
`P.C[0].X[23] = 0; `P.C[1].X[23] = 0; `P.C[2].X[23] = 0; \
91+
`P.C[0].X[24] = 0; `P.C[1].X[24] = 0; `P.C[2].X[24] = 0; \
92+
`P.C[0].X[25] = 0; `P.C[1].X[25] = 0; `P.C[2].X[25] = 0; \
93+
`P.C[0].X[26] = 0; `P.C[1].X[26] = 0; `P.C[2].X[26] = 0; \
94+
`P.C[0].X[27] = 0; `P.C[1].X[27] = 0; `P.C[2].X[27] = 0; \
95+
`P.C[0].X[28] = 0; `P.C[1].X[28] = 0; `P.C[2].X[28] = 0; \
96+
`P.C[0].X[29] = 0; `P.C[1].X[29] = 0; `P.C[2].X[29] = 0; \
97+
`P.C[0].X[30] = 0; `P.C[1].X[30] = 0; `P.C[2].X[30] = 0; \
98+
`P.C[0].X[31] = 0; `P.C[1].X[31] = 0; `P.C[2].X[31] = 0; \
99+
`P.C[0].X[32] = 0; `P.C[1].X[32] = 0; `P.C[2].X[32] = 0; \
100+
`P.C[0].X[33] = 0; `P.C[1].X[33] = 0; `P.C[2].X[33] = 0; \
101+
`P.C[0].X[34] = 0; `P.C[1].X[34] = 0; `P.C[2].X[34] = 0; \
102+
`P.C[0].X[35] = 0; `P.C[1].X[35] = 0; `P.C[2].X[35] = 0; \
103+
`P.C[0].X[36] = 0; `P.C[1].X[36] = 0; `P.C[2].X[36] = 0;

Classes/FCryptoMemory.uc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,32 @@ static final function MemSet_SBytes64(
176176
}
177177
}
178178

179+
static final function MemSet_UInt16_Static37(
180+
out int S[37],
181+
byte C,
182+
int NumBytes,
183+
optional int Offset = 0
184+
)
185+
{
186+
local int IntIndex;
187+
local int ByteIndex;
188+
local int Shift;
189+
local int Mask;
190+
191+
Shift = 8;
192+
Mask = 0xff << Shift;
193+
IntIndex = Offset;
194+
for (ByteIndex = 0; ByteIndex < NumBytes; ++ByteIndex)
195+
{
196+
S[IntIndex] = (S[IntIndex] & ~Mask) | ((C & 0xff) << Shift);
197+
// Shift = (Shift + 8) % 16;
198+
Shift = (Shift + 8) & 15;
199+
// IntIndex += ByteIndex % 2;
200+
IntIndex += ByteIndex & 1;
201+
Mask = 0xff << Shift;
202+
}
203+
}
204+
179205
// TODO: is this even needed?
180206
// // Specialized for FCryptoEC_Prime.Jacobian 2D arrays.
181207
// static final function MemCpy_Jacobian_Monty(

Classes/FCryptoTestMutator.uc

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class FCryptoTestMutator extends Mutator
4545
config(Mutator_FCryptoTest);
4646

4747
`include(FCrypto\Classes\FCryptoMacros.uci);
48+
`include(FCrypto\Classes\FCryptoEllipticCurveMacros.uci);
4849

4950
var private FCryptoGMPClient GMPClient;
5051
var private FCryptoUtils Utils;
@@ -131,6 +132,8 @@ var(FCryptoTests) editconst const array<string> KAT_AES_CBC;
131132
*/
132133
var(FCryptoTests) editconst const array<string> KAT_AES_CTR;
133134

135+
var const FCryptoEC_Prime.Jacobian ZERO_JACOBIAN;
136+
134137
// Callback to FCryptoGMPClient::RandPrime.
135138
final simulated function AddRandomPrime(
136139
const out array<byte> P
@@ -196,7 +199,7 @@ simulated event PreBeginPlay()
196199
{
197200
Utils = new (self) class'FCryptoUtils';
198201

199-
TestDelegatesToRun.Length = 5;
202+
TestDelegatesToRun.Length = 7;
200203
TestDelegatesToRun[0].TestDelegate = TestMemory;
201204
TestDelegatesToRun[0].TestName = NameOf(TestMemory);
202205
TestDelegatesToRun[1].TestDelegate = TestOperations;
@@ -205,8 +208,13 @@ simulated event PreBeginPlay()
205208
TestDelegatesToRun[2].TestName = NameOf(TestMath);
206209
TestDelegatesToRun[3].TestDelegate = TestAesCt;
207210
TestDelegatesToRun[3].TestName = NameOf(TestAesCt);
211+
208212
TestDelegatesToRun[4].TestDelegate = TestSpeed;
209213
TestDelegatesToRun[4].TestName = NameOf(TestSpeed);
214+
TestDelegatesToRun[5].TestDelegate = TestSpeed;
215+
TestDelegatesToRun[5].TestName = NameOf(TestSpeed);
216+
TestDelegatesToRun[6].TestDelegate = TestSpeed;
217+
TestDelegatesToRun[6].TestName = NameOf(TestSpeed);
210218

211219
super.PreBeginPlay();
212220
}
@@ -1406,6 +1414,13 @@ private final simulated function int TestSpeed()
14061414
local int BenchmarkRound;
14071415
local array<int> X;
14081416
local array<int> Y;
1417+
local FCryptoEC_Prime.Jacobian Jacobian1;
1418+
local FCryptoEC_Prime.Jacobian Jacobian2;
1419+
1420+
// TODO: need to benchmark whether using static arrays in some places
1421+
// actually offers a performance benefit over just using dynamic
1422+
// arrays everywhere. FCryptoEC_Prime would be a good candidate
1423+
// for such benchmarks.
14091424

14101425
// TODO: Design for FCQWORD arithmetic.
14111426
Dummy = 0xFFFFFFFF;
@@ -1576,7 +1591,7 @@ private final simulated function int TestSpeed()
15761591
Y.Length = 0;
15771592
Y.Length = 1024;
15781593
Clock(Q);
1579-
for (BenchmarkRound = 0; BenchmarkRound < 512; ++BenchmarkRound)
1594+
for (BenchmarkRound = 0; BenchmarkRound < 1024; ++BenchmarkRound)
15801595
{
15811596
class'FCryptoAES'.static.AddRoundKey(X, Y);
15821597
}
@@ -1589,13 +1604,35 @@ private final simulated function int TestSpeed()
15891604
Y.Length = 0;
15901605
Y.Length = 1024;
15911606
Clock(Q);
1592-
for (BenchmarkRound = 0; BenchmarkRound < 512; ++BenchmarkRound)
1607+
for (BenchmarkRound = 0; BenchmarkRound < 1024; ++BenchmarkRound)
15931608
{
15941609
class'FCryptoAES'.static.AddRoundKey_NoTempVars(X, Y);
15951610
}
15961611
UnClock(Q);
15971612
`fclog("Qclock (AddRoundKey (no temp vars))=" $ Q);
15981613

1614+
Q = 0;
1615+
Clock(Q);
1616+
for (BenchmarkRound = 0; BenchmarkRound < 512; ++BenchmarkRound)
1617+
{
1618+
`ZERO_JACOBIAN(Jacobian1);
1619+
}
1620+
UnClock(Q);
1621+
`fclog("Qclock (zero jacobian (macro) )=" $ Q);
1622+
1623+
Q = 0;
1624+
Clock(Q);
1625+
for (BenchmarkRound = 0; BenchmarkRound < 512; ++BenchmarkRound)
1626+
{
1627+
Jacobian2 = default.ZERO_JACOBIAN;
1628+
}
1629+
UnClock(Q);
1630+
`fclog("Qclock (zero jacobian (assignment))=" $ Q);
1631+
1632+
// Silence unused variable warnings.
1633+
Jacobian1.C[0].X[0] = Jacobian2.C[0].X[0];
1634+
Jacobian2.C[0].X[0] = Jacobian1.C[0].X[0];
1635+
15991636
return 0;
16001637
}
16011638

0 commit comments

Comments
 (0)