Skip to content

Implementation of Elliptic Curve Cryptography in ns-2

Caution:

Implementation of real cryptography algorithm in a simulator is not advisable because, the scope of a simulator is to minimize the overall simulation time – but doing real encryption and decryption on a simulator may considerably increase the simulation time. If you see the size of the original ecc code, then you will certainly realize it. We have just ported some freely available ecc code to ns-2 – We just converted a standard C implementation of the algorithm as a C++ level Application Agent of ns2.  Most importantly, the code presenter here is only for understanding this implementation – one can not compile this code without some missing information.

 

Elliptic Curve Cryptography (ECC)

Just google to learn about ECC. Start your learning from[1].

For installing ns-2 you may refer [2]

 

 

Implementation of ECCAgent as Application Agent in ns-2

 

The Actual Encryption and Decryption Part of the eccDH.cc Code

 

/********************************************************************************
* *
* NS2 Routines to implement protocols, Diffie-Hellman, Massey-Omura and *
* ElGamal for elliptic curve analogs. *
* *
********************************************************************************/
#include "agent.h"
#include <stdio.h>
#include <string.h>
#include "eccDH.h"
#include "packet.h"

INDEX log_2( ELEMENT x);
INDEX Lambda[2][field_prime];
INDEX lg2_m;
char mydata[100];

int opt_quadratic(FIELD2N *a,FIELD2N *b,FIELD2N *y);
void fofx(FIELD2N *x,CURVE *curv,FIELD2N *f);
void esum (POINT *p1,POINT *p2,POINT *p3,CURVE *curv);
void edbl (POINT *p1,POINT *p3,CURVE *curv);
void esub (POINT *p1,POINT *p2,POINT *p3,CURVE *curv);
void copy_point (POINT *p1,POINT *p2);
void elptic_mul(FIELD2N *k, POINT *p, POINT *r, CURVE *curv);
void one (FIELD2N *place);

void InitCurveAndPoint();
void CreateKeys();
void CreateDummyData();
void CreateData(char* data);
void HideDataForAddress(int ToAddress);
void RecoverDataFromAddress(int FromAddress);
void PrintKeysOfAddress(int Address);

void eccdemo();

void ECKGP();
void authen_secret();
CURVE Base_curve;

void rot_right(FIELD2N *a);
void rot_left(FIELD2N *a);
void null(FIELD2N *a);
void copy (FIELD2N *a,FIELD2N *b);
void genlambda();
void genlambda2();
void opt_mul(FIELD2N *a,FIELD2N *b,FIELD2N *c);
void opt_inv(FIELD2N *a,FIELD2N *result);



void one(FIELD2N*);
void random_field( FIELD2N *value);
void Mother(unsigned long *pSeed);
void opt_embed( FIELD2N *data, CURVE *curv, INDEX incrmt,INDEX root,POINT *pnt);
void rand_curve ( CURVE *curv);

void rand_point(POINT *point, CURVE *curve);
void DH_gen_send_key( POINT *Base_point, CURVE *E, FIELD2N *my_private,POINT *My_public);
void DH_gen_send_key( POINT *Base_point, POINT *My_public, CURVE *E, FIELD2N *my_private);
void DH_key_share(POINT *Base_point,POINT *their_public, CURVE *E,FIELD2N *my_private,FIELD2N *shared_secret);
void send_elgamal(POINT *Base_point, CURVE *Base_curve, POINT *Their_public, FIELD2N *raw_data, POINT *Hidden_data, POINT *Random_point);
//void send_elgamal(FIELD2N *raw_data,POINT *Base_point,POINT *Their_public,POINT *Hidden_data,POINT *Random_point, CURVE *Base_curve);
//void receive_elgamal(FIELD2N *my_private, FIELD2N *raw_data, POINT *Base_point, POINT *Hidden_data, POINT *Random_point,CURVE *Base_curve);
void receive_elgamal(POINT *Base_point, CURVE *Base_curve, FIELD2N *my_private, POINT *Hidden_data, POINT *Random_point, FIELD2N *raw_data);
void authen_secret( FIELD2N *d, FIELD2N *k, POINT *Q, POINT *R, POINT *other_Q, POINT *other_R, POINT *W);
void ECKGP(POINT *pnt, POINT *R,CURVE *crv, FIELD2N *k);
void print_field(char *string, FIELD2N *field);
void print_data(char *string, FIELD2N *field,int Format);
void print_point(char *string, POINT *point);
void print_curve(char *string,CURVE *curv);

/* random seed is accessable to everyone, not best way, but functional. */

unsigned long random_seed=0x139b25fe;

/* below is from Mother code, till end of mother. Above is all my fault. */

#include <string.h>

static short mother1[10];
static short mother2[10];
static short mStart=1;

#define m16Long 65536L /* 2^16 */
#define m16Mask 0xFFFF /* mask for lower 16 bits */
#define m15Mask 0x7FFF /* mask for lower 15 bits */
#define m31Mask 0x7FFFFFFF /* mask for 31 bits */
#define m32Double 4294967295.0 /* 2^32-1 */

/* Mother **************************************************************
| George Marsaglia's The mother of all random number generators
| producing uniformly distributed pseudo random 32 bit values with
| period about 2^250.
|
| The arrays mother1 and mother2 store carry values in their
| first element, and random 16 bit numbers in elements 1 to 8.
| These random numbers are moved to elements 2 to 9 and a new
| carry and number are generated and placed in elements 0 and 1.
| The arrays mother1 and mother2 are filled with random 16 bit values
| on first call of Mother by another generator. mStart is the switch.
|
| Returns:
| A 32 bit random number is obtained by combining the output of the
| two generators and returned in *pSeed. It is also scaled by
| 2^32-1 and returned as a double between 0 and 1
|
| SEED:
| The inital value of *pSeed may be any long value
*/



