Skip to content

Commit

Permalink
Handle SHA256 properly on incoming web service / grade calls. (sakaip…
Browse files Browse the repository at this point in the history
…roject#5313)

* SAK-33898 - Support SHA265 LTI Launches

* Handle incoming SHA-256 signed web services calls

When the oauth_signature_method is HMAC-SHA256, then the oauth_body_hash
is also supposed to be computed using SHA256

Also update the unit tests to handle HMAC-SHA256
  • Loading branch information
csev authored Feb 15, 2018
1 parent c2225c7 commit 645be19
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
protected void doPostForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String ipAddress = request.getRemoteAddr();

log.debug("Basic LTI Service request from IP={}", ipAddress);
log.debug("Basic LTI Service Form request from IP={}", ipAddress);

String allowOutcomes = ServerConfigurationService.getString(
SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED, SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED_DEFAULT);
Expand Down Expand Up @@ -280,12 +280,14 @@ protected void doPostForm(HttpServletRequest request, HttpServletResponse respon
return;
}

// Perform the Outcomee first because we use the SakaiBLTIUtil code for this
// This is for the pre-LTI 1.x "Sakai Basic Outcomes" and is probably never used
// Perform the Outcome here because we use SakaiBLTIUtil.handleGradebook()
if ( "basicoutcome".equals(message_type) ) {
processOutcome(request, response, lti_message_type, sourcedid, theMap);
return;
}


// No point continuing without a sourcedid
if(BasicLTIUtil.isBlank(sourcedid)) {
doError(request, response, theMap, "outcomes.missing", "sourcedid", null);
Expand Down Expand Up @@ -388,6 +390,7 @@ protected void doPostForm(HttpServletRequest request, HttpServletResponse respon
String base_string = null;
try {
base_string = OAuthSignatureMethod.getBaseString(oam);
log.debug("base_string={}",base_string);
} catch (Exception e) {
log.error(e.getLocalizedMessage(), e);
base_string = null;
Expand Down Expand Up @@ -433,7 +436,7 @@ protected void doPostForm(HttpServletRequest request, HttpServletResponse respon
return;
}

// Perform the message-specific handling
// These are the Sakai-post form extensions
if ( "toolsetting".equals(message_type) ) processSetting(request, response, lti_message_type, site, siteId, placement_id, pitch, user_id, theMap);

if ( "roster".equals(message_type) ) processRoster(request, response, lti_message_type, site, siteId, placement_id, pitch, user_id, theMap);
Expand Down Expand Up @@ -801,7 +804,8 @@ protected void doPostXml(HttpServletRequest request, HttpServletResponse respons
return;
}

// Handle the outcomes here using the new SakaiBLTIUtil code
// Handle the LTI 1.x Outcomes
// Perform the Outcome here because we use SakaiBLTIUtil.handleGradebook()
if ( allowOutcomes != null && "basicoutcome".equals(message_type) ) {
processOutcomeXml(request, response, lti_message_type, sourcedid, pox);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@
$sourcedid = $_REQUEST['sourcedid'];
if (get_magic_quotes_gpc()) $sourcedid = stripslashes($sourcedid);

$signature = false;
if ( isset($_REQUEST['oauth_signature_method']) ) $signature = $_REQUEST['oauth_signature_method'];

?>
<p>
<form method="POST">
Service URL: <input type="text" name="url" size="100" disabled="true" value="<?php echo(htmlent_utf8($_REQUEST['url']));?>"/></br>
lis_result_sourcedid: <input type="text" name="sourcedid" disabled="true" size="100" value="<?php echo(htmlent_utf8($sourcedid));?>"/></br>
OAuth Consumer Key: <input type="text" name="key" disabled="true" size="80" value="<?php echo(htmlent_utf8($_REQUEST['key']));?>"/></br>
OAuth Consumer Secret: <input type="text" name="secret" size="80" value="<?php echo(htmlent_utf8($oauth_consumer_secret));?>"/></br>
OAuth Signature Method: <input type="text" name="oauth_signature_method" value="<?php echo(htmlent_utf8($_REQUEST['oauth_signature_method']));?>"/></br>
</p><p>
Grade to Send to LMS: <input type="text" name="grade" value="<?php echo(htmlent_utf8($_REQUEST['grade']));?>"/>
(i.e. 0.95)<br/>
Expand Down Expand Up @@ -96,7 +100,8 @@
exit();
}

$response = sendOAuthBody($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $postBody);
$more_headers = false;
$response = sendOAuthBody($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $postBody, $more_headers, $signature);
global $LastOAuthBodyBaseString;
$lbs = $LastOAuthBodyBaseString;

Expand Down
3 changes: 3 additions & 0 deletions basiclti/basiclti-docs/resources/docs/sakai-api-test/tool.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
print '&key='.urlencode($_POST['oauth_consumer_key']);
print '&secret=secret';
print '&url='.urlencode($_POST['lis_outcome_service_url']);
if ( isset($_POST['oauth_signature_method']) && $_POST['oauth_signature_method'] != 'HMAC-SHA1' ) {
print '&oauth_signature_method='.urlencode($_POST['oauth_signature_method']).'">';
}
print '&accepted='.urlencode($_POST['ext_outcome_data_values_accepted']).'">';
print 'Test LTI 1.1 Outcome Service</a>.</p>'."\n";
$found = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ function get_curl($url, $header) {

function sendOAuthBody($method, $endpoint, $oauth_consumer_key, $oauth_consumer_secret, $content_type, $body, $more_headers=false, $signature=false)
{
if ( $signature == "HMAC-256") {
if ( $signature == "HMAC-SHA256") {
$hash = base64_encode(hash('sha256', $body, TRUE));
} else {
$hash = base64_encode(sha1($body, TRUE));
Expand Down
12 changes: 11 additions & 1 deletion basiclti/tsugi-util/src/java/org/tsugi/pox/IMSPOXRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class IMSPOXRequest {
private String header = null;
private String oauth_body_hash = null;
private String oauth_consumer_key = null;
private String oauth_signature_method = null;

public boolean valid = false;
private String operation = null;
Expand Down Expand Up @@ -194,6 +195,10 @@ public void loadFromRequest(HttpServletRequest request)
String [] pieces = parm.split("\"");
oauth_consumer_key = URLDecoder.decode(pieces[1]);
}
if ( parm.startsWith("oauth_signature_method=") ) {
String [] pieces = parm.split("\"");
oauth_signature_method = URLDecoder.decode(pieces[1]);
}
}
}

Expand All @@ -204,6 +209,7 @@ public void loadFromRequest(HttpServletRequest request)
}

log.debug("OBH={}", oauth_body_hash);
log.debug("OSM={}", oauth_signature_method);
final char[] buffer = new char[0x10000];
try {
StringBuilder out = new StringBuilder();
Expand All @@ -222,7 +228,10 @@ public void loadFromRequest(HttpServletRequest request)
}

try {
MessageDigest md = MessageDigest.getInstance("SHA1");
MessageDigest md = MessageDigest.getInstance("SHA-1");
if ( "HMAC-SHA256".equals(oauth_signature_method) ) {
md = MessageDigest.getInstance("SHA-256");
}
md.update(postBody.getBytes());
byte[] output = Base64.encode(md.digest());
String hash = new String(output);
Expand Down Expand Up @@ -296,6 +305,7 @@ public void validateRequest(String oauth_consumer_key, String oauth_secret, Http

try {
base_string = OAuthSignatureMethod.getBaseString(oam);
log.debug("POX base_string={}",base_string);
} catch (Exception e) {
base_string = null;
}
Expand Down

0 comments on commit 645be19

Please sign in to comment.