From dbb664067e51303861a690607bba04fedc0b957f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tom=C3=A1=C5=A1=20Brada?= Date: Tue, 22 Dec 2015 12:44:15 +0100 Subject: [PATCH] DHT: ECC Auth and PoW. ECC Auth and PoW ready for testing. --- CRAuth.pas | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ECC.pas | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ ServerLoop.pas | 3 +- brodnetd.pas | 3 +- curve25519.pp | 26 ++++++++ dht.pas | 163 +++++++++++++++++++++++++++++++------------------- dhtPersist.pas | 21 +++++-- opcode.pas | 3 +- 8 files changed, 495 insertions(+), 69 deletions(-) create mode 100644 CRAuth.pas create mode 100644 ECC.pas create mode 100644 curve25519.pp diff --git a/CRAuth.pas b/CRAuth.pas new file mode 100644 index 0000000..cf34be5 --- /dev/null +++ b/CRAuth.pas @@ -0,0 +1,184 @@ +unit CRAuth; +{Challenge-Response Authenticator} +INTERFACE +USES NetAddr,ECC,SHA1,Chat,ServerLoop,MemStream,opcode; +type + tAuth=object + ch:tChat; + Challenge:tEccKey; + RemotePub:tEccKey; + Valid:Boolean; + PoWValid:Boolean; + error:byte; + Callback:procedure of object; + procedure Init(const iRemote:tNetAddr); + procedure Cancel; + private + procedure ReplyRes(msg:tSMsg; data:boolean); + procedure ReplyPow(msg:tSMsg; data:boolean); + procedure Done; + procedure Timeout; + procedure Conclusion; + end; + +IMPLEMENTATION + +procedure tAuth.Init(const iRemote:tNetAddr); + var ms:tMemoryStream; + begin + Assert(assigned(Callback) and (not iRemote.isNil)); + Valid:=FALSE; + PoWValid:=FALSE; + Error:=255; + Ch.Init(iRemote); + Ch.OnDispose:=@Done; + Ch.OnTimeout:=@Timeout; + Ch.Callback:=@ReplyRes; + Ch.SetTimeout(8001,3000); + {generate and send challenge} + Ch.StreamInit(ms,66); + ECC.CreateChallenge(challenge); + Ms.WriteByte(opcode.crAuthReq); + Ms.WriteByte(1); + Ms.Write(ECC.PublicKey,sizeof(PublicKey)); + Ms.Write(challenge,sizeof(challenge)); + Ch.Send(Ms); +end; + +procedure tAuth.ReplyRes(msg:tSMsg; data:boolean); + var r:tMemoryStream absolute msg.Stream; + var status:byte; + var RPub:^tEccKey; + var resp:^tEccKey; + var vresp:tSha1Digest; + begin + if not data then exit; + status:=r.readbyte; {todo, set error (eg: unsuported meth)} + RPub:=r.readptr(sizeof(tEccKey)); + resp:=r.readptr(sizeof(tEccKey)); + ECC.CreateResponse(Challenge,vresp,RPub^); + Valid:=CompareByte(resp^,vresp,sizeof(vresp))=0; + if (status and 128)=1 then begin + {expecting pow} + Ch.Callback:=@ReplyPow; + Ch.Ack; + end else Conclusion; +end; +procedure tAuth.ReplyPow(msg:tSMsg; data:boolean); + var r:tMemoryStream absolute msg.Stream; + var ptp:byte;{Proof TyPe} + var nonce:^tEccKey; + var pts: ^tPoWTimeStamp; + begin + if not data then exit; + ptp:=r.readbyte; {todo} + nonce:=r.ReadPtr(sizeof(tEccKey)); + pts:=r.ReadPtr(2); + PoWValid:=VerifyPoW(nonce^,RemotePub,pts^); + Conclusion; +end; +procedure tAuth.Timeout; + begin + error:=251; + Callback; + Ch.Close; +end; +procedure tAuth.Conclusion; + var ms:tMemoryStream; + begin + error:=0; + Ch.StreamInit(ms,2); + ms.WriteByte(byte(Valid)); + ms.WriteByte(byte(PowValid)); + Ch.Send(ms); + Callback; + ch.Close; +end; +procedure tAuth.Done; + begin + {called by chat} + FreeMem(@self,sizeof(self)); +end; + +type tServer=object + ch:^tChat; + pub:tEccKey; + procedure SendRep(msg:tSMsg; data:boolean); + procedure SendPow(msg:tSMsg; data:boolean); + procedure Last(msg:tSMsg; data:boolean); + procedure Close; +end; + +procedure AuthHandler(var ch:tChat; msg:tSMsg); + var srv:^tServer; + begin + msg.stream.skip(1); {initcode} + new(srv); + srv^.ch:=@ch; + ch.OnTimeout:=@srv^.Close; + srv^.SendRep(msg,true); + {reply with hash} + {wait ack} + {reply pow} + {wait reply} +end; + +procedure tServer.SendRep(msg:tSMsg; data:boolean); + var r:tMemoryStream absolute msg.Stream; + var ms:tMemoryStream; + var ver:byte; + var chal:^tEccKey; + var resp:tSha1Digest; + begin + ver:=r.ReadByte; {todo} + r.Read(pub,sizeof(pub)); + chal:=r.readptr(sizeof(tEccKey)); + CreateResponse(chal^,resp,pub); + ch^.StreamInit(ms,66); {todo} + ms.WriteByte(128); + ms.Write(PublicKey,sizeof(PublicKey)); + ms.Write(resp,sizeof(resp)); + ch^.SetTimeout(8000,0);{no reply expected} + ch^.send(ms); +end; + +procedure tServer.SendPow(msg:tSMsg; data:boolean); + var ms:tMemoryStream; + begin + if data then exit; + ch^.StreamInit(ms,66); {todo} + ms.WriteByte(1); + ms.Write(PublicPoW,sizeof(PublicPoW)); + ms.Write(PublicPoWTS,2); + ch^.SetTimeout(8000,2000); + ch^.send(ms); +end; + +procedure tServer.Last(msg:tSMsg; data:boolean); + var r:tMemoryStream absolute msg.Stream; + var Valid,ValidPoW:byte; + begin + if not data then exit; {unlikely} + Valid:=r.ReadByte; + ValidPoW:=r.ReadByte; + if (Valid>0)or(ValidPoW>0) then begin + writeln('CRAuth: Our auth failed on remote, reason pub=',Valid,' pow=',ValidPoW); + Writeln('CRAuth: remote ',string(ch^.remote),' ',string(pub)); + end; + Close; +end; +procedure tServer.Close; + begin + ch^.Close; + FreeMem(@self,sizeof(self)); +end; + +procedure tAuth.Cancel; + begin + error:=247; + Ch.Close; +end; + +BEGIN + SetChatHandler(opcode.crAuthReq,@AuthHandler); +END. diff --git a/ECC.pas b/ECC.pas new file mode 100644 index 0000000..97d6f42 --- /dev/null +++ b/ECC.pas @@ -0,0 +1,161 @@ +unit ECC; + +INTERFACE +uses Curve25519,Sha1; +type tEccKey=t256BitKey; +type tPoWTimeStamp=Word; + +var SecretKey:tEccKey; +var PublicKey:tEccKey; +var PublicPoW:t256BitKey; +var PublicPoWTS:Word; +var ZeroDigest:tSha1Digest; +const cDig3PowMask=%0010; +const cPoWValidWeeks=1; +const cWeekDays=5; +const cWeekEpoch=40179; +procedure CreateChallenge(out Challenge: tEccKey); +procedure CreateResponse(const Challenge: tEccKey; out Response: tSha1Digest; const srce:tEccKey); +function VerifyPoW(const proof:t256BitKey; const RemotePub:tEccKey; const stamp:Word):boolean; + +operator :=(k:tEccKey) s:string; + +IMPLEMENTATION +uses SysUtils,StrUtils,DateUtils; + +procedure CreateChallenge(out Challenge: tEccKey); + var i:byte; + begin + for i:=0 to 31 do challenge[i]:=Random(256); +end; + +procedure CreateResponse(const Challenge: tEccKey; out Response: tSha1Digest; const srce:tEccKey); + var Shared:tEccKey; + var shactx:tSha1Context; + begin + curve25519.curve25519(shared,secretkey,srce); + Sha1Init(shactx); + Sha1Update(shactx,challenge,sizeof(challenge)); + Sha1Update(shactx,shared,sizeof(shared)); + Sha1Final(shactx,Response); +end; + +var week:Word; + +function VerifyPoW(const proof:t256BitKey; const RemotePub:tEccKey; const stamp:Word):boolean; + var shactx:tSha1Context; + var digest:tSha1Digest; + begin + if abs(Week-BEtoN(stamp))<=cPoWValidWeeks then begin + Sha1Init(shactx); + Sha1Update(shactx,proof,sizeof(proof)); + Sha1Update(shactx,RemotePub,sizeof(RemotePub)); + Sha1Update(shactx,stamp,sizeof(stamp)); + Sha1Final(shactx,digest); + result:=(CompareByte(digest,ZeroDigest,3)=0)and((digest[3]and cDig3PoWMask)=0); + end else result:=false; +end; + +const cPoWFN='proof.dat'; +procedure PoWLoadFromFile; + var f:file of byte; + begin + assign(f,cPoWFN); + reset(f,1); + blockread(f,PublicPoW,sizeof(PublicPoW)); + blockread(f,PublicPoWTS,2); + close(f); +end; +procedure PoWGenerate; + var f:file of byte; + var i:byte; + var counter:LongWord; + var start:tDateTime; + begin + assign(f,cPoWFN); + rewrite(f,1); + write('ECC: Generating PoW, this may take a while...'); + PublicPowTS:=NtoBE(Week); + Start:=Now; counter:=0; + repeat + for i:=0 to 31 do PublicPoW[i]:=Random(256); + inc(counter); + until VerifyPoW(PublicPoW,PublicKey,PublicPoWTS); + writeln(' PoW found in ',(Now-start)*SecsPerDay:1:0,'s speed=',counter/((Now-start)*SecsPerDay):1:0,'h/s'); + blockwrite(f,PublicPoW,sizeof(PublicPoW)); + blockwrite(f,PublicPoWTS,sizeof(PublicPoWTS)); + close(f); +end; + +const cSeckeyFN='secret.dat'; +procedure LoadFromFile; + var f:file of tEccKey; + begin + assign(f,cSecKeyFN); + reset(f); + read(f,SecretKey); + close(f); +end; + +procedure SaveGenerated; + var f:file of tEccKey; + begin + assign(f,cSecKeyFN); + rewrite(f); + //fpchmod + write(f,SecretKey); + close(f); +end; + +procedure Generate; + {$IFDEF UNIX} + var f:file of tEccKey; + begin + assign(f,'/dev/urandom'); + reset(f); + read(f,SecretKey); + close(f); + {$ELSE} + begin + {$WARNING Not enough Random in windows license key} + {$ERROR This unit requires UNIX-compatile operating system} + {$ENDIF} +end; + +procedure DerivePublic; + begin + Curve25519.Curve25519(PublicKey,SecretKey,Curve25519.BasePoint); +end; + +operator :=(k:tEccKey) s:string; + begin + Setlength(s,64); + BinToHex(@k,@s[1],32); +end; + +BEGIN + FillChar(ZeroDigest,sizeof(ZeroDigest),0); + week:=trunc((Now-cWeekEpoch)/cWeekDays); + //writeln('ECC: Today is W',Week); + try LoadFromFile; + except on e:Exception do begin + writeln('ECC: '+e.message+' while loading secret key'); + Generate; + (*until (PublicKey[31]=0)and(PublicKey[30]=0);*) + SaveGenerated; + writeln('ECC: random secret key saved to '+cSecKeyFN); + end end; + DerivePublic; + writeln('ECC: pubkey is ',string(PublicKey)); + try + PoWLoadFromFile; + if not VerifyPoW(PublicPoW,PublicKey,PublicPoWTS) + then raise eXception.Create('invalid or expired proof'); + if (week-BEtoN(PublicPowTS))>=cPoWValidWeeks + then raise eXception.Create('proof about to expire'); + except on e:Exception do begin + writeln('ECC: '+e.message+' while loading proof'); + PoWGenerate; + end end; + writeln('ECC: ProofOfWork valid for W',BEtoN(PublicPowTS)); +END. diff --git a/ServerLoop.pas b/ServerLoop.pas index da9fcf5..3d7fe1a 100644 --- a/ServerLoop.pas +++ b/ServerLoop.pas @@ -408,6 +408,7 @@ end; var i:byte; var nb:array [0..0] of byte; BEGIN + writeln('ServerLoop: BrodNetD'); mNow:=0; umNow:=0; Randomize; @@ -424,4 +425,4 @@ BEGIN OnTerminate:=nil; Flush(OUTPUT); SetTextBuf(OUTPUT,nb); -END. \ No newline at end of file +END. diff --git a/brodnetd.pas b/brodnetd.pas index c450af8..ab22896 100644 --- a/brodnetd.pas +++ b/brodnetd.pas @@ -11,8 +11,9 @@ uses cthreads,ServerLoop ,dht ,dhtBootStatic ,dhtPersist + ,ECC ; BEGIN ServerLoop.Main; -END. \ No newline at end of file +END. diff --git a/curve25519.pp b/curve25519.pp new file mode 100644 index 0000000..eb35ca7 --- /dev/null +++ b/curve25519.pp @@ -0,0 +1,26 @@ +{$mode objfpc} +unit curve25519; +interface + +{ + curve25519_athlon.h version 20050915 + D. J. Bernstein + Public domain. + Converted with h2pas +} + +{$L curve25519.a} + +type t256BitKey=packed array [0..31] of byte; +const basepoint:t256BitKey=(9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); +procedure curve25519_athlon(_para1:pointer; _para2:pointer; _para3:pointer);cdecl;external; +procedure curve25519(out ek:t256BitKey; const e:t256BitKey; const k:t256BitKey); + +implementation + +procedure curve25519(out ek:t256BitKey; const e:t256BitKey; const k:t256BitKey); + begin + curve25519_athlon(@ek,@e,@k); +end; + +end. diff --git a/dht.pas b/dht.pas index 0836611..0ad9004 100644 --- a/dht.pas +++ b/dht.pas @@ -12,7 +12,7 @@ unit DHT; INTERFACE uses NetAddr,Store1; -type tPID=Store1.tFID; +type tPID=Store1.tFID; {reQ: ids can be shorter} type tPeerPub=object ID :tPID; Addr :tNetAddr; @@ -25,13 +25,16 @@ procedure GetNextNode(var ibkt:pointer; var ix:byte; out peer:tPeerPub); procedure InsertNode(const peer:tPeerPub); IMPLEMENTATION -uses ServerLoop,MemStream,opcode; +uses ServerLoop,Chat,MemStream,opcode,sha1,ecc,CRAuth; type tPeer=object(tPeerPub) ReqDelta:word; LastMsgFrom, LastResFrom :tMTime; + Ban:boolean; + Verify: ^CRAuth.tAuth; {nil when verified} + procedure VerifyCallback; end; tPeer_ptr=^tPeer; tBucket_ptr=^tBucket; @@ -42,7 +45,7 @@ type ModifyTime: tMTime; //ll: ^tll; desperate:word; - next: tBucket_ptr; + next: ^tBucket; function MatchPrefix(const tp:tFID):boolean; procedure Refresh; end; @@ -140,63 +143,85 @@ procedure SplitBucket(ob:tBucket_ptr); Shedule(2000,@nb^.Refresh); end; -procedure UpdateNode(const id:tFID; const addr:tNetAddr); - var bkt:^tBucket; +procedure VerifyInit(b:tBucket_ptr; i:byte); forward; + +function CheckNode(const id: tPID; const addr: tNetAddr): boolean; + {return false if node is banned} + {update or insert} + {initiate auth on insert and also on id conflict} + {replace only old, banned and free slots} + var b:^tBucket; var i,fr:byte; + var dup:boolean; label again; begin if id=MyID then exit; + CheckNode:=false; again: - bkt:=FindBucket(id); - if not assigned(bkt) then begin - New(Table); //todo - bkt:=Table; - bkt^.Prefix:=MyID; - bkt^.Depth:=0; - bkt^.ModifyTime:=mNow; - bkt^.next:=nil; - bkt^.desperate:=3; - for i:=1 to high(bkt^.peer) do bkt^.peer[i].addr.Clear; - Shedule(2000,@bkt^.Refresh); + b:=FindBucket(id); + fr:=0; dup:=false; + if not assigned(b) then begin + New(Table); b:=Table; + b^.Prefix:=MyID; + b^.Depth:=0; + b^.ModifyTime:=mNow; + b^.next:=nil; + b^.desperate:=3; + for i:=1 to high(b^.peer) do b^.peer[i].addr.Clear; + for i:=1 to high(b^.peer) do b^.peer[i].ban:=false; + Shedule(2000,@b^.Refresh); + end; + for i:=1 to high(b^.peer) do begin {check for ban and dup} + if (b^.peer[i].Ban) and (b^.peer[i].Addr=addr) then exit; + if (fr=0)and(b^.peer[i].Addr.isNil) then fr:=i; + if (b^.peer[i].ReqDelta<2)and(b^.peer[i].ID=id) then begin + fr:=i;dup:=true;break + end; + end; + if fr=0 then for i:=1 to high(b^.peer) do begin {check for old/banned} + if (b^.peer[i].ReqDelta>=2) then fr:=i; + if (fr=0) and (b^.peer[i].Ban) then fr:=i; end; - fr:=0; - for i:=1 to high(bkt^.peer) - do if (fr=0)and bkt^.peer[i].addr.isNil then fr:=i - else if (bkt^.peer[i].ReqDelta<2) then begin - {found node in the bucket} - if (bkt^.peer[i].id=id) then begin - bkt^.peer[i].LastMsgFrom:=mNow; - bkt^.peer[i].ReqDelta:=0; - exit - end; - if bkt^.peer[i].addr=addr then exit; - end - else if (fr=0)or (bkt^.peer[i].id=id) - then fr:=i; if fr=0 then begin - if bkt^.MatchPrefix(MyID) - then begin - SplitBucket(bkt); + if b^.MatchPrefix(MyID) then begin + SplitBucket(b); goto again; - end; {the bucket is full!} - {drop new node and hope nodes in the bucket are good} + end (*else bucket is full and not splittable*) end else begin - writeln('DHT: AddNode ',string(id),string(addr),' to ',string(bkt^.prefix),'/',bkt^.depth,'#',fr); - bkt^.ModifyTime:=mNow; - bkt^.peer[fr].ID:=ID; - bkt^.peer[fr].Addr:=Addr; - bkt^.peer[fr].LastMsgFrom:=mNow; - bkt^.peer[fr].LastResFrom:=0; - bkt^.peer[fr].ReqDelta:=0; - end; + if dup then begin + if (b^.peer[i].addr=addr) then begin + b^.peer[i].LastMsgFrom:=mNow; + b^.peer[i].ReqDelta:=0; + CheckNode:=true; + end else begin + {todo conflict} + VerifyInit(b,fr); + end + end else begin + {add node here} + if (not b^.peer[fr].Addr.isNil)and assigned(b^.peer[fr].Verify) + then b^.peer[fr].Verify^.Cancel; + writeln('DHT: AddNode ',string(id),string(addr),' to ',string(b^.prefix),'/',b^.depth,'#',fr); + b^.ModifyTime:=mNow; + b^.peer[fr].ID:=ID; + b^.peer[fr].Addr:=Addr; + b^.peer[fr].LastMsgFrom:=mNow; + b^.peer[fr].LastResFrom:=0; + b^.peer[fr].ReqDelta:=0; + b^.peer[fr].ban:=false; + b^.peer[fr].Verify:=nil; + VerifyInit(b,fr); + CheckNode:=true; + end + end end; procedure InsertNode(const peer:tPeerPub); begin - UpdateNode(peer.id,peer.addr); + CheckNode(peer.id,peer.addr); end; -procedure GetNextNode(var ibkt:tBucket_ptr; var ix:byte; const id:tPID; maxrd:word); +procedure GetNextNode(var ibkt:tBucket_ptr; var ix:byte; const id:tPID; maxrd:word; bans:boolean); var bkt:^tBucket; begin if not assigned(ibkt) then exit; @@ -208,14 +233,16 @@ procedure GetNextNode(var ibkt:tBucket_ptr; var ix:byte; const id:tPID; maxrd:wo bkt:=bkt^.next; if not assigned(bkt) then break; end; - until (not bkt^.peer[ix].Addr.isNil)and(bkt^.peer[ix].ReqDelta self'); @@ -364,7 +391,7 @@ procedure tBucket.Refresh; {1 of 10 times try to contact dead nodes in attempt to recover from network split} stich:=Random(cStichRar)=0; for i:=1 to high(tBucket.peer) - do if (not peer[i].Addr.isNil) then begin + do if (not peer[i].Addr.isNil) and (not peer[i].Ban) then begin if peer[i].ReqDelta>0 then begin if (peer[i].ReqDelta<=3)xor stich then begin {this will get rid of half-dead nodes} @@ -384,10 +411,10 @@ procedure tBucket.Refresh; {try to recover bucket full of bad nodes} if (ol=0){and(not rtr)} then begin rv:=0; rvb:=@self; - GetNextNode(rvb,rv,prefix,desperate); + GetNextNode(rvb,rv,prefix,desperate,false); if not assigned(rvb) then begin rv:=0; rvb:=Table; {in extreme cases, try the whole table} - GetNextNode(rvb,rv,prefix,desperate); + GetNextNode(rvb,rv,prefix,desperate,true); end; if assigned(rvb) then begin writeln('DHT: Recover ',string(prefix),'/',depth,' try ',copy(string(rvb^.peer[rv].id),1,6),string(rvb^.peer[rv].addr)); @@ -401,14 +428,30 @@ procedure tBucket.Refresh; Shedule(wait,@Refresh); end; - {to bootstrap: ping address to get ID and insert to bucket/il ping may get lost: separate bootstrap unit :) now jut Ass-U-Me wont get lost} +procedure VerifyInit(b:tBucket_ptr; i:byte); + begin + with b^.peer[i] do begin + if assigned(Verify) then exit; + new(Verify); + Verify^.Callback:=@VerifyCallback; + Verify^.Init(Addr); + end +end; +procedure tPeer.VerifyCallback; + begin + if not( Verify^.Valid and Verify^.PowValid ) then begin + Ban:=true; + end else Ban:=false; {just in case} + Verify:=nil; {it will free itelf} +end; + BEGIN SetMsgHandler(opcode.dhtRequest,@recvRequest); SetMsgHandler(opcode.dhtSelect,@recvSelect); SetMsgHandler(opcode.dhtReqAck,@recvReqAck); SetMsgHandler(opcode.dhtWazzup,@recvWazzup); -END. \ No newline at end of file +END. diff --git a/dhtPersist.pas b/dhtPersist.pas index ff990c1..cf5f6e7 100644 --- a/dhtPersist.pas +++ b/dhtPersist.pas @@ -8,7 +8,7 @@ unit dhtPersist; INTERFACE IMPLEMENTATION -uses NetAddr,ServerLoop,DHT,SysUtils,Store1; +uses NetAddr,ServerLoop,DHT,SysUtils,Store1,ECC; const ndfn='nodes.dat'; const idfn='idhash.txt'; @@ -68,11 +68,19 @@ procedure LoadID; writeln('dhtPersist: can not open id file ',idfn); exit end; readln(nd,line); - writeln('dhtPersist: set ID to ',line); + writeln('dhtPersist: set ID to ',line,' from '+idfn); dht.MyID:=line; close(nd); end; +procedure LoadIDFromECC; + var id:dht.tPID; + begin + Move(ECC.PublicKey,id,20); + writeln('dhtPersist: set ID to ',string(id),' from ECC'); + dht.MyID:=id; +end; + procedure LoadIDFromArgs; var oi:word; const opt='-id'; @@ -80,7 +88,7 @@ procedure LoadIDFromArgs; oi:=OptIndex(opt); if oi>0 then begin assert(OptParamCount(oi)=1,opt+'(pid:sha1)'); - writeln('dhtPersist: set ID to '+paramstr(oi+1)); + writeln('dhtPersist: set ID to '+paramstr(oi+1),' from '+opt); MyID:=tPID(paramstr(oi+1)); end; end; @@ -93,7 +101,7 @@ procedure LoadIDRandom; if oi>0 then begin assert(OptParamCount(oi)=0,opt+'()'); for b:=0 to 19 do MyID[b]:=Random(256); - writeln('dhtPersist: set ID to ',string(MyID)); + writeln('dhtPersist: set ID to ',string(MyID),' random'); end; end; @@ -122,7 +130,8 @@ end; procedure t.Init; begin - LoadID; + (*LoadID*); + LoadIDFromECC; LoadIDFromArgs; LoadIDRandom; Shedule(2000,@doSoon); @@ -136,4 +145,4 @@ end; var o:t; BEGIN o.init; -END. \ No newline at end of file +END. diff --git a/opcode.pas b/opcode.pas index 3210e1b..f67aaf0 100644 --- a/opcode.pas +++ b/opcode.pas @@ -15,6 +15,7 @@ const {dgram opcode} dhtWazzup=13; const {chat init} upFileServer=2; + crAuthReq=3; const {FS opcodes} {c}upOPEN=7; {s}upINFO=8; @@ -37,4 +38,4 @@ const {FS opcodes} upErrTroll=99; IMPLEMENTATION -END. \ No newline at end of file +END. -- 2.11.4.GIT