diff --git a/tool/src/main/java/migt/At_Hash.java b/tool/src/main/java/migt/At_Hash_check.java similarity index 98% rename from tool/src/main/java/migt/At_Hash.java rename to tool/src/main/java/migt/At_Hash_check.java index fd250d8..5cdbabf 100644 --- a/tool/src/main/java/migt/At_Hash.java +++ b/tool/src/main/java/migt/At_Hash_check.java @@ -12,9 +12,9 @@ * 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 class At_Hash_check extends Module { - public At_Hash() { + public At_Hash_check() { } @Override diff --git a/tool/src/main/java/migt/At_Hash_update.java b/tool/src/main/java/migt/At_Hash_update.java new file mode 100644 index 0000000..2100c8e --- /dev/null +++ b/tool/src/main/java/migt/At_Hash_update.java @@ -0,0 +1,131 @@ +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_update extends Module { + String sign_key = ""; + + public At_Hash_update(JSONObject o) { + sign_key = o.getString("sign key"); + } + + @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 = ""; + + JSONObject o = null; + + try { + 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 = ""; + JWT j = new JWT(); + try { + j.parse(id_token); + JSONObject oo = new JSONObject(j.header); + alg = oo.getString("alg"); + + } 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("=", ""); + + j.payload = Tools.editJson( + EditOperation.Jwt_action.EDIT, + j.payload, + "$.at_hash", + null, + "", + at_hash_generated); + + String new_id_token = ""; + try { + j.sign = true; + j.private_key_pem = sign_key; + new_id_token = j.build(); + } catch (ParsingException e) { + applicable = false; + return; + } + + o.put("id_token", new_id_token); + + ((Operation_API) imported_api).message.setBody(false, o.toString()); + + applicable = true; // this means that all the steps that precedes the check were accomplished correctly + result = true; + } + + public Operation_API exporter() { + return (Operation_API) this.imported_api; + } +} diff --git a/tool/src/main/java/migt/Operation.java b/tool/src/main/java/migt/Operation.java index a0b12af..4d38b55 100644 --- a/tool/src/main/java/migt/Operation.java +++ b/tool/src/main/java/migt/Operation.java @@ -41,6 +41,7 @@ public class Operation extends Module { private SessionOperation.SessionAction sessionAction; // submodules private boolean at_hash_verify; + private At_Hash_update at_hash_update; /** * Instantiate an operation @@ -152,6 +153,10 @@ public Operation(JSONObject operation_json, if (operation_json.has("at_hash_verify")) { at_hash_verify = operation_json.getBoolean("at_hash_verify"); } + + if (operation_json.has("at_hash_update")) { + at_hash_update = new At_Hash_update(operation_json.getJSONObject("at_hash_update")); + } } private void init() { @@ -451,7 +456,8 @@ public void execute() { } } - // execute the message operations and the decode ops + // Execute other modules + // The order of execution is very important try { applicable = true; executeMessageOperations(this); @@ -468,7 +474,7 @@ public void execute() { return; if (at_hash_verify) { - At_Hash at = new At_Hash(); + At_Hash_check at = new At_Hash_check(); at.loader(api); at.execute(); setResult(at); @@ -477,6 +483,16 @@ public void execute() { } } + if (at_hash_update != null) { + at_hash_update.loader(api); + at_hash_update.execute(); + this.setAPI(at_hash_update.exporter()); + setResult(at_hash_update); + if (!applicable | !result) { + return; + } + } + // TODO: move this here instead of Execute Actives //executeSessionOps(, api.vars); //if (!applicable | !result) diff --git a/tool/src/test/java/At_Hash_Test.java b/tool/src/test/java/At_Hash_Test.java index d1c346b..8fee173 100644 --- a/tool/src/test/java/At_Hash_Test.java +++ b/tool/src/test/java/At_Hash_Test.java @@ -1,4 +1,4 @@ -import migt.At_Hash; +import migt.At_Hash_check; import migt.HTTPReqRes; import migt.Operation_API; import org.junit.jupiter.api.Test; @@ -48,7 +48,7 @@ public HTTPReqRes init_message_token_resp() { public void test_At_Hash() throws NoSuchAlgorithmException { HTTPReqRes test_message = init_message_token_resp(); - At_Hash ah = new At_Hash(); + At_Hash_check ah = new At_Hash_check(); Operation_API o = new Operation_API(test_message, false);