|
|
|
|
@ -3,64 +3,47 @@ RocketChat.OTR.Room = class { |
|
|
|
|
this.userId = userId; |
|
|
|
|
this.roomId = roomId; |
|
|
|
|
this.peerId = roomId.replace(userId, ''); |
|
|
|
|
this.established = new ReactiveVar(false); |
|
|
|
|
this.establishing = new ReactiveVar(false); |
|
|
|
|
this.rsaReady = new ReactiveVar(false); |
|
|
|
|
this.aesReady = new ReactiveVar(false); |
|
|
|
|
this.publicKeyJWK = null; |
|
|
|
|
this.publicKey = null; |
|
|
|
|
this.privateKey = null; |
|
|
|
|
this.peerPublicKey = null; |
|
|
|
|
this.sharedSecret = null; |
|
|
|
|
this.sharedSecretJWK = null; |
|
|
|
|
|
|
|
|
|
this.keyPair = null; |
|
|
|
|
this.exportedPublicKey = null; |
|
|
|
|
this.sessionKey = null; |
|
|
|
|
|
|
|
|
|
this.serial = 0; |
|
|
|
|
this.peerSerial = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
handshake() { |
|
|
|
|
handshake(refresh) { |
|
|
|
|
this.establishing.set(true); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
this.getPublicAndPrivateKeys(false).then(() => { |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'handshake', { roomId: this.roomId, userId: this.userId, publicKey: this.publicKeyJWK }); |
|
|
|
|
this.firstPeer = true; |
|
|
|
|
this.generateKeyPair(false).then(() => { |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'handshake', { roomId: this.roomId, userId: this.userId, publicKey: this.bytesToHexString(this.exportedPublicKey), refresh: refresh }); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
acknowledge() { |
|
|
|
|
this.rsaReady.set(true); |
|
|
|
|
this.getPublicAndPrivateKeys(false).then(() => { |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'acknowledge', { roomId: this.roomId, userId: this.userId, publicKey: this.publicKeyJWK }); |
|
|
|
|
}); |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'acknowledge', { roomId: this.roomId, userId: this.userId, publicKey: this.bytesToHexString(this.exportedPublicKey) }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
deny() { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
this.reset(); |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'deny', { roomId: this.roomId, userId: this.userId }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
end() { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
this.reset(); |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'end', { roomId: this.roomId, userId: this.userId }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sharedSecretHandshake() { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
this.getSharedSecret().then(() => { |
|
|
|
|
this.encryptRSA(JSON.stringify(this.sharedSecretJWK), this.peerPublicKey).then((sharedSecret) => { |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'sharedSecret-handshake', { roomId: this.roomId, userId: this.userId, sharedSecret: sharedSecret }); |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sharedSecretAcknowledge() { |
|
|
|
|
reset() { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.aesReady.set(true); |
|
|
|
|
this.encryptAES(localStorage.getItem('sharedSecret')).then((args) => { |
|
|
|
|
[sharedSecret, iv] = args; |
|
|
|
|
RocketChat.Notifications.notifyUser(this.peerId, 'otr', 'sharedSecret-acknowledge', { roomId: this.roomId, userId: this.userId, sharedSecret: sharedSecret, iv: iv }); |
|
|
|
|
}); |
|
|
|
|
this.established.set(false); |
|
|
|
|
this.keyPair = null; |
|
|
|
|
this.exportedPublicKey = null; |
|
|
|
|
this.sessionKey = null; |
|
|
|
|
this.serial = null; |
|
|
|
|
this.peerSerial = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bytesToHexString(bytes) { |
|
|
|
|
@ -90,155 +73,115 @@ RocketChat.OTR.Room = class { |
|
|
|
|
return arrayBuffer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getPublicAndPrivateKeys(refreshKeys) { |
|
|
|
|
if (!this.privateKey || !this.publicKey || refreshKeys) { |
|
|
|
|
// Generate private and public keys
|
|
|
|
|
return window.crypto.subtle.generateKey({ |
|
|
|
|
name: "RSA-OAEP", |
|
|
|
|
modulusLength: 2048, //can be 1024, 2048, or 4096
|
|
|
|
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), |
|
|
|
|
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
|
|
|
|
}, |
|
|
|
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
|
|
|
|
["encrypt", "decrypt"] //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
|
|
|
|
|
) |
|
|
|
|
.then((key) => { |
|
|
|
|
// export private key
|
|
|
|
|
this.privateKey = key.privateKey; |
|
|
|
|
this.publicKey = key.publicKey; |
|
|
|
|
|
|
|
|
|
// export public key
|
|
|
|
|
return window.crypto.subtle.exportKey( |
|
|
|
|
"jwk", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
|
|
|
|
|
key.publicKey //can be a publicKey or privateKey, as long as extractable was true
|
|
|
|
|
) |
|
|
|
|
.then((publicKey) => { |
|
|
|
|
this.publicKeyJWK = publicKey; |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
console.error(err); |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
return Promise.resolve(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
importPublicKey(publicKey) { |
|
|
|
|
return window.crypto.subtle.importKey( |
|
|
|
|
"jwk", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
|
|
|
|
|
publicKey, |
|
|
|
|
{ //these are the algorithm options
|
|
|
|
|
name: "RSA-OAEP", |
|
|
|
|
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
|
|
|
|
}, |
|
|
|
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
|
|
|
|
["encrypt"] //"encrypt" or "wrapKey" for public key import or
|
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
importSharedSecret(sharedSecret) { |
|
|
|
|
return window.crypto.subtle.importKey( |
|
|
|
|
"jwk", //can be "jwk" or "raw"
|
|
|
|
|
sharedSecret, |
|
|
|
|
{ //this is the algorithm options
|
|
|
|
|
name: "AES-CBC", |
|
|
|
|
}, |
|
|
|
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
|
|
|
|
["encrypt", "decrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
encryptRSA(message, publicKey) { |
|
|
|
|
// Encrypt with peer's public key
|
|
|
|
|
return window.crypto.subtle.encrypt({ |
|
|
|
|
name: "RSA-OAEP", |
|
|
|
|
}, |
|
|
|
|
publicKey, |
|
|
|
|
new TextEncoder("UTF-8").encode(message) //ArrayBuffer of data you want to encrypt
|
|
|
|
|
) |
|
|
|
|
.then((encrypted) => { |
|
|
|
|
return this.bytesToHexString(encrypted); |
|
|
|
|
generateKeyPair(refreshKeys) { |
|
|
|
|
// Generate an ephemeral key pair.
|
|
|
|
|
return window.crypto.subtle.generateKey({ |
|
|
|
|
name: 'ECDH', |
|
|
|
|
namedCurve: 'P-256' |
|
|
|
|
}, false, ['deriveKey', 'deriveBits']).then((keyPair) => { |
|
|
|
|
this.keyPair = keyPair; |
|
|
|
|
return crypto.subtle.exportKey('spki', keyPair.publicKey); |
|
|
|
|
}) |
|
|
|
|
.then((exportedPublicKey) => { |
|
|
|
|
this.exportedPublicKey = exportedPublicKey; |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
console.log(err); |
|
|
|
|
return message; |
|
|
|
|
console.error(err); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
decryptRSA(message) { |
|
|
|
|
return window.crypto.subtle.decrypt({ |
|
|
|
|
name: "RSA-OAEP", |
|
|
|
|
}, |
|
|
|
|
this.privateKey, |
|
|
|
|
new this.hexStringToUint8Array(message) //ArrayBuffer of the data
|
|
|
|
|
) |
|
|
|
|
.then((decrypted) => { |
|
|
|
|
//returns an ArrayBuffer containing the decrypted data
|
|
|
|
|
return new TextDecoder("UTF-8").decode(new Uint8Array(decrypted)); |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
console.log(err); |
|
|
|
|
return message; |
|
|
|
|
importPublicKey(publicKey) { |
|
|
|
|
return window.crypto.subtle.importKey('spki', this.hexStringToUint8Array(publicKey), { |
|
|
|
|
name: 'ECDH', |
|
|
|
|
namedCurve: 'P-256' |
|
|
|
|
}, false, []).then((peerPublicKey) => { |
|
|
|
|
return crypto.subtle.deriveBits({ |
|
|
|
|
name: 'ECDH', |
|
|
|
|
namedCurve: 'P-256', |
|
|
|
|
public: peerPublicKey |
|
|
|
|
}, this.keyPair.privateKey, 256); |
|
|
|
|
}).then((bits) => { |
|
|
|
|
return crypto.subtle.digest({ |
|
|
|
|
name: 'SHA-256' |
|
|
|
|
}, bits); |
|
|
|
|
}).then((hashedBits) => { |
|
|
|
|
// We truncate the hash to 128 bits.
|
|
|
|
|
var sessionKeyData = new Uint8Array(hashedBits).slice(0, 16); |
|
|
|
|
return crypto.subtle.importKey('raw', sessionKeyData, { |
|
|
|
|
name: 'AES-GCM' |
|
|
|
|
}, false, ['encrypt', 'decrypt']); |
|
|
|
|
}).then((sessionKey) => { |
|
|
|
|
// Session key available.
|
|
|
|
|
this.sessionKey = sessionKey; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getSharedSecret() { |
|
|
|
|
return window.crypto.subtle.generateKey({ |
|
|
|
|
name: "AES-CBC", |
|
|
|
|
length: 256, //can be 128, 192, or 256
|
|
|
|
|
}, |
|
|
|
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
|
|
|
|
["encrypt", "decrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
|
|
|
|
) |
|
|
|
|
.then((sharedSecret) => { |
|
|
|
|
this.sharedSecret = sharedSecret; |
|
|
|
|
// export public key
|
|
|
|
|
return window.crypto.subtle.exportKey( |
|
|
|
|
"jwk", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
|
|
|
|
|
sharedSecret //can be a publicKey or privateKey, as long as extractable was true
|
|
|
|
|
) |
|
|
|
|
.then((sharedSecretJWK) => { |
|
|
|
|
this.sharedSecretJWK = sharedSecretJWK; |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
encrypt(message) { |
|
|
|
|
// var clearText = new Uint8Array(message);
|
|
|
|
|
var clearText = new TextEncoder("UTF-8").encode(message); |
|
|
|
|
var userId = new TextEncoder("UTF-8").encode(this.userId); |
|
|
|
|
this.serial++; |
|
|
|
|
var data = new Uint8Array(1 + 1 + userId.length + clearText.length); |
|
|
|
|
data[0] = this.serial; |
|
|
|
|
data[1] = userId.length; |
|
|
|
|
data.set(userId, 2); |
|
|
|
|
data.set(clearText, 2 + userId.length); |
|
|
|
|
|
|
|
|
|
encryptAES(message) { |
|
|
|
|
let iv = window.crypto.getRandomValues(new Uint8Array(16)); |
|
|
|
|
return window.crypto.subtle.encrypt({ |
|
|
|
|
name: "AES-CBC", |
|
|
|
|
//Don't re-use initialization vectors!
|
|
|
|
|
//Always generate a new iv every time your encrypt!
|
|
|
|
|
iv: iv, |
|
|
|
|
}, |
|
|
|
|
this.sharedSecret, //from generateKey or importKey above
|
|
|
|
|
new TextEncoder("UTF-8").encode(message) //ArrayBuffer of data you want to encrypt
|
|
|
|
|
) |
|
|
|
|
.then((encrypted) => { |
|
|
|
|
return [this.bytesToHexString(encrypted), this.bytesToHexString(iv)]; |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
console.log(err); |
|
|
|
|
return message; |
|
|
|
|
var iv = crypto.getRandomValues(new Uint8Array(12)); |
|
|
|
|
|
|
|
|
|
return crypto.subtle.encrypt({ |
|
|
|
|
name: 'AES-GCM', |
|
|
|
|
iv: iv |
|
|
|
|
}, this.sessionKey, data).then((cipherText) => { |
|
|
|
|
cipherText = new Uint8Array(cipherText); |
|
|
|
|
var output = new Uint8Array(iv.length + cipherText.length); |
|
|
|
|
output.set(iv, 0); |
|
|
|
|
output.set(cipherText, iv.length); |
|
|
|
|
return this.bytesToHexString(output); |
|
|
|
|
}).catch((e) => { |
|
|
|
|
console.log(e); |
|
|
|
|
return ""; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
decryptAES(message, iv) { |
|
|
|
|
return window.crypto.subtle.decrypt( |
|
|
|
|
{ |
|
|
|
|
name: "AES-CBC", |
|
|
|
|
iv: new this.hexStringToUint8Array(iv), //The initialization vector you used to encrypt
|
|
|
|
|
}, |
|
|
|
|
this.sharedSecret, //from generateKey or importKey above
|
|
|
|
|
new this.hexStringToUint8Array(message) //ArrayBuffer of the data
|
|
|
|
|
) |
|
|
|
|
.then((decrypted) => { |
|
|
|
|
//returns an ArrayBuffer containing the decrypted data
|
|
|
|
|
return new TextDecoder("UTF-8").decode(new Uint8Array(decrypted)); |
|
|
|
|
decrypt(message) { |
|
|
|
|
var cipherText = new this.hexStringToUint8Array(message); |
|
|
|
|
var iv = cipherText.slice(0, 12); |
|
|
|
|
cipherText = cipherText.slice(12); |
|
|
|
|
|
|
|
|
|
return crypto.subtle.decrypt({ |
|
|
|
|
name: 'AES-GCM', |
|
|
|
|
iv: iv |
|
|
|
|
}, this.sessionKey, cipherText).then((data) => { |
|
|
|
|
data = new Uint8Array(data); |
|
|
|
|
|
|
|
|
|
var serial = data[0]; |
|
|
|
|
var userId = data.slice(2, 2 + data[1]); |
|
|
|
|
var clearText = data.slice(2 + data[1]); |
|
|
|
|
|
|
|
|
|
// To copy over and make sure we do not have a shallow slice with simply non-zero byteOffset.
|
|
|
|
|
userId = new TextDecoder("UTF-8").decode(new Uint8Array(userId)); |
|
|
|
|
clearText = new TextDecoder("UTF-8").decode(new Uint8Array(clearText)); |
|
|
|
|
|
|
|
|
|
// This prevents any replay attacks. Or attacks where messages are changed in order.
|
|
|
|
|
// If message is from the same userId as me, serials must be equal
|
|
|
|
|
if (userId === this.userId && serial !== this.serial) { |
|
|
|
|
throw new Error("Invalid serial."); |
|
|
|
|
} else if (userId !== this.userId) { |
|
|
|
|
// If serial difference is larger than one, message is out of order
|
|
|
|
|
var checkSerial = serial - this.peerSerial; |
|
|
|
|
if (checkSerial !== 1 && checkSerial !== -255) { |
|
|
|
|
throw new Error("Invalid serial."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// update serial number to the last received serial
|
|
|
|
|
this.peerSerial = serial; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return clearText; |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
console.log(err); |
|
|
|
|
.catch((e) => { |
|
|
|
|
console.log(e); |
|
|
|
|
return message; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
@ -248,88 +191,72 @@ RocketChat.OTR.Room = class { |
|
|
|
|
switch(type) { |
|
|
|
|
case 'handshake': |
|
|
|
|
let timeout = null; |
|
|
|
|
this.establishing.set(true); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
swal({ |
|
|
|
|
title: "<i class='icon-key alert-icon'></i>" + TAPi18n.__("OTR"), |
|
|
|
|
text: TAPi18n.__("Username_wants_to_start_otr_Do_you_want_to_accept", { username: user.username }), |
|
|
|
|
html: true, |
|
|
|
|
showCancelButton: true, |
|
|
|
|
confirmButtonText: TAPi18n.__("Yes"), |
|
|
|
|
cancelButtonText: TAPi18n.__("No") |
|
|
|
|
}, (isConfirm) => { |
|
|
|
|
if (isConfirm) { |
|
|
|
|
Meteor.clearTimeout(timeout); |
|
|
|
|
this.importPublicKey(data.publicKey).then((publicKey) => { |
|
|
|
|
this.peerPublicKey = publicKey; |
|
|
|
|
|
|
|
|
|
establishConnection = () => { |
|
|
|
|
this.establishing.set(true); |
|
|
|
|
Meteor.clearTimeout(timeout); |
|
|
|
|
this.generateKeyPair(false).then(() => { |
|
|
|
|
this.importPublicKey(data.publicKey).then(() => { |
|
|
|
|
this.firstPeer = false; |
|
|
|
|
FlowRouter.goToRoomById(data.roomId); |
|
|
|
|
Meteor.defer(() => { |
|
|
|
|
this.established.set(true); |
|
|
|
|
this.acknowledge(); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
Meteor.clearTimeout(timeout); |
|
|
|
|
this.deny(); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (data.refresh && this.established.get()) { |
|
|
|
|
this.reset(); |
|
|
|
|
establishConnection(); |
|
|
|
|
} else { |
|
|
|
|
if (this.established.get()) { |
|
|
|
|
this.reset(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
swal({ |
|
|
|
|
title: "<i class='icon-key alert-icon'></i>" + TAPi18n.__("OTR"), |
|
|
|
|
text: TAPi18n.__("Username_wants_to_start_otr_Do_you_want_to_accept", { username: user.username }), |
|
|
|
|
html: true, |
|
|
|
|
showCancelButton: true, |
|
|
|
|
confirmButtonText: TAPi18n.__("Yes"), |
|
|
|
|
cancelButtonText: TAPi18n.__("No") |
|
|
|
|
}, (isConfirm) => { |
|
|
|
|
if (isConfirm) { |
|
|
|
|
establishConnection(); |
|
|
|
|
} else { |
|
|
|
|
Meteor.clearTimeout(timeout); |
|
|
|
|
this.deny(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
timeout = Meteor.setTimeout(() => { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
swal.close(); |
|
|
|
|
}, 10000); |
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'acknowledge': |
|
|
|
|
this.importPublicKey(data.publicKey).then((publicKey) => { |
|
|
|
|
this.peerPublicKey = publicKey; |
|
|
|
|
this.rsaReady.set(true); |
|
|
|
|
this.sharedSecretHandshake(); |
|
|
|
|
this.importPublicKey(data.publicKey).then(() => { |
|
|
|
|
this.established.set(true); |
|
|
|
|
}); |
|
|
|
|
break; |
|
|
|
|
case 'sharedSecret-handshake': |
|
|
|
|
this.decryptRSA(data.sharedSecret) |
|
|
|
|
.then((sharedSecret) => { |
|
|
|
|
this.sharedSecretJWK = JSON.parse(sharedSecret); |
|
|
|
|
localStorage.setItem('sharedSecret', sharedSecret); |
|
|
|
|
this.importSharedSecret(this.sharedSecretJWK) |
|
|
|
|
.then((sharedSecret) => { |
|
|
|
|
this.sharedSecret = sharedSecret; |
|
|
|
|
this.sharedSecretAcknowledge(); |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
swal(TAPi18n.__("Error establishing encrypted connection"), null, "error"); |
|
|
|
|
}); |
|
|
|
|
break; |
|
|
|
|
case 'sharedSecret-acknowledge': |
|
|
|
|
this.decryptAES(data.sharedSecret, data.iv) |
|
|
|
|
.then((sharedSecret) => { |
|
|
|
|
if (sharedSecret === JSON.stringify(this.sharedSecretJWK)) { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.aesReady.set(true); |
|
|
|
|
} else { |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
swal(TAPi18n.__("Error establishing encrypted connection"), null, "error"); |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'deny': |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
swal(TAPi18n.__("Denied"), null, "error"); |
|
|
|
|
if (this.establishing.get()) { |
|
|
|
|
this.reset(); |
|
|
|
|
swal(TAPi18n.__("Denied"), null, "error"); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'end': |
|
|
|
|
this.establishing.set(false); |
|
|
|
|
this.rsaReady.set(false); |
|
|
|
|
this.aesReady.set(false); |
|
|
|
|
swal(TAPi18n.__("Ended"), null, "error"); |
|
|
|
|
if (this.established.get()) { |
|
|
|
|
this.reset(); |
|
|
|
|
swal(TAPi18n.__("Ended"), null, "error"); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|