-
Notifications
You must be signed in to change notification settings - Fork 470
/
DemoToken.java
206 lines (172 loc) · 6.71 KB
/
DemoToken.java
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
package org.jgroups.auth;
import org.jgroups.*;
import org.jgroups.annotations.Property;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.protocols.AUTH;
import org.jgroups.util.Util;
import java.io.DataInput;
import java.io.DataOutput;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* AuthToken implementation which shows how to send messages back and forth in order to perform authentication. Could
* be used as a template for a challenge-response based AuthToken impl.
* @author Bela Ban
* @since 3.3
*/
public class DemoToken extends AuthToken implements AUTH.UpHandler {
protected static final short ID=1555; // the ID to fetch a DemoHeader from a message
@Property(description="How long to wait (in ms) for a response to a challenge")
protected long block_time=5000;
// Used to correlate pending challenge requests sent with responses received
protected final Map<Address,Entry> pending_requests=new HashMap<>();
static {
ClassConfigurator.add((short)1555, DemoHeader.class);
}
public String getName() {return "org.jgroups.auth.DemoToken";}
public void init() {auth.register(this);}
public boolean authenticate(AuthToken token, Message msg) {
Address sender=msg.getSrc();
// 1. send a challenge to the sender
Message challenge=new Message(sender).setFlag(Message.Flag.OOB);
byte[] buf=generateRandomBytes();
DemoHeader hdr=new DemoHeader(buf);
challenge.putHeader(ID, hdr);
Entry entry=new Entry(buf);
pending_requests.put(sender, entry); // here we'd have to check if a latch already exists...
if(log.isTraceEnabled())
log.trace(auth.getAddress() + ": sending challenge to " + sender);
auth.getDownProtocol().down(challenge);
try {
entry.latch.await(block_time, TimeUnit.MILLISECONDS);
pending_requests.remove(sender);
boolean result=entry.hash > 0 && entry.hash == hash(encrypt(entry.challenge));
if(log.isTraceEnabled())
log.trace(auth.getAddress() + ": authentication of " + sender + ": " + result + " (hash=" + entry.hash + ")");
return result;
}
catch(InterruptedException e) {
return false;
}
}
public void writeTo(DataOutput out) throws Exception {}
public void readFrom(DataInput in) throws Exception {}
public int size() {return 0;}
public boolean handleUpMessage(Message msg) {
DemoHeader hdr=msg.getHeader(ID);
if(hdr == null)
return true;
switch(hdr.type) {
case DemoHeader.CHALLENGE:
if(log.isTraceEnabled())
log.trace(auth.getAddress() + ": received CHALLENGE from " + msg.getSrc());
long hash=hash(encrypt(hdr.payload));
Message response=new Message(msg.getSrc()).setFlag(Message.Flag.OOB);
response.putHeader(ID, new DemoHeader(hash));
if(log.isTraceEnabled())
log.trace(auth.getAddress() + ": sending RESPONSE to " + msg.getSrc());
auth.getDownProtocol().down(response);
break;
case DemoHeader.RESPONSE:
if(log.isTraceEnabled())
log.trace(auth.getAddress() + ": received RESPONSE from " + msg.getSrc());
Entry entry=pending_requests.get(msg.getSrc());
if(entry == null) {
// error message
break;
}
entry.setResponse(hdr.hash);
break;
}
return false; // don't pass up
}
protected static byte[] generateRandomBytes() {
byte[] retval=new byte[10]; // here we'd have to generate a buffer with random contents
for(int i=0; i < retval.length; i++)
retval[i]=(byte)Util.random(Byte.MAX_VALUE);
return retval;
}
protected static byte[] encrypt(byte[] buf) {
return buf; // here real encryption would have to be provided...
}
// A real hash would have to be provided here...
protected static long hash(byte[] buf) {
long retval=0;
for(int i=0; i < buf.length; i++)
retval+=buf[i];
return retval;
}
protected static class Entry {
protected final CountDownLatch latch=new CountDownLatch(1);
protected final byte[] challenge;
protected long hash; // set by response
public Entry(byte[] challenge) {
this.challenge=challenge;
}
public void setResponse(long hash) {
this.hash=hash;
latch.countDown();
}
}
public static class DemoHeader extends Header {
protected static final byte CHALLENGE = 1;
protected static final byte RESPONSE = 2;
protected byte type;
protected byte[] payload; // CHALLENGE
protected long hash; // RESPONSE
public DemoHeader() {
}
public DemoHeader(byte[] payload) {
this.type=CHALLENGE;
this.payload=payload;
}
public DemoHeader(long hash) {
type=RESPONSE;
this.hash=hash;
}
public short getMagicId() {return 1555;}
public Supplier<? extends Header> create() {
return DemoHeader::new;
}
public void writeTo(DataOutput out) throws Exception {
out.writeByte(type);
switch(type) {
case CHALLENGE:
Util.writeByteBuffer(payload, out);
break;
case RESPONSE:
out.writeLong(hash);
break;
}
}
public void readFrom(DataInput in) throws Exception {
type=in.readByte();
switch(type) {
case CHALLENGE:
payload=Util.readByteBuffer(in);
break;
case RESPONSE:
hash=in.readLong();
break;
}
}
public int serializedSize() {
int retval=Global.BYTE_SIZE; // type
switch(type) {
case CHALLENGE:
retval+=Util.size(payload);
break;
case RESPONSE:
retval+=Global.LONG_SIZE;
break;
}
return retval;
}
public String toString() {
return type == CHALLENGE? "CHALLENGE" : "RESPONSE" + ", payload=" + (payload != null? payload.length : 0) + " bytes";
}
}
}