void Mother(unsigned long *pSeed)
{
 unsigned long number,
 number1,
 number2;
 short n,
 *p;
 unsigned short sNumber;

 /* Initialize motheri with 9 random values the first time */
 if (mStart) {
 sNumber= *pSeed&m16Mask; /* The low 16 bits */
 number= *pSeed&m31Mask; /* Only want 31 bits */

 p=mother1;
 for (n=18;n--;) {
 number=30903*sNumber+(number>>16); 
 /* One line multiply-with-cary */
 *p++=sNumber=number&m16Mask;
 if (n==9)
 p=mother2;
 }
 /* make cary 15 bits */
 mother1[0]&=m15Mask;
 mother2[0]&=m15Mask;
 mStart=0;
 }

 /* Move elements 1 to 8 to 2 to 9 */
 memmove(mother1+2,mother1+1,8*sizeof(short));
 memmove(mother2+2,mother2+1,8*sizeof(short));

 /* Put the carry values in numberi */
 number1=mother1[0];
 number2=mother2[0];

 /* Form the linear combinations */

number1+=1941*mother1[2]+1860*mother1[3]+1812*mother1[4]+1776*mother1[5]+
 1492*mother1[6]+1215*mother1[7]+1066*mother1[8]+12013*mother1[9];

number2+=1111*mother2[2]+2222*mother2[3]+3333*mother2[4]+4444*mother2[5]+
 5555*mother2[6]+6666*mother2[7]+7777*mother2[8]+9272*mother2[9];

 /* Save the high bits of numberi as the new carry */
 mother1[0]=number1/m16Long;
 mother2[0]=number2/m16Long;
 /* Put the low bits of numberi into motheri[1] */
 mother1[1]=m16Mask&number1;
 mother2[1]=m16Mask&number2;

 /* Combine the two 16 bit random numbers into one 32 bit */
 *pSeed=(((long)mother1[1])<<16)+(long)mother2[1];

 /* Return a double value between 0 and 1 
 return ((double)*pSeed)/m32Double; */
}



/* Generate a random bit pattern which fits in a FIELD2N size variable.
 Calls Mother as many times as needed to create the value.
*/


void random_field( FIELD2N *value)
{
 INDEX i;
 
 SUMLOOP(i)
 {
 Mother( &random_seed);
 value->e[i] = random_seed;
 }
 value->e[0] &= UPRMASK;
}

/* embed data onto a curve.
 Enter with data, curve, ELEMENT offset to be used as increment, and
 which root (0 or 1).
 Returns with point having data as x and correct y value for curve.
 Will use y[0] for last bit of root clear, y[1] for last bit of root set.
 if ELEMENT offset is out of range, default is 0.
*/

void opt_embed( FIELD2N *data, CURVE *curv, INDEX incrmt,INDEX root, POINT *pnt)
{
 FIELD2N f, y[2];
 INDEX inc = incrmt;
 INDEX i;
 
 if ( (inc < 0) || (inc > NUMWORD) ) inc = 0;
 copy( data, &pnt->x);
 fofx( &pnt->x, curv, &f);
 while (opt_quadratic( &pnt->x, &f, y))
 {
 pnt->x.e[inc]++;
 fofx( &pnt->x, curv, &f);
 }
 copy ( &y[root&1], &pnt->y);
}

/* generate a random curve for a given field size.
 Enter with pointer to storage space for returned curve.
 Returns with curve.form = 0, curve.a2 = 0 and curve.a6
 as a random bit pattern. This is for the equation
 
 y^2 + xy = x^3 + a_2x^2 + a_6
*/

void rand_curve ( CURVE *curv)

{
 curv->form = 0;
 random_field( &curv->a6);
 null( &curv->a2);
}

/* generate a random point on a given curve.
 Enter with pointer to curve and one pointer 
 to storage space for returned point. Returns 
 one of solutions to above equation. Negate point
 to get other solution.
*/

void rand_point(POINT *point, CURVE *curve)

{
 FIELD2N rf;

 random_field( &rf);
 opt_embed( &rf, curve, NUMWORD, rf.e[NUMWORD]&1, point);
}

/* Compute a Diffie-Hellman key exchange.

 First routine computes senders public key.
 Enter with public point Base_point which sits on public curve E and
 senders private key my_private.
 Returns public key point My_public = my_private*Base_point to be sent 
 to other side.
*/

void DH_gen_send_key( POINT *Base_point, CURVE *E, FIELD2N *my_private,POINT *My_public)

{
 elptic_mul( my_private, Base_point, My_public, E);
}

/* Second routine computes shared secret that is same for sender and
 receiver.
 Enter with public point Base_point which sits on public curve E along with 
 senders public key their_public and receivers private key k.
 Returns shared_secret as x component of kP
*/

void DH_key_share(POINT *Base_point,POINT *their_public, CURVE *E,FIELD2N *my_private,FIELD2N *shared_secret)

{
 POINT temp;
 
 elptic_mul( my_private, their_public, &temp, E);
 copy (&temp.x, shared_secret);
}

/* Send data to another person using ElGamal protocol. Send Hidden_data and
 Random_point to other side. */

void send_elgamal(POINT *Base_point, CURVE *Base_curve, POINT *Their_public, FIELD2N *raw_data, POINT *Hidden_data, POINT *Random_point)
{
 FIELD2N random_value;
 POINT hidden_point, raw_point;
 
/* create random point to help hide the data */
 
 random_field (&random_value);
 elptic_mul (&random_value, Base_point, Random_point, Base_curve);

/* embed raw data onto the chosen curve, Assume raw data is contained in
 least significant ELEMENTs of the field variable and we won't hurt anything
 using the most significant to operate on. Uses the first root for y value.
*/
 
 opt_embed( raw_data, Base_curve, 0, 0, &raw_point);

/* Create the hiding value using the other person's public key */

 elptic_mul( &random_value, Their_public, &hidden_point, Base_curve);
 esum( &hidden_point, &raw_point, Hidden_data, Base_curve);
}

/* Recieve data from another person using ElGamal protocol. We get
 Hidden_data and Random_point and output raw_data. */

void receive_elgamal(POINT *Base_point, CURVE *Base_curve, FIELD2N *my_private, POINT *Hidden_data, POINT *Random_point, FIELD2N *raw_data)
{
 POINT hidden_point, raw_point;

/* compute hidden point using my private key and the random point */

 elptic_mul( my_private, Random_point, &hidden_point, Base_curve);
 esub( Hidden_data, &hidden_point, &raw_point, Base_curve);
 copy(&raw_point.x, raw_data);
}

/* MQV method to establish shared secret.
 Enter with other servers permenent (other_Q) and 
 ephemeral (other_R) keys,
 this servers permenent key (skey, pkey) and 
 ephemeral (dkey, dpoint) keys.
 Returns shared secret point W. Only W.x is useful as key, 
 and further checks may be necessary to confirm link established.
*/

void authen_secret( FIELD2N *d, FIELD2N *k, POINT *Q, POINT *R, POINT *other_Q, POINT *other_R, POINT *W)

