From fdc81e26bc551bcd5f21ed55a76cd0ad7550ebb4 Mon Sep 17 00:00:00 2001 From: Tuomo Kriikkula Date: Tue, 13 Aug 2024 13:28:02 +0300 Subject: [PATCH] Further EC implementation & scaffolding --- Classes/FCryptoBigInt.uc | 323 +++++++++++++++++++++++++++++++++++++ Classes/FCryptoEC_Prime.uc | 137 +++++++++++++++- 2 files changed, 459 insertions(+), 1 deletion(-) diff --git a/Classes/FCryptoBigInt.uc b/Classes/FCryptoBigInt.uc index 7dbcbc2..7eb1d34 100644 --- a/Classes/FCryptoBigInt.uc +++ b/Classes/FCryptoBigInt.uc @@ -310,6 +310,34 @@ static final function MemSet_UInt16( } } +// See MemSet_UInt16. +// TODO: move to dedicated memory class. +static final function MemSet_UInt16_Static37( + out int S[37], + byte C, + int NumBytes, + optional int Offset = 0 +) +{ + local int IntIndex; + local int ByteIndex; + local int Shift; + local int Mask; + + Shift = 8; + Mask = 0xff << Shift; + IntIndex = Offset; + for (ByteIndex = 0; ByteIndex < NumBytes; ++ByteIndex) + { + S[IntIndex] = (S[IntIndex] & ~Mask) | ((C & 0xff) << Shift); + // Shift = (Shift + 8) % 16; + Shift = (Shift + 8) & 15; + // IntIndex += ByteIndex % 2; + IntIndex += ByteIndex & 1; + Mask = 0xff << Shift; + } +} + /** * C-style memset operation. * Offset is the number of byte values @@ -330,6 +358,22 @@ static final function MemSet_Byte( } } +// See MemSet_Byte. +static final function MemSet_Byte_Static66( + out byte S[66], + byte C, + int NumBytes, + optional int Offset = 0 +) +{ + local int ByteIndex; + + for (ByteIndex = Offset; ByteIndex < NumBytes; ++ByteIndex) + { + S[ByteIndex] = C; + } +} + /** * C-style memcpy operation. * Offsets are the number of uint16_t values @@ -442,6 +486,18 @@ static final function Zero( MemSet_UInt16(X, 0, ((BitLen + 15) >>> 4) * SIZEOF_UINT16_T, 1); } +// See Zero. +static final function Zero_Static37( + out int X[37], + int BitLen +) +{ + // *x ++ = bit_len; + // memset(x, 0, ((bit_len + 15) >> 4) * sizeof *x); + X[0] = BitLen & 0xFFFF; // @ALIGN-32-16. + MemSet_UInt16_Static37(X, 0, ((BitLen + 15) >>> 4) * SIZEOF_UINT16_T, 1); +} + /* * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[] * is unmodified, but the carry is still computed and returned. The @@ -477,6 +533,64 @@ static final function int Add( return Cc; } +// See Add. +static final function int Add_Static37( + out int A[37], + const out int B[37], + int Ctl +) +{ + local int Cc; + local int U; + local int M; + local int Aw; + local int Bw; + local int Naw; + + Cc = 0; + M = (A[0] + 31) >>> 4; + + for (U = 1; U < M; ++U) + { + Aw = A[U]; + Bw = B[U]; + Naw = Aw + Bw + Cc; + Cc = Naw >>> 15; + A[U] = MUX(Ctl, Naw & 0x7FFF, Aw) & 0xFFFF; // @ALIGN-32-16. + } + + return Cc; +} + +// See Add. +static final function int Add_Static37_DynB( + out int A[37], + const out array B, + int Ctl +) +{ + local int Cc; + local int U; + local int M; + local int Aw; + local int Bw; + local int Naw; + + Cc = 0; + M = (A[0] + 31) >>> 4; + + for (U = 1; U < M; ++U) + { + Aw = A[U]; + Bw = B[U]; + Naw = Aw + Bw + Cc; + Cc = Naw >>> 15; + A[U] = MUX(Ctl, Naw & 0x7FFF, Aw) & 0xFFFF; // @ALIGN-32-16. + } + + return Cc; +} + /* * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0, * then a[] is unmodified, but the carry is still computed and returned. @@ -517,6 +631,65 @@ static final function int Sub( return Cc; } +// See Sub. +static final function int Sub_Static37_DynB( + out int A[37], + const out array B, + int Ctl +) +{ + local int Cc; + local int U; + local int M; + local int Aw; + local int Bw; + local int Naw; + + Cc = 0; + M = (A[0] + 31) >>> 4; + + for (U = 1; U < M; ++U) + { + Aw = A[U]; + Bw = B[U]; + Naw = Aw - Bw - Cc; + CC = Naw >>> 31; + A[U] = MUX(Ctl, Naw & 0x7FFF, Aw) & 0xFFFF; // @ALIGN-32-16. + } + + return Cc; +} + +// See Sub. +static final function int Sub_Static37( + out int A[37], + const out int B[37], + int Ctl +) +{ + local int Cc; + local int U; + local int M; + local int Aw; + local int Bw; + local int Naw; + + Cc = 0; + M = (A[0] + 31) >>> 4; + + for (U = 1; U < M; ++U) + { + Aw = A[U]; + Bw = B[U]; + Naw = Aw - Bw - Cc; + CC = Naw >>> 31; + A[U] = MUX(Ctl, Naw & 0x7FFF, Aw) & 0xFFFF; // @ALIGN-32-16. + } + + return Cc; +} + + /* * Compute the actual bit length of an integer. The argument X should * point to the first (least significant) value word of the integer. @@ -1161,6 +1334,54 @@ static final function Encode( } } +// See Encode. +static final function Encode_Static66( + out byte Dst[66], + int Len, + const out array X +) +{ + local int U; + local int XLen; + local int Acc; + local int AccLen; + + XLen = (X[0] + 15) >>> 4; + if (XLen == 0) + { + // NOTE: BearSSL assumes all parameters are user-allocated. + // In UnrealScript we'll make an exception here to avoid a bug + // where MemSet is called with Len == 0. TODO: SHOULD WE DO THIS? + // Probably no way to avoid this since we are not dealing with + // pointers in UScript like original BearSSL code does. + if (Len == 0) + { + Len = 1; + } + + // memset(dst, 0, len); + MemSet_Byte_Static66(Dst, 0, Len); + return; + } + U = 1; + Acc = 0; + AccLen = 0; + while (Len-- > 0) + { + if (AccLen < 8) + { + if (U <= XLen) + { + Acc += X[U++] << AccLen; + } + AccLen += 15; + } + Dst[Len] = Acc; + Acc = Acc >>> 8; + AccLen -= 8; + } +} + /* * Convert a modular integer back from Montgomery representation. The * integer x[] MUST be lower than m[], but with the same announced bit @@ -1847,6 +2068,38 @@ static final function ModPow( } } +// See ModPow. +static final function ModPow_S37_S66_Dyn_S37_S37( + out int X[37], + const out byte E[66], + int ELen, + const out array M, + int M0I, + out int T1[37], + out int T2[37] +) +{ + local int MLen; + local int K; + local int Ctl; + + // TODO: body for this func. + + MLen = ((M[0] + 31) >>> 4) * SIZEOF_UINT16_T; + // MemCpy(T1, X, MLen); + // ToMonty(T1, M); + // Zero(X, M[0]); + // X[1] = 1; + // for (K = 0; K < (ELen << 3); ++K) + // { + // Ctl = (E[ELen - 1 - (K >>> 3)] >>> (K & 7)) & 1; + // MontyMul_S37_S37_S37_DynM(T2, X, T1, M, M0I); + // CCOPY(Ctl, X, T2, MLen); + // MontyMul_S37_S37_S37_DynM(T2, T1, T1, M, M0I); + // MemCpy(T1, T2, MLen); + // } +} + /* * Compute a modular Montgomery multiplication. d[] is filled with the * value of x*y/R modulo m[] (where R is the Montgomery factor). The @@ -1925,6 +2178,76 @@ static final function MontyMul( Sub(D, M, NEQ(DH, 0) | NOT(Sub(D, M, 0))); } +// See MontyMul. +static final function MontyMul_S37_S37_S37_DynM( + out int D[37], + const out int X[37], + const out int Y[37], + const out array M, + int M0I +) +{ + local int Len; + local int Len4; + local int U; + local int V; + local int Dh; + local int F; + local int Xu; + local int R; + local int Zh; + local int Z; + + Len = (M[0] + 15) >>> 4; + Len4 = Len & ~3; + Zero_Static37(D, M[0]); + Dh = 0; + for (U = 0; U < Len; ++U) + { + Xu = X[U + 1]; + // f = MUL15((d[1] + MUL15(x[u + 1], y[1])) & 0x7FFF, m0i) & 0x7FFF; + F = (((D[1] + (X[U + 1] * Y[1])) & 0x7FFF) * M0I) & 0x7FFF; + R = 0; + for (V = 0; V < Len4; V += 4) + { + Z = D[V + 1] + (Xu * Y[V + 1]) + (F * M[V + 1]) + R; + R = Z >>> 15; + D[V/*+0*/] = Z & 0x7FFF; + Z = D[V + 2] + (Xu * Y[V + 2]) + (F * M[V + 2]) + R; + R = Z >>> 15; + D[V + 1] = Z & 0x7FFF; + Z = D[V + 3] + (Xu * Y[V + 3]) + (F * M[V + 3]) + R; + R = Z >>> 15; + D[V + 2] = Z & 0x7FFF; + Z = D[V + 4] + (Xu * Y[V + 4]) + (F * M[V + 4]) + R; + R = Z >>> 15; + D[V + 3] = Z & 0x7FFF; + } + + for (Z = 0; V < Len; ++V) + { + Z = D[V + 1] + (Xu * Y[V + 1]) + (F * M[V + 1]) + R; + R = Z >>> 15; + D[V/*+0*/] = Z & 0x7FFF; + } + + Zh = Dh + R; + D[Len] = Zh & 0x7FFF; + Dh = Zh >>> 15; + } + + /* + * Restore the bit length (it was overwritten in the loop above). + */ + D[0] = M[0]; + + /* + * d[] may be greater than m[], but it is still lower than twice + * the modulus. + */ + Sub_Static37_DynB(D, M, NEQ(DH, 0) | NOT(Sub_Static37_DynB(D, M, 0))); +} + /* * Compute a modular exponentiation. x[] MUST be an integer modulo m[] * (same announced bit length, lower value). m[] MUST be odd. The diff --git a/Classes/FCryptoEC_Prime.uc b/Classes/FCryptoEC_Prime.uc index 2c4c5d6..d98a51e 100644 --- a/Classes/FCryptoEC_Prime.uc +++ b/Classes/FCryptoEC_Prime.uc @@ -240,7 +240,9 @@ static final function int RunCode( local int U; local int Op; local int D; + local int D_Tmp; local int A; + local int A_Tmp; local int B; local int Ctl; local int PLen; @@ -252,7 +254,7 @@ static final function int RunCode( * Copy the two operands in the dedicated registers. */ // memcpy(t[P1x], P1->c, 3 * I15_LEN * sizeof(uint16_t)); - // memcpy(t[P2x], P2->c, 3 * I15_LEN * sizeof(uint16_t)); + // memcpy(t[P2x], P2->c, 3 * I15_LEN * sizeof(uint16_t)); // class'FCryptoMemory'.static.MemCpy_Jacobian_Monty(T[`P1x], P1.C, 222); // class'FCryptoMemory'.static.MemCpy_Jacobian_Monty(T[`P2x], P2.C, 222); T[`P1x].X[ 0] = P1.C[0].X[ 0]; T[`P1y].X[ 0] = P1.C[1].X[ 0]; T[`P1z].X[ 0] = P1.C[2].X[ 0]; @@ -349,14 +351,105 @@ static final function int RunCode( switch (Op) { case 0: + // memcpy(t[d], t[a], I15_LEN * sizeof(uint16_t)); + T[D ].X[ 0] = T[A ].X[ 0]; + T[D ].X[ 1] = T[A ].X[ 1]; + T[D ].X[ 2] = T[A ].X[ 2]; + T[D ].X[ 3] = T[A ].X[ 3]; + T[D ].X[ 4] = T[A ].X[ 4]; + T[D ].X[ 5] = T[A ].X[ 5]; + T[D ].X[ 6] = T[A ].X[ 6]; + T[D ].X[ 7] = T[A ].X[ 7]; + T[D ].X[ 8] = T[A ].X[ 8]; + T[D ].X[ 9] = T[A ].X[ 9]; + T[D ].X[10] = T[A ].X[10]; + T[D ].X[11] = T[A ].X[11]; + T[D ].X[12] = T[A ].X[12]; + T[D ].X[13] = T[A ].X[13]; + T[D ].X[14] = T[A ].X[14]; + T[D ].X[15] = T[A ].X[15]; + T[D ].X[16] = T[A ].X[16]; + T[D ].X[17] = T[A ].X[17]; + T[D ].X[18] = T[A ].X[18]; + T[D ].X[19] = T[A ].X[19]; + T[D ].X[20] = T[A ].X[20]; + T[D ].X[21] = T[A ].X[21]; + T[D ].X[22] = T[A ].X[22]; + T[D ].X[23] = T[A ].X[23]; + T[D ].X[24] = T[A ].X[24]; + T[D ].X[25] = T[A ].X[25]; + T[D ].X[26] = T[A ].X[26]; + T[D ].X[27] = T[A ].X[27]; + T[D ].X[28] = T[A ].X[28]; + T[D ].X[29] = T[A ].X[29]; + T[D ].X[30] = T[A ].X[30]; + T[D ].X[31] = T[A ].X[31]; + T[D ].X[32] = T[A ].X[32]; + T[D ].X[33] = T[A ].X[33]; + T[D ].X[34] = T[A ].X[34]; + T[D ].X[35] = T[A ].X[35]; + T[D ].X[36] = T[A ].X[36]; + + D_Tmp = D + 1; + A_Tmp = A + 1; + T[D_Tmp].X[ 0] = T[A_Tmp].X[ 0]; + T[D_Tmp].X[ 1] = T[A_Tmp].X[ 1]; + T[D_Tmp].X[ 2] = T[A_Tmp].X[ 2]; + T[D_Tmp].X[ 3] = T[A_Tmp].X[ 3]; + T[D_Tmp].X[ 4] = T[A_Tmp].X[ 4]; + T[D_Tmp].X[ 5] = T[A_Tmp].X[ 5]; + T[D_Tmp].X[ 6] = T[A_Tmp].X[ 6]; + T[D_Tmp].X[ 7] = T[A_Tmp].X[ 7]; + T[D_Tmp].X[ 8] = T[A_Tmp].X[ 8]; + T[D_Tmp].X[ 9] = T[A_Tmp].X[ 9]; + T[D_Tmp].X[10] = T[A_Tmp].X[10]; + T[D_Tmp].X[11] = T[A_Tmp].X[11]; + T[D_Tmp].X[12] = T[A_Tmp].X[12]; + T[D_Tmp].X[13] = T[A_Tmp].X[13]; + T[D_Tmp].X[14] = T[A_Tmp].X[14]; + T[D_Tmp].X[15] = T[A_Tmp].X[15]; + T[D_Tmp].X[16] = T[A_Tmp].X[16]; + T[D_Tmp].X[17] = T[A_Tmp].X[17]; + T[D_Tmp].X[18] = T[A_Tmp].X[18]; + T[D_Tmp].X[19] = T[A_Tmp].X[19]; + T[D_Tmp].X[20] = T[A_Tmp].X[20]; + T[D_Tmp].X[21] = T[A_Tmp].X[21]; + T[D_Tmp].X[22] = T[A_Tmp].X[22]; + T[D_Tmp].X[23] = T[A_Tmp].X[23]; + T[D_Tmp].X[24] = T[A_Tmp].X[24]; + T[D_Tmp].X[25] = T[A_Tmp].X[25]; + T[D_Tmp].X[26] = T[A_Tmp].X[26]; + T[D_Tmp].X[27] = T[A_Tmp].X[27]; + T[D_Tmp].X[28] = T[A_Tmp].X[28]; + T[D_Tmp].X[29] = T[A_Tmp].X[29]; + T[D_Tmp].X[30] = T[A_Tmp].X[30]; + T[D_Tmp].X[31] = T[A_Tmp].X[31]; + T[D_Tmp].X[32] = T[A_Tmp].X[32]; + T[D_Tmp].X[33] = T[A_Tmp].X[33]; + T[D_Tmp].X[34] = T[A_Tmp].X[34]; + T[D_Tmp].X[35] = T[A_Tmp].X[35]; + T[D_Tmp].X[36] = T[A_Tmp].X[36]; break; case 1: + Ctl = class'FCryptoBigInt'.static.Add_Static37(T[D].X, T[A].X, 1); + Ctl = Ctl | class'FCryptoBigInt'.static.NOT( + class'FCryptoBigInt'.static.Sub_Static37_DynB(T[D].X, Cc.P, 0)); + class'FCryptoBigInt'.static.Sub_Static37_DynB(T[D].X, Cc.P, Ctl); break; case 2: + class'FCryptoBigInt'.static.Add_Static37_DynB(T[D].X, Cc.P, + class'FCryptoBigInt'.static.Sub_Static37(T[D].X, T[A].X, 1)); break; case 3: + class'FCryptoBigInt'.static.MontyMul_S37_S37_S37_DynM( + T[D].X, T[A].X, T[B].X, Cc.P, Cc.P0i); break; case 4: + PLen = (Cc.P[0] - (Cc.P[0] >>> 4) + 7) >>> 3; + class'FCryptoBigInt'.static.Encode_Static66(Tp, PLen, Cc.P); + Tp[PLen - 1] -= 2; + class'FCryptoBigInt'.static.ModPow_S37_S66_Dyn_S37_S37( + T[D].X, Tp, PLen, Cc.P, Cc.P0i, T[A].X, T[B].X); break; default: R = R & class'FCryptoBigInt'.static.BIsZero_Static37(T[D].X); @@ -364,6 +457,48 @@ static final function int RunCode( } } + /* + * Copy back result. + */ + // memcpy(P1->c, t[P1x], 3 * I15_LEN * sizeof(uint16_t)); + P1.C[0].X[ 0] = T[`P1x].X[ 0]; P1.C[1].X[ 0] = T[`P1y].X[ 0]; P1.C[2].X[ 0] = T[`P1z].X[ 0]; + P1.C[0].X[ 1] = T[`P1x].X[ 1]; P1.C[1].X[ 1] = T[`P1y].X[ 1]; P1.C[2].X[ 1] = T[`P1z].X[ 1]; + P1.C[0].X[ 2] = T[`P1x].X[ 2]; P1.C[1].X[ 2] = T[`P1y].X[ 2]; P1.C[2].X[ 2] = T[`P1z].X[ 2]; + P1.C[0].X[ 3] = T[`P1x].X[ 3]; P1.C[1].X[ 3] = T[`P1y].X[ 3]; P1.C[2].X[ 3] = T[`P1z].X[ 3]; + P1.C[0].X[ 4] = T[`P1x].X[ 4]; P1.C[1].X[ 4] = T[`P1y].X[ 4]; P1.C[2].X[ 4] = T[`P1z].X[ 4]; + P1.C[0].X[ 5] = T[`P1x].X[ 5]; P1.C[1].X[ 5] = T[`P1y].X[ 5]; P1.C[2].X[ 5] = T[`P1z].X[ 5]; + P1.C[0].X[ 6] = T[`P1x].X[ 6]; P1.C[1].X[ 6] = T[`P1y].X[ 6]; P1.C[2].X[ 6] = T[`P1z].X[ 6]; + P1.C[0].X[ 7] = T[`P1x].X[ 7]; P1.C[1].X[ 7] = T[`P1y].X[ 7]; P1.C[2].X[ 7] = T[`P1z].X[ 7]; + P1.C[0].X[ 8] = T[`P1x].X[ 8]; P1.C[1].X[ 8] = T[`P1y].X[ 8]; P1.C[2].X[ 8] = T[`P1z].X[ 8]; + P1.C[0].X[ 9] = T[`P1x].X[ 9]; P1.C[1].X[ 9] = T[`P1y].X[ 9]; P1.C[2].X[ 9] = T[`P1z].X[ 9]; + P1.C[0].X[10] = T[`P1x].X[10]; P1.C[1].X[10] = T[`P1y].X[10]; P1.C[2].X[10] = T[`P1z].X[10]; + P1.C[0].X[11] = T[`P1x].X[11]; P1.C[1].X[11] = T[`P1y].X[11]; P1.C[2].X[11] = T[`P1z].X[11]; + P1.C[0].X[12] = T[`P1x].X[12]; P1.C[1].X[12] = T[`P1y].X[12]; P1.C[2].X[12] = T[`P1z].X[12]; + P1.C[0].X[13] = T[`P1x].X[13]; P1.C[1].X[13] = T[`P1y].X[13]; P1.C[2].X[13] = T[`P1z].X[13]; + P1.C[0].X[14] = T[`P1x].X[14]; P1.C[1].X[14] = T[`P1y].X[14]; P1.C[2].X[14] = T[`P1z].X[14]; + P1.C[0].X[15] = T[`P1x].X[15]; P1.C[1].X[15] = T[`P1y].X[15]; P1.C[2].X[15] = T[`P1z].X[15]; + P1.C[0].X[16] = T[`P1x].X[16]; P1.C[1].X[16] = T[`P1y].X[16]; P1.C[2].X[16] = T[`P1z].X[16]; + P1.C[0].X[17] = T[`P1x].X[17]; P1.C[1].X[17] = T[`P1y].X[17]; P1.C[2].X[17] = T[`P1z].X[17]; + P1.C[0].X[18] = T[`P1x].X[18]; P1.C[1].X[18] = T[`P1y].X[18]; P1.C[2].X[18] = T[`P1z].X[18]; + P1.C[0].X[19] = T[`P1x].X[19]; P1.C[1].X[19] = T[`P1y].X[19]; P1.C[2].X[19] = T[`P1z].X[19]; + P1.C[0].X[20] = T[`P1x].X[20]; P1.C[1].X[20] = T[`P1y].X[20]; P1.C[2].X[20] = T[`P1z].X[20]; + P1.C[0].X[21] = T[`P1x].X[21]; P1.C[1].X[21] = T[`P1y].X[21]; P1.C[2].X[21] = T[`P1z].X[21]; + P1.C[0].X[22] = T[`P1x].X[22]; P1.C[1].X[22] = T[`P1y].X[22]; P1.C[2].X[22] = T[`P1z].X[22]; + P1.C[0].X[23] = T[`P1x].X[23]; P1.C[1].X[23] = T[`P1y].X[23]; P1.C[2].X[23] = T[`P1z].X[23]; + P1.C[0].X[24] = T[`P1x].X[24]; P1.C[1].X[24] = T[`P1y].X[24]; P1.C[2].X[24] = T[`P1z].X[24]; + P1.C[0].X[25] = T[`P1x].X[25]; P1.C[1].X[25] = T[`P1y].X[25]; P1.C[2].X[25] = T[`P1z].X[25]; + P1.C[0].X[26] = T[`P1x].X[26]; P1.C[1].X[26] = T[`P1y].X[26]; P1.C[2].X[26] = T[`P1z].X[26]; + P1.C[0].X[27] = T[`P1x].X[27]; P1.C[1].X[27] = T[`P1y].X[27]; P1.C[2].X[27] = T[`P1z].X[27]; + P1.C[0].X[28] = T[`P1x].X[28]; P1.C[1].X[28] = T[`P1y].X[28]; P1.C[2].X[28] = T[`P1z].X[28]; + P1.C[0].X[29] = T[`P1x].X[29]; P1.C[1].X[29] = T[`P1y].X[29]; P1.C[2].X[29] = T[`P1z].X[29]; + P1.C[0].X[30] = T[`P1x].X[30]; P1.C[1].X[30] = T[`P1y].X[30]; P1.C[2].X[30] = T[`P1z].X[30]; + P1.C[0].X[31] = T[`P1x].X[31]; P1.C[1].X[31] = T[`P1y].X[31]; P1.C[2].X[31] = T[`P1z].X[31]; + P1.C[0].X[32] = T[`P1x].X[32]; P1.C[1].X[32] = T[`P1y].X[32]; P1.C[2].X[32] = T[`P1z].X[32]; + P1.C[0].X[33] = T[`P1x].X[33]; P1.C[1].X[33] = T[`P1y].X[33]; P1.C[2].X[33] = T[`P1z].X[33]; + P1.C[0].X[34] = T[`P1x].X[34]; P1.C[1].X[34] = T[`P1y].X[34]; P1.C[2].X[34] = T[`P1z].X[34]; + P1.C[0].X[35] = T[`P1x].X[35]; P1.C[1].X[35] = T[`P1y].X[35]; P1.C[2].X[35] = T[`P1z].X[35]; + P1.C[0].X[36] = T[`P1x].X[36]; P1.C[1].X[36] = T[`P1y].X[36]; P1.C[2].X[36] = T[`P1z].X[36]; + return R; }