[hebe/insomnia] Refactor, extract signer from insomnia plugin

This commit is contained in:
Mikołaj Pich 2020-02-18 00:06:50 +01:00
parent 7fb3debe91
commit 25cbf7cf60
No known key found for this signature in database
GPG key ID: F62B26E36D4C4BAA
3 changed files with 116 additions and 108 deletions

1
.gitignore vendored
View file

@ -62,3 +62,4 @@ typings/
.idea/
vendor
package-lock.json

View file

@ -1,120 +1,64 @@
const uuid = require('uuid/v4');
const {getSignatureValues} = require("./signer");
aCrypto = require('crypto');
const uuidv4 = require('uuid/v4');
function getOrThrow(context, name) {
let header = context.request.getHeader(name);
if (header === null || header.length === 0) {
throw `No ${name} header`;
}
return header;
}
module.exports.requestHooks = [
async (context) => {
if (context.request.hasHeader('PrivateKey')) {
let body = context.request.getBodyText();
if (!context.request.hasHeader('PrivateKey')) return;
const pkey = "-----BEGIN PRIVATE KEY-----\n" + getOrThrow(context, "PrivateKey") + "\n-----END PRIVATE KEY-----";
const fingerprint = getOrThrow(context, "Fingerprint");
const deviceModel = getOrThrow(context, "DeviceModel");
const firebaseToken = getOrThrow(context, "FirebaseToken");
let pkey = context.request.getHeader('PrivateKey');
if (pkey === null) {
throw 'No PrivateKey header';
}
pkey = "-----BEGIN PRIVATE KEY-----\n"+context.request.getHeader('PrivateKey')+"\n-----END PRIVATE KEY-----";
let fingerprint = context.request.getHeader('Fingerprint');
if (fingerprint === null || fingerprint.length === 0) {
throw 'No Fingerprint header';
}
let deviceModel = context.request.getHeader('DeviceModel');
if (deviceModel === null || fingerprint.length === 0) {
throw 'No DeviceModel header';
}
let firebaseToken = context.request.getHeader('FirebaseToken');
if (firebaseToken === null || firebaseToken.length === 0) {
throw 'No FirebaseToken header';
}
let timestamp = +context.request.getHeader('Timestamp');
if (timestamp === 0) timestamp = +new Date();
const timestampStrHeader = new Date(timestamp + 1000).toUTCString();
let timestamp = + context.request.getHeader('Timestamp');
if (timestamp === 0)
timestamp = + new Date();
const body = getWrappedBody(context.request.getMethod(), context.request.getBodyText(), fingerprint, firebaseToken, timestamp);
context.request.setBodyText(body);
let timestampStr = new Date(timestamp + 3600*1000 + 1000).toUTCString();
let timestampStrHeader = new Date(timestamp + 1000).toUTCString();
const {digest, signature, headers, keyId, canonicalUrl} = getSignatureValues(fingerprint, pkey, body, context.request.getUrl(), timestamp);
context.request.setHeader('User-Agent', 'okhttp/3.11.0');
context.request.setHeader('vOS', 'Android');
context.request.setHeader('vDeviceModel', deviceModel);
context.request.setHeader('vAPI', 1);
context.request.setHeader('vDate', timestampStrHeader);
context.request.setHeader('vCanonicalUrl', canonicalUrl);
context.request.setHeader('Signature', `keyId="${keyId}",headers="${headers}",algorithm="sha256withrsa",signature=Base64(SHA256withRSA(${signature}))`);
if (body != null) context.request.setHeader('Digest', `SHA-256=${digest}`);
if (context.request.getMethod() !== 'POST') {
if (context.request.getMethod() !== 'GET') {
throw 'Incorrect request method (GET or POST).';
}
body = null
}
else {
console.log(body);
body = JSON.parse(body);
console.log(body);
body = JSON.stringify({
AppName: 'DzienniczekPlus 2.0',
AppVersion: '1.0',
CertificateId: fingerprint,
Envelope: body,
FirebaseToken: firebaseToken,
API: 1,
RequestId: uuidv4(),
Timestamp: timestamp,
TimestampFormatted: timestampStr
});
context.request.setBodyText(body);
console.log(body);
}
let url = context.request.getUrl().match("(api/mobile/.+)");
if (url == null) {
throw 'The URL does not seem correct (does not match `(api/mobile/.+)` regex).';
}
url = encodeURIComponent(url[0]).toLowerCase();
let digest = "";
if (body != null) {
let hash = aCrypto.createHash('SHA256');
hash.update(body);
digest = hash.digest("base64");
}
let signData = [
['vCanonicalUrl', url],
body == null ? null : ['Digest', digest],
['vDate', timestampStrHeader]
];
let headers = "";
let values = "";
let first = true;
for (let data in signData) {
data = signData[data];
if (data == null)
continue;
if (!first)
headers += " ";
first = false;
headers += data[0];
values += data[1];
}
console.log(headers);
console.log(values);
let sign = aCrypto.createSign('RSA-SHA256');
sign.update(values);
let signature = sign.sign(pkey, "base64");
context.request.removeHeader('PrivateKey');
context.request.removeHeader('Fingerprint');
context.request.removeHeader('DeviceModel');
context.request.removeHeader('Timestamp');
context.request.removeHeader('FirebaseToken');
context.request.setHeader('User-Agent', 'okhttp/3.11.0');
context.request.setHeader('vOS', 'Android');
context.request.setHeader('vDeviceModel', deviceModel);
context.request.setHeader('vAPI', 1);
context.request.setHeader('vDate', timestampStrHeader);
context.request.setHeader('vCanonicalUrl', url);
if (body != null)
context.request.setHeader('Digest', 'SHA-256='+digest);
context.request.setHeader('Signature', 'keyId="'+fingerprint+'",headers="'+headers+'",algorithm="sha256withrsa",signature=Base64(SHA256withRSA('+signature+'))');
}
context.request.removeHeader('PrivateKey');
context.request.removeHeader('Fingerprint');
context.request.removeHeader('DeviceModel');
context.request.removeHeader('Timestamp');
context.request.removeHeader('FirebaseToken');
}
];
function getWrappedBody(method, body, fingerprint, firebaseToken, timestamp) {
if (method !== 'POST') {
if (method !== 'GET') throw 'Incorrect request method (GET or POST).';
else return null
}
return JSON.stringify({
AppName: 'DzienniczekPlus 2.0',
AppVersion: '1.0',
CertificateId: fingerprint,
Envelope: JSON.parse(body),
FirebaseToken: firebaseToken,
API: 1,
RequestId: uuid(),
Timestamp: timestamp,
TimestampFormatted: new Date(timestamp + 3600 * 1000 + 1000).toUTCString()
});
}