{
 POINT S, T, U;

/* compute U = R' + x'a'Q' from other sides data */

 elptic_mul(&other_Q->x, other_Q, &S, &Base_curve);
 elptic_mul(&other_R->x, &S, &T, &Base_curve);
 esum(other_R, &T, &U, &Base_curve);

/* compute (k + xad)U the hard way. Need modulo math routines to make this
 quicker, but no need to know point order this way.
*/

 elptic_mul(d, &U, &S, &Base_curve);
 elptic_mul(&Q->x, &S, &T, &Base_curve);
 elptic_mul(&R->x, &T, &S, &Base_curve);
 elptic_mul(k, &U, &T, &Base_curve);
 esum(&S, &T, W, &Base_curve);
}

/* Generate a key pair, a random value plus a point.
 This was called ECKGP for Elliptic Curve Key Generation
 Primitive in an early draft of IEEE P1363.

 Input: Base point on curve (pnt, crv)
 
 Output: secret key k and random point R
*/

void ECKGP(POINT *pnt, POINT *R,CURVE *crv, FIELD2N *k)
{
 random_field( k);
 elptic_mul( k, pnt, R, crv);
}

void create_field(char* data, FIELD2N *value)

{
 int len = strlen((const char *) data);
 printf("\n%d : %s ", len, data);
 
 memcpy(&value->e[0], data, len);
 value->e[0] &= UPRMASK; 
}
 
void print_field(char *string, FIELD2N *field)
{
 INDEX i;
 
 printf("%s : ", string);
 SUMLOOP(i) printf("%8x ", field->e[i]);
 printf("\n");
}

void print_data(char *string, FIELD2N *field,int Format)
{
 INDEX i;
 int k;
 if(Format==1) //Hex
 {
 //printf("%s : ", string);
 SUMLOOP(i) printf("%8x", field->e[i]);
 printf("\n");
 }

 if(Format==2) //char
 {
 char *p = (char * ) &field->e[0];
 //printf("%s : ", string);
 //SUMLOOP(i) printf("%4c", field->e[i]);
 
 for(k=0;k< sizeof(FIELD2N);k++) 
 printf("%c",*p++);
 printf("\n");
 } 
}

void print_point(char *string, POINT *point)
{
 printf("%s\n", string);
 print_field( "x", &point->x);
 print_field( "y", &point->y);
 printf("\n");
}

void print_curve(char *string,CURVE *curv)
{
 printf("%s\n", string);
 printf("form = %d\n", curv->form);
 if (curv->form) print_field( "a2", &curv->a2);
 print_field( "a6", &curv->a6);
 printf("\n");
}





/************************************************************************
* *
* Elliptic curves over Galois Fields. These routines find points *
* on a curve, add and double points for type 1 optimal normal bases. *
* *
* For succint explanaition, see Menezes, page 92 *
* *
* This file modified 6/23/97 to work with generalized ONB and *
* tunable field sizes. mgr *
* *
************************************************************************/




/************************************************************************
* Note that the following is obvious to mathematicians. I thought it *
* was pretty cool when I discovered it myself, <sigh>. *
* *
* Routine to solve quadradic equation. Enter with coeficients *
* a and b and it returns solutions y[2]: y^2 + ay + b = 0. *
* If Tr(b/a^2) != 0, returns y=0 and error code 1. *
* If Tr(b/a^2) == 0, returns y[2] and error code 0. *
* If solution fails, returns y=0 and error code 2. *
* *
* Algorithm used based on normal basis GF math. Since (a+b)^2 = *
* a^2 + b^2 it follows that (a+b)^.5 = a^.5 + b^.5. Note that squaring*
* is a left shift and rooting is a right shift in a normal basis. *
* Transforming the source equation with y = ax and dividing by a^2 *
* gives: *
* x^2 + x + b/a^2 = 0 *
* *
* or x = x^.5 + (b/a^2)^.5 *
* *
* Let k_i = the ith significant bit of (b/a^2)^.5 and *
* x_i = the ith significant bit of x. *
* The above equation is equivelent to a bitwise representation as *
* *
* x_i = x_(i+1) + k_i *
* or *
* x(i+1) = x_i + k_i. *
* *
* Since both x and x+1 are solutions, and 1 is represented by all *
* bits set in a normal basis, we can start with x_0 = 0 or 1 at our *
* pleasure and use the recursion relation to discover every bit of x. *
* The answer is then ax and ax+a returned in y[0] and y[1] respectively*
* If the sum of x_(n-1) + k_(n-1) != x_0, returns error code 2 and *
* y = 0. *
* *
* error code returns *
* 0 y[0] and y[1] values *
* 1 y[0] = y[1] = 0 *
* 2 mathematicly impossible !!!! *
* *
************************************************************************/

int opt_quadratic(FIELD2N *a,FIELD2N *b,FIELD2N *y)

{
 INDEX i, el, bits;
 FIELD2N z, k, a2;
 ELEMENT r, t, mask;

/* test for a=0. Return y = square root of b. */

 r = 0;
 SUMLOOP(i) r |= a->e[i];
 if (!r) 
 {
 copy( b, &y[0]);
 rot_right( &y[0]);
 copy( &y[0], &y[1]);
 return(0);
 }

/* find a^-2 */

 opt_inv( a, &a2);
 rot_left(&a2);

/* find k=(b/a^2)^.5 */

 opt_mul( b, &a2, &k);
 rot_right(&k);
 r = 0;

/* check that Tr(k) is zero. Combine all words first. */

 SUMLOOP(i) r ^= k.e[i];

/* take trace of word, combining half of all the bits each time */

 mask = -1L;
 for (bits = WORDSIZE/2; bits > 0; bits >>= 1)
 {
 mask >>= bits;
 r = ((r & mask) ^ (r >> bits));
 } 

/* if not zero, return error code 1. */

 if (r) 
 {
 null(&y[0]);
 null(&y[1]);
 return(1);
 }

/* point is valid, proceed with solution. mask points to bit i,
which is known, in x bits previously found and k (=b/a^2)^.5. */

 null(&z);
 mask = 1;
 for (bits=0; bits < NUMBITS ; bits++) 
 {

/* source long word could be different than destination */

 i = NUMWORD - bits/WORDSIZE;
 el = NUMWORD - (bits + 1)/WORDSIZE;

/* use present bits to compute next one */

 r = k.e[i] & mask;
 t = z.e[i] & mask;
 r ^= t;

/* same word, so just shift result up */

 if ( el == i ) 
 {
 r <<= 1;
 z.e[el] |= r;
 mask <<= 1;
 } 
 else 
 {

/* different word, reset mask and use a 1 */

 mask = 1;
 if (r) z.e[el] = 1;
 }
 }

/* test that last bit generates a zero */

 r = k.e[0] & UPRBIT;
 t = z.e[0] & UPRBIT;
 if ( r^t ) 
 {
 null(&y[0]);
 null(&y[1]);
 return(2);
 }

/* convert solution back via y = az */

 opt_mul(a, &z, &y[0]);

/* and create complementary (z+1) solution y = az + a */

 null (&y[1]);
 SUMLOOP(i) y[1].e[i] = y[0].e[i] ^ a->e[i];

/* no errors, bye! */

 return(0);
}

