finitefields 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ellipticcurves.ts CHANGED
@@ -7,16 +7,17 @@
7
7
  *
8
8
  */
9
9
 
10
- import type { GF,PrimeField } from './galois.js';
11
- import { just, nothing, type Maybe, type Nothing } from './maybe.js';
10
+ import { PrimeField, type GF } from './galois.js';
11
+ import { empty, fold, just, nothing, type Maybe, type Nothing } from './maybe.js';
12
12
  import { Util } from './galois.js';
13
13
 
14
- let 𝒪:Nothing;
15
14
  let log:(...x:any[])=>void;
16
15
  let fail:(...x:any[])=>never;
16
+ let mkcurve:(field:PrimeField)=>ECCurve;
17
+
18
+ let birch17:()=>ECCurve;
17
19
  let p256:()=>ECCurve;
18
20
 
19
- 𝒪 = nothing();
20
21
  log = console.log;
21
22
  fail = (...args:any[]): never => {
22
23
  console.error(...args);
@@ -25,9 +26,106 @@ fail = (...args:any[]): never => {
25
26
  return void 0 as never;
26
27
  }
27
28
 
29
+ mkcurve = (field:PrimeField): ECCurve => {
30
+ let bitlen:bigint;
31
+ let n:bigint;
32
+ let x:bigint, y:bigint;
33
+ let q:Point;
34
+ let mq:Coordinate;
35
+ let p:bigint;
36
+ let ret:boolean;
37
+ let test:(t:Coordinate,f:PrimeField)=>boolean;
38
+ let formula:ECformula;
39
+
40
+ formula = (a:bigint,b:bigint,f:PrimeField)=>(x:bigint,y:bigint) =>
41
+ (f.add(f.add( // y^2 =
42
+ f.exp(x, 3n), // x^3
43
+ f.mul(a,x)), // + ax
44
+ b) % f.p); // + b
45
+
46
+ test = (mt:Coordinate,f:PrimeField): boolean => {
47
+ let y2:bigint;
48
+ let y2_:bigint;
49
+ let x:bigint, y:bigint;
50
+ let t:Point;
51
+ let a_:bigint, b_:bigint;
52
+ let p_:bigint;
53
+
54
+ if (empty(mt))
55
+ return true;
56
+ else
57
+ t = fold(mt);
58
+
59
+ p_ = f.p;
60
+ x=t.x, y=t.y;
61
+ a_ = 2n;
62
+ b_ = 2n;
63
+
64
+ y2 = Util.sqmul(y, 2n, p_);
65
+ y2_ = formula(a_, b_, f)(x,y);
66
+
67
+ return (y2 === y2_);
68
+ }
69
+
70
+ x=y = 0n;
71
+ ret = false;
72
+ p = field.p;
73
+ bitlen = Util.countbits(p);
74
+ y = Util.rnd(bitlen, p) % p;
75
+
76
+ if (p<4)
77
+ return fail();
78
+
79
+ for ((n=Util.sqmul(2n,bitlen,p)-1n); n; n--) {
80
+ x = (!x)?Util.rnd(bitlen, p) % p:x--;
81
+ q = new Point([x,y]);
82
+ mq = (q['∞'])?nothing():just(q);
83
+ if (test(mq,field)) {
84
+ (ret as unknown as number)++;
85
+ break;
86
+ }
87
+ y = (!y)?Util.rnd(bitlen, p) % p:y++;
88
+ q = new Point([x,y]);
89
+ mq = (q['∞'])?nothing():just(q);
90
+ if (test(mq,field)) {
91
+ (ret as unknown as number)++;
92
+ break;
93
+ }
94
+ }
95
+
96
+ return (!ret)?fail('curve generation failed'):{
97
+ '#E': p,
98
+ 'θ': p,
99
+ 'a': 3n,
100
+ 'b': 2n,
101
+ 'p': [x,y],
102
+ 'q': [x,y],
103
+ bitlen,
104
+ formula
105
+ };
106
+ }
107
+
108
+ birch17 = ():ECCurve => ({
109
+ '#E': 17n,
110
+ 'θ': 17n,
111
+ a: 2n,
112
+ b: 2n,
113
+ // p: [ 10n, 11n ],
114
+ // q: [ 10n, 11n ],
115
+ p: [ 5n, 1n ],
116
+ q: [ 5n, 1n ],
117
+ bitlen: 5n,
118
+ formula: (a:bigint,b:bigint,f:PrimeField)=>(x:bigint,y:bigint) =>
119
+ (f.add(f.add( // y^2 =
120
+ f.exp(x, 3n), // x^3
121
+ f.mul(a,x)), // + ax
122
+ b) % f.p) // + b
123
+ });
124
+
28
125
  p256 = ():ECCurve => ({
29
126
  '#E': 0xffffffff00000000_ffffffffffffffff_bce6faada7179e84_f3b9cac2fc632551n,
30
127
  'θ': 0xffffffff00000001_0000000000000000_00000000ffffffff_ffffffffffffffffn,
128
+ 'a': -3n,
31
129
  'b': 0x5ac635d8aa3a93e7_b3ebbd55769886bc_651d06b0cc53b0f6_3bce3c3e27d2604bn,
32
130
  'p':
33
131
  [
@@ -41,16 +139,17 @@ p256 = ():ECCurve => ({
41
139
  ],
42
140
  bitlen: 256n,
43
141
  formula: (a:bigint,b:bigint,f:PrimeField)=>(x:bigint,y:bigint) =>
44
- f.add(f.add( // y^2 =
142
+ (f.add(f.add( // y^2 =
45
143
  f.exp(x, 3n), // x^3
46
144
  f.mul(a,x)), // + ax
47
- b) // + b
145
+ b) % f.p) // + b
48
146
  });
49
147
 
50
148
  type Tuple<a> = readonly [a,a];
51
149
  type Coordinate = Maybe<Point>;
52
150
  type infinity = false | true;
53
151
  type ECformula = (a:bigint,b:bigint,f:GF)=>Function;
152
+ type Slope = bigint;
54
153
 
55
154
  interface Tuples {
56
155
  'p': Tuple<bigint>;
@@ -59,6 +158,7 @@ interface Tuples {
59
158
 
60
159
  interface IECCurve {
61
160
  'θ': bigint;
161
+ 'a': bigint;
62
162
  'b': bigint;
63
163
  bitlen: bigint;
64
164
  formula: ECformula;
@@ -72,6 +172,8 @@ interface Iellipticcurves<a> {
72
172
  constructor:Function;
73
173
  add:(a:Coordinate,b:Coordinate)=>Coordinate;
74
174
  double:(a:Coordinate)=>Coordinate;
175
+ get p(): Coordinate;
176
+ get q(): Coordinate;
75
177
  }
76
178
 
77
179
  interface Icurve extends IECCurve {
@@ -95,11 +197,11 @@ class Point implements Ipoint {
95
197
  constructor(point:Tuple<bigint>) {
96
198
  [ this['x'], this['y'], this['∞'] ] = [0n,0n,false];
97
199
 
98
- switch (point.every((xy:bigint):xy is bigint=>!!xy)) {
99
- case true:
200
+ switch (point.every((xy:bigint):xy is bigint=>!xy)) {
201
+ case false:
100
202
  [ this['x'], this['y'] ] = point;
101
203
  break;
102
- case false:
204
+ case true:
103
205
  (this['∞'] as unknown as number)++;
104
206
  break;
105
207
  default:
@@ -113,6 +215,7 @@ class Point implements Ipoint {
113
215
  class Curve<a> implements Icurve {
114
216
  #E: bigint;
115
217
  public θ: bigint;
218
+ public a: bigint;
116
219
  public b: bigint;
117
220
  public p: Point;
118
221
  public q: Point;
@@ -127,6 +230,8 @@ class Curve<a> implements Icurve {
127
230
  || fail(m);
128
231
  this.θ = curve.θ
129
232
  || fail(m);
233
+ this.a = curve.a
234
+ || fail(m);
130
235
  this.b = curve.b
131
236
  || fail(m);
132
237
  this.bitlen = curve.bitlen
@@ -140,36 +245,161 @@ class Curve<a> implements Icurve {
140
245
  }
141
246
  }
142
247
 
143
- abstract class EllipticCurves<a> implements Iellipticcurves<a> {
248
+ abstract class EllipticCurves<a extends GF> implements Iellipticcurves<a> {
144
249
  public abstract backend: a;
145
250
  private curve: Curve<a>;
146
251
 
147
- protected addpoints(a:Coordinate,b:Coordinate): Coordinate {
148
- return a;
252
+ private eq(p:Point,q:Point): boolean {
253
+ return (
254
+ (!p || !q) ?
255
+ fail() :
256
+ (p['∞'] && q['∞']) ?
257
+ true :
258
+ (p['∞'] && !q['∞']) ?
259
+ false :
260
+ (!p['∞'] && q['∞']) ?
261
+ false :
262
+ ((p.x === q.x) && (p.y === q.y))
263
+ );
264
+ }
265
+
266
+ private isprimefield(f:GF): f is PrimeField {
267
+ return (f instanceof PrimeField);
268
+ }
269
+
270
+ private slope(p_:Point,q_:Point): (a:bigint,p:bigint)=>Slope {
271
+ let f:a;
272
+ const { x:x1,y:y1 } = p_;
273
+ const { x:x2,y:y2 } = q_;
274
+
275
+ if (!this.isprimefield(this.backend))
276
+ return fail();
277
+ else
278
+ f = this.backend;
279
+
280
+ return (this.eq(p_,q_)) ?
281
+ (a:bigint,p:bigint): bigint =>
282
+ (f.mul(
283
+ f.add(a,
284
+ f.mul(3n, f.exp(x1, 2n))
285
+ ),
286
+ f.inv(f.mul(y1, 2n))
287
+ ) % p) :
288
+ (a:bigint,p:bigint): bigint =>
289
+ (f.mul(
290
+ f.sub(y2, y1),
291
+ f.inv(f.sub(x2, x1))
292
+ ) % p);
293
+ }
294
+
295
+ private isinfinity(f:PrimeField,p:Point,q:Point): boolean {
296
+ let invpy:bigint;
297
+ let invqy:bigint;
298
+ const [ px,py ]:[bigint,bigint] = [p.x,p.y];
299
+ const [ qx,qy ]:[bigint,bigint] = [q.x,q.y];
300
+
301
+ invpy = f.mul(-1n, py);
302
+ invqy = f.mul(-1n,qy);
303
+ // log('isinfinity(p) -> !!p[∞] -> ', !!(p['∞']));
304
+ // log(p);
305
+ return ((px===qx) && (py===invqy) && (qy===invpy)) ?
306
+ true : // Q = P + (-P) === ∞
307
+ false;
308
+ }
309
+
310
+ protected addpoints(p:Coordinate,q:Coordinate): Coordinate {
311
+ let s:Slope;
312
+ let p_:Point;
313
+ let q_:Point;
314
+ let x:bigint;
315
+ let y:bigint;
316
+ let t:Point;
317
+ let mt:Coordinate;
318
+ let Δx:bigint, Δy:bigint;
319
+ let f:a;
320
+
321
+ if (!this.isprimefield(this.backend))
322
+ return fail();
323
+ else
324
+ f = this.backend;
325
+
326
+ if (empty(p) && empty(q))
327
+ return <Maybe<Point>>nothing();
328
+ else if (empty(p))
329
+ return <Maybe<Point>>just(q as unknown as Point);
330
+ else if (empty(q))
331
+ return <Maybe<Point>>just(p as unknown as Point);
332
+ else
333
+ [p_, q_] = [fold(p), fold(q)];
334
+
335
+ const { x:x1,y:y1 } = p_;
336
+ const { x:x2,y:y2 } = q_;
337
+
338
+ if (this.isinfinity(f,p_,q_))
339
+ return nothing();
340
+
341
+ s = this.slope(p_, q_)(this.curve.a, this.backend.p);
342
+ Δx = f.add(x1, x2);
343
+ // x = (f.sub(s, f.mul(s, s)) % this.backend.p);
344
+ x = f.sub(f.mul(s,s),Δx);
345
+ Δy = f.sub(x1, x);
346
+ y = (f.sub(
347
+ f.mul(s, Δy),
348
+ y1
349
+ ) % this.backend.p);
350
+
351
+ t = new Point([x,y]);
352
+ // log('t', t);
353
+ mt = just(t);
354
+
355
+ return mt;
149
356
  }
150
357
 
151
358
  protected doublepoint(a:Coordinate): Coordinate {
152
- return a;
359
+ return this.add(a, a);
153
360
  }
154
361
 
155
362
  public add(a:Coordinate,b:Coordinate): Coordinate {
156
- if ((a === 𝒪) && (b === 𝒪))
363
+ if (empty(a) && empty(b))
157
364
  return <Maybe<Point>>nothing();
158
- else if (a === 𝒪)
159
- return <Maybe<Point>>just(b as unknown as Point);
160
- else if (b === 𝒪)
161
- return <Maybe<Point>>just(a as unknown as Point);
365
+ else if (empty(a))
366
+ return b;
367
+ else if (empty(b))
368
+ return a;
162
369
  else
163
370
  return <Maybe<Point>>this.addpoints(a,b);
164
371
  }
165
372
 
166
373
  public double(a:Coordinate): Coordinate {
167
- if (a === 𝒪)
374
+ if (empty(a))
168
375
  return nothing();
169
376
  else
170
377
  return this.doublepoint(a);
171
378
  }
172
379
 
380
+ public get p(): Coordinate {
381
+ let p:Point;
382
+ let mp:Coordinate;
383
+
384
+ p = this.curve.p;
385
+ mp = (p['∞']) ?
386
+ nothing() :
387
+ just(p);
388
+
389
+ return mp;
390
+ }
391
+ public get q(): Coordinate {
392
+ let q:Point;
393
+ let mq:Coordinate;
394
+
395
+ q = this.curve.q;
396
+ mq = (q['∞']) ?
397
+ nothing() :
398
+ just(q);
399
+
400
+ return mq;
401
+ }
402
+
173
403
  constructor(curve:ECCurve) {
174
404
  this.curve = new Curve<a>(curve);
175
405
 
@@ -190,4 +420,5 @@ class ecℤp extends EllipticCurves<PrimeField>
190
420
  }
191
421
  }
192
422
 
193
- export { ecℤp, p256 };
423
+ export { Point,ecℤp,birch17,p256,mkcurve };
424
+ export type { Coordinate,ECCurve };
package/galois.ts CHANGED
@@ -32,18 +32,28 @@ enum methods {
32
32
  DoubleAndAdd
33
33
  }
34
34
 
35
- const arithmeticsℤp = (p:bigint,op:fieldops) =>
36
- (op==fieldops.$add) ?
37
- (a:bigint,b:bigint=0n) => (a+b)%p :
38
- (op==fieldops.$sub) ?
39
- (a:bigint,b:bigint=0n) => ((a-b)<1)?(a-b+p):(a-b)%p :
40
- (op==fieldops.$mul) ?
41
- (a:bigint,b:bigint=0n) => (!a||!b)?0n:Util.dblad(a,b,p) :
42
- (op==fieldops.$inv) ?
43
- (a:bigint) => (!a)?fail('0 has no inverse'):Util.sqmul(a,(p-2n),p) :
44
- (op==fieldops.$exp) ?
45
- (a:bigint,b:bigint=0n) => (!a)?0n:(!b)?1n:Util.sqmul(a,b,p) :
46
- fail('unknown field operation');
35
+ const arithmeticsℤp = (p:bigint,op:fieldops) => {
36
+ let x:bigint;
37
+
38
+ return (
39
+ (op==fieldops.$add) ?
40
+ (a:bigint,b:bigint=0n) => ((x=((a+b)%p))<0n)?((x+p)%p):x :
41
+ (op==fieldops.$sub) ?
42
+ (a:bigint,b:bigint=0n) => ((x=((a-b)%p))<0n)?((x+p)%p):x :
43
+ (op==fieldops.$mul) ?
44
+ (a:bigint,b:bigint=0n) =>
45
+ (!a||!b)?0n:((x=Util.dblad(a,b,p)%p)<0)?((x+p)%p):x :
46
+ (op==fieldops.$inv) ?
47
+ (a:bigint) =>
48
+ (!a)?fail('0 has no inverse'):(a<0)?-a%p:Util.sqmul(a,(p-2n),p) :
49
+ (op==fieldops.$exp) ?
50
+ (a:bigint,b:bigint=0n):bigint =>
51
+ (!a)?0n:(!b)?1n:(b<0) ?
52
+ arithmeticsℤp(p,fieldops.$inv)(Util.sqmul(a,-b,p)) :
53
+ Util.sqmul(a,b,p) :
54
+ fail('unknown field operation')
55
+ );
56
+ }
47
57
 
48
58
  Util = {
49
59
  countbits(x:bigint): bigint {
@@ -80,7 +90,7 @@ Util = {
80
90
  // return v;
81
91
  // }
82
92
 
83
- return true;
93
+ // return true;
84
94
 
85
95
  if (p<2n)
86
96
  return true;
@@ -145,10 +155,30 @@ Util = {
145
155
  let sign:string;
146
156
  let a:bigint;
147
157
  let e:bigint;
158
+ let minus:boolean;
148
159
 
149
160
  // sign = (method==methods.SquareAndMultiply) ? '^' : 'x';
150
161
  // log(`${a_}${sign}${e_} (mod ${p}) = `);
151
162
 
163
+ minus = false;
164
+ if (((a_===0n) || (e_===0n)) && (method===methods.DoubleAndAdd))
165
+ return 0n;
166
+ else if (a_ === 0n)
167
+ return 0n;
168
+
169
+ if (a_<0) {
170
+ (minus as unknown as number) ^= 1;
171
+ a_*=-1n;
172
+ a_%=p;
173
+ }
174
+ if ((e_<0) && (method===methods.DoubleAndAdd)) {
175
+ (minus as unknown as number) ^= 1;
176
+ e_*=1n;
177
+ e_%=p;
178
+ }
179
+ else if (e_<0)
180
+ return fail('cannot raise to a negative exponent');
181
+
152
182
  switch (method) {
153
183
  case methods.SquareAndMultiply:
154
184
  sq = (y:bigint,p_:bigint):bigint =>
@@ -193,7 +223,9 @@ Util = {
193
223
  return void 0;
194
224
  });
195
225
 
196
- return (val%p);
226
+ return (minus && (method===methods.DoubleAndAdd)) ?
227
+ ((val*-1n)%p) :
228
+ (val%p);
197
229
  }
198
230
  }
199
231
 
package/main.ts CHANGED
@@ -1,23 +1,62 @@
1
1
  /* main.ts */
2
2
  import { Polynomial } from './polynomial.js';
3
3
  import { Util,PrimeField } from './galois.js';
4
- import { ecℤp, p256 } from './ellipticcurves.js';
4
+ import { birch17, ecℤp, mkcurve, p256,Point } from './ellipticcurves.js';
5
5
  import { empty, fold, just, nothing } from './maybe.js';
6
+ import type { Coordinate, ECCurve } from './ellipticcurves.js';
6
7
  import type { Maybe, Just, Nothing } from './maybe.js';
7
8
 
8
9
  let log:(...x:any[])=>void;
9
10
  log = console.log;
10
11
 
11
- let foo:Maybe<string>;
12
- foo = just(1..toString());
13
- log(empty(foo));
14
-
15
- // let field:PrimeField;
16
- // let curve:ecℤp;
12
+ let field:PrimeField;
13
+ let curve:ecℤp;
14
+ let mp:Coordinate;
15
+ let mq:Coordinate;
16
+ let mt:Coordinate;
17
+ let p:Point;
18
+ let q:Point;
19
+ let mycurve:ECCurve;
20
+ let n:bigint;
17
21
 
22
+ field = new PrimeField(birch17().θ, birch17().bitlen);
23
+ curve = new ecℤp(field, birch17());
24
+ // log(curve);
18
25
  // field = new PrimeField(p256().θ, p256().bitlen);
19
- // log(field);
20
26
  // curve = new ecℤp(field, p256());
27
+ // field = new PrimeField(17n,5n);
28
+ // mycurve = mkcurve(field);
29
+ // log(mycurve);
30
+ // log(field);
21
31
  // log(curve);
22
32
 
23
- export { Polynomial,Util,PrimeField,ecℤp,p256 };
33
+ // log('[5,1] + [5,16] = ');
34
+ // q = new Point([5n,16n]);
35
+ // mq = just(q);
36
+ // mp = curve.p;
37
+ // mt = curve.add(mp, mq);
38
+ // log (mt);
39
+ // log(curve.add(mp, mt));
40
+
41
+ // log(mp);
42
+ // process.exit(0);
43
+ // p = undefined as any;
44
+
45
+ mp = curve.p;
46
+ mq = curve.q;
47
+ log(curve.p);
48
+
49
+ // mp = curve.add(curve.p, curve.q);
50
+ // mp = curve.q;
51
+
52
+ q = fold(mq);
53
+ p = fold(curve.p);
54
+ for (n=0n; n<22n; n++) {
55
+ mp = curve.add(mp, mq);
56
+ log(mp);
57
+ }
58
+ log(mp);
59
+
60
+ export { Polynomial,Util,PrimeField,ecℤp,Point };
61
+ export { p256,empty,fold,just,nothing };
62
+ export type { Coordinate,Maybe,Just,Nothing }
package/maybe.ts CHANGED
@@ -32,7 +32,7 @@ let x:Function;
32
32
 
33
33
  nothing = (): Nothing => [] as Nothing;
34
34
  just = <a>(x:a): Just<a> => [x] as Just<a>;
35
- empty = <a>(x:Maybe<a>): x is Nothing => !(x&&x.length);
35
+ empty = <a>(x:Maybe<a>): boolean => (!x || !x.length);
36
36
  fold = <a>(x:Maybe<a>): a|never =>
37
37
  (!empty(x))?x[0] as a:fail("folding nothing") as never;
38
38
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "finitefields",
3
3
  "type": "module",
4
- "version": "0.0.9",
4
+ "version": "0.0.11",
5
5
  "description": "Galois/Finite field crypto and raw elliptic curves",
6
6
  "main": "main.js",
7
7
  "scripts": {