-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
198 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package migt; | ||
|
||
import org.apache.commons.codec.binary.Base64; | ||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
||
import java.security.MessageDigest; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.util.Arrays; | ||
|
||
/** | ||
* Module used to check the correctness of the at_hash parameter inside of the id_token wrt to the released access_token | ||
* https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken | ||
*/ | ||
public class At_Hash extends Module { | ||
|
||
public At_Hash() { | ||
} | ||
|
||
@Override | ||
public void loader(API api) { | ||
if (!(api instanceof Operation_API)) { | ||
throw new RuntimeException("Tried to load an api not supported in At_Hash module"); | ||
} | ||
imported_api = api; | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
if (imported_api == null) { | ||
throw new RuntimeException("imported API is null in module At_Hash"); | ||
} | ||
|
||
if (((Operation_API) imported_api).is_request) { | ||
throw new RuntimeException("Expecting a response got request in At_Hash module"); | ||
} | ||
|
||
// parse message body and take id_token and access_token values | ||
String body = new String(((Operation_API) imported_api).message.getBody(false)); | ||
|
||
String id_token = ""; | ||
String access_token = ""; | ||
|
||
try { | ||
JSONObject o = new JSONObject(body); | ||
id_token = o.getString("id_token"); | ||
access_token = o.getString("access_token"); | ||
} catch (JSONException e) { | ||
throw new RuntimeException("Invalid JSON in body"); | ||
} | ||
|
||
// parse id_token jwt taking alg and at_hash parameters | ||
String alg = ""; | ||
String at_hash = ""; | ||
try { | ||
JWT j = new JWT(); | ||
j.parse(id_token); | ||
JSONObject o = new JSONObject(j.header); | ||
alg = o.getString("alg"); | ||
o = new JSONObject(j.payload); | ||
at_hash = o.getString("at_hash"); | ||
|
||
} catch (ParsingException | JSONException e) { | ||
System.out.println(e); | ||
result = false; | ||
return; | ||
} | ||
|
||
// select the hashing algorithm based on the ID_TOKEN alg header parameter | ||
String hash_alg = alg.substring(1); | ||
byte[] hashed; | ||
try { | ||
switch (hash_alg) { | ||
case "S256": | ||
hashed = MessageDigest.getInstance("SHA-256").digest(access_token.getBytes()); | ||
break; | ||
case "S384": | ||
hashed = MessageDigest.getInstance("SHA-384").digest(access_token.getBytes()); | ||
break; | ||
case "S512": | ||
hashed = MessageDigest.getInstance("SHA-512").digest(access_token.getBytes()); | ||
break; | ||
default: | ||
System.out.println("At_Hash module: unsupported hashing alg: " + alg); | ||
result = false; | ||
return; | ||
} | ||
} catch (NoSuchAlgorithmException e) { | ||
throw new RuntimeException("At_Hash: Invalid algorithm selected to hash content"); | ||
} | ||
|
||
// select the first 128 bits of the hash of the access token | ||
byte[] left = Arrays.copyOfRange(hashed, 0, 16); | ||
|
||
// base64url encode the previous value | ||
String at_hash_generated = Base64.encodeBase64URLSafeString(left); | ||
|
||
// remove "=" characters | ||
at_hash_generated = at_hash_generated.replaceAll("=", ""); | ||
|
||
applicable = true; // this means that all the steps that precedes the check were accomplished correctly | ||
|
||
// check previous value is equal to at_hash | ||
result = at_hash_generated.equals(at_hash); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import migt.At_Hash; | ||
import migt.HTTPReqRes; | ||
import migt.Operation_API; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
public class At_Hash_Test { | ||
|
||
public HTTPReqRes init_message_token_resp() { | ||
String raw= "HTTP/1.1 200 OK\r\n" + | ||
"Date: Fri, 22 Dec 2023 13:12:13 GMT\r\n" + | ||
"Server: WSGIServer/0.2 CPython/3.10.13\r\n" + | ||
"Content-Type: application/json\r\n" + | ||
"X-Frame-Options: DENY\r\n" + | ||
"Content-Length: 2037\r\n" + | ||
"X-Content-Type-Options: nosniff\r\n" + | ||
"Referrer-Policy: same-origin\r\n" + | ||
"Cross-Origin-Opener-Policy: same-origin\r\n" + | ||
"\r\n"; | ||
|
||
|
||
List<String> headers = new ArrayList<>(); | ||
|
||
Collections.addAll(headers, raw.split("\r\n")); | ||
|
||
int body_offset = raw.length(); | ||
|
||
raw += "{\"access_token\": \"eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlBkMk45LVRael9BV1MzR0ZDa29ZZFJhWFhsczhZUGh4X2RfRXo3SndqUUkifQ.eyJpc3MiOiJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AiLCJzdWIiOiIyMmE4M2FhZmRlOWUyYzhkZmU2MzM1NTk3ZDk1MTNlMzYzMDdhOWI0NjI1NjVkYTg4MzM5ZTQzMDEyOGE0ODlhIiwiYXVkIjpbImh0dHA6Ly9jaWUtcHJvdmlkZXIub3JnOjgwMDIvb2lkYy9vcCIsIi9vaWRjL29wL3VzZXJpbmZvIl0sImNsaWVudF9pZCI6Imh0dHA6Ly9yZWx5aW5nLXBhcnR5Lm9yZzo4MDAxIiwic2NvcGUiOiJvcGVuaWQgb2ZmbGluZV9hY2Nlc3MiLCJqdGkiOiJmMTA0MWMxYi1hNDYyLTQ3ZWYtOTJjNi0zYWU0ZDNkZTgzMjIiLCJleHAiOjE3MDMyNTI3MTMsImlhdCI6MTcwMzI1MDczM30.bSlYNNyB8zdvE4M-9aiMEeI9NAsd12w47BCb_5ywZqLZMEJ06NYSHzhnJKzonh2TW32I9VFeB8ZxlyTmiFpaLuTm4onN2FfHIWeWYgwwAo-0JgUdjNGS07Vy4EkqZeFChDJCcI4uUriIFEG4u2dnTILNjJC1qcjA3CIlPn7kz9RkDfGw4zAFlOQZ9oVJj5LFUHfB7oDem2z0uJehw5gXHEVBi0hcA1Lj10i8rVuTqhRfCoOdxZwBuTq7eH6z6jCSIplyPIhVqY-dhGQrDvR_tMY4Ulz7Xd0EVjvs09H9QT1tDz9e8WNTF_UbQV8nEaTkbOzN9AC9C0JdJ76O0kH_yg\", \"id_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6IlBkMk45LVRael9BV1MzR0ZDa29ZZFJhWFhsczhZUGh4X2RfRXo3SndqUUkifQ.eyJzdWIiOiIyMmE4M2FhZmRlOWUyYzhkZmU2MzM1NTk3ZDk1MTNlMzYzMDdhOWI0NjI1NjVkYTg4MzM5ZTQzMDEyOGE0ODlhIiwibm9uY2UiOiJFNzdKenN0NDNNdjNsUmNyZ2lSRW01U3lRNjNCMTd4VyIsImF0X2hhc2giOiJhVDVmd21tNmFaZmdoTUpYNGZ0Q0N3IiwiY19oYXNoIjoiZm8wVHB3cDRMVm03MmhwcFNsVVpLUSIsImF1ZCI6WyJodHRwOi8vcmVseWluZy1wYXJ0eS5vcmc6ODAwMSJdLCJpc3MiOiJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AiLCJhY3IiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9TcGlkTDIiLCJqdGkiOiJiYjE5ZGUwYi0xNGQ3LTQzNGEtOTVmNS1jMWRlNWNhNzhkNWQiLCJmYW1pbHlfbmFtZSI6Im1hcmFkb25hIiwiZ2l2ZW5fbmFtZSI6InBlcHBlIiwiZXhwIjoxNzAzMjUyNzEzLCJpYXQiOjE3MDMyNTA3MzN9.h6cUctsaauPpwgPlL_S5A1v_FxWsgAPh1lppizSOwmw0k0r8XlA8EhxXF7NuTG0fyF-TvIR-XzjOovZyv7nfp2uTExzwjBK36S9qixRlLdZeAP5keMTzBGQRAvRzf3xCfdbo9Hz3wdFVAaZL4owbFZJrEf79j6bkJ7EYWPpaxgT2iWoaBDLNBh9cNhRAisBjpKF7Pg9Qjcgh06JBWTdcaGXxt7RITNhh03_kXNmHXc1tNyHqBMhUfPpdDgn0qzbhRm4lRPKg93sMvLTpyETsPsTiVrlBZqZpR-0DpSDRWibCw40vcPS0fbRgHZUWmrxPiyqh9e7hr3MM2Gbejg8THg\", \"token_type\": \"Bearer\", \"expires_in\": 1980, \"scope\": \"openid offline_access\"}"; | ||
byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); | ||
|
||
HTTPReqRes message = new HTTPReqRes(null, raw_b); | ||
message.body_offset_resp = body_offset; | ||
message.setHeaders(false, headers); | ||
message.isResponse = true; | ||
message.isRequest = false; | ||
|
||
return message; | ||
} | ||
|
||
@Test | ||
public void test_At_Hash() throws NoSuchAlgorithmException { | ||
HTTPReqRes test_message = init_message_token_resp(); | ||
|
||
At_Hash ah = new At_Hash(); | ||
|
||
Operation_API o = new Operation_API(test_message, false); | ||
|
||
ah.loader(o); | ||
ah.execute(); | ||
assertTrue(ah.getResult()); | ||
} | ||
} |