/* compute R.H.S. f(x) = x^3 + a2*x^2 + a6 
 curv.form = 0 implies a2 = 0, so no extra multiply. 
 curv.form = 1 is the "twist" curve.
*/

void fofx(FIELD2N *x,CURVE *curv,FIELD2N *f)

{

 FIELD2N x2,x3;
 INDEX i;

 copy(x, &x2);
 rot_left(&x2);
 opt_mul(x, &x2, &x3);
 if (curv->form) opt_mul(&x2, &curv->a2, f);
 else null(f);
 SUMLOOP(i)
 f->e[i] ^= (x3.e[i] ^ curv->a6.e[i]);
}

/****************************************************************************
* *
* Implement elliptic curve point addition for optimal normal basis form. *
* This follows R. Schroeppel, H. Orman, S. O'Mally, "Fast Key Exchange with*
* Elliptic Curve Systems", CRYPTO '95, TR-95-03, Univ. of Arizona, Comp. *
* Science Dept. *
* *
* This version is faster for inversion processes requiring fewer *
* multiplies than projective math version. For NUMBITS = 148 or 226 this *
* is the case because only 10 multiplies are required for inversion but *
* 15 multiplies for projective math. I leave it as a paper to be written *
* [HINT!!] to propagate TR-95-03 to normal basis inversion. In that case *
* inversion will require order 2 multiplies and this method would be far *
* superior to projective coordinates. *
****************************************************************************/

void esum (POINT *p1,POINT *p2,POINT *p3,CURVE *curv)
{
 INDEX i;
 FIELD2N x1, y1, theta, onex, theta2;

/* compute theta = (y_1 + y_2)/(x_1 + x_2) */

 null(&x1);
 null(&y1);
 SUMLOOP(i) 
 {
 x1.e[i] = p1->x.e[i] ^ p2->x.e[i];
 y1.e[i] = p1->y.e[i] ^ p2->y.e[i];
 }
 opt_inv( &x1, &onex);
 opt_mul( &onex, &y1, &theta);
 copy( &theta, &theta2);
 rot_left(&theta2);

/* with theta and theta^2, compute x_3 */

 if (curv->form)
 SUMLOOP (i)
 p3->x.e[i] = theta.e[i] ^ theta2.e[i] ^ p1->x.e[i] ^ p2->x.e[i]
 ^ curv->a2.e[i];
 else
 SUMLOOP (i)
 p3->x.e[i] = theta.e[i] ^ theta2.e[i] ^ p1->x.e[i] ^ p2->x.e[i];

/* next find y_3 */

 SUMLOOP (i) x1.e[i] = p1->x.e[i] ^ p3->x.e[i];
 opt_mul( &x1, &theta, &theta2);
 SUMLOOP (i) p3->y.e[i] = theta2.e[i] ^ p3->x.e[i] ^ p1->y.e[i];
}

/* elliptic curve doubling routine for Schroeppel's algorithm over normal
 basis. Enter with p1, p3 as source and destination as well as curv
 to operate on. Returns p3 = 2*p1.
*/

void edbl (POINT *p1,POINT *p3,CURVE *curv)
{
 FIELD2N x1, y1, theta, theta2, t1;
 INDEX i;

/* first compute theta = x + y/x */

 opt_inv( &p1->x, &x1);
 opt_mul( &x1, &p1->y, &y1);
 SUMLOOP (i) theta.e[i] = p1->x.e[i] ^ y1.e[i];

/* next compute x_3 */

 copy( &theta, &theta2);
 rot_left(&theta2);
 if(curv->form)
 SUMLOOP (i) p3->x.e[i] = theta.e[i] ^ theta2.e[i] ^ curv->a2.e[i];
 else
 SUMLOOP (i) p3->x.e[i] = theta.e[i] ^ theta2.e[i];

/* and lastly y_3 */

 one( &y1);
 SUMLOOP (i) y1.e[i] ^= theta.e[i];
 opt_mul( &y1, &p3->x, &t1);
 copy( &p1->x, &x1);
 rot_left( &x1);
 SUMLOOP (i) p3->y.e[i] = x1.e[i] ^ t1.e[i];
}

/* subtract two points on a curve. just negates p2 and does a sum.
 Returns p3 = p1 - p2 over curv.
*/

void esub (POINT *p1,POINT *p2,POINT *p3,CURVE *curv)

{
 POINT negp;
 INDEX i;

 copy ( &p2->x, &negp.x);
 null (&negp.y);
 SUMLOOP(i) negp.y.e[i] = p2->x.e[i] ^ p2->y.e[i];
 esum (p1, &negp, p3, curv);
}

/* need to move points around, not just values. Optimize later. */

void copy_point (POINT *p1,POINT *p2)

{
 copy (&p1->x, &p2->x);
 copy (&p1->y, &p2->y);
}

/* Routine to compute kP where k is an integer (base 2, not normal basis)
 and P is a point on an elliptic curve. This routine assumes that K
 is representable in the same bit field as x, y or z values of P.
 This is for simplicity, larger or smaller fields can be independently 
 implemented.
 Enter with: integer k, source point P, curve to compute over (curv) and 
 Returns with: result point R.

 Reference: Koblitz, "CM-Curves with good Cryptografic Properties", 
 Springer-Verlag LNCS #576, p279 (pg 284 really), 1992
*/

void elptic_mul(FIELD2N *k,POINT *p,POINT *r, CURVE *curv)

