Skip to content

Commit

Permalink
Implemented signed credential requests
Browse files Browse the repository at this point in the history
  • Loading branch information
kezike committed Jan 30, 2019
1 parent 55a7cef commit b87924b
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 73 deletions.
85 changes: 62 additions & 23 deletions app/issuer.js
Expand Up @@ -91,8 +91,8 @@ SolidIss = {
currentTabLink: '', // { SolidIss.issueTabLink, SolidIss.reviewTabLink }
currentTabCnt: '', // { SolidIss.issueTabCnt, SolidIss.reviewTabCnt }

// Message element ids
messageIds: [],
// Message element info
messageInfo: [], // has fields 'uri', 'inspectId', 'verifyId'

// Action element id delimiter
actionElemIdDelim: '-',
Expand Down Expand Up @@ -120,6 +120,17 @@ SolidIss = {
$(document).on('click', '#revoke-tab-link', SolidIss.displayTab);
$(document).on('click', '#switch-acct', util.switchAccounts);
$(document).on('click', '#switch-role', util.switchRoles);
/*// Close credential inpection modal when button pressed
$(messageModal).on('click', closeButtonId, () => {
messageModal.css("display","none");
});
// Close credential inpection modal when click scope is out of modal bounds
$(window).on('click', (event) => {
if (event.target == messageModal) {
messageModal.css("display","none");
}
});*/
},

// Display tab
Expand Down Expand Up @@ -178,18 +189,44 @@ SolidIss = {
$('#msg-board').empty();
for (var i = 0; i < inboxContent.length; i++) {
var inboxItem = inboxContent[i];
var messageId = inboxItem[util.ldObjField][util.ldTermValueField];
var reqMsgElem = SolidIss.formatRequestMessageElement(messageId, i);
var messageUri = inboxItem.object.value;
var messageObj = {uri: messageUri};
var reqMsgElem = SolidIss.formatRequestMessageElement(messageObj, i);
SolidIss.messageInfo.push(messageObj);
$('#msg-board').append(reqMsgElem);
SolidIss.messageIds.push(messageId);
}
SolidIss.verifyRequests();
},

// Load content of issue tab
loadRevokeTab: async function() {
await util.trackSession();
},

verifyRequests: async function() {
for (var i = 0; i < SolidIss.messageInfo.length; i++) {
var messageObj = SolidIss.messageInfo[i];
var messageUri = messageObj.uri;
var messageVerifyId = messageObj.verifyId;
console.log(`messageVerifyId: ${messageVerifyId}`);
var verifyResult = await util.verifyDocument(messageUri, {checkCredStatus: false, verifyRequest: true});
// TODO - Set 'src' attribute of verifyRequest img and remove 'hidden' class
var messageVerifyElem = $('#' + messageVerifyId);
console.log(`verifyRequestsResult: ${verifyResult.verified}`);
if (verifyResult.verified) {
var verifyImageElem = messageVerifyElem.find(".verify-img");
console.log(`verifyImageElem: ${verifyImageElem}`);
verifyImageElem.attr('src', "./img/approve.png");
messageVerifyElem.removeClass('hidden');
} else {
var verifyImageElem = messageVerifyElem.find(".verify-img");
console.log(`verifyImageElem: ${JSON.stringify(verifyImageElem)}`);
verifyImageElem.attr('src', "./img/decline.png");
messageVerifyElem.removeClass('hidden');
}
}
},

formatActionElementIdx: function(actionId, idx) {
return parseInt(actionId.split(SolidIss.actionElemIdDelim)[1]);
},
Expand All @@ -198,16 +235,24 @@ SolidIss = {
return action + SolidIss.actionElemIdDelim + idx;
},

formatRequestMessageElement: function(messageId, messageIdx) {
formatRequestMessageElement: function(messageObj, messageIdx) {
var credReqMsgLabel = `Credential Request ${messageIdx + 1}`;
var inspectId = SolidIss.formatActionElementId("inspect", messageIdx);
var approveId = SolidIss.formatActionElementId("approve", messageIdx);
var declineId = SolidIss.formatActionElementId("decline", messageIdx);
/*var approveId = SolidIss.formatActionElementId("approve", messageIdx);
var declineId = SolidIss.formatActionElementId("decline", messageIdx);*/
var verifyId = SolidIss.formatActionElementId("verify", messageIdx);
var downloadId = SolidIss.formatActionElementId("download", messageIdx);
messageObj.inspectId = inspectId;
messageObj.verifyId = verifyId;
var messageUri = messageObj.uri;
var header = "<tr>";
var bodyLine1 = `<td style="padding:15px"><h4 style="color:blue; display:inline"><a href=${messageId} target="_blank">${credReqMsgLabel}</a></h4></td>`;
var bodyLine2 = `<td id=${inspectId} class=inspect-cred style="padding:15px"><input type="image" src="./img/inspect.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
var bodyLine3 = `<td id=${approveId} class=approve-cred style="padding:15px"><input type="image" src="./img/approve.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
var bodyLine4 = `<td id=${declineId} class=decline-cred style="padding:15px"><input type="image" src="./img/decline.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
var bodyLine1 = `<td style="padding:15px"><h4 style="color:blue; display:inline"><a href=${messageUri} target="_blank">${credReqMsgLabel}</a></h4></td>`;
var bodyLine2 = `<td id="${inspectId}" class="inspect-cred" style="padding:15px"><input type="image" src="./img/inspect.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
/*var bodyLine3 = `<td id="${approveId}" class="approve-cred" style="padding:15px"><input type="image" src="./img/approve.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
var bodyLine4 = `<td id="${declineId}" class="decline-cred" style="padding:15px"><input type="image" src="./img/decline.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
var body = `${bodyLine1}${bodyLine2}${bodyLine3}${bodyLine4}`;*/
var bodyLine3 = `<td id="${downloadId}" class="download-msg" style="padding:15px"><input type="image" src="./img/download.png" height=25 width=25 style="display:inline; left:50em" /></td>`;
var bodyLine4 = `<td id="${verifyId}" class="verify-request hidden" style="padding:15px"><img class="verify-img" src="" height=25 width=25 style="display:inline; left:50em" /></td>`;
var body = `${bodyLine1}${bodyLine2}${bodyLine3}${bodyLine4}`;
var footer = "</tr>";
var message = `${header}${body}${footer}`;
Expand All @@ -219,13 +264,11 @@ SolidIss = {
var actionElem = $(event.target).closest(".inspect-cred");
var actionElemId = actionElem.attr('id');
var messageModal = $("#msg-modal");
var closeButton = $(event.target).closest(".close");
var closeButtonId = closeButton.attr('id');
console.log(`Inspect Credential Target: ${actionElemId}`);

// Fetch credential request message
var actionElemIdx = SolidIss.formatActionElementIdx(actionElemId);
var messageUri = SolidIss.messageIds[actionElemIdx];
var messageUri = SolidIss.messageInfo[actionElemIdx].uri;
var message = await util.genericFetch(messageUri);

// Populate and display credential request message modal
Expand All @@ -234,16 +277,10 @@ SolidIss = {
messageModal.css("display","block");

// Close credential inpection modal when button pressed
$(document).on('click', closeButtonId, () => {
$(document).on('click', "#msg-modal-close", () => {
// console.log(`closeButtonId: ${closeButtonId}`);
messageModal.css("display","none");
});

// Close credential inpection modal when click scope is out of modal bounds
$(document).on('click', (event) => {
if (event.target == messageModal) {
messageModal.css("display","none");
}
});
},

approveCredential: async function(event) {
Expand Down Expand Up @@ -544,6 +581,7 @@ SolidIss = {
var revokeCredReasonElem = $('#revoke-cred-reason');
var revokeCredId = revokeCredIdElem.val();
var revokeCredReason = revokeCredReasonElem.val();
var revokeCredDate = new Date();

// Validate inputs
if (revokeCredId === "") {
Expand Down Expand Up @@ -597,6 +635,7 @@ SolidIss = {
var insertions = [];
insertions.push($rdf.st($rdf.sym(revokeCredSub), SVC(util.svcCredStatusField), util.svcCredStatusRevoked, $rdf.sym(revokeCred)));
insertions.push($rdf.st($rdf.sym(revokeCredSub), SVC(util.svcRevReasonField), revokeCredReason, $rdf.sym(revokeCred)));
insertions.push($rdf.st($rdf.sym(revokeCredSub), SVC(util.svcRevDateField), revokeCredDate, $rdf.sym(revokeCred)));
var deletions = util.fetcher.store.match($rdf.sym(revokeCredSub), SVC(util.svcCredStatusField), undefined, $rdf.sym(revokeCred));
updater.update(deletions, insertions, (uri, ok, message, response) => {
if (ok) {
Expand Down
15 changes: 11 additions & 4 deletions app/subject.js
Expand Up @@ -170,14 +170,21 @@ SolidSub = {

// Sign credential request
// TODO - util.signDocument()
var requestJsonLdStr = await util.serialize(null, requestStore, subjectId, util.contentTypeJsonLd);
var requestJsonLd = JSON.parse(requestJsonLdStr)[0];
var requestSignedJsonLd = await util.signDocument(requestJsonLd);
var requestSignedJsonLdStr = JSON.stringify(requestSignedJsonLd, null, 4);
console.log(`requestSignedJsonLdStr: ${requestSignedJsonLdStr}`);

// Serialize credential request to N3
/*// Serialize credential request to N3
var requestBase = request.value;
var requestN3 = await util.serialize(null, requestStore, requestBase, util.contentTypeN3);
var requestN3 = await util.serialize(null, requestStore, requestBase, util.contentTypeN3);*/

// Submit credential request; TODO - Add logic to inform user of network error (ie. inexistent URI, timeout, etc.)
util.postOptions.headers[util.contentTypeField] = util.contentTypeN3;
util.postOptions.body = requestN3;
// util.postOptions.headers[util.contentTypeField] = util.contentTypeN3;
// util.postOptions.body = requestN3;
util.postOptions.headers[util.contentTypeField] = util.contentTypePlain;
util.postOptions.body = requestSignedJsonLdStr;
util.fetcher.load(issuerInbox, util.postOptions);

// Clear input fields
Expand Down
112 changes: 71 additions & 41 deletions app/util.js
Expand Up @@ -74,6 +74,8 @@ SolidUtil = {
LDP: $rdf.Namespace('http://www.w3.org/ns/ldp#'),
// SVC issuer id predicate
svcIssuerIdField: 'issuerId',
// SVC subject id predicate
svcSubjectIdField: 'subjectId',
// SVC revocation list predicate
svcRevListField: 'revocationList',
// SVC credential status predicate
Expand All @@ -88,6 +90,8 @@ SolidUtil = {
svcCredIdField: 'credentialId',
// SVC revocation reason predicate
svcRevReasonField: 'revocationReason',
// SVC revocation date predicate
svcRevDateField: 'revocationDate',
// VC credential status predicate
vcCredStatus: 'credentialStatus',
// SEC public key predicate
Expand Down Expand Up @@ -196,6 +200,17 @@ SolidUtil = {
return revReason;
},

// Retrieve date of credential revocation
getRevDate: async function(credStatusUri) {
await SolidUtil.fetcher.load(credStatusUri);
const credStatusQueryResult = SolidUtil.fetcher.store.match(undefined, SVC(SolidUtil.svcRevDateField), undefined);
if (credStatusQueryResult.length == 0) {
return null;
}
const revDate = credStatusQueryResult[0].object.value;
return revDate;
},

// Track status of user session
trackSession: async function() {
var sessionPromise = new Promise((resolve, reject) => {
Expand Down Expand Up @@ -475,9 +490,18 @@ SolidUtil = {
},

// Verify document
verifyDocument: async function(signedDocUri, /*verifyConfig*/) {
/*
User may pass in verification configuration parameters with the following keys:
checkCredStatus: Determines whether to check credential status during verification
verifyRequest: Determines whether this is a request verification
*/
verifyDocument: async function(signedDocUri) {
// Specifying verification configuration
// TODO - allow specification of publicKey["@id"], publicKey.owner, and publicKeyOwner["@id"] in verifyConfig
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
console.log(config);
var checkCredStatus = ("checkCredStatus" in config) ? config.checkCredStatus : true;
var verifyRequest = ("verifyRequest" in config) ? config.verifyRequest : false;

// Verification result
var result = { verified: false, error: {} };

Expand All @@ -487,43 +511,49 @@ SolidUtil = {

// Discover credential issuer ID
const issuerId = signedDocStore.match(undefined, SVC(SolidUtil.svcIssuerIdField), undefined)[0].object.value;
const pubKeyId = await SolidUtil.getPubKeyRemoteUri(issuerId);
console.log(`Pub key ID: ${pubKeyId}`);
console.log(`Pub key ID Stringified: ${JSON.stringify(pubKeyId)}`);
const issuerPubKey = await SolidUtil.getPubKeyRemoteContent(issuerId);
const publicKeyPem = issuerPubKey;
console.log(`Issuer ID: ${issuerId}`);
const subjectId = signedDocStore.match(undefined, SVC(SolidUtil.svcSubjectIdField), undefined)[0].object.value;
const controllerId = verifyRequest ? subjectId : issuerId;
const pubKeyId = await SolidUtil.getPubKeyRemoteUri(controllerId);
/*console.log(`Pub key ID: ${pubKeyId}`);
console.log(`Pub key ID Stringified: ${JSON.stringify(pubKeyId)}`);*/
const controllerPubKey = await SolidUtil.getPubKeyRemoteContent(controllerId);
const publicKeyPem = controllerPubKey;
/*console.log(`Issuer ID: ${issuerId}`);
console.log(`Pub key ID: ${pubKeyId}`);
console.log(`Issuer Pub Key:\n${issuerPubKey}`);

// Discover credential issuer revocation list
const credStatusUri = signedDocStore.match(undefined, VC(SolidUtil.vcCredStatus), undefined)[0].object.value;
console.log(`Cred Status URI: ${credStatusUri}`);
const credStatus = await SolidUtil.getCredStatus(credStatusUri);
console.log(`Cred Status: ${credStatus}`);
switch(credStatus) {
case null:
result.verified = false;
result.error.message = 'The issuer of this credential has moved the credential status list';
return;
case SolidUtil.svcCredStatusActive:
break;
case SolidUtil.svcCredStatusExpired:
result.verified = false;
result.error.message = 'This credential has expired';
return result;
case SolidUtil.svcCredStatusRevoked:
result.verified = false;
let revReason = await SolidUtil.getRevReason(credStatusUri);
if (!revReason) {
revReason = `Issuer with ID '${issuerId}' neglected to provide reason for revocation`;
}
result.error.message = `This credential has been revoked by issuer with ID '${issuerId}' for the following reason: '${revReason}'`;
return result;
default:
result.verified = false;
result.error.message = 'Cannot properly verify this credential because it does not include a valid status';
return result;
console.log(`Issuer Pub Key:\n${controllerPubKey}`);*/

console.log(`CHECK CRED STATUS: ${checkCredStatus}`);
if (checkCredStatus) {
// Discover credential issuer revocation list
const credStatusUri = signedDocStore.match(undefined, VC(SolidUtil.vcCredStatus), undefined)[0].object.value;
console.log(`Cred Status URI: ${credStatusUri}`);
const credStatus = await SolidUtil.getCredStatus(credStatusUri);
console.log(`Cred Status: ${credStatus}`);
switch(credStatus) {
case null:
result.verified = false;
result.error.message = 'The issuer of this credential has moved the credential status list';
return;
case SolidUtil.svcCredStatusActive:
break;
case SolidUtil.svcCredStatusExpired:
result.verified = false;
result.error.message = 'This credential has expired';
return result;
case SolidUtil.svcCredStatusRevoked:
result.verified = false;
let revReason = await SolidUtil.getRevReason(credStatusUri);
let revDate = await SolidUtil.getRevDate(credStatusUri);
if (!revReason) {
revReason = `Issuer with ID '${issuerId}' neglected to provide reason for revocation`;
}
result.error.message = `This credential was revoked on ${revDate} by issuer with ID '${issuerId}' for the following reason: '${revReason}'`;
return result;
default:
result.verified = false;
result.error.message = 'Cannot properly verify this credential because it does not include a valid status';
return result;
}
}

// Parse JSON-LD string into JSON in preparation for verification
Expand All @@ -534,14 +564,14 @@ SolidUtil = {
"@context": jsigs.SECURITY_CONTEXT_URL,
type: SolidUtil.jsigsRsaVerificationKey,
id: pubKeyId,
controller: issuerId,
controller: controllerId,
publicKeyPem
};

// Specify the public key controller
const controller = {
"@context": jsigs.SECURITY_CONTEXT_URL,
id: issuerId,
id: controllerId,
publicKey: [publicKey],
assertionMethod: [publicKey.id]
};
Expand All @@ -551,7 +581,7 @@ SolidUtil = {
const {AssertionProofPurpose} = jsigs.purposes;
const {RSAKeyPair} = jsigs;
const privateKeyPem = null; // We do not know the private key of the signer
console.log(`Verifying signed credential with key pair:\n${privateKeyPem}\n${issuerPubKey}`);
console.log(`Verifying signed credential with key pair:\n${privateKeyPem}\n${controllerPubKey}`);
const key = new RSAKeyPair({...publicKey, privateKeyPem});
result = await jsigs.verify(signedDoc, {
suite: new RsaSignature2018({key}),
Expand Down
6 changes: 3 additions & 3 deletions css/styles.css
Expand Up @@ -58,15 +58,15 @@
}

/* The Close Button */
.close {
.msg-modal-close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}

.close:hover,
.close:focus {
.msg-modal-close:hover,
.msg-modal-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
Expand Down
Binary file added img/download.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion issuer.html
Expand Up @@ -36,7 +36,8 @@ <h3 id="name"></h3>
<div id="msg-modal" class="modal">
<!-- Modal content -->
<div class="msg-modal-content">
<span class="close">&times;</span>
<span id="msg-modal-close" class="close">&times;</span>
<!--<button id="msg-modal-close" class="close">&times;</button>-->
<div id="msg-modal-text" style="white-space: pre-wrap"></div>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions ont/svc.n3
Expand Up @@ -227,3 +227,10 @@ svc:revocationReason a rdf:Property ;
rdfs:comment "Reasoning behind credential revocation"@en ;
rdfs:domain svc:CredentialStatusList ;
rdfs:range rdfs:Literal .

svc:revocationDate a rdf:Property ;
rdfs:isDefinedBy <http://dig.csail.mit.edu/2018/svc#> ;
rdfs:label "revocationDate"@en ;
rdfs:comment "Date of credential revocation"@en ;
rdfs:domain svc:CredentialStatusList ;
rdfs:range xsd:dateTime .

0 comments on commit b87924b

Please sign in to comment.