xref: /inferno-os/appl/lib/ida/ida.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Ida;
2
3#
4# M Rabin, ``Efficient Dispersal of Information for Security,
5#	Load Balancing, and Fault Tolerance'', JACM 36(2), April 1989, pp. 335-348
6#	the scheme used below is that suggested at the top of page 340
7#
8
9include "sys.m";
10	sys: Sys;
11
12include "rand.m";
13	rand: Rand;
14
15include "ida.m";
16
17invtab: array of int;
18
19init()
20{
21	sys = load Sys Sys->PATH;
22	rand = load Rand Rand->PATH;
23	rand->init(sys->pctl(0, nil)^(sys->millisec()<<8));
24	# the table is in a separate module so that
25	# the copy in the module initialisation section is discarded
26	# after unloading, preventing twice the space being used
27	idatab := load Idatab Idatab->PATH;
28	invtab = idatab->init();	# the big fella
29	idatab = nil;
30}
31
32Field: con 65537;
33Fmax: con Field-1;
34
35div(a, b: int): int
36{
37	return mul(a, invtab[b]);
38}
39
40mul(a, b: int): int
41{
42	if(a == Fmax && b == Fmax)	# avoid overflow
43		return 1;
44	return int((big(a*b) & 16rFFFFFFFF) % big Field);
45}
46
47sub(a, b: int): int
48{
49	return ((a-b)+Field)%Field;
50}
51
52add(a, b: int): int
53{
54	return (a + b)%Field;
55}
56
57#
58# return a fragment representing the encoded version of data
59#
60fragment(data: array of byte, m: int): ref Frag
61{
62	nb := len data;
63	nw := (nb+1)/2;
64	a := array[m] of {* => rand->rand(Fmax)+1};	# no zero elements
65	f := array[(nw + m - 1)/m] of int;
66	o := 0;
67	i := 0;
68	for(k := 0; k < len f; k++){
69		c := 0;
70		for(j := 0; j < m && i < nb; j++){
71			b := int data[i++] << 8;
72			if(i < nb)
73				b |= int data[i++];
74			c = add(c, mul(b, a[j]));
75		}
76		f[o++] = c;
77	}
78	return ref Frag(nb, m, a, f, nil);
79}
80
81#
82# return the data encoded by the given set of fragments
83#
84reconstruct(frags: array of ref Frag): (array of byte, string)
85{
86	if(len frags < 1 || len frags < (m := frags[0].m))
87		return (nil, "too few fragments");
88	fraglen := len frags[0].enc;
89
90	a := array[m] of array of int;
91	for(j := 0; j < len a; j++){
92		a[j] = frags[j].a;
93		if(len a[j] != m)
94			return (nil, "inconsistent encoding matrix");
95		if(len frags[j].enc != fraglen)
96			return (nil, "inconsistent fragments");
97	}
98	ainv := minvert(a);
99	out := array[fraglen*2*m] of byte;
100	o := 0;
101	for(k := 0; k < fraglen; k++){
102		for(i := 0; i < m; i++){
103			row := ainv[i];
104			b := 0;
105			for(j = 0; j < m; j++)
106				b = add(b, mul(frags[j].enc[k], row[j]));
107			if((b>>16) != 0)
108				return (nil, "corrupt output");
109			out[o++] = byte (b>>8);
110			out[o++] = byte b;
111		}
112	}
113	if(frags[0].dlen < len out)
114		out = out[0: frags[0].dlen];
115	return (out, nil);
116}
117
118#
119# Rabin's paper gives a way of building an encoding matrix that can then
120# be inverted in O(m^2) operations, compared to O(m^3) for the following,
121# but m is small enough it doesn't seem worth the added complication,
122# and it's only done once per set
123#
124minvert(a: array of array of int): array of array of int
125{
126	m := len a;	# it's square
127	out := array[m] of {* => array[m*2] of {* => 0}};
128	for(r := 0; r < m; r++){
129		out[r][0:] = a[r];
130		out[r][m+r] = 1;	# identity matrix
131	}
132	for(r = 0; r < m; r++){
133		x := out[r][r];	# by construction, cannot be zero, unless later corrupted
134		for(c := 0; c < 2*m; c++)
135			out[r][c] = div(out[r][c], x);
136		for(r1 := 0; r1 < m; r1++)
137			if(r1 != r){
138				y := div(out[r1][r], out[r][r]);
139				for(c = 0; c < 2*m; c++)
140					out[r1][c] = sub(out[r1][c], mul(y, out[r][c]));
141			}
142	}
143	for(r = 0; r < m; r++)
144		out[r] = out[r][m:];
145	return out;
146}
147
148Val: adt {
149	v:	int;
150	n:	int;
151};
152
153addval(vl: list of ref Val, v: int): list of ref Val
154{
155	for(l := vl; l != nil; l = tl l)
156		if((hd l).v == v){
157			(hd l).n++;
158			return vl;
159		}
160	return ref Val(v, 1) :: vl;
161}
162
163mostly(vl: list of ref Val): ref Val
164{
165	if(len vl == 1)
166		return hd vl;
167	v: ref Val;
168	for(; vl != nil; vl = tl vl)
169		if(v == nil || (hd vl).n > v.n)
170			v = hd vl;
171	return v;
172}
173
174#
175# return a consistent set of Frags: all parameters agree with the majority,
176# and obviously bad fragments have been discarded
177#
178# in the absence of error, they  should all have the same value, so lists are fine;
179# could separately return the discarded ones, out of interest
180#
181consistent(frags: array of ref Frag): array of ref Frag
182{
183	t := array[len frags] of ref Frag;
184	t[0:] = frags;
185	frags = t;
186	ds: list of ref Val;	# data size
187	ms: list of ref Val;
188	fls: list of ref Val;
189	for(i := 0; i < len frags; i++){
190		f := frags[i];
191		if(f != nil){
192			ds = addval(ds, f.dlen);
193			ms = addval(ms, f.m);
194			fls = addval(fls, len f.enc);
195		}
196	}
197	dv := mostly(ds);
198	mv := mostly(ms);
199	flv := mostly(fls);
200	if(mv == nil || flv == nil || dv == nil)
201		return nil;
202	for(i = 0; i < len frags; i++){
203		f := frags[i];
204		if(f == nil || f.m != mv.v || f.m != len f.a || len f.enc != flv.v || f.dlen != dv.v || badfrag(f)){	# inconsistent: drop it
205			if(i+1 < len frags)
206				frags[i:] = frags[i+1:];
207			frags = frags[0:len frags-1];
208		}
209	}
210	if(len frags == 0)
211		return nil;
212	return frags;
213}
214
215badfrag(f: ref Frag): int
216{
217	for(i := 0; i < len f.a; i++){
218		v := f.a[i];
219		if(v <= 0 || v >= Field)
220			return 1;
221	}
222	for(i = 0; i < len f.a; i++){
223		v := f.enc[i];
224		if(v == 0 || v >= Field)
225			return 1;
226	}
227	return 0;
228}
229