susi-qemu 0.0.2 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/susi +9 -4
- data/lib/disk.rb +7 -5
- data/lib/novnc/core/base64.js +104 -0
- data/lib/novnc/core/crypto/aes.js +178 -0
- data/lib/novnc/core/crypto/bigint.js +34 -0
- data/lib/novnc/core/crypto/crypto.js +90 -0
- data/lib/novnc/core/crypto/des.js +330 -0
- data/lib/novnc/core/crypto/dh.js +55 -0
- data/lib/novnc/core/crypto/md5.js +82 -0
- data/lib/novnc/core/crypto/rsa.js +132 -0
- data/lib/novnc/core/decoders/copyrect.js +27 -0
- data/lib/novnc/core/decoders/h264.js +321 -0
- data/lib/novnc/core/decoders/hextile.js +181 -0
- data/lib/novnc/core/decoders/jpeg.js +146 -0
- data/lib/novnc/core/decoders/raw.js +59 -0
- data/lib/novnc/core/decoders/rre.js +44 -0
- data/lib/novnc/core/decoders/tight.js +393 -0
- data/lib/novnc/core/decoders/tightpng.js +27 -0
- data/lib/novnc/core/decoders/zlib.js +51 -0
- data/lib/novnc/core/decoders/zrle.js +185 -0
- data/lib/novnc/core/deflator.js +84 -0
- data/lib/novnc/core/display.js +575 -0
- data/lib/novnc/core/encodings.js +53 -0
- data/lib/novnc/core/inflator.js +65 -0
- data/lib/novnc/core/input/domkeytable.js +311 -0
- data/lib/novnc/core/input/fixedkeys.js +129 -0
- data/lib/novnc/core/input/gesturehandler.js +567 -0
- data/lib/novnc/core/input/keyboard.js +294 -0
- data/lib/novnc/core/input/keysym.js +616 -0
- data/lib/novnc/core/input/keysymdef.js +688 -0
- data/lib/novnc/core/input/util.js +191 -0
- data/lib/novnc/core/input/vkeys.js +116 -0
- data/lib/novnc/core/input/xtscancodes.js +173 -0
- data/lib/novnc/core/ra2.js +312 -0
- data/lib/novnc/core/rfb.js +3257 -0
- data/lib/novnc/core/util/browser.js +172 -0
- data/lib/novnc/core/util/cursor.js +249 -0
- data/lib/novnc/core/util/element.js +32 -0
- data/lib/novnc/core/util/events.js +138 -0
- data/lib/novnc/core/util/eventtarget.js +35 -0
- data/lib/novnc/core/util/int.js +15 -0
- data/lib/novnc/core/util/logging.js +56 -0
- data/lib/novnc/core/util/strings.js +28 -0
- data/lib/novnc/core/websock.js +365 -0
- data/lib/novnc/screen.html +21 -0
- data/lib/novnc/vendor/pako/lib/utils/common.js +45 -0
- data/lib/novnc/vendor/pako/lib/zlib/adler32.js +27 -0
- data/lib/novnc/vendor/pako/lib/zlib/constants.js +47 -0
- data/lib/novnc/vendor/pako/lib/zlib/crc32.js +36 -0
- data/lib/novnc/vendor/pako/lib/zlib/deflate.js +1846 -0
- data/lib/novnc/vendor/pako/lib/zlib/gzheader.js +35 -0
- data/lib/novnc/vendor/pako/lib/zlib/inffast.js +324 -0
- data/lib/novnc/vendor/pako/lib/zlib/inflate.js +1527 -0
- data/lib/novnc/vendor/pako/lib/zlib/inftrees.js +322 -0
- data/lib/novnc/vendor/pako/lib/zlib/messages.js +11 -0
- data/lib/novnc/vendor/pako/lib/zlib/trees.js +1195 -0
- data/lib/novnc/vendor/pako/lib/zlib/zstream.js +24 -0
- data/lib/output.rb +11 -0
- data/lib/qmp.rb +6 -0
- data/lib/ssh.rb +3 -1
- data/lib/susi.rb +7 -6
- data/lib/version.rb +1 -1
- data/lib/vm.rb +44 -26
- data/lib/vnc.rb +34 -31
- metadata +57 -1
@@ -0,0 +1,312 @@
|
|
1
|
+
import { encodeUTF8 } from './util/strings.js';
|
2
|
+
import EventTargetMixin from './util/eventtarget.js';
|
3
|
+
import legacyCrypto from './crypto/crypto.js';
|
4
|
+
|
5
|
+
class RA2Cipher {
|
6
|
+
constructor() {
|
7
|
+
this._cipher = null;
|
8
|
+
this._counter = new Uint8Array(16);
|
9
|
+
}
|
10
|
+
|
11
|
+
async setKey(key) {
|
12
|
+
this._cipher = await legacyCrypto.importKey(
|
13
|
+
"raw", key, { name: "AES-EAX" }, false, ["encrypt, decrypt"]);
|
14
|
+
}
|
15
|
+
|
16
|
+
async makeMessage(message) {
|
17
|
+
const ad = new Uint8Array([(message.length & 0xff00) >>> 8, message.length & 0xff]);
|
18
|
+
const encrypted = await legacyCrypto.encrypt({
|
19
|
+
name: "AES-EAX",
|
20
|
+
iv: this._counter,
|
21
|
+
additionalData: ad,
|
22
|
+
}, this._cipher, message);
|
23
|
+
for (let i = 0; i < 16 && this._counter[i]++ === 255; i++);
|
24
|
+
const res = new Uint8Array(message.length + 2 + 16);
|
25
|
+
res.set(ad);
|
26
|
+
res.set(encrypted, 2);
|
27
|
+
return res;
|
28
|
+
}
|
29
|
+
|
30
|
+
async receiveMessage(length, encrypted) {
|
31
|
+
const ad = new Uint8Array([(length & 0xff00) >>> 8, length & 0xff]);
|
32
|
+
const res = await legacyCrypto.decrypt({
|
33
|
+
name: "AES-EAX",
|
34
|
+
iv: this._counter,
|
35
|
+
additionalData: ad,
|
36
|
+
}, this._cipher, encrypted);
|
37
|
+
for (let i = 0; i < 16 && this._counter[i]++ === 255; i++);
|
38
|
+
return res;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
export default class RSAAESAuthenticationState extends EventTargetMixin {
|
43
|
+
constructor(sock, getCredentials) {
|
44
|
+
super();
|
45
|
+
this._hasStarted = false;
|
46
|
+
this._checkSock = null;
|
47
|
+
this._checkCredentials = null;
|
48
|
+
this._approveServerResolve = null;
|
49
|
+
this._sockReject = null;
|
50
|
+
this._credentialsReject = null;
|
51
|
+
this._approveServerReject = null;
|
52
|
+
this._sock = sock;
|
53
|
+
this._getCredentials = getCredentials;
|
54
|
+
}
|
55
|
+
|
56
|
+
_waitSockAsync(len) {
|
57
|
+
return new Promise((resolve, reject) => {
|
58
|
+
const hasData = () => !this._sock.rQwait('RA2', len);
|
59
|
+
if (hasData()) {
|
60
|
+
resolve();
|
61
|
+
} else {
|
62
|
+
this._checkSock = () => {
|
63
|
+
if (hasData()) {
|
64
|
+
resolve();
|
65
|
+
this._checkSock = null;
|
66
|
+
this._sockReject = null;
|
67
|
+
}
|
68
|
+
};
|
69
|
+
this._sockReject = reject;
|
70
|
+
}
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
_waitApproveKeyAsync() {
|
75
|
+
return new Promise((resolve, reject) => {
|
76
|
+
this._approveServerResolve = resolve;
|
77
|
+
this._approveServerReject = reject;
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
81
|
+
_waitCredentialsAsync(subtype) {
|
82
|
+
const hasCredentials = () => {
|
83
|
+
if (subtype === 1 && this._getCredentials().username !== undefined &&
|
84
|
+
this._getCredentials().password !== undefined) {
|
85
|
+
return true;
|
86
|
+
} else if (subtype === 2 && this._getCredentials().password !== undefined) {
|
87
|
+
return true;
|
88
|
+
}
|
89
|
+
return false;
|
90
|
+
};
|
91
|
+
return new Promise((resolve, reject) => {
|
92
|
+
if (hasCredentials()) {
|
93
|
+
resolve();
|
94
|
+
} else {
|
95
|
+
this._checkCredentials = () => {
|
96
|
+
if (hasCredentials()) {
|
97
|
+
resolve();
|
98
|
+
this._checkCredentials = null;
|
99
|
+
this._credentialsReject = null;
|
100
|
+
}
|
101
|
+
};
|
102
|
+
this._credentialsReject = reject;
|
103
|
+
}
|
104
|
+
});
|
105
|
+
}
|
106
|
+
|
107
|
+
checkInternalEvents() {
|
108
|
+
if (this._checkSock !== null) {
|
109
|
+
this._checkSock();
|
110
|
+
}
|
111
|
+
if (this._checkCredentials !== null) {
|
112
|
+
this._checkCredentials();
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
approveServer() {
|
117
|
+
if (this._approveServerResolve !== null) {
|
118
|
+
this._approveServerResolve();
|
119
|
+
this._approveServerResolve = null;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
disconnect() {
|
124
|
+
if (this._sockReject !== null) {
|
125
|
+
this._sockReject(new Error("disconnect normally"));
|
126
|
+
this._sockReject = null;
|
127
|
+
}
|
128
|
+
if (this._credentialsReject !== null) {
|
129
|
+
this._credentialsReject(new Error("disconnect normally"));
|
130
|
+
this._credentialsReject = null;
|
131
|
+
}
|
132
|
+
if (this._approveServerReject !== null) {
|
133
|
+
this._approveServerReject(new Error("disconnect normally"));
|
134
|
+
this._approveServerReject = null;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
async negotiateRA2neAuthAsync() {
|
139
|
+
this._hasStarted = true;
|
140
|
+
// 1: Receive server public key
|
141
|
+
await this._waitSockAsync(4);
|
142
|
+
const serverKeyLengthBuffer = this._sock.rQpeekBytes(4);
|
143
|
+
const serverKeyLength = this._sock.rQshift32();
|
144
|
+
if (serverKeyLength < 1024) {
|
145
|
+
throw new Error("RA2: server public key is too short: " + serverKeyLength);
|
146
|
+
} else if (serverKeyLength > 8192) {
|
147
|
+
throw new Error("RA2: server public key is too long: " + serverKeyLength);
|
148
|
+
}
|
149
|
+
const serverKeyBytes = Math.ceil(serverKeyLength / 8);
|
150
|
+
await this._waitSockAsync(serverKeyBytes * 2);
|
151
|
+
const serverN = this._sock.rQshiftBytes(serverKeyBytes);
|
152
|
+
const serverE = this._sock.rQshiftBytes(serverKeyBytes);
|
153
|
+
const serverRSACipher = await legacyCrypto.importKey(
|
154
|
+
"raw", { n: serverN, e: serverE }, { name: "RSA-PKCS1-v1_5" }, false, ["encrypt"]);
|
155
|
+
const serverPublickey = new Uint8Array(4 + serverKeyBytes * 2);
|
156
|
+
serverPublickey.set(serverKeyLengthBuffer);
|
157
|
+
serverPublickey.set(serverN, 4);
|
158
|
+
serverPublickey.set(serverE, 4 + serverKeyBytes);
|
159
|
+
|
160
|
+
// verify server public key
|
161
|
+
let approveKey = this._waitApproveKeyAsync();
|
162
|
+
this.dispatchEvent(new CustomEvent("serververification", {
|
163
|
+
detail: { type: "RSA", publickey: serverPublickey }
|
164
|
+
}));
|
165
|
+
await approveKey;
|
166
|
+
|
167
|
+
// 2: Send client public key
|
168
|
+
const clientKeyLength = 2048;
|
169
|
+
const clientKeyBytes = Math.ceil(clientKeyLength / 8);
|
170
|
+
const clientRSACipher = (await legacyCrypto.generateKey({
|
171
|
+
name: "RSA-PKCS1-v1_5",
|
172
|
+
modulusLength: clientKeyLength,
|
173
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
174
|
+
}, true, ["encrypt"])).privateKey;
|
175
|
+
const clientExportedRSAKey = await legacyCrypto.exportKey("raw", clientRSACipher);
|
176
|
+
const clientN = clientExportedRSAKey.n;
|
177
|
+
const clientE = clientExportedRSAKey.e;
|
178
|
+
const clientPublicKey = new Uint8Array(4 + clientKeyBytes * 2);
|
179
|
+
clientPublicKey[0] = (clientKeyLength & 0xff000000) >>> 24;
|
180
|
+
clientPublicKey[1] = (clientKeyLength & 0xff0000) >>> 16;
|
181
|
+
clientPublicKey[2] = (clientKeyLength & 0xff00) >>> 8;
|
182
|
+
clientPublicKey[3] = clientKeyLength & 0xff;
|
183
|
+
clientPublicKey.set(clientN, 4);
|
184
|
+
clientPublicKey.set(clientE, 4 + clientKeyBytes);
|
185
|
+
this._sock.sQpushBytes(clientPublicKey);
|
186
|
+
this._sock.flush();
|
187
|
+
|
188
|
+
// 3: Send client random
|
189
|
+
const clientRandom = new Uint8Array(16);
|
190
|
+
window.crypto.getRandomValues(clientRandom);
|
191
|
+
const clientEncryptedRandom = await legacyCrypto.encrypt(
|
192
|
+
{ name: "RSA-PKCS1-v1_5" }, serverRSACipher, clientRandom);
|
193
|
+
const clientRandomMessage = new Uint8Array(2 + serverKeyBytes);
|
194
|
+
clientRandomMessage[0] = (serverKeyBytes & 0xff00) >>> 8;
|
195
|
+
clientRandomMessage[1] = serverKeyBytes & 0xff;
|
196
|
+
clientRandomMessage.set(clientEncryptedRandom, 2);
|
197
|
+
this._sock.sQpushBytes(clientRandomMessage);
|
198
|
+
this._sock.flush();
|
199
|
+
|
200
|
+
// 4: Receive server random
|
201
|
+
await this._waitSockAsync(2);
|
202
|
+
if (this._sock.rQshift16() !== clientKeyBytes) {
|
203
|
+
throw new Error("RA2: wrong encrypted message length");
|
204
|
+
}
|
205
|
+
const serverEncryptedRandom = this._sock.rQshiftBytes(clientKeyBytes);
|
206
|
+
const serverRandom = await legacyCrypto.decrypt(
|
207
|
+
{ name: "RSA-PKCS1-v1_5" }, clientRSACipher, serverEncryptedRandom);
|
208
|
+
if (serverRandom === null || serverRandom.length !== 16) {
|
209
|
+
throw new Error("RA2: corrupted server encrypted random");
|
210
|
+
}
|
211
|
+
|
212
|
+
// 5: Compute session keys and set ciphers
|
213
|
+
let clientSessionKey = new Uint8Array(32);
|
214
|
+
let serverSessionKey = new Uint8Array(32);
|
215
|
+
clientSessionKey.set(serverRandom);
|
216
|
+
clientSessionKey.set(clientRandom, 16);
|
217
|
+
serverSessionKey.set(clientRandom);
|
218
|
+
serverSessionKey.set(serverRandom, 16);
|
219
|
+
clientSessionKey = await window.crypto.subtle.digest("SHA-1", clientSessionKey);
|
220
|
+
clientSessionKey = new Uint8Array(clientSessionKey).slice(0, 16);
|
221
|
+
serverSessionKey = await window.crypto.subtle.digest("SHA-1", serverSessionKey);
|
222
|
+
serverSessionKey = new Uint8Array(serverSessionKey).slice(0, 16);
|
223
|
+
const clientCipher = new RA2Cipher();
|
224
|
+
await clientCipher.setKey(clientSessionKey);
|
225
|
+
const serverCipher = new RA2Cipher();
|
226
|
+
await serverCipher.setKey(serverSessionKey);
|
227
|
+
|
228
|
+
// 6: Compute and exchange hashes
|
229
|
+
let serverHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2);
|
230
|
+
let clientHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2);
|
231
|
+
serverHash.set(serverPublickey);
|
232
|
+
serverHash.set(clientPublicKey, 4 + serverKeyBytes * 2);
|
233
|
+
clientHash.set(clientPublicKey);
|
234
|
+
clientHash.set(serverPublickey, 4 + clientKeyBytes * 2);
|
235
|
+
serverHash = await window.crypto.subtle.digest("SHA-1", serverHash);
|
236
|
+
clientHash = await window.crypto.subtle.digest("SHA-1", clientHash);
|
237
|
+
serverHash = new Uint8Array(serverHash);
|
238
|
+
clientHash = new Uint8Array(clientHash);
|
239
|
+
this._sock.sQpushBytes(await clientCipher.makeMessage(clientHash));
|
240
|
+
this._sock.flush();
|
241
|
+
await this._waitSockAsync(2 + 20 + 16);
|
242
|
+
if (this._sock.rQshift16() !== 20) {
|
243
|
+
throw new Error("RA2: wrong server hash");
|
244
|
+
}
|
245
|
+
const serverHashReceived = await serverCipher.receiveMessage(
|
246
|
+
20, this._sock.rQshiftBytes(20 + 16));
|
247
|
+
if (serverHashReceived === null) {
|
248
|
+
throw new Error("RA2: failed to authenticate the message");
|
249
|
+
}
|
250
|
+
for (let i = 0; i < 20; i++) {
|
251
|
+
if (serverHashReceived[i] !== serverHash[i]) {
|
252
|
+
throw new Error("RA2: wrong server hash");
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
// 7: Receive subtype
|
257
|
+
await this._waitSockAsync(2 + 1 + 16);
|
258
|
+
if (this._sock.rQshift16() !== 1) {
|
259
|
+
throw new Error("RA2: wrong subtype");
|
260
|
+
}
|
261
|
+
let subtype = (await serverCipher.receiveMessage(
|
262
|
+
1, this._sock.rQshiftBytes(1 + 16)));
|
263
|
+
if (subtype === null) {
|
264
|
+
throw new Error("RA2: failed to authenticate the message");
|
265
|
+
}
|
266
|
+
subtype = subtype[0];
|
267
|
+
let waitCredentials = this._waitCredentialsAsync(subtype);
|
268
|
+
if (subtype === 1) {
|
269
|
+
if (this._getCredentials().username === undefined ||
|
270
|
+
this._getCredentials().password === undefined) {
|
271
|
+
this.dispatchEvent(new CustomEvent(
|
272
|
+
"credentialsrequired",
|
273
|
+
{ detail: { types: ["username", "password"] } }));
|
274
|
+
}
|
275
|
+
} else if (subtype === 2) {
|
276
|
+
if (this._getCredentials().password === undefined) {
|
277
|
+
this.dispatchEvent(new CustomEvent(
|
278
|
+
"credentialsrequired",
|
279
|
+
{ detail: { types: ["password"] } }));
|
280
|
+
}
|
281
|
+
} else {
|
282
|
+
throw new Error("RA2: wrong subtype");
|
283
|
+
}
|
284
|
+
await waitCredentials;
|
285
|
+
let username;
|
286
|
+
if (subtype === 1) {
|
287
|
+
username = encodeUTF8(this._getCredentials().username).slice(0, 255);
|
288
|
+
} else {
|
289
|
+
username = "";
|
290
|
+
}
|
291
|
+
const password = encodeUTF8(this._getCredentials().password).slice(0, 255);
|
292
|
+
const credentials = new Uint8Array(username.length + password.length + 2);
|
293
|
+
credentials[0] = username.length;
|
294
|
+
credentials[username.length + 1] = password.length;
|
295
|
+
for (let i = 0; i < username.length; i++) {
|
296
|
+
credentials[i + 1] = username.charCodeAt(i);
|
297
|
+
}
|
298
|
+
for (let i = 0; i < password.length; i++) {
|
299
|
+
credentials[username.length + 2 + i] = password.charCodeAt(i);
|
300
|
+
}
|
301
|
+
this._sock.sQpushBytes(await clientCipher.makeMessage(credentials));
|
302
|
+
this._sock.flush();
|
303
|
+
}
|
304
|
+
|
305
|
+
get hasStarted() {
|
306
|
+
return this._hasStarted;
|
307
|
+
}
|
308
|
+
|
309
|
+
set hasStarted(s) {
|
310
|
+
this._hasStarted = s;
|
311
|
+
}
|
312
|
+
}
|