1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
implement Authproto;
include "sys.m";
sys: Sys;
include "draw.m";
include "keyring.m";
keyring: Keyring;
IPint: import keyring;
SK, PK, Certificate, DigestState: import Keyring;
include "security.m";
include "bufio.m";
include "sexprs.m";
sexprs: Sexprs;
Sexp: import sexprs;
include "spki.m";
spki: SPKI;
include "daytime.m";
daytime: Daytime;
include "keyreps.m";
keyreps: Keyreps;
Keyrep: import keyreps;
include "../authio.m";
authio: Authio;
Aattr, Aval, Aquery: import Authio;
Attr, IO, Key, Authinfo: import authio;
# at end of authentication, sign a hash of the authenticated username and
# a secret known only to factotum. that certificate can act as
# a later proof that this factotum has authenticated that user,
# and hence factotum will disclose certificates that allow disclosure
# only to that username.
Debug: con 0;
Maxmsg: con 4000;
Error0, Error1: exception(string);
init(f: Authio): string
{
authio = f;
sys = load Sys Sys->PATH;
spki = load SPKI SPKI->PATH;
spki->init();
sexprs = load Sexprs Sexprs->PATH;
sexprs->init();
keyring = load Keyring Keyring->PATH;
daytime = load Daytime Daytime->PATH;
keyreps = load Keyreps Keyreps->PATH;
keyreps->init();
return nil;
}
interaction(attrs: list of ref Attr, io: ref IO): string
{
ai: ref Authinfo;
(key, err) := io.findkey(attrs, "proto=infauth");
if(key == nil)
return err;
info: ref Keyring->Authinfo;
(info, err) = keytoauthinfo(key);
if(info == nil)
return err;
anysigner := int authio->lookattrval(key.attrs, "anysigner");
rattrs: list of ref Sexp;
{
# send auth protocol version number
sendmsg(io, array of byte "1");
# get auth protocol version number
if(int string getmsg(io) != 1)
raise Error0("incompatible authentication protocol");
# generate alpha**r0
p := info.p;
low := p.shr(p.bits()/4);
r0 := rand(low, p, Random->NotQuiteRandom);
αr0 := info.alpha.expmod(r0, p);
# trim(αr0); the IPint library should do this for us, i think.
# send alpha**r0 mod p, mycert, and mypk
sendmsg(io, array of byte αr0.iptob64());
sendmsg(io, array of byte keyring->certtostr(info.cert));
sendmsg(io, array of byte keyring->pktostr(info.mypk));
# get alpha**r1 mod p, hiscert, hispk
αr1 := IPint.b64toip(string getmsg(io));
# trying a fast one
if(p.cmp(αr1) <= 0)
raise Error0("implausible parameter value");
# if alpha**r1 == alpha**r0, someone may be trying a replay
if(αr0.eq(αr1))
raise Error0("possible replay attack");
hiscert := keyring->strtocert(string getmsg(io));
if(hiscert == nil && !anysigner)
raise Error0(sys->sprint("bad certificate: %r"));
buf := getmsg(io);
hispk := keyring->strtopk(string buf);
if(!anysigner){
# verify their public key
if(verify(info.spk, hiscert, buf) == 0)
raise Error0("pk doesn't match certificate"); # likely the signers don't match.
# check expiration date - in seconds of epoch
if(hiscert.exp != 0 && hiscert.exp <= now())
raise Error0("certificate expired");
}
buf = nil;
# sign alpha**r0 and alpha**r1 and send
αcert := sign(info.mysk, "sha", 0, array of byte (αr0.iptob64() + αr1.iptob64()));
sendmsg(io, array of byte keyring->certtostr(αcert));
# get signature of alpha**r1 and alpha**r0 and verify
αcert = keyring->strtocert(string getmsg(io));
if(αcert == nil)
raise Error0("alpha**r1 doesn't match certificate");
if(verify(hispk, αcert, array of byte (αr1.iptob64() + αr0.iptob64())) == 0)
raise Error0(sys->sprint("bad certificate: %r"));
ai = ref Authinfo;
# we are now authenticated and have a common secret, alpha**(r0*r1)
if(!anysigner)
rattrs = sl(ss("signer") :: principal(info.spk) :: nil) :: rattrs;
rattrs = sl(ss("remote-pk") :: principal(hispk) :: nil) :: rattrs;
rattrs = sl(ss("local-pk") :: principal(info.mypk) :: nil) :: rattrs;
rattrs = sl(ss("secret") :: sb(αr1.expmod(r0, p).iptobytes()) :: nil) :: rattrs;
ai.suid = hispk.owner;
ai.cuid = info.mypk.owner;
sendmsg(io, array of byte "OK");
}exception e{
Error0 =>
err = e;
senderr(io, e);
break;
Error1 =>
senderr(io, "missing your authentication data");
x: string = e;
return "remote: "+x;
}
{
while(string getmsg(io) != "OK")
;
}exception e{
Error0 =>
return e;
Error1 =>
x: string = e;
return "remote: "+x;
}
if(err != nil)
return err;
return negotiatecrypto(io, key, ai, rattrs);
}
negotiatecrypto(io: ref IO, key: ref Key, ai: ref Authinfo, attrs: list of ref Sexp): string
{
role := authio->lookattrval(key.attrs, "role");
alg: string;
{
if(role == "client"){
alg = authio->lookattrval(key.attrs, "alg");
if(alg == nil)
alg = "md5/rc4_256";
sendmsg(io, array of byte alg);
}else if(role == "server"){
alg = string getmsg(io);
if(!algcompatible(alg, sys->tokenize(authio->lookattrval(key.attrs, "algs"), " ").t1))
raise Error0("unsupported client algorithm");
}
}exception e{
Error0 or
Error1 =>
return e;
}
if(alg != nil)
attrs = sl(ss("alg") :: ss(alg) :: nil) :: attrs;
ai.secret = sl(attrs).pack();
io.done(ai);
return nil;
}
algcompatible(nil: string, nil: list of string): int
{
return 1; # XXX
}
principal(pk: ref Keyring->PK): ref Sexp
{
return spki->(Keyrep.pk(pk).mkkey()).sexp();
}
ipint(i: int): ref IPint
{
return IPint.inttoip(i);
}
rand(p, q: ref IPint, nil: int): ref IPint
{
if(p.cmp(q) > 0)
(p, q) = (q, p);
diff := q.sub(p);
q = nil;
if(diff.cmp(ipint(2)) < 0){
sys->print("rand range must be at least 2");
return IPint.inttoip(0);
}
l := diff.bits();
T := ipint(1).shl(l);
l = ((l + 7) / 8) * 8;
slop := T.div(diff).t1;
r: ref IPint;
do{
r = IPint.random(0, l);
}while(r.cmp(slop) < 0);
r = r.div(diff).t1.add(p);
return r;
}
now(): int
{
return daytime->now();
}
Hashfn: type ref fn(a: array of byte, alen: int, digest: array of byte, state: ref DigestState): ref DigestState;
hashalg(ha: string): Hashfn
{
case ha {
"sha" or
"sha1" =>
return keyring->sha1;
"md4" =>
return keyring->md4;
"md5" =>
return keyring->md5;
}
return nil;
}
sign(sk: ref SK, ha: string, exp: int, buf: array of byte): ref Certificate
{
state := hashalg(ha)(buf, len buf, nil, nil);
return keyring->sign(sk, exp, state, ha);
}
verify(pk: ref PK, cert: ref Certificate, buf: array of byte): int
{
state := hashalg(cert.ha)(buf, len buf, nil, nil);
return keyring->verify(pk, cert, state);
}
getmsg(io: ref IO): array of byte raises (Error0, Error1)
{
while((buf := io.read()) == nil || (n := len buf) < 5)
io.toosmall(5);
if(len buf != 5)
raise Error0("io error: (impossible?) msg length " + string n);
h := string buf;
if(h[0] == '!')
m := int h[1:];
else
m = int h;
while((buf = io.read()) == nil || (n = len buf) < m)
io.toosmall(m);
if(len buf != m)
raise Error0("io error: (impossible?) msg length " + string m);
if(h[0] == '!'){
sys->print("got remote error: %s, len %d\n", string buf, len string buf);
raise Error1(string buf);
}
return buf;
}
sendmsg(io: ref IO, buf: array of byte)
{
h := sys->aprint("%4.4d\n", len buf);
io.write(h, len h);
io.write(buf, len buf);
}
senderr(io: ref IO, e: string)
{
buf := array of byte e;
h := sys->aprint("!%3.3d\n", len buf);
io.write(h, len h);
io.write(buf, len buf);
}
keytoauthinfo(key:ref Key): (ref Keyring->Authinfo, string)
{
if((s := authio->lookattrval(key.secrets, "!authinfo")) == nil){
# XXX could look up authinfo by hash at this point
return (nil, "no authinfo attribute");
}
return strtoauthinfo(s);
}
strtoauthinfo(s: string): (ref Keyring->Authinfo, string)
{
(se, err, nil) := Sexp.parse(s);
if(se == nil)
return (nil, err);
els := se.els();
if(len els != 5)
return (nil, "bad authinfo contents");
ai := ref Keyring->Authinfo;
if((ai.spk = keyring->strtopk((hd els).astext())) == nil)
return (nil, "bad signer public key");
els = tl els;
if((ai.cert = keyring->strtocert((hd els).astext())) == nil)
return (nil, "bad certificate");
els = tl els;
if((ai.mysk = keyring->strtosk((hd els).astext())) == nil)
return (nil, "bad secret/public key");
if((ai.mypk = keyring->sktopk(ai.mysk)) == nil)
return (nil, "cannot make pk from sk");
els = tl els;
if((ai.alpha = IPint.bytestoip((hd els).asdata())) == nil)
return (nil, "bad value for alpha");
els = tl els;
if((ai.p = IPint.bytestoip((hd els).asdata())) == nil)
return (nil, "bad value for p");
return (ai, nil);
}
authinfotostr(ai: ref Keyring->Authinfo): string
{
return (ref Sexp.List(
ss(keyring->pktostr(ai.spk)) ::
ss(keyring->certtostr(ai.cert)) ::
ss(keyring->sktostr(ai.mysk)) ::
sb(ai.alpha.iptobytes()) ::
sb(ai.p.iptobytes()) ::
nil
)).b64text();
}
ss(s: string): ref Sexp.String
{
return ref Sexp.String(s, nil);
}
sb(d: array of byte): ref Sexp.Binary
{
return ref Sexp.Binary(d, nil);
}
sl(l: list of ref Sexp): ref Sexp
{
return ref Sexp.List(l);
}
|