diff --git a/language/buildNumber.properties b/language/buildNumber.properties index 571dce803..a9ec2b4cc 100644 --- a/language/buildNumber.properties +++ b/language/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Sat Dec 28 07:19:34 CST 2024 -buildNumber\\d*=13761 +#Mon Dec 30 08:03:09 CST 2024 +buildNumber\\d*=13765 diff --git a/language/src/main/docs/crypto.odt b/language/src/main/docs/crypto.odt index 86f643522..4b975aea0 100644 Binary files a/language/src/main/docs/crypto.odt and b/language/src/main/docs/crypto.odt differ diff --git a/language/src/main/java/org/qdl_lang/evaluate/SystemEvaluator.java b/language/src/main/java/org/qdl_lang/evaluate/SystemEvaluator.java index fa76218b7..3a1fe40b1 100644 --- a/language/src/main/java/org/qdl_lang/evaluate/SystemEvaluator.java +++ b/language/src/main/java/org/qdl_lang/evaluate/SystemEvaluator.java @@ -1178,7 +1178,7 @@ private void doInputForm(Polyad polyad, State state) { argName = ((VariableNode) polyad.getArguments().get(0)).getVariableReference(); gotOne = true; } else { - if ((!gotOne) && polyad.getArguments().get(0) instanceof ExpressionImpl) { + if ((!gotOne) && (polyad.getArguments().get(0) instanceof ExpressionImpl || polyad.getArguments().get(0) instanceof ConstantNode)) { String out = InputFormUtil.inputForm(polyad.evalArg(0, state)); if (out == null) { out = ""; @@ -1187,6 +1187,7 @@ private void doInputForm(Polyad polyad, State state) { polyad.setResultType(Constant.STRING_TYPE); polyad.setResult(out); return; + }else{ } } diff --git a/language/src/main/java/org/qdl_lang/extensions/crypto/Crypto.java b/language/src/main/java/org/qdl_lang/extensions/crypto/Crypto.java index e338d25b6..490f1235d 100644 --- a/language/src/main/java/org/qdl_lang/extensions/crypto/Crypto.java +++ b/language/src/main/java/org/qdl_lang/extensions/crypto/Crypto.java @@ -19,6 +19,7 @@ import org.qdl_lang.exceptions.BadStemValueException; import org.qdl_lang.extensions.QDLFunction; import org.qdl_lang.extensions.QDLMetaModule; +import org.qdl_lang.extensions.QDLVariable; import org.qdl_lang.state.State; import org.qdl_lang.util.NoOpScalarImpl; import org.qdl_lang.util.ProcessScalarImpl; @@ -99,7 +100,7 @@ public Object evaluate(Object[] objects, State state) throws Throwable { } else { throw new BadArgException(getName() + " is missing the type of the key. Must be RSA or EC", 0); } - if (type.equals("RSA")) { + if (type.equals(RSA_TYPE)) { unknownType = false; if (stem.containsKey("length")) { keyLength = stem.getLong("length").intValue(); @@ -113,7 +114,7 @@ public Object evaluate(Object[] objects, State state) throws Throwable { webKey = getJwkUtil().createRSAKey(keyLength, alg); } - if (type.equals("EC")) { + if (type.equals(EC_TYPE)) { unknownType = false; if (stem.containsKey("curve")) { curve = stem.getString("curve"); @@ -125,7 +126,7 @@ public Object evaluate(Object[] objects, State state) throws Throwable { } webKey = getJwkUtil().createECKey(curve, alg); } - if (type.equals("AES")) { + if (type.equals(AES_TYPE)) { // See https://www.rfc-editor.org/rfc/rfc7518.html#section-6.4 unknownType = false; EncryptionMethod encryptionMethod = null; @@ -301,7 +302,8 @@ protected void addTypeHelp(List dd) { dd.add(" " + JWKS_TYPE + " - (default) JWKS key or set of keys"); dd.add(" " + PKCS_1_TYPE + " - PKCS 1, PEM encoded RSA private key"); dd.add(" " + PKCS_8_TYPE + " - PKCS 8, PEM encoded unencrypted private key"); - dd.add(" " + X509_TYPE + " - X509, PEM encoded public key"); + dd.add(" " + PKCS_8_PUBLIC_TYPE + " - PKCS 8 public, PEM encoded public key"); + dd.add(" " + X509_TYPE + " - \"X509\", PEM encoded public key. This is really PKCS 8 public, but used in X 509 certificates."); } public Object importPKCS(Object[] objects, State state) throws Throwable { @@ -323,6 +325,7 @@ public Object importPKCS(Object[] objects, State state) throws Throwable { privateKey = KeyUtil.fromPKCS8PEM(rawFile); break; case X509_TYPE: + case PKCS_8_PUBLIC_TYPE: publicKey = KeyUtil.fromX509PEM(rawFile); break; default: @@ -495,6 +498,7 @@ protected Object exportPKCS(Object[] objects, State state) throws Throwable { content = KeyUtil.toPKCS8PEM(jwk.privateKey); break; case X509_TYPE: + case PKCS_8_PUBLIC_TYPE: content = KeyUtil.toX509PEM(jwk.publicKey); break; default: @@ -505,9 +509,45 @@ protected Object exportPKCS(Object[] objects, State state) throws Throwable { } public static final String JWKS_TYPE = "jwks"; - public static final String PKCS_1_TYPE = "pkcs_1"; - public static final String PKCS_8_TYPE = "pkcs_8"; + public static final String PKCS_1_TYPE = "pkcs1"; + public static final String PKCS_8_TYPE = "pkcs8"; + public static final String PKCS_8_PUBLIC_TYPE = "public"; public static final String X509_TYPE = "x509"; + public static final String RSA_TYPE = "rsa"; + public static final String EC_TYPE = "elliptic"; + public static final String AES_TYPE = "aes"; + QDLStem types; + + public QDLStem getKeyTypes() { + if (types == null) { + types = new QDLStem(); + types.put("jwks", JWKS_TYPE); + types.put("pkcs1", PKCS_1_TYPE); + types.put("pkcs8", PKCS_8_TYPE); + types.put("public", PKCS_8_PUBLIC_TYPE); + types.put("x509", X509_TYPE); + types.put("rsa", RSA_TYPE); + types.put("ec", EC_TYPE); + types.put("aes", AES_TYPE); + } + return types; + } + + public static String KEY_TYPES_STEM_NAME = "$$KEY_TYPE."; + + public class KeyType implements QDLVariable { + QDLStem keyTypes = null; + + @Override + public String getName() { + return KEY_TYPES_STEM_NAME; + } + + @Override + public Object getValue() { + return getKeyTypes(); + } + } private JWK getJwk(PublicKey publicKey) { @@ -685,17 +725,16 @@ public List getDocumentation(int argCount) { List dd = new ArrayList<>(); switch (argCount) { case 2: - dd.add(getName() + "(key., arg|arg.) - encrypt a string or stem of them. If the key is symmetric, do symmetric encryption, otherwise encrypt with the private key"); + dd.add(getName() + "(arg|arg., key.) - encrypt a string or stem of them. If the key is symmetric, do symmetric encryption, otherwise encrypt with the private key"); break; case 3: - dd.add(getName() + "(key., arg|arg., use_private) - encrypt a string or stem of them with the private key if use_private is true"); + dd.add(getName() + "(arg|arg., key., use_private) - encrypt a string or stem of them with the private key if use_private is true"); dd.add(" or use the public key if false. Default is true"); break; } dd.add("key. - the RSA or symmetric key to use. N.B. Elliptic keys are not supported at this time."); dd.add("arg|arg. - a string or a stem of strings"); if (argCount == 3) { - dd.add("use_private - use the private key (if true, this is the default) and the public key if false"); } dd.add("NOTE: You can only encrypt a string with an RSA key that has fewer bits than the key."); @@ -705,16 +744,16 @@ public List getDocumentation(int argCount) { dd.add("One final reminder is that if encrypt/decrypt with one key and decrypt/encrypt with the" + "\nother or you will get an error"); dd.add("E.g."); - dd.add(" " + getName() + "(key., 'marizy doats')"); + dd.add(" " + getName() + "('marizy doats', key.)"); dd.add("(whole bunch of base 64 stuff that depends on the key)"); dd.add("Since this was encrypted with the private key, you would need to specify using the"); dd.add("public key in " + DECRYPT_NAME + " (which is, incidentally, the default there)."); dd.add("\nE.g. Symmetric example"); dd.add("Here, a symmetric key (AES) is created and used."); dd.add(" aes. := crypto#create_key({'type':'AES','alg':'A256GCM','length':512})\n" + - " crypto#encrypt(aes., 'woof woof woof') \n" + + " crypto#encrypt('woof woof woof', aes.) \n" + "67dmKZ6lqHwSt-mIZGs\n" + - " crypto#decrypt(aes., '67dmKZ6lqHwSt-mIZGs')\n" + + " crypto#decrypt('67dmKZ6lqHwSt-mIZGs', aes.)\n" + "woof woof woof\n"); return dd; } @@ -919,28 +958,28 @@ protected QDLStem webKeyToStem(JSONWebKey jsonWebKey) { */ public Object sDeOrEnCrypt(Object[] objects, boolean isEncrypt, String name) { byte[] key = null; - if (objects[0] instanceof QDLStem) { + if (objects[1] instanceof QDLStem) { // check that it is a JWK of type octet - QDLStem sKey = (QDLStem) objects[0]; + QDLStem sKey = (QDLStem) objects[1]; if (sKey.containsKey("kty")) { if (!sKey.getString("kty").equals("oct")) { - throw new BadArgException("Incorrect key type. Must be of type 'oct' (octet-encoded)", 0); + throw new BadArgException("Incorrect key type. Must be of type 'oct' (octet-encoded)", 1); } if (sKey.containsKey("k")) { key = Base64.decodeBase64(sKey.getString("k")); } else { - throw new BadArgException("Incorrect key format: missing 'k' entry for bytes", 0); + throw new BadArgException("Incorrect key format: missing 'k' entry for bytes", 1); } } } else { - if (!(objects[0] instanceof String)) { - throw new BadArgException("the first argument to " + name + " must be a base64 encoded key", 0); + if (!(objects[1] instanceof String)) { + throw new BadArgException("the first argument to " + name + " must be a base64 encoded key", 1); } - key = Base64.decodeBase64((String) objects[0]); + key = Base64.decodeBase64((String) objects[1]); } ProcessSymmetricDeorEncrypt processSymmetricDeorEncrypt = new ProcessSymmetricDeorEncrypt(key, isEncrypt); - return QDLAggregateUtil.process(objects[1], processSymmetricDeorEncrypt); + return QDLAggregateUtil.process(objects[0], processSymmetricDeorEncrypt); } protected class ProcessSymmetricDeorEncrypt extends ProcessScalarImpl { @@ -1059,6 +1098,7 @@ protected QDLStem certToStem(X509Certificate x509Certificate) { QDLStem subject = new QDLStem(); subject.put("x500", x509Certificate.getSubjectX500Principal().getName()); subject.put("dn", x509Certificate.getSubjectDN().getName()); + try { if (x509Certificate.getSubjectAlternativeNames() != null) { QDLStem altNames = processAltNames(x509Certificate.getSubjectAlternativeNames()); @@ -1077,6 +1117,7 @@ protected QDLStem certToStem(X509Certificate x509Certificate) { for (String x : x509Certificate.getNonCriticalExtensionOIDs()) { noncriticalOIDS.getQDLList().add(x); } + QDLStem oids = new QDLStem(); oids.put("critical", criticalOIDS); oids.put("noncritical", noncriticalOIDS); @@ -1236,50 +1277,39 @@ public int[] getArgCount() { public Object evaluate(Object[] objects, State state) throws Throwable { QDLStem certStem = (QDLStem) objects[0]; if (!certStem.containsKey("encoded")) { - throw new IllegalStateException("certs must contain encoded key"); + throw new BadArgException("certs must contain 'encoded' key", 0); } String cert = certStem.getString("encoded"); X509Certificate[] certs = CertUtil.fromX509PEM(cert); - // better be one? - boolean isScalar = false; - String oid = null; - QDLStem oidStem = null; - if (objects[1] instanceof String) { - oid = (String) objects[1]; - isScalar = true; - } else { - if (objects[1] instanceof QDLStem) { - oidStem = (QDLStem) objects[1]; - } else { - throw new BadArgException(getName() + "requires a string or stem as the second argument", 0); - } + ProcessOIDS processOIDS = new ProcessOIDS(certs[0]); + return QDLAggregateUtil.process(objects[1], processOIDS); + } + + protected class ProcessOIDS extends ProcessScalarImpl{ + public ProcessOIDS(X509Certificate x509Certificate) { + this.x509Certificate = x509Certificate; } - X509Certificate x509Certificate = certs[0]; - QDLStem outStem = new QDLStem(); - if (isScalar) { - byte[] bb = x509Certificate.getExtensionValue(oid); - if (bb == null) { - return QDLNull.getInstance(); - } + + X509Certificate x509Certificate; + @Override + public Object getDefaultValue(Object value) { + return QDLNull.getInstance(); + } + + @Override + public Object process(String oidKey) { + byte[] bb = x509Certificate.getExtensionValue(oidKey); + if(bb == null){ + return QDLNull.getInstance(); + } return Base64.encodeBase64URLSafeString(bb); } - for (Object key : oidStem.keySet()) { - String oidKey; - byte[] bb; - if (key instanceof Long) { - oidKey = oidStem.getString((Long) key); - } else { - oidKey = oidStem.getString((String) key); - } - bb = x509Certificate.getExtensionValue(oidKey); - if (bb == null) { - outStem.putLongOrString(key, QDLNull.getInstance()); - } else { - outStem.putLongOrString(key, Base64.encodeBase64URLSafeString(bb)); - } + + @Override + public Object process(Object key, String oidKey) { + return process(oidKey); } - return outStem; } @Override @@ -1287,13 +1317,12 @@ public List getDocumentation(int argCount) { List dd = new ArrayList<>(); dd.add(getName() + "(cert., oid | oids.) - get the given OIDs from a given cert."); dd.add("cert. - a single X509 certificate to be probed."); - dd.add(" oid - a string that is the oid (object id), e.g. '2.5.29.15'"); + dd.add(" oid - a string or (set of such strings) that is the oid (object id), e.g. '2.5.29.15'"); dd.add("oids. - a stem of oids whose keys are the return values. Only string-valued OIDs are supported."); - dd.add("You pass in either an oid or stem of them"); dd.add("This returns the base 64 encoded octet stream. Best we can do in general..."); dd.add("If there is no such value, a null is returned."); dd.add("An OID (object identifier) is a bit of X 509 voodoo that allows for"); - dd.add("addressing attributes. These are very specific and not standardizes, hence are"); + dd.add("addressing attributes. These are very specific and not standardized, hence are"); dd.add("bona fide low-level operations, but often the only way to get certain custom values"); dd.add("E.g. to get the EPPN (if present) from a cert"); dd.add(" " + getName() + "(cert., {'eppn':'1.3.6.1.4.1.5923.1.1.1.6'}"); diff --git a/language/src/main/java/org/qdl_lang/extensions/crypto/CryptoModule.java b/language/src/main/java/org/qdl_lang/extensions/crypto/CryptoModule.java index 102be0af6..1d51d117f 100644 --- a/language/src/main/java/org/qdl_lang/extensions/crypto/CryptoModule.java +++ b/language/src/main/java/org/qdl_lang/extensions/crypto/CryptoModule.java @@ -2,6 +2,7 @@ import org.qdl_lang.extensions.JavaModule; import org.qdl_lang.extensions.QDLFunction; +import org.qdl_lang.extensions.QDLVariable; import org.qdl_lang.module.Module; import org.qdl_lang.state.State; @@ -40,6 +41,9 @@ public Module newInstance(State state) { funcs.add(crypto.new FromJWT()); funcs.add(crypto.new VerifyJWT()); cryptoModule.addFunctions(funcs); + ArrayList vars = new ArrayList<>(); + vars.add(crypto.new KeyType()); + cryptoModule.addVariables(vars); if (state != null) { cryptoModule.init(state); } diff --git a/tests/src/test/java/org/qdl_lang/CryptoTest.java b/tests/src/test/java/org/qdl_lang/CryptoTest.java new file mode 100644 index 000000000..b7e13f771 --- /dev/null +++ b/tests/src/test/java/org/qdl_lang/CryptoTest.java @@ -0,0 +1,202 @@ +package org.qdl_lang; + +import edu.uiuc.ncsa.security.core.util.DebugUtil; +import org.qdl_lang.extensions.crypto.Crypto; +import org.qdl_lang.parsing.QDLInterpreter; +import org.qdl_lang.state.State; + +public class CryptoTest extends AbstractQDLTester { + /* + These files contain QDl scripts that just return the keys. This is so the test for + importing them is a separate test. + */ + String EC_PUBLIC_KEY = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/ec_public.qdl"; + String EC_KEY = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/ec.qdl"; + String RSA_KEY = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/rsa.qdl"; + String RSA_PUBLIC_KEY = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/rsa_public.qdl"; + String AES_KEY = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/aes.qdl"; + /* + These are test items in PEM format to check reading, import, etc. + */ + String TEST_CERT = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/github.pem"; + String TEST_PKCS1 = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/pkcs1.pem"; + String TEST_PKCS8 = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/pkcs8.pem"; + String TEST_PKCS8_PUBLIC = DebugUtil.getDevPath() + "/qdl/tests/src/test/resources/crypto/pkcs8_public.pem"; + + public void testSymmetricEncryption() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "aes. := script_load('" + AES_KEY + "');"); + addLine(script, "c := j_load('crypto');"); + addLine(script, "test_string := 'woof woof woof';"); + addLine(script, "encrypted_string := 'LXwYRcBgwmIpFUtAF10';"); + addLine(script, "test_set := {'arf',42,{'fnord'}};"); + addLine(script, "encrypted_set := {{'PH0YUYQ'},'O2ER',42};"); + addLine(script, "encrypt_string_ok := c#encrypt(test_string, aes.) == encrypted_string;"); + addLine(script, "decrypt_string_ok := c#decrypt(encrypted_string, aes.) == test_string;"); + addLine(script, "encrypt_set_ok := c#encrypt(test_set, aes.) == encrypted_set;"); + addLine(script, "decrypt_set_ok := c#decrypt(encrypted_set, aes.) == test_set;"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("encrypt_string_ok", state) : "symmetric encryption for string failed"; + assert getBooleanValue("decrypt_string_ok", state) : "symmetric decryption for string failed"; + assert getBooleanValue("encrypt_set_ok", state) : "symmetric encryption for set failed"; + assert getBooleanValue("decrypt_set_ok", state) : "symmetric decryption for set failed"; + } + + public void testRSAEncryption() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "rsa. := script_load('" + RSA_KEY + "');"); + addLine(script, "c := j_load('crypto');"); + addLine(script, "test_string := 'woof woof woof';"); + addLine(script, "encrypted_string := 'FWSjwdYsxmoZK0fHjb7pzo0y0_Cw1VDUNC4_LIpJRqJP3uwDbA6z2_8SE72Tn70cOFzLP45Ydhj4Fetbyz6EV6P-S4-FX5Kqj0KlR37FEQ3IlPs3boemZfn8RpBUQ5zZDTwRLjeDLG34iL1hk3bC2JLefFxzCbvVgK0EnMpCa7PGdsHAbcu_2mm4rw8uNrCAzFaNUBRZ_LYVnP2PwzM34Pl09OEr4E10q41YpXeNMSexxJzfUnkubmxNaTomY_iETElgrtOLeNcf8k-tGP9P9_zB7fgvE4ujJJCT3mvT0Fl1EhHa0ji2vN7vsiu2L8Qmh2SNNOHiUZoGoySNzMMWkw';"); + addLine(script, "test_set := {'arf',42,{'fnord'}};"); + addLine(script, "encrypted_set := {'D9aV2v7iXkdaHUcxmREh2btp35mFiiZpgdhK_CqMG46PNM2RATwZIUJMv1KvxIPmZwu0H2Fx1y-wBAEqeqVG0gopvk-Ts3MbsIwdt6Bq_znN54gPzZJSIp9jbwdiqM1HOkipAnwjTlA5WQ-glFgs_bFl-yHaIifnDtvRBDWoRYbOQGvAuo_HDi40_FT7xSYV-haRO4ofqINMnz-geeVt-KnSEvv_43pqDLLlOBTwdeLvkFJ4OASQ3jY6bKM4NmgAD-2ne9jgH2so7JgoFugWVQhIi2Jpjr2zr3Aw6RfAUCjbOdmxYFxwr0FIoxTUKuS38Pv9iociKZ2hobxgJDH7hA',{'ZBPnmBx8bC1Dwm8YcbCrkTRat14RdUDWsw7UDtBLYH1JuCxkdSqqb7nicyMVIFMvkv5T21dLSCtdQxfkoHFSEmWlPtW6KISdXQPVSHILehhvWYKAAZ52d7kbIgRmJnAsrL_3DvnRX05sGuWRIAQ3dQH3h4P0OliT7b-GvhPVEe6Wjxyi6LQnH3gcK2SbkQ5OyQtvkVNVVOt8ZZrPtemPc3jehNcgHX_U19ORVNmYajj9awxX_C32QsTAl6QveQaA-4_UmCVkDzZZRL66liaAjPo9hSbLChoADvSEDRLi96IJd2FW_J0M0qX_DghsHy0mS0qCehgpI54XOnwIdzz7EA'},42};"); + addLine(script, "encrypt_string_ok := c#encrypt(test_string, rsa.) == encrypted_string;"); + addLine(script, "decrypt_string_ok := c#decrypt(encrypted_string, rsa.) == test_string;"); + addLine(script, "encrypt_set_ok := c#encrypt(test_set, rsa.) == encrypted_set;"); + addLine(script, "decrypt_set_ok := c#decrypt(encrypted_set, rsa.) == test_set;"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("encrypt_string_ok", state) : "RSA encryption for string failed"; + assert getBooleanValue("decrypt_string_ok", state) : "RSA decryption for string failed"; + assert getBooleanValue("encrypt_set_ok", state) : "RSA encryption for set failed"; + assert getBooleanValue("decrypt_set_ok", state) : "RSA decryption for set failed"; + } + + public void testToPublic() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "rsa. := script_load('" + RSA_KEY + "');"); + addLine(script, "rsa_public. := script_load('" + RSA_PUBLIC_KEY + "');"); + addLine(script, "ec. := script_load('" + EC_KEY + "');"); + addLine(script, "ec_public. := script_load('" + EC_PUBLIC_KEY + "');"); + addLine(script, "aes. := script_load('" + AES_KEY + "');"); + addLine(script, "c := j_load('crypto');"); + addLine(script, "rsa_ok := false ∉ (c#to_public(rsa.) == rsa_public.);"); + addLine(script, "ec_ok := false ∉ (c#to_public(ec.) == ec_public.);"); + addLine(script, "aes_ok := false ∉ (c#to_public(aes.) == aes.);"); // symmetric key is public + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("rsa_ok", state) : "convert RSA to public key failed"; + assert getBooleanValue("ec_ok", state) : "convert EC to public key failed"; + assert getBooleanValue("aes_ok", state) : "convert AES to public key failed"; + } + + public void testJWTRSA() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "rsa. := script_load('" + RSA_KEY + "');"); + addLine(script, "c := j_load('crypto');"); + addLine(script, "test. := {'A':'p','B':{'integer':42},'C':3.1415};"); + addLine(script, "jwt := c#to_jwt(test., rsa.);"); + addLine(script, "verified := c#verify(jwt, rsa.);"); + addLine(script, "payload. := c#from_jwt(jwt);"); + addLine(script, "ok := false ∉ test. == payload.;"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("verified", state) : "JWT with RSA verified failed"; + assert getBooleanValue("ok", state) : "JWT payload failed"; + } + + public void testJWTEC() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "ec. := script_load('" + EC_KEY + "');"); + addLine(script, "c := j_load('crypto');"); + addLine(script, "test. := {'A':'p','B':{'integer':42},'C':3.1415};"); + addLine(script, "jwt := c#to_jwt(test., ec.);"); + addLine(script, "verified := c#verify(jwt, ec.);"); + addLine(script, "payload. := c#from_jwt(jwt);"); + addLine(script, "ok := false ∉ test. == payload.;"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("verified", state) : "JWT with EC key verified failed"; + assert getBooleanValue("ok", state) : "JWT payload failed"; + } + + public void testReadCert() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "c := j_load('crypto');"); + addLine(script, "git. := c#read_x509('" + TEST_CERT + "');"); + addLine(script, "alg_ok := git.'algorithm'.'name' == 'SHA256withECDSA';"); + addLine(script, "iss_ok := git.'issuer'.'alt_names'.'dNSName' == 'www.github.com';"); + addLine(script, "expires_at_ok := git.'not_after' == 1741391999000;"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("alg_ok", state) : "JWT with EC key verified failed"; + assert getBooleanValue("iss_ok", state) : "JWT with EC key verified failed"; + assert getBooleanValue("expires_at_ok", state) : "JWT with EC key verified failed"; + } + public void testReadCertOIDs() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "c := j_load('crypto');"); + addLine(script, "git. := c#read_x509('" + TEST_CERT + "');"); + addLine(script, "critical. :=['BAQDAgeA','BAIwAA'];"); + addLine(script, "noncritical. :=['BIIBcASCAWwBagB3AM8RVu7VLnyv84db2Wkum-kacWdKsBfsrAHSW3fOzDsIAAABjhY68BkAAAQDAEgwRgIhAPug3P_ag7xUZpZauquwFAHNAfSFGEwubXWh4ymDV81rAiEApZzSrtn6bENVhX_qi_t_-LQf9oBwdIIiL9AlwQKto6kAdgCi4wrkRe-9rZt-OO1HZ3dT14JbhJTXK14bLMS5UKRH5wAAAY4WOu_4AAAEAwBHMEUCIQDK6kQhUAyTRzwFVWkXRBuKx-gTDLnElApA57wS8xThbwIgYAgi7OPEEWUemSpyxrtRnLbjL8HrFmeS1TD817mrmEIAdwBOdaMnXJoQwzhbbNTfP1LrHfDgjhuNacCx-mSxYpo53wAAAY4WOu_3AAAEAwBIMEYCIQD7w69DOmBF_fW4sGwITyS0JR--yJFPvNZKp5eWIDT1NQIhANwHtef3toQMwEpcht2bkpn0aO9HKgX2yQPn_gad6gxb','BHgwdjBPBggrBgEFBQcwAoZDaHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAjBggrBgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20'," + + "'BBYEFDtoPzQ69Uc0yu-mTj2avV5uesyf'," + + "'BB4wHIIKZ2l0aHViLmNvbYIOd3d3LmdpdGh1Yi5jb20'," + + "'BEIwQDA0BgsrBgEEAbIxAQICBzAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZngQwBAgE'," + + "'BBgwFoAU9oUKOxGG4QR9DqoLLNLuzGR7e64'," + + "'BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC'];"); + addLine(script, "read_critical. := c#read_oid(git., git.'oids'.'critical');"); + addLine(script, "c_count_ok := size(critical.) == size(read_critical.);"); + addLine(script, "critical_ok := false ∉ read_critical. == critical.;"); + + addLine(script, "read_noncritical. ≔ c#read_oid(git., git.'oids'.'noncritical');"); + addLine(script, "nonc_count_ok := size(noncritical.) == size(read_noncritical.);"); + addLine(script, "noncritical_ok := false ∉ read_noncritical. == noncritical.;"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("c_count_ok", state) : "missing critical OIDs"; + assert getBooleanValue("critical_ok", state) : "incorrect critical OIDs"; + assert getBooleanValue("nonc_count_ok", state) : "missing noncritical OIDs"; + assert getBooleanValue("noncritical_ok", state) : "incorrect noncritical OIDs"; + } + public void testReadPKCS1() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "c := j_load('crypto');"); + addLine(script, "pkcs1. := c#import('" + TEST_PKCS1 + "', '" + Crypto.PKCS_1_TYPE + "');"); + // testing whole thing is messy, so we test parts that must work. + addLine(script, "dp_ok := pkcs1.'dp' == 'zhnLPN9F1Lqj-rWPry2fjfDulOpXCc4Q5NSlhvoYIxAoI0K7uaP4oGfRfHAFFCP4Q0NTpHIaFUNpf0ZiHfpz1hIaLHoClRGfw9J3elBImbYBdbVh5BIKZQ3QQl4lrXun3MlStoq5KILrgjby5SCqQtCrX7r_HvdBSmeixzbj0AU';"); + addLine(script, "e_ok := pkcs1.'e' == 'AQAB';"); + addLine(script, "q_ok := pkcs1.'q' == 'xwb2ZPwgCDVUSBYZxZdbrVOujrkW2Uk4KE7KVKMWLpDaCK2-c_LYXbsAJJ6JRecy-36XCUBgbzhFnFMdpoz9zOQ4_-t9DLvspwjy7jjLoSNAeUm26jTNVi5rNdySWJLdM0g5hPEOdbxSy8FrSaSfKKYca3DKeFPz1QCBg0jd_NE';"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("dp_ok", state) : "PKCS1 import key failed dp"; + assert getBooleanValue("e_ok", state) : "PKCS1 import key failed e"; + assert getBooleanValue("q_ok", state) : "PKCS1 import key failed q"; + } + public void testReadPKCS8() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "c := j_load('crypto');"); + addLine(script, "pkcs8. := c#import('" + TEST_PKCS8 + "', '" + Crypto.PKCS_8_TYPE + "');"); + // testing whole thing is messy, so we test parts that must work. + addLine(script, "dp_ok := pkcs8.'dp' == 'Q1E8HMJjtSp_vpg8Dgu8jLVBSWcpQ9wBjKaiYhC85BmNRTsE8QWLuIwUWam5lWqAdbM4Q31-ZKtj8cr4quD80ugx2Y_8c43N4-3MVErALf1agFZ52uZbkJSHJ7EytRaMbmy3cJ2raZ3ssd_-2AyBGHTbSsyzW2nnmHGycOSZm6E';"); + addLine(script, "e_ok := pkcs8.'e' == 'AQAB';"); + addLine(script, "q_ok := pkcs8.'q' == 'um0SMkYShdxFHLHWV1J9KqHpYH2kYx13lNhSRfspRKwjfsNyQWyIAAOdL8tW_O7458r9efkDv0MGR2I64nz2kdXMO7t3NLy2zP3HMVXws3HKjODiHD-3ebo6hxWLAiBqZ3KRsZChMzBPjK5bcXZdE7joGrmMOc2FbG7dSghfVwc';"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("dp_ok", state) : "PKCS8 import key failed dp"; + assert getBooleanValue("e_ok", state) : "PKCS8 import key failed e"; + assert getBooleanValue("q_ok", state) : "PKCS8 import key failed q"; + } + public void testReadPKCS8Public() throws Throwable { + StringBuffer script = new StringBuffer(); + addLine(script, "c := j_load('crypto');"); + addLine(script, "pkcs8. := c#import('" + TEST_PKCS8_PUBLIC + "', '" + Crypto.PKCS_8_PUBLIC_TYPE + "');"); + // testing whole thing is messy, so we test parts that must work. + addLine(script, "n_ok := 'jn-jMHBqZoAsQ9EkBd4iwx9QckvJe1gUf9K_0iH37fHypqXkxSnITv4Hw4P-HZTWa94Re9ZLv223LJETEOoc5inK-rm1h-R5UVvJIWz7HU' < pkcs8.'n';"); + addLine(script, "e_ok := pkcs8.'e' == 'AQAB';"); + State state = testUtils.getNewState(); + QDLInterpreter interpreter = new QDLInterpreter(null, state); + interpreter.execute(script.toString()); + assert getBooleanValue("n_ok", state) : "PKCS8 public key import failed n"; + assert getBooleanValue("e_ok", state) : "PKCS8 public key import failed e"; + } +} diff --git a/tests/src/test/java/org/qdl_lang/TestSuite.java b/tests/src/test/java/org/qdl_lang/TestSuite.java index 41f61e273..c82cfcfcb 100644 --- a/tests/src/test/java/org/qdl_lang/TestSuite.java +++ b/tests/src/test/java/org/qdl_lang/TestSuite.java @@ -26,10 +26,11 @@ OldModuleTests.class, GlomTest.class, SerializationTest.class, - // Without the VFS tests, all other tests (156 of them) take 1.532 s. (av. 9.82 ms per test) - // Running this next test adds a full 3 seconds for the initial database connection - // and unzipping. - // Point is that this is quite fast since pretty much every test creates a parser and executes it. + CryptoTest.class, + // Without the VFS tests, all other tests (569 of them, often with multiple parts, 1/1/2025) takes 6.559 s. + // (av. 11.6 ms per test) + // Running this next test adds up to several seconds for the initial database latency, + // Point is that QDL is actually quite fast since pretty much every test creates a parser and executes it. VFSTest.class }) diff --git a/tests/src/test/resources/crypto/aes.qdl b/tests/src/test/resources/crypto/aes.qdl new file mode 100644 index 000000000..2264845c4 --- /dev/null +++ b/tests/src/test/resources/crypto/aes.qdl @@ -0,0 +1,10 @@ +/* + AES (symmetric) key used for testing the cryptography module +*/ +return( +{ + 'k':'WhN3I-AXrQ1PNTwveDsB-eLR74HGDkC6_O5OmjBDBbrMejkTHpPPmRmlg9zwcW80FDS_sgPLU5ZFrU8WZLH1J_vvf8drILidqc2_Jyt5QteXYI1bxP82zVGGdOWDECKmHCQq0-D3PKcHeLJrpbPEKU7BklKayY9v-b9GhKdnhUeIzWkeq3ol-12PpVMk7hPsjpxbpFecqez6mVLiZBmbifaC0j4xv13WS1HQ2GbgFQN11MJRfhk63n0JxMZ1IlluTGW4rNNqL51d6Np9vLgQ9AaU49R6QFMx2VDxt3WXkpcqnQzctlmOtdpKdmEzrTZH06sL1Xda62KOZEwK6Detfg', + 'kid':'16776DE0877FFE3B', + 'kty':'oct' + } + ); diff --git a/tests/src/test/resources/crypto/ec.qdl b/tests/src/test/resources/crypto/ec.qdl new file mode 100644 index 000000000..213090cca --- /dev/null +++ b/tests/src/test/resources/crypto/ec.qdl @@ -0,0 +1,15 @@ +/* + Elliptic curve key used for testing the cryptography module +*/ +return( + {'alg':'ES256', + 'crv':'P-256', + 'd':'fOuEaM72yugYAlO14f5bj_aZO5S6h2nfVOAhzkxrbbI', + 'iat':1735649982, + 'kid':'D57E9C2511070859', + 'kty':'EC', + 'use':'sig', + 'x':'kTz5JR4GN26jBnXlTpcs3yYw1jirl6aF0YkZNNXoW24', + 'y':'uFtnSWEy1D1-iUNZXXYhNhBoufiR5U2iY95e7dV1JOY' + } +); diff --git a/tests/src/test/resources/crypto/ec_public.qdl b/tests/src/test/resources/crypto/ec_public.qdl new file mode 100644 index 000000000..1da648144 --- /dev/null +++ b/tests/src/test/resources/crypto/ec_public.qdl @@ -0,0 +1,10 @@ +return( +{ + 'alg':'ES256', + 'kid':'D57E9C2511070859', + 'kty':'EC', + 'use':'sig', + 'x':'AJE8-SUeBjduowZ15U6XLN8mMNY4q5emhdGJGTTV6Ftu', + 'y':'ALhbZ0lhMtQ9folDWV12ITYQaLn4keVNomPeXu3VdSTm' + } +); \ No newline at end of file diff --git a/tests/src/test/resources/crypto/github.pem b/tests/src/test/resources/crypto/github.pem new file mode 100644 index 000000000..7c41370ce --- /dev/null +++ b/tests/src/test/resources/crypto/github.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEozCCBEmgAwIBAgIQTij3hrZsGjuULNLEDrdCpTAKBggqhkjOPQQDAjCBjzEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5T +ZWN0aWdvIEVDQyBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMB4X +DTI0MDMwNzAwMDAwMFoXDTI1MDMwNzIzNTk1OVowFTETMBEGA1UEAxMKZ2l0aHVi +LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABARO/Ho9XdkY1qh9mAgjOUkW +mXTb05jgRulKciMVBuKB3ZHexvCdyoiCRHEMBfFXoZhWkQVMogNLo/lW215X3pGj +ggL+MIIC+jAfBgNVHSMEGDAWgBT2hQo7EYbhBH0Oqgss0u7MZHt7rjAdBgNVHQ4E +FgQUO2g/NDr1RzTK76ZOPZq9Xm56zJ8wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB +/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARCMEAw +NAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNv +bS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcBAQR4MHYwTwYIKwYBBQUHMAKGQ2h0 +dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb0VDQ0RvbWFpblZhbGlkYXRpb25T +ZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3Rp +Z28uY29tMIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwDPEVbu1S58r/OHW9lp +LpvpGnFnSrAX7KwB0lt3zsw7CAAAAY4WOvAZAAAEAwBIMEYCIQD7oNz/2oO8VGaW +WrqrsBQBzQH0hRhMLm11oeMpg1fNawIhAKWc0q7Z+mxDVYV/6ov7f/i0H/aAcHSC +Ii/QJcECraOpAHYAouMK5EXvva2bfjjtR2d3U9eCW4SU1yteGyzEuVCkR+cAAAGO +Fjrv+AAABAMARzBFAiEAyupEIVAMk0c8BVVpF0QbisfoEwy5xJQKQOe8EvMU4W8C +IGAIIuzjxBFlHpkqcsa7UZy24y/B6xZnktUw/Ne5q5hCAHcATnWjJ1yaEMM4W2zU +3z9S6x3w4I4bjWnAsfpksWKaOd8AAAGOFjrv9wAABAMASDBGAiEA+8OvQzpgRf31 +uLBsCE8ktCUfvsiRT7zWSqeXliA09TUCIQDcB7Xn97aEDMBKXIbdm5KZ9GjvRyoF +9skD5/4GneoMWzAlBgNVHREEHjAcggpnaXRodWIuY29tgg53d3cuZ2l0aHViLmNv +bTAKBggqhkjOPQQDAgNIADBFAiEAru2McPr0eNwcWNuDEY0a/rGzXRfRrm+6XfZe +SzhYZewCIBq4TUEBCgapv7xvAtRKdVdi/b4m36Uyej1ggyJsiesA +-----END CERTIFICATE----- \ No newline at end of file diff --git a/tests/src/test/resources/crypto/pkcs1.pem b/tests/src/test/resources/crypto/pkcs1.pem new file mode 100644 index 000000000..7ddb5a77a --- /dev/null +++ b/tests/src/test/resources/crypto/pkcs1.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEApWgvEY7U9ha0beYJOw+uH3UQx6NEl+LQdFMJ3z/mhNwnjtMS +i3gcDoFEg1vWWPPfKH5WpJ8KWzSG2safh9VS+jFOveGVkkNf/3EEj4ThTb8//VCW +NhyCSUv6FBfxElKir9FawGsXdUc6u/tpJfUW4oVLyPGe0t6sBYfHNscUEw0iLifP +YttwPvZsv5bEzmns7NPhdJtpfVSAsvYs4scqY+ekEdvw6H/CMQXUkh23lfJBGZ52 +HiX9m4XOiuA5IUiGeIHSY8FTMpxlZGbEFQCJ6ejfmrDMVNgjpkyKtGLN22RDmMJr +F5ieaVkJ4vm4l7175IFovVGywQ/8Q7RsvmpZjQIDAQABAoIBAAacfPEjN+DkNG8G +GE1biZapS7PnEzlM8A8OOKFLQRADarNbPW0Ern5n7VCgzZb9nGhWcGkhED1oApHh +CYRY4vmsGd0eFKfleINRxaBk68p7P/gEjhR4nD6IwmSmNlUIp4cGg62YOjZOVAsq +d9/0BWelc0uDWNlN3CtWa4CFgYhnWAZdyOd0qr2BgoHS/ARz1KYiXwvB9M8gn3TY +apeUE6K8taGJowHvs9SrtBmVHODCs5DrQdPCMs+nOC6W0YhtZN9Wj9WMfa9VnOxH +EPaDYXmHhNOfIWKfYPCHz0d2yi79vkmBWitGxUZU59uEnKCk5hncNR+0/NZEOygI +5ILA9qECgYEA1MF5519lPVA3w67ENSravE2mA5vN//KwJswRtVw2KrQWnGSgZCEf +YIPx9/MSB3bKL4HVEiwyRb2yEOk8LxFLO66g9NwEC3PfQ2zl6PVWk/TVzwTNBnSZ +uX3du+t/9tgMlSb8izOXo/NSjNvg//Qw3qDTbJ7BhN7cAIk3c66TT/0CgYEAxwb2 +ZPwgCDVUSBYZxZdbrVOujrkW2Uk4KE7KVKMWLpDaCK2+c/LYXbsAJJ6JRecy+36X +CUBgbzhFnFMdpoz9zOQ4/+t9DLvspwjy7jjLoSNAeUm26jTNVi5rNdySWJLdM0g5 +hPEOdbxSy8FrSaSfKKYca3DKeFPz1QCBg0jd/NECgYEAzhnLPN9F1Lqj+rWPry2f +jfDulOpXCc4Q5NSlhvoYIxAoI0K7uaP4oGfRfHAFFCP4Q0NTpHIaFUNpf0ZiHfpz +1hIaLHoClRGfw9J3elBImbYBdbVh5BIKZQ3QQl4lrXun3MlStoq5KILrgjby5SCq +QtCrX7r/HvdBSmeixzbj0AUCgYEAxL/iRjRkKhkmJ1kJwyZ8r5zhHCBnMQhL5rT1 +GbRbUk9J63hupUr1j7s0Sf8bbyo+YRZkkvW8H1a+oeocjrAkmi8nFf+jEDqamQmk +tdEZpEMoEn7Hv0HRl5etzoItfjg5Sd/lVMHEbKluJBUiTZva5yFHSQModMlxayjj +33fiU3ECgYARCQUzdu+EglaJd+7F4uC1AVrICEVRX1uiHJcZe/b/tPm4z3QV5D5U +vG/5BhSHBxeOv1Ir+DWWo/oYTy2ipHsMBx+0W78zmBnY22bq/sVCaLzvyXXjXIvO +qQ5Ahp6OMvv/MvjHdtMzP0MNCHdpSxjixvkK9dF4MBaN8S7BcNVLjQ== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/tests/src/test/resources/crypto/pkcs1_public.pem b/tests/src/test/resources/crypto/pkcs1_public.pem new file mode 100644 index 000000000..aa6c39805 --- /dev/null +++ b/tests/src/test/resources/crypto/pkcs1_public.pem @@ -0,0 +1,8 @@ +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEApWgvEY7U9ha0beYJOw+uH3UQx6NEl+LQdFMJ3z/mhNwnjtMSi3gc +DoFEg1vWWPPfKH5WpJ8KWzSG2safh9VS+jFOveGVkkNf/3EEj4ThTb8//VCWNhyC +SUv6FBfxElKir9FawGsXdUc6u/tpJfUW4oVLyPGe0t6sBYfHNscUEw0iLifPYttw +PvZsv5bEzmns7NPhdJtpfVSAsvYs4scqY+ekEdvw6H/CMQXUkh23lfJBGZ52HiX9 +m4XOiuA5IUiGeIHSY8FTMpxlZGbEFQCJ6ejfmrDMVNgjpkyKtGLN22RDmMJrF5ie +aVkJ4vm4l7175IFovVGywQ/8Q7RsvmpZjQIDAQAB +-----END RSA PUBLIC KEY----- \ No newline at end of file diff --git a/tests/src/test/resources/crypto/pkcs8.pem b/tests/src/test/resources/crypto/pkcs8.pem new file mode 100644 index 000000000..80c5bfd7c --- /dev/null +++ b/tests/src/test/resources/crypto/pkcs8.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOf6MwcGpmgCxD +0SQF3iLDH1ByS8l7WBR/0r/SIfft8fKmpeTFKchO/gfDg/4dlNZr3hF71ku/bbcs +kRMQ6hzmKcr6ubWH5HlRW8khbPsdT/6HFse8lODXzAZvZT+EHvE4BIYVKhFBWSJO +qQ75dSybdiTd11BWyoBbLctH4y3IwWkZARvKMX3mitD+hFn/cSVJFtP1URmv1dbW +s/4DrB2+M6KBlMMIbUOA43VP+UwDmQWCnixUrBlVL//WLWKrWo3Oknu4SrIopKBr +fcl32IVn+v84zB0AAB8ZeZ1DJB37DaHC10CLfHontmR3oBz9IB57UpHNl/YE/GyB +pnL/My43AgMBAAECggEALlwdMfGo1B8oB4o/r38FDTkfWYgJjUzrImjkyk8N08Zu +3MPFCVYeGoDv8q57GpGlLuPJCWJ/M1DVErVCIDMiYsk/BdIsWoE87PixF2RW0EMJ +ulZxdgtzmRGCGHS2tNvCO6jRHIqaoYmHrVK4EfWKpStLNDdMJmhsWP2tkH47E4BI +yhVcRbNRBmk3X+I9yuEFuVSYUU6URkiaUZdczClQeabhvBlrHZqHeS4qyfRzWn77 +PSXlLnpKVGcx7h4gqtxM6IZpmWjSrzM3kX7pPHFR+9xRNrUS5VY7hx7a6BjdeWKP +mETJFcYlC2vMhp30YNiiNF26yd6l2uJB6HK0JSSM0QKBgQDDrclWzMvVA5NfOBlS +6J3JvuSTKQUwc/gDi3IfDlzvbWfKIyIKnFaLqKu/B7CZYOAv0mB/yBDrjzAiUuqP +lf2L1OgbdBbXFL/1DPhHqNkETzcLHakKJ0T0n7AFXQVodBs3ci4Rucighfb1oThj +JAHIP6DMp5KLFcRNOcg12uvzUQKBgQC6bRIyRhKF3EUcsdZXUn0qoelgfaRjHXeU +2FJF+ylErCN+w3JBbIgAA50vy1b87vjnyv15+QO/QwZHYjrifPaR1cw7u3c0vLbM +/ccxVfCzccqM4OIcP7d5ujqHFYsCIGpncpGxkKEzME+Mrltxdl0TuOgauYw5zYVs +bt1KCF9XBwKBgENRPBzCY7Uqf76YPA4LvIy1QUlnKUPcAYymomIQvOQZjUU7BPEF +i7iMFFmpuZVqgHWzOEN9fmSrY/HK+Krg/NLoMdmP/HONzePtzFRKwC39WoBWedrm +W5CUhyexMrUWjG5st3Cdq2md7LHf/tgMgRh020rMs1tp55hxsnDkmZuhAoGBAJJd +nBG4jkZmoCRdQ4mfjAHyyQYm3u+qbP6BsGadQNNXyy51SLkw+r34GGsz3IINJYLn +Doe7CEQb3UwiRVUZA4WQNB8Zmgffui3LeZ60eJyKVJqy3ROMuwJpQhZYxaDPznqV +qumcQdOstGghZE36vi0D2cdRslSXlY+Co17CoAf1AoGAQ3Mj3B/8NlT9pAXRgHhB +Il6Inm/OP0Lz7bbGN/oL4heZr6/xzG3psCNNDvrXhae1vEU+BUr1QKZEwGXctaxX +mtjgp37KiMtwOcH+kJrnYrkGDjT3zdFH30vqZmUQZLIyiisIclxiWTwjPE2AQfWt +wlUWlxIUlMpkoVsmja7NSa8= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/tests/src/test/resources/crypto/pkcs8_public.pem b/tests/src/test/resources/crypto/pkcs8_public.pem new file mode 100644 index 000000000..9064d64eb --- /dev/null +++ b/tests/src/test/resources/crypto/pkcs8_public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjn+jMHBqZoAsQ9EkBd4i +wx9QckvJe1gUf9K/0iH37fHypqXkxSnITv4Hw4P+HZTWa94Re9ZLv223LJETEOoc +5inK+rm1h+R5UVvJIWz7HU/+hxbHvJTg18wGb2U/hB7xOASGFSoRQVkiTqkO+XUs +m3Yk3ddQVsqAWy3LR+MtyMFpGQEbyjF95orQ/oRZ/3ElSRbT9VEZr9XW1rP+A6wd +vjOigZTDCG1DgON1T/lMA5kFgp4sVKwZVS//1i1iq1qNzpJ7uEqyKKSga33Jd9iF +Z/r/OMwdAAAfGXmdQyQd+w2hwtdAi3x6J7Zkd6Ac/SAee1KRzZf2BPxsgaZy/zMu +NwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/src/test/resources/crypto/readme.txt b/tests/src/test/resources/crypto/readme.txt new file mode 100644 index 000000000..ba0d83ef2 --- /dev/null +++ b/tests/src/test/resources/crypto/readme.txt @@ -0,0 +1,4 @@ +This directory has files for testing the QDL cryptography module. +QDL scripts return a fixed key for testing. +There is also the current github public X509 cert in github.pem +Various public and private key formats for PEM are supported \ No newline at end of file diff --git a/tests/src/test/resources/crypto/rsa.qdl b/tests/src/test/resources/crypto/rsa.qdl new file mode 100644 index 000000000..4b593419b --- /dev/null +++ b/tests/src/test/resources/crypto/rsa.qdl @@ -0,0 +1,19 @@ +/* + RSA 2048 bit key used for testing the cryptography module +*/ +return( + {'alg':'RS256', + 'd':'m09jBZ-02WOFbjkzNrxfo-Hnyw6-9lOVIy2sw1x0ZpykpP9YhenLsqjbn4PMf5FHlMIYNevDQ_tnoz00qPK5_aU0PvnVb-I1opiXZDj6D6Pp6EPf_9gukVRhWp7OURov62g7K1Ok9dy-pxGezY7KBhmtcw4DwUf6TyvmI9sU4EdL4SIyThWvBA15w4GMMNJoBUEm81yMbwFB-h6qSzVlqk4O33KDDVAGqAZkVwjaVW2GmjRO4uJ94PCSyjhaTaZ38m1HOuSAeKQ4fpVaIukZqUZ771q49BB6TJosiAN9gzmygws1bZLd_xiJWexqh771ji58RWt7gvsvf0LpevpJJQ', + 'dp':'JTxjy0rRkeKWGhS6arNODbsZ-p_0DIWxeTD9Tg1NIXJuK6yn-kg7LrFnKG3sfYvZXHZ-1aSeht9dbkp2t2J4LD1WKU4rreovS3oWReMucpncGIXqGQMlhkKx1tjsUTk4kQRszWKULRSWkhQjjpoJECe7R9TNU9IrkcPbt5gdKkc', + 'dq':'h_iDE5LBCKDHNjIJRgmuHAgI5wGkbhJTwNincKzOs0LBLvNKtvnCGS3faLhT9bYEp84BLhISVlqf8lSSbgEFE4fBnR0aIvWkotFxE3XQokFsjrtl2ly4MAPGz5ol1vaLkML5mT7JSu4MDceRbl65DyulSWXBfVe2lbbghkgrm70', + 'e':'AQAB', + 'iat':1735649963, + 'kid':'E27317A047F4ABBB', + 'kty':'RSA', + 'n':'p9l1YGCQ7CxBAKpazj0YmaNEiGJaqI-djD-CTZJ7rfQ8TsqQy-2mxDZZgdlFeubKGFCaK_JGxxeGcmqcawoZBMEaOI1smT3J_iBnmcWoviohGzUC5ibxA_5OSfiQyE6XzkmXwDLc1iRVPbda0KlvXUyw-J09aHyiw1urHJkLFl5mP5MBQiuNtSAJ4vBMSfLstkaiMI-fyCMyQ1T6kdm9y5alPu5ix4o-Ia2T_Hz_tdcW7d2flnv6D_Jk3fuR1AidIRi3Vso7Gsc5c_lflMiBBeVYxB3QdR5xopOfWi0HQoCu2DQQ7FAllX2LCTl4GRxdMlqipqVDgHP0rJRn6iex2Q', + 'p':'7QpLosQeQU2bmxzVJBMERyWQ49Mbo3DRpWXwm9QEDAZBv916Cv8EUyr9iLe8cxcfnO20qHs-B3UNytU62udlZepTkTQR5-0IOO3ERSo06Q5QxpVpuvO0V2-UBL5bUC7LHXEQLu60-C-iqv_RHsykeKv2V0iDtUnpqxd8ZKe32Os', + 'q':'tUZkh6OJCDooorHpu3YUgaQkHZfNK9-jccpSDzUwK-s76Ga0WA3g_41PCPyzrRqPqJucczYRhVpy6FluN_2EL0T3XsNTkCMzsaqNMh-fIx76yF8DJOpGvsSoSeminxDgLWdNQIvUWPxWwmQWfIxQMATFyzW01nrmurFyxfVoL0s', + 'qi':'OGJv6iP_WUhDklGrU2f86tc1pfjJpmILZv8W2MfM6uTOR3OcBpUGmW0Lx6k_WprxM1C0J3UIx6iOZ1B7VPu01bGV6JoEuEfycYPsGf7yc1oVAPfdn8pFYzLASzbfIq78X7689xmMLBxuFKB6MFsJBc3ZDfYw1aSGbCpCdMXhbx8', + 'use':'sig' + } + ); diff --git a/tests/src/test/resources/crypto/rsa_public.qdl b/tests/src/test/resources/crypto/rsa_public.qdl new file mode 100644 index 000000000..854a3352e --- /dev/null +++ b/tests/src/test/resources/crypto/rsa_public.qdl @@ -0,0 +1,10 @@ +return( +{ + 'alg':'RS256', + 'e':'AQAB', + 'kid':'E27317A047F4ABBB', + 'kty':'RSA', + 'n':'AKfZdWBgkOwsQQCqWs49GJmjRIhiWqiPnYw_gk2Se630PE7KkMvtpsQ2WYHZRXrmyhhQmivyRscXhnJqnGsKGQTBGjiNbJk9yf4gZ5nFqL4qIRs1AuYm8QP-Tkn4kMhOl85Jl8Ay3NYkVT23WtCpb11MsPidPWh8osNbqxyZCxZeZj-TAUIrjbUgCeLwTEny7LZGojCPn8gjMkNU-pHZvcuWpT7uYseKPiGtk_x8_7XXFu3dn5Z7-g_yZN37kdQInSEYt1bKOxrHOXP5X5TIgQXlWMQd0HUecaKTn1otB0KArtg0EOxQJZV9iwk5eBkcXTJaoqalQ4Bz9KyUZ-onsdk', + 'use':'sig' + } +); \ No newline at end of file