Skip to content

Commit

Permalink
Add a new optional authentication protocol.
Browse files Browse the repository at this point in the history
If the new authentication protocol is used, the tool using the signer will not only get a speaks-for credential, but an authentication token which allows it to ensure that the new credential was just authorized by the user. This only works in the postMessage flow and not in the new PHP/post flow.
  • Loading branch information
Jonathon Duerig committed Sep 8, 2014
1 parent af93064 commit 0378b53
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 12 deletions.
64 changes: 58 additions & 6 deletions geni-auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,29 @@ var genilib = {};
genilib.trustedHost = 'https://www.emulab.net';
genilib.trustedPath = '/protogeni/speaks-for/index.html';

genilib.authorize = function(id, cert, callback, defaultMA, userBundle)
genilib.authorize = function(idOrObject, cert, callback, defaultMA, userBundle)
{
var id;
var authenticate;

// If the user invokes with just a single object, it is a dictionary of options
if (typeof(idOrObject) === 'object')
{
id = idOrObject.id;
cert = idOrObject.toolCertificate;
callback = idOrObject.complete;
defaultMA = idOrObject.defaultMA;
userBundle = idOrObject.userBundle;
authenticate = idOrObject.authenticate;
console.log('set args based on dictionary');
}
else
{
id = idOrObject;
}

shouldAuthenticate = (authenticate !== undefined)

var wrapper = {};

wrapper.other = window.open(genilib.trustedHost + genilib.trustedPath +
Expand All @@ -21,7 +42,8 @@ genilib.authorize = function(id, cert, callback, defaultMA, userBundle)
{
data = {
certificate: cert,
tool: true
tool: true,
auth: shouldAuthenticate
};
if (userBundle)
{
Expand All @@ -40,19 +62,49 @@ genilib.authorize = function(id, cert, callback, defaultMA, userBundle)
event.origin === genilib.trustedHost &&
event.data.id && event.data.id === id && event.data.credential)
{
wrapper.credential = event.data.credential;
wrapper.credentialId = event.data.id;
wrapper.authenticationToken = event.data.userToken;
wrapper.encryptedCredential = event.data.encryptedCredential;
if (shouldAuthenticate && event.data.userCertificate)
{
console.log('authenticating');
authenticate(event.data.userCertificate, wrapper.authenticationToken, wrapper.toolAuthSuccess, wrapper.toolAuthFailure);
}
else
{
console.log('sendAck');
wrapper.sendAck();
}
}
};

wrapper.sendAck = function () {
window.removeEventListener('message', wrapper.listener, false);
// wrapper.other.removeEventListener('close', wrapper.close, false);

data = {
id: event.data.id,
id: wrapper.credentialId,
ack: true
};
console.log('Sending ack to ' + genilib.trustedHost);
wrapper.other.postMessage(data, genilib.trustedHost);

callback(event.data.credential);
}
};
callback(wrapper.credential, wrapper.encryptedCredential);
};

wrapper.toolAuthSuccess = function (toolToken) {
data = {
id: wrapper.credentialId,
toolToken: toolToken
};
console.log('Sending tool token to ' + genilib.trustedHost);
wrapper.other.postMessage(data, genilib.trustedHost);
};

wrapper.toolAuthFailure = function () {
wrapper.sendAck();
};

wrapper.close = function (event) {
window.removeEventListener('message', wrapper.message, false);
Expand Down
84 changes: 78 additions & 6 deletions xml-signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {
if (window.opener) {
window.addEventListener('message', messageToolCert, false);
window.opener.postMessage(data, '*');
console.log('posting ready message');
} else {
var fake_event = { data: { certificate: myCert, tool: true } }
messageToolCert(fake_event);
Expand Down Expand Up @@ -185,6 +186,19 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {
return result;
}

function wrapCertificates(list)
{
var result = "";
var i = 0;
for (; i < list.length; i += 1)
{
result += "-----BEGIN CERTIFICATE-----\n";
result += list[i];
result += "-----END CERTIFICATE-----\n";
}
return result;
}

function extractKey(pem)
{
var inKey = false;
Expand Down Expand Up @@ -339,6 +353,10 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {
};
}

var speaksForCredential;
var decryptedPrivateKey;
var userSecret;

function clickSign(event)
{
event.preventDefault();
Expand All @@ -358,6 +376,7 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {
try
{
var decrypted = forge.pki.decryptRsaPrivateKey(encryptedKey, password);
decryptedPrivateKey = decrypted;
var sig = new SignedXml();
sig.addReference("//*[local-name(.)='credential']");
sig.signingKey = decrypted;
Expand All @@ -367,12 +386,17 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {
sig.keyInfoProvider = new GENIKeyInfo(certList);
}
sig.computeSignature(xml);
speaksForCredential = sig.getSignedXml();
var userToken = generateUserToken(speakerCert);
var data = {
id: toolId,
credential: sig.getSignedXml()
credential: speaksForCredential,
userToken: userToken,
userCertificate: wrapCertificates(certList)
};
if (window.opener) {
window.opener.postMessage(data, '*');
console.log('posting credential');
} else {
// This can't be the only way to redirect with POST, sigh...
var backForm = document.createElement('form');
Expand Down Expand Up @@ -433,15 +457,62 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {

function messageAck(event)
{
if (event.source === window.opener && event.data && event.data.id &&
event.data.id === toolId && event.data.ack)
{
window.close();
}
console.log('messageAck');
if (event.source === window.opener && event.data && event.data.id &&
event.data.id === toolId)
{
if (event.data.ack)
{
window.close();
}
else if (event.data.toolToken)
{
sendAuthorizationToken(event.data.toolToken);
}
}
}

function sendAuthorizationToken(toolToken)
{
var encryptedCredential = encryptWithSecrets(userSecret, getToolSecret(toolToken));
var data = {
id: toolId,
encryptedCredential: encryptedCredential,
credential: speaksForCredential
};
window.opener.postMessage(data, '*');
console.log('posting auth token');
}

function generateUserToken(speakerCert)
{
var secretBytes = forge.random.getBytesSync(32);
userSecret = forge.util.bytesToHex(secretBytes);
var p7 = forge.pkcs7.createEnvelopedData();
var cert = forge.pki.certificateFromPem(wrapCertificates([speakerCert]));
p7.addRecipient(cert);
p7.content = forge.util.createBuffer(userSecret);
p7.encrypt();
return forge.pkcs7.messageToPem(p7);
}

function getToolSecret(toolToken)
{
var p7 = forge.pkcs7.messageFromPem(toolToken);
p7.decrypt(p7.recipients[0], decryptedPrivateKey);
return p7.content.toString();
}

function encryptWithSecrets(userSecret, toolSecret)
{
var md = forge.md.sha256.create();
md.update(speaksForCredential + userSecret + toolSecret);
return md.digest().toHex();
}

function messageCert(event)
{
console.log('messageCert');
if (event.source === certWindow && event.data && event.data.authority)
{
if (event.data.certificate)
Expand Down Expand Up @@ -483,6 +554,7 @@ function ($, _, error, forge, sigExport, xmlText, noKeyText, authorizeText) {
credential: cred
};
window.opener.postMessage(data, '*');
console.log('posting reflect cred');
window.removeEventListener('message', messageCert);
window.addEventListener('message', messageAck);
}
Expand Down

0 comments on commit 0378b53

Please sign in to comment.