I am currently trying to translate a website's processes to python requests calls so we can avoid using a headless browser to automate entries into the European TracesNT system. Actually entering data can already be shortened to easy POST calls, but these use cookies acquired at login. The login has also been automated already till the point of having to enter the password. I have been able to reverse engineer most of the calls made to the server and just need to encrypt the password to put into the data being posted to the server.
This is done by the following JS:
whenJQueryReady(function() {
if ("undefined" === typeof jQuery) throw Error("srp.js JavaScript requires jQuery");
$(document).ready(function() {
function u(b) {
return "undefined" !== typeof b && b.value && 0 !== b.value.length && jcv_isEditable(b)
}
function w(b, a, c, d, e) {
Jose.Utils.importRsaPublicKey(b, d.getKeyEncryptionAlgorithm()).then(function(f) {
f = new Jose.JoseJWE.Encrypter(d, f);
f.addHeader("kid", b.kid);
f.addHeader("tx", a.elements.TxId.value.substring(0, 40));
for (var h = [], g = {}, m = 0; m < c.length; g = {
$jscomp$loop$prop$field$2: g.$jscomp$loop$prop$field$2
},
m++)
if (g.$jscomp$loop$prop$field$2 = a.elements[c[m]], u(g.$jscomp$loop$prop$field$2)) {
var p = f.encrypt(g.$jscomp$loop$prop$field$2.value).then(function(k) {
return function(r) {
k.$jscomp$loop$prop$field$2.value = r;
"password" !== k.$jscomp$loop$prop$field$2.type && (k.$jscomp$loop$prop$field$2.type = "password");
return !0
}
}(g));
h.push(p)
} Promise.all(h).then(function(k) {
a.submit();
return !0
}).catch(function(k) {
e();
logError(k)
})
}).catch(function(f) {
logError("Unable to import server JWK key: " + f);
e()
})
}
function A(b,
a, c, d) {
function e(l) {
for (var v = 0, n = void 0; v < g && (n = l + v) <= f; v++) {
var t = sha3_256.digest(n + p);
a: {
for (var q = 0; q < c.length; q++)
if ((t[q] & c[q]) !== c[q]) {
t = !1;
break a
} t = !0
}
if (t) {
h && (l = performance.now(), console.log("[POW] solution found: " + n + " in " + (l - x) + " milliseconds, hash=" + sha3_256.hex(n + p)));
k.attr("aria-valuenow", 100).css("width", "100%");
d(n);
return
}
}
n = l + g;
m && console.log("[POW] attempt: " + n);
l = 100 * k.width() / k.parent().width() + r;
99 < l && (l = 50, r = 1);
k.attr("aria-valuenow", l).css("width", l + "%");
window.requestAnimationFrame(e.bind(this,
n))
}
var f = ECAS.ProofOfWork.Settings.getMaxSafeInt(),
h = ECAS.ProofOfWork.Settings.isLoggingEnabled(),
g = ECAS.ProofOfWork.Settings.getBatchSize(),
m = h && 0 < g && g < f,
p = b + a,
k = $("#progressBarId"),
r = 2,
x = null;
h && (console.log("[POW] Starting proof of work computation for algorithm=SHA3-256, challenge=" + b + NaN + a + ", mask=" + c), x = performance.now());
window.requestAnimationFrame(e.bind(this, 0))
}
function B(b) {
b = b.substring(1, b.length - 1);
b = b.split(/\s*,\s*/);
for (var a = [], c = 0, d = b.length; c < d; c++) a.push(parseInt(b[c], 16));
return a
}
function C(b, a, c) {
var d = $("#captchaPleaseWait");
d.on("shown.bs.modal", function(e) {
e = b.elements.TxId.value;
var f = B(b.elements.proofOfWork.dataset.complexityMask);
A(e, a, f, function(h) {
var g = $("#proofOfWork");
g.prop("disabled", !1);
g.val(h);
d.find(".progressBarStart").toggle();
d.find(".progressBarEnd").toggle();
c();
return !0
})
}).modal({
backdrop: "static",
keyboard: !1,
show: !0
})
}
function y(b, a) {
$.get(ECAS.getContextPath() + "/srp/keys", function(c) {
w(c, b, a, new Jose.WebCryptographer, function() {
var d = new Jose.WebCryptographer;
d.setKeyEncryptionAlgorithm("RSA-OAEP");
d.setContentEncryptionAlgorithm("A256CBC-HS512");
w(c, b, a, d, function() {
b.submit()
})
})
}, "json").fail(function(c, d, e) {
logError("Unable to fetch JWK key from server: " + e);
b.submit()
})
}
var z = $("#loginForm, #expiredLoginForm, #changePasswordForm, #initialisePasswordLoginForm, #passwordSignatureForm, #smsSignatureStep1Form, #processSignatureForm, #softwareTokenSignatureForm, #addMobileDeviceForm, #changeMobileDevicePinCodeForm, #resetMobileDevicePinCodeForm, #eimExternalRegisterForm, #initialisePasswordCaptchaForm, #eimAddMobilePhoneNumberForm"),
D = {
loginForm: {
enc: ["password", "newPassword", "confirmNewPassword"],
pow: {
enabled: !0,
fields: []
}
},
expiredLoginForm: {
enc: ["password", "newPassword", "confirmNewPassword"],
pow: {
enabled: !0,
fields: []
}
},
changePasswordForm: {
enc: ["password", "newPassword", "confirmNewPassword"]
},
initialisePasswordLoginForm: {
enc: ["strongPassword", "confirmNewStrongPassword"]
},
passwordSignatureForm: {
enc: ["password"]
},
smsSignatureStep1Form: {
enc: ["password"]
},
processSignatureForm: {
enc: ["password"]
},
softwareTokenSignatureForm: {
enc: ["password"]
},
addMobileDeviceForm: {
enc: ["pinCode", "pinCodeConfirmation"]
},
changeMobileDevicePinCodeForm: {
enc: ["currentPinCode", "newPinCode", "newPinCodeConfirmation"]
},
resetMobileDevicePinCodeForm: {
enc: ["newPinCode", "newPinCodeConfirmation"]
},
eimExternalRegisterForm: {
enc: ["captcha"],
pow: {
enabled: !0,
fields: ["captcha"]
}
},
initialisePasswordCaptchaForm: {
enc: ["captcha"],
pow: {
enabled: !0,
fields: ["captcha"]
}
},
eimAddMobilePhoneNumberForm: {
enc: ["captcha"],
pow: {
enabled: !0,
fields: ["captcha"]
}
}
};
z.length && Jose && Jose.caniuse &&
Jose.caniuse() && z.each(function() {
$(this).submit(function(b) {
var a = this,
c = D[a.id],
d = c.enc,
e = c.pow;
c = null;
if (e && e.enabled && a.data && 1 === a.data.clicks && !isCancelled(a) && ECAS && ECAS.ProofOfWork && ECAS.ProofOfWork.Settings && ECAS.ProofOfWork.Settings.isEnabled()) {
var f = a.elements.proofOfWork;
if ("undefined" !== typeof f) {
for (var h = "", g = 0; g < e.fields.length; g++) {
var m = a.elements[e.fields[g]];
u(m) && (h += m.value)
}
if (0 !== h.length || 0 === e.fields.length || "false" === f.dataset[e.fields[0] + "Enabled"]) c = function(p) {
return C(a,
h, p)
}
}
}
e = !1;
for (f = 0; f < d.length; f++)
if (u(a.elements[d[f]])) {
e = !0;
break
} if (a.data && 1 === a.data.clicks && !isCancelled(a)) {
if (e && !a.encrypted) return a.encrypted = !0, b.preventDefault(), null !== c ? c(function() {
y(a, d);
return !0
}) : y(a, d), !1;
if (null !== c && !a.powComputed) return a.powComputed = !0, b.preventDefault(), c(function() {
a.submit();
return !0
}), !1
}
})
})
})
});
Additional info:
Keys supplied by the server and entered into
data = {
"kty": "RSA",
"e": "<>",
"kid": "<>",
"n": "<>"
}
I've been able to boil most of this down to the following python script but this does not fully work yet:
from jose import constants, jwe
def encrypt(message: str, data: dict):
key_algorithm = constants.ALGORITHMS.RSA_OAEP
content_algorithm = constants.ALGORITHMS.A256CBC_HS512
encrypted = jwe.encrypt(
plaintext=bytes(message, "utf-8"),
key=data,
encryption=content_algorithm,
algorithm=key_algorithm,
kid=data['kid'])
return encrypted
Data is in this case the keys and message is the password
Sadly this does not work yet and I get a simple Incorrect password error on the page and don't recieve all my cookies. What could I have done wrong and how do I fix this?
p.s. I'm new to posting on stackoverflow so if any aditional info is needed please mention it :D
Solved
The python-jose library jwe.encrypt does not take any extra headers into account, therefor the tx header (see js script) was not added and my jwk was lacking that