{
 char blncd[NUMBITS+1];
 INDEX bit_count, i;
 ELEMENT notzero;
 FIELD2N number;
 POINT temp;

/* make sure input multiplier k is not zero.
 Return point at infinity if it is.
*/
 copy( k, &number);
 notzero = 0;
 SUMLOOP (i) notzero |= number.e[i];
 if (!notzero)
 {
 null (&r->x);
 null (&r->y);
 return;
 }

/* convert integer k (number) to balanced representation.
 Called non-adjacent form in "An Improved Algorithm for
 Arithmetic on a Family of Elliptic Curves", J. Solinas
 CRYPTO '97. This follows algorithm 2 in that paper.
*/
 bit_count = 0;
 while (notzero)
 {
 /* if number odd, create 1 or -1 from last 2 bits */
 
 if ( number.e[NUMWORD] & 1 )
 {
 blncd[bit_count] = 2 - (number.e[NUMWORD] & 3);
 
 /* if -1, then add 1 and propagate carry if needed */
 
 if ( blncd[bit_count] < 0 )
 {
 for (i=NUMWORD; i>=0; i--)
 {
 number.e[i]++;
 if (number.e[i]) break;
 }
 }
 }
 else
 blncd[bit_count] = 0;
 
 /* divide number by 2, increment bit counter, and see if done */
 
 number.e[NUMWORD] &= ~0 << 1;
 rot_right( &number);
 bit_count++;
 notzero = 0;
 SUMLOOP (i) notzero |= number.e[i];
 }
 
/* now follow balanced representation and compute kP */

 bit_count--;
 copy_point(p,r); /* first bit always set */
 while (bit_count > 0) 
 {
 edbl(r, &temp, curv);
 bit_count--;
 switch (blncd[bit_count]) 
 {
 case 1: esum (p, &temp, r, curv);
 break;
 case -1: esub (&temp, p, r, curv);
 break;
 case 0: copy_point (&temp, r);
 }
 }
}

/* One is not what it appears to be. In any normal basis, "1" is the sum of
all powers of the generator. So this routine puts ones to fill the number size
being used in the address of the FIELD2N supplied. */

void one (FIELD2N *place)

{
 INDEX i;

 SUMLOOP(i) place->e[i] = -1L;
 place->e[0] &= UPRMASK;
}






/************************************************************************************
* *
* Alex project routines for generalized and variable length ONB mathematics. *
* copied from original source and modified with new math. Must be optimized for *
* specific platforms later. Specific implementations should remove C constructs *
* in favor of assembler for more speed. *
 *
************************************************************************************/



void rot_left(FIELD2N *a)

{
 INDEX i;
 ELEMENT bit,temp;

 bit = (a->e[0] & UPRBIT) ? 1L : 0L;
 for (i=NUMWORD; i>=0; i--) {
 temp = (a->e[i] & MSB) ? 1L : 0L;
 a->e[i] = ( a->e[i] << 1) | bit;
 bit = temp;
 }
 a->e[0] &= UPRMASK;
}

void rot_right(FIELD2N *a)

{
 INDEX i;
 ELEMENT bit,temp;

 bit = (a->e[NUMWORD] & 1) ? UPRBIT : 0L;
 SUMLOOP(i) {
 temp = ( a->e[i] >> 1) | bit;
 bit = (a->e[i] & 1) ? MSB : 0L;
 a->e[i] = temp;
 }
 a->e[0] &= UPRMASK;
}

void null(FIELD2N *a)

{
 INDEX i;

 SUMLOOP(i) a->e[i] = 0;
}

void copy (FIELD2N *a,FIELD2N *b)

{
 INDEX i;

 SUMLOOP(i) b->e[i] = a->e[i];
}

/* binary search for most significant bit within word */

INDEX log_2( ELEMENT x)

{
 INDEX k, lg2;
 ELEMENT ebit, bitsave, bitmask;

 lg2 = 0;
 bitsave = x; /* grab bits we're interested in. */
 k = WORDSIZE/2; /* first see if msb is in top half */
 bitmask = -1L<<k; /* of all bits */
 while (k)
 {
 ebit = bitsave & bitmask; /* did we hit a bit? */
 if (ebit) /* yes */
 {
 lg2 += k; /* increment degree by minimum possible offset */
 bitsave = ebit; /* and zero out non useful bits */
 }
 k /= 2;
 bitmask ^= (bitmask >> k);
 }
 return( lg2);
}

/* create Lambda [i,j] table. indexed by j, each entry contains the
value of i which satisfies 2^i + 2^j = 1 || 0 mod field_prime. There are
two 16 bit entries per index j except for zero. See references for
details. Since 2^0 = 1 and 2^2n = 1, 2^n = -1 and the first entry would
be 2^0 + 2^n = 0. Multiplying both sides by 2, it stays congruent to
zero. So Half the table is unnecessary since multiplying exponents by
2 is the same as squaring is the same as rotation once. Lambda[0][0] stores
n = (field_prime - 1)/2. The terms congruent to one must be found via
lookup in the log table. Since every entry for (i,j) also generates an
entry for (j,i), the whole 1D table can be built quickly.
*/

void genlambda()
{
 INDEX i, logof, n, index;
 INDEX log2[field_prime], twoexp;

 for (i=0; i<field_prime; i++) log2[i] = -1;

/* build antilog table first */

 twoexp = 1;
 for (i=0; i<field_prime; i++) 
 {
 log2[twoexp] = i;
 twoexp = (twoexp << 1) % field_prime;
 }

/* compute n for easy reference */

 n = (field_prime - 1)/2;
 
/* fill in first vector with indicies shifted by half table size */

 Lambda[0][0] = n;
 for (i=1; i<field_prime; i++) 
 Lambda[0][i] = (Lambda[0][i-1] + 1) % NUMBITS;

/* initialize second vector with known values */
 
 Lambda[1][0]= -1; /* never used */
 Lambda[1][1] = n;
 Lambda[1][n] = 1;

/* loop over result space. Since we want 2^i + 2^j = 1 mod field_prime
 it's a ton easier to loop on 2^i and look up i then solve the silly
 equations. Think about it, make a table, and it'll be obvious. */

 for (i=2; i<=n; i++) {
 index = log2[i];
 logof = log2[field_prime - i + 1];
 Lambda[1][index] = logof;
 Lambda[1][logof] = index;
 }
/* last term, it's the only one which equals itself. See references. */

 Lambda[1][log2[n+1]] = log2[n+1];

/* find most significant bit of NUMBITS. This is int(log_2(NUMBITS)). 
 Used in opt_inv to count number of bits. */

 lg2_m = log_2((ELEMENT)(NUMBITS - 1));
 
}

/* Type 2 ONB initialization. Fills 2D Lambda matrix. */