View file

@ -0,0 +1,63 @@
const aCrypto = require('crypto');
function getDigest(body) {
if (body == null) return "";
const hash = aCrypto.createHash('SHA256');
hash.update(body);
return hash.digest("base64");
}
function getSignature(values, pkey) {
const sign = aCrypto.createSign('RSA-SHA256');
sign.update(values);
return sign.sign(pkey, "base64");
}
function getCanonicalUrl(fullUrl) {
const url = fullUrl.match("(api/mobile/.+)");
if (url == null) throw 'The URL does not seem correct (does not match `(api/mobile/.+)` regex)';
return encodeURIComponent(url[0]).toLowerCase();
}
function getHeadersList(body, digest, canonicalUrl, timestamp) {
const timestampStrHeader = new Date(timestamp + 1000).toUTCString();
let signData = [
['vCanonicalUrl', canonicalUrl],
body == null ? null : ['Digest', digest],
['vDate', timestampStrHeader]
];
let headers = "";
let values = "";
let first = true;
for (let data in signData) {
data = signData[data];
if (data == null)
continue;
if (!first)
headers += " ";
first = false;
headers += data[0];
values += data[1];
}
return {"headers": headers, "values": values};
}
function getSignatureValues(fingerprint, pkey, body, fullUrl, timestamp) {
const canonicalUrl = getCanonicalUrl(fullUrl);
const digest = getDigest(body);
const {headers, values} = getHeadersList(body, digest, canonicalUrl, timestamp);
const signature = getSignature(values, pkey);
return {
"digest": digest,
"keyId": fingerprint,
"headers": headers,
"signature": signature,
"canonicalUrl": canonicalUrl
};
}
exports.getSignatureValues = getSignatureValues;