void genlambda2()
{
 INDEX i, logof[4], n, index, j, k;
 INDEX log2[field_prime], twoexp;

/* build log table first. For the case where 2 generates the quadradic
 residues instead of the field, duplicate all the entries to ensure 
 positive and negative matches in the lookup table (that is, -k mod
 field_prime is congruent to entry field_prime + k). */

 twoexp = 1;
 for (i=0; i<NUMBITS; i++)
 {
 log2[twoexp] = i;
 twoexp = (twoexp << 1) % field_prime;
 }
 if (twoexp == 1) /* if so, then deal with quadradic residues */
 {
 twoexp = 2*NUMBITS;
 for (i=0; i<NUMBITS; i++)
 {
 log2[twoexp] = i;
 twoexp = (twoexp << 1) % field_prime;
 }
 }
 else
 {
 for (i=NUMBITS; i<field_prime-1; i++)
 {
 log2[twoexp] = i;
 twoexp = (twoexp << 1) % field_prime;
 }
 }
 
/* first element in vector 1 always = 1 */

 Lambda[0][0] = 1;
 Lambda[1][0] = -1;

/* again compute n = (field_prime - 1)/2 but this time we use it to see if
 an equation applies */
 
 n = (field_prime - 1)/2;

/* as in genlambda for Type I we can loop over 2^index and look up index 
 from the log table previously built. But we have to work with 4 
 equations instead of one and only two of those are useful. Look up 
 all four solutions and put them into an array. Use two counters, one
 called j to step thru the 4 solutions and the other called k to track
 the two valid ones.
 
 For the case when 2 generates quadradic residues only 2 equations are
 really needed. But the same math works due to the way we filled the
 log2 table.
*/

 twoexp = 1; 
 for (i=1; i<n; i++)
 {
 twoexp = (twoexp<<1) % field_prime;
 logof[0] = log2[field_prime + 1 - twoexp];
 logof[1] = log2[field_prime - 1 - twoexp];
 logof[2] = log2[twoexp - 1];
 logof[3] = log2[twoexp + 1];
 k = 0;
 j = 0;
 while (k<2)
 {
 if (logof[j] < n)
 {
 Lambda[k][i] = logof[j];
 k++;
 }
 j++;
 }
 }

/* find most significant bit of NUMBITS. This is int(log_2(NUMBITS)). 
 Used in opt_inv to count number of bits. */

 lg2_m = log_2((ELEMENT)(NUMBITS - 1));
}

/* Generalized Optimal Normal Basis multiply. Assumes two dimensional Lambda vector
 already initialized. Will work for both type 1 and type 2 ONB. Enter with pointers
 to FIELD2N a, b and result area c. Returns with c = a*b over GF(2^NUMBITS).
*/

void opt_mul(FIELD2N *a,FIELD2N *b,FIELD2N *c)

{
 INDEX i, j;
 INDEX k, zero_index, one_index;
 ELEMENT bit, temp;
 FIELD2N amatrix[NUMBITS], copyb;
 
/* clear result and copy b to protect original */

 null(c);
 copy(b, &copyb);

/* To perform the multiply we need two rotations of the input a. Performing all
 the rotations once and then using the Lambda vector as an index into a table
 makes the multiply almost twice as fast.
*/

 copy( a, &amatrix[0]);
 for (i = 1; i < NUMBITS; i++)
 {
 copy( &amatrix[i-1], &amatrix[i]);
 rot_right( &amatrix[i]);
 }

/* Lambda[1][0] is non existant, deal with Lambda[0][0] as speical case. */

 zero_index = Lambda[0][0];
 SUMLOOP (i) c->e[i] = copyb.e[i] & amatrix[zero_index].e[i];

/* main loop has two lookups for every position. */

 for (j = 1; j<NUMBITS; j++)
 {
 rot_right( &copyb);
 zero_index = Lambda[0][j];
 one_index = Lambda[1][j];
 SUMLOOP (i) c->e[i] ^= copyb.e[i] &
 (amatrix[zero_index].e[i] ^ amatrix[one_index].e[i]);
 }
}

/* Generic ONB inversion routine.
 Input is pointer to ONB number.
 Output is inverse of input, overwrites input in place.
*/

void opt_inv(FIELD2N *a,FIELD2N *result)

{
 FIELD2N shift, temp;
 INDEX m, s, r, rsft;
 
/* initialize s to lg2_m computed in genlambda. Since msb is always set,
 initialize result to input a and skip first math loop.
*/

 s = lg2_m - 1;
 copy( a, result);
 m = NUMBITS - 1;

/* create window over m and walk up chain of terms */

 while (s >= 0)
 {
 r = m >> s;
 copy( result, &shift);
 for (rsft = 0; rsft < (r>>1); rsft++) rot_left( &shift);
 opt_mul( result, &shift, &temp);
 if ( r&1 ) /* if window value odd */
 {
 rot_left( &temp); /* do extra square */
 opt_mul( &temp, a, result); /* and multiply */
 }
 else copy( &temp, result);
 s--;
 }
 rot_left(result); /* final squaring */
}

 

 

The Agent Implementation Part of  the eccDH.cc Code

class eccDHAgent : public Agent {
public:
 eccDHAgent();
protected:
 int command(int argc, const char*const* argv);
private:
 int my_var1;
 double my_var2;
 void MyPrivFunc(void);
};


static class eccDHAgentClass : public TclClass {
public:
 eccDHAgentClass() : TclClass("Agent/eccDHAgent") {}
 TclObject* create(int, const char*const*) {
 return(new eccDHAgent());
 }
} class_eccDH_agent;


eccDHAgent::eccDHAgent() : Agent(PT_UDP) {
 bind("my_var1_otcl", &my_var1);
 bind("my_var2_otcl", &my_var2);
}


int eccDHAgent::command(int argc, const char*const* argv) {
 if(argc == 2) {
 if(strcmp(argv[1], "InitCurveAndPoint") == 0) {
 InitCurveAndPoint();
 return(TCL_OK);
 }
 if(strcmp(argv[1], "CreateKeys") == 0) {
 CreateKeys();
 return(TCL_OK);
 }
 if(strcmp(argv[1], "CreateDummyData") == 0) {
 CreateDummyData();
 return(TCL_OK);
 }
 if(strcmp(argv[1], "Demo") == 0) {
 eccdemo();
 return(TCL_OK);
 }
 }
 if(argc == 3) {
 
 if(strcmp(argv[1], "CreateData") == 0) {
 strcpy((char*)mydata, argv[2]);
 CreateData(mydata);
 return(TCL_OK);
 }
 if(strcmp(argv[1], "HideDataForAddress") == 0) {
 HideDataForAddress( atoi(argv[2]) );
 return(TCL_OK);
 }
 if(strcmp(argv[1], "RecoverDataFromAddress") == 0) {
 RecoverDataFromAddress( atoi(argv[2]) ); 
 return(TCL_OK);
 }
 if(strcmp(argv[1], "PrintKeysOfAddress") == 0) {
 PrintKeysOfAddress( atoi(argv[2]) ); 
 return(TCL_OK);
 }
 
 } 
 return(Agent::command(argc, argv));
}


void eccDHAgent::MyPrivFunc(void) {
 Tcl& tcl = Tcl::instance();
 tcl.eval("puts \"Message From MyPrivFunc\"");
 tcl.evalf("puts \" my_var1 = %d\"", my_var1);
 tcl.evalf("puts \" my_var2 = %f\"", my_var2);
}


FIELD2N PrivateKey[25], PublicKey[25], send_data,get_data;
CURVE koblitz, rnd_crv;
POINT Base, Point[25] ,Hidden_data,Random_point;
INDEX i;
 

void InitCurveAndPoint()
{
 
#ifdef TYPE2
 genlambda2();
#else
 genlambda();
#endif
 // printf("Size of Data : %d\n",MAXLONG);
 printf("create random curve and point\n\n");

 rand_curve(&rnd_crv);
 print_curve("random curve", &rnd_crv);
 rand_point(&Base, &rnd_crv);
 print_point("Base point", &Base);
}

void CreateKeys()
{ int i;
 printf("\nCreating Private and Public keys\n\n");
 for(int i= 0;i<25;i++)
 {
 random_field(&PrivateKey[i]);
 // print_field("PrivateKey ", &PrivateKey[i]);
 DH_gen_send_key( &Base, &rnd_crv, &PrivateKey[i], &Point[i]);
 //print_point("Public Key :", &Point[i]); 
 }
}

void PrintKeysOfAddress(int Address)
{ 
 print_field("PrivateKey ", &PrivateKey[Address]);
 print_point("Public Key :", &Point[Address]); 
}

void CreateDummyData()
{
 printf("\nCreating Dummy Message data\n\n");
 random_field( &send_data);
 print_data("Data to be Sent ", &send_data,1);
}

void CreateData(char* data)
{
 printf("\nCreating Message data\n\n");
 create_field(data, &send_data);
 print_data("Data to be Sent ", &send_data,2);
}

void HideDataForAddress(int ToAddress)
{
 printf("\nHide data on curve and send from SideA to SideB\n\n");
 send_elgamal( &Base, &rnd_crv, &Point[ToAddress],
 &send_data, &Hidden_data, &Random_point);
 print_point("Hidden data", &Hidden_data);
 print_point("Random point", &Random_point);

}

void RecoverDataFromAddress(int FromAddress)
{
 printf("\nRecover Transmitted Message At %d\n\n", FromAddress);
 receive_elgamal( &Base, &rnd_crv, &PrivateKey[FromAddress],
 &Hidden_data, &Random_point, &get_data);
 
 print_data("Received data :", &get_data,2);
}

The eccDH.h

 

/****** eccDH.h *****/
/****************************************************************
* *
* These are structures used to create elliptic curve *
* points and parameters. "form" is a just a fast way to check *
* if a2 == 0. *
* form equation *
* *
* 0 y^2 + xy = x^3 + a_6 *
* 1 y^2 + xy = x^3 + a_2*x^2 + a_6 *
* *
****************************************************************/
/*** field2n.h ***/

#define WORDSIZE (sizeof(int)*8)
#define NUMBITS 134
#define TYPE2
/*#undef TYPE2 */

#ifdef TYPE2
#define field_prime ((NUMBITS<<1)+1)
#else
#define field_prime (NUMBITS+1)
#endif

#define NUMWORD (NUMBITS/WORDSIZE)
#define UPRSHIFT (NUMBITS%WORDSIZE)
#define MAXLONG (NUMWORD+1)

#define MAXBITS (MAXLONG*WORDSIZE)
#define MAXSHIFT (WORDSIZE-1)
#define MSB (1L<<MAXSHIFT)

#define UPRBIT (1L<<(UPRSHIFT-1))
#define UPRMASK (~(-1L<<UPRSHIFT))
#define SUMLOOP(i) for(i=0; i<MAXLONG; i++)

typedef short int INDEX;

typedef unsigned long ELEMENT;

typedef struct {
 ELEMENT e[MAXLONG];
} FIELD2N;


typedef struct 
{
 INDEX form;
 FIELD2N a2;
 FIELD2N a6;
} CURVE;

/* coordinates for a point */

typedef struct 
{
 FIELD2N x;
 FIELD2N y;
} POINT;

The TCL Simulation Code to test the Working of EccDH Agent 

 

Initializing Some Important Parameters

set val(chan) Channel/WirelessChannel ;# channel type
set val(prop) Propagation/TwoRayGround ;# radio-propagation model
set val(ant) Antenna/OmniAntenna ;# Antenna type
set val(ll) LL ;# Link layer type
set val(ifq) Queue/DropTail/PriQueue ;# Interface queue type
set val(ifqlen) 50 ;# max packet in ifq
set val(netif) Phy/WirelessPhy ;# network interface type
set val(mac) Mac/802_11 ;# MAC type
set val(rp) AODV


# unity gain, omni-directional antennas
# set up the antennas to be centered in the node and 1.5 meters above it
Antenna/OmniAntenna set X_ 0
Antenna/OmniAntenna set Y_ 0
Antenna/OmniAntenna set Z_ 1.5
Antenna/OmniAntenna set Gt_ 1.0
Antenna/OmniAntenna set Gr_ 1.0

# Initialize the SharedMedia interface with parameters to make
# it work like the 914MHz Lucent WaveLAN DSSS radio interface
Phy/WirelessPhy set CPThresh_ 10.0
Phy/WirelessPhy set CSThresh_ 1.559e-11
Phy/WirelessPhy set RXThresh_ 3.652e-10
Phy/WirelessPhy set Rb_ 2*1e6

#inital transmission power of all the nodes
Phy/WirelessPhy set Pt_ 0.2819;# Change it if required
Phy/WirelessPhy set freq_ 914e+6 
Phy/WirelessPhy set L_ 1.0

set ECC_AGENT_PORT 42

set TotalNodes 3

# size of the topography
set val(x) 600
set val(y) 600

 

Setting up the Node Parameters

set chan_1_ [new $val(chan)]

$ns node-config -adhocRouting $val(rp) \
 -llType $val(ll) \
 -macType $val(mac) \
 -ifqType $val(ifq) \
 -ifqLen $val(ifqlen) \
 -antType $val(ant) \
 -propType $val(prop) \
 -phyType $val(netif) \
 -topoInstance $topo \
 -agentTrace ON \
 -routerTrace ON \
 -macTrace ON \
 -movementTrace OFF \
 -channel $chan_1_ 
# subclass Agent/MessagePassing to make it do flooding

 

Defining the Function os the Inherited Agent

Class Agent/EccMessagePassing -superclass Agent/MessagePassing

Agent/EccMessagePassing instproc recv {source sport size data} {
 $self instvar node_ Side
 global ns ECC_AGENT_PORT eccAPP
 
 if {$sport==$ECC_AGENT_PORT && $source == 0 } {
 $ns trace-annotate "[$node_ node-addr] Recieving Encrypted Message From $source"
 $eccAPP RecoverData
 }
}


Agent/EccMessagePassing instproc HideAndSendDataTo {addr} {
 $self instvar node_ Side
 global ns ECC_AGENT_PORT eccAPP
 $eccAPP CreateDummyData
 $eccAPP HideAndSendData
 $self sendto 10 "DummyData" $addr $ECC_AGENT_PORT
 $ns trace-annotate "[$node_ node-addr] Sending Encrypted Message to $addr"
}

set rng [new RNG]
$rng seed 0 

# Create SideA Node
 set SideANode [$ns node]
 $SideANode color "black"
 $ns at 0 "$SideANode label Side-A"
 $SideANode set Y_ 300
 $SideANode set X_ 75
 $SideANode set Z_ 0.0
 $ns initial_node_pos $SideANode 50
 set EccAgent(0) [new Agent/EccMessagePassing]
 $SideANode attach $EccAgent(0) $ECC_AGENT_PORT
 $EccAgent(0) set Side "A"
 
# Create Middle Node
 set MiddleNode [$ns node]
 $MiddleNode color "black"
 $MiddleNode set Y_ 200
 $MiddleNode set X_ 300
 $MiddleNode set Z_ 0.0
 $ns initial_node_pos $MiddleNode 50
 set EccAgent(1) [new Agent/EccMessagePassing]
 $MiddleNode attach $EccAgent(1) $ECC_AGENT_PORT
 $EccAgent(1) set Side "M"
 
# Create SideB Node
 set SideBNode [$ns node]
 $SideBNode color "black"
 $ns at 0 "$SideBNode label Side-B"
 $SideBNode set Y_ 300
 $SideBNode set X_ 525
 $SideBNode set Z_ 0.0
 $ns initial_node_pos $SideBNode 50
 set EccAgent(2) [new Agent/EccMessagePassing]
 $SideBNode attach $EccAgent(2) $ECC_AGENT_PORT
 $EccAgent(1) set Side "B"


set eccAPP [new Agent/eccDHAgent]
#$eccDH Demo

$ns at 0.01 "$ns trace-annotate {Selecting A Random Curve and Point}"
$ns at 0.01 "$eccAPP InitCurveAndPoint"
$ns at 0.02 "$eccAPP CreateSideAKeys"
$ns at 0.03 "$eccAPP CreateSideBKeys"


# now set up some events

$ns at 0.1 "$EccAgent(0) HideAndSendDataTo 2 "
$ns at 0.2 "$EccAgent(0) HideAndSendDataTo 2 "
$ns at 0.3 "$EccAgent(0) HideAndSendDataTo 2 "

 

 

The Simple Covered Communication Scenario

The following Nam output shows the simple, covered communication scenario that we did on MANET

 

Side-A is start sending Encrypted data to Side -B via a middle node

 

Side-A is sending Encrypted data to Side -B via a middle node

 

Side-B is Receiving Encrypted data from Side -B via the middle node


The Encryption and Decryption output 

 

We will see the following output in the terminal window while running the above simulation. The output will obviously explain the covered communication.

 

create random curve and point

random curve
form = 0
a6 : 1c 5e629417 3dbdf669 b9fca0fe cd2165b0

Base point
x : c 358df1ea 9ebc2e42 2fbec069 dde73d2c 
y : 9 eb318786 772fce50 72bbc1f8 22ed38bb


Creating SideA private and Public keys

Side A secret: : c d2a3e242 4ce7401a 58e0e961 b20afcdf 
Side A public key
x : 34 69023735 749bc2f7 27123ddd 13c421e8 
y : 2a 82945bef 8826b76a 59602c5 1caaf73a


Creating SideB private and Public keys

Side B secret: : 39 99d659e8 3428a5da 9b130925 aed734d8 
Side B public key
x : 26 8856d11 8ce1eed7 2390aeae 7b3bf293 
y : 1f 1f18ec52 652f16ee 9fa9b2c3 36c8422f


Creating Dummy Message data

Data to be Sent : 16 f93ff6c8 e42f891b d8aeabdf cd419f2f

Hide data on curve and send from SideA to SideB

Hidden data
x : 31 b71d6293 bb851393 2a92dbb5 fc19958c 
y : 3d d2ff2282 87d2accb 970f677a c5c82180

Random point
x : 12 2abaa729 775cb900 bf443998 548c3e9c 
y : 1e 24719fb0 5c4e03db e67be1f0 b46cac9f


Recover Transmitted Message
Received data : : 16 f93ff6c8 e42f891b d8aeabdf cd419f2f 

 

Conclusion

We have successfully integrated the ECC code in ns2 and did some preliminary simulations using the eccDH Agent. If you are doing some serious research, then you may need to do few more additional things to use this code.

Related work

We have ported the above ECC module to the ns-3 simulator so that we can use that ECC module in ns-3 simulations. we may see that in a future article.

 

References

 

  1. https://en.wikipedia.org/wiki/Elliptic-curve_cryptography
  2. https://www.projectguideline.com/installing-ns3-35-in-debian-10-chroot-jail-under-debian-11-host-os-or-any-version-of-linux-host/

 

For Assistance in Protocol Implementation, Simulations & Analysis of Industrial as well as Scholarly Research Works, you may Contact Us.

WhatsApp chatDiscuss Through WhatsApp

Call 91 9843779735

Send an e-Mail Message.




This site is protected by reCAPTCHA and the Google
Privacy Policy and
Terms of Service apply.

WhatsApp Discuss Through WhatsApp