package com.laytonsmith.core.functions;
import com.laytonsmith.PureUtilities.Common.StringUtils;
import com.laytonsmith.PureUtilities.Version;
import com.laytonsmith.annotations.api;
import com.laytonsmith.annotations.core;
import com.laytonsmith.core.CHVersion;
import com.laytonsmith.core.Optimizable;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.constructs.CBoolean;
import com.laytonsmith.core.constructs.CByteArray;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREPluginInternalException;
import com.laytonsmith.core.exceptions.CRE.CRERangeException;
import com.laytonsmith.core.exceptions.CRE.CREThrowable;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.EnumSet;
import java.util.Set;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.mindrot.jbcrypt.BCrypt;
/**
*
*/
@core
public class Crypto {
public static String docs() {
return "Provides common cryptographic functions";
}
private static CString getHMAC(String algorithm, Target t, Construct[] args) {
try {
SecretKeySpec signingKey = new SecretKeySpec(args[0].val().getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(signingKey);
byte[] hmac = mac.doFinal(args[1].val().getBytes());
String hash = StringUtils.toHex(hmac).toLowerCase();
return new CString(hash, t);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
throw new CREPluginInternalException("An error occured while trying to hash your data", t, ex);
}
}
@api
public static class rot13 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "rot13";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {val} Returns the rot13 version of val. Note that rot13(rot13(val)) returns val";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
String s = args[0].val();
StringBuilder b = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= 'a' && c <= 'm') {
c += 13;
} else if (c >= 'n' && c <= 'z') {
c -= 13;
} else if (c >= 'A' && c <= 'M') {
c += 13;
} else if (c >= 'A' && c <= 'Z') {
c -= 13;
}
b.append(c);
}
return new CString(b.toString(), t);
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "rot13('string')"),
new ExampleScript("Basic usage", "rot13('fgevat')"),};
}
}
@api
public static class md5 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "md5";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {val} Returns the md5 hash of the specified string. The md5 hash is no longer considered secure, so you should"
+ " not use it for storage of sensitive data, however for general hashing, it is a quick and easy solution. md5 is"
+ " a one way hashing algorithm.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(args[0].val().getBytes());
String hash = StringUtils.toHex(digest.digest()).toLowerCase();
return new CString(hash, t);
} catch (NoSuchAlgorithmException ex) {
throw new CREPluginInternalException("An error occured while trying to hash your data", t, ex);
}
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "md5('string')"),
new ExampleScript("Basic usage", "md5('String')"),};
}
}
@api
public static class sha1 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "sha1";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {val} Returns the sha1 hash of the specified string. Note that sha1 is considered more secure than md5,"
+ " but is also not considered secure. sha-256 should be used instead for storing sensitive"
+ " data. It is a one way hashing algorithm.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA1");
digest.update(args[0].val().getBytes());
String hash = StringUtils.toHex(digest.digest()).toLowerCase();
return new CString(hash, t);
} catch (NoSuchAlgorithmException ex) {
throw new CREPluginInternalException("An error occured while trying to hash your data", t, ex);
}
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "sha1('string')"),
new ExampleScript("Basic usage", "sha1('String')"),};
}
}
@api
public static class sha256 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "sha256";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {val} Returns the sha256 hash of the specified string. Note that sha256 is considered more secure than sha1 and md5, and is"
+ " typically used when storing sensitive data. It is a one way hashing algorithm.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
digest.update(args[0].val().getBytes());
String hash = StringUtils.toHex(digest.digest()).toLowerCase();
return new CString(hash, t);
} catch (NoSuchAlgorithmException ex) {
throw new CREPluginInternalException("An error occured while trying to hash your data", t, ex);
}
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "sha256('string')"),
new ExampleScript("Basic usage", "sha256('String')"),};
}
}
@api
public static class sha512 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "sha512";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {val} Returns the sha512 hash of the specified string. Note that sha512"
+ " is considered more secure than sha1 and md5 (and sha256, because it takes longer to calculate),"
+ " and is typically used when storing sensitive data. It is a one way hashing algorithm.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_2;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-512");
digest.update(args[0].val().getBytes());
String hash = StringUtils.toHex(digest.digest()).toLowerCase();
return new CString(hash, t);
} catch (NoSuchAlgorithmException ex) {
throw new CREPluginInternalException("An error occured while trying to hash your data", t, ex);
}
}
@Override
public Set<Optimizable.OptimizationOption> optimizationOptions() {
return EnumSet.of(
Optimizable.OptimizationOption.CONSTANT_OFFLINE,
Optimizable.OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "sha512('string')"),
new ExampleScript("Basic usage", "sha512('String')"),};
}
}
@api
public static class bcrypt extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CRERangeException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
int log_rounds = 5;
if (args.length == 2) {
log_rounds = Static.getInt32(args[1], t);
}
try {
String hash = BCrypt.hashpw(args[0].val(), BCrypt.gensalt(log_rounds));
return new CString(hash, t);
} catch (IllegalArgumentException ex) {
throw new CRERangeException(ex.getMessage(), t);
}
}
@Override
public String getName() {
return "bcrypt";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
return "string {val, [workload]} Encrypts a value using bcrypt, using the specified workload, or 5 if none"
+ " provided. BCrypt is supposedly more secure than SHA1, and"
+ " certainly more secure than md5. Note that using bcrypt is slower, which is one of its security"
+ " advantages, however, setting the workload to higher numbers"
+ " will take exponentially more time. A workload of 5 is a moderate operation, which should"
+ " complete in under a second, however, setting it to 10 will take"
+ " many seconds, and setting it to 15 will take a few minutes. The workload must be between 5"
+ " and 31. See the documentation for check_bcrypt for full usage. Bcrypt is recommended for"
+ " password hashing, whereas sha-* functions are not.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "bcrypt('string')", ":$2a$05$aBMYDJAu6C3O.142N/n7yO6Dl3KC0L/zHUEZnOXQuaX13XUKec8Gy"),
new ExampleScript("Basic usage", "bcrypt('String')", ":$2a$05$jYm.4yath40V2DqjipWSje3Ed0ZNLO8IcDjIF50PJoPvWSmF1J7L2"),};
}
}
@api
public static class check_bcrypt extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return null;
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
return CBoolean.get(BCrypt.checkpw(args[0].val(), args[1].val()));
}
@Override
public String getName() {
return "check_bcrypt";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "boolean {plaintext, hash} Checks to see if this plaintext password does in fact hash to the hash"
+ " specified. Unlike md5 or sha1, simply comparing hashes won't work. Consider the following"
+ " usage:\n"
+ "string @plain = 'plaintext';\n"
+ "string @hash = bcrypt(@plain);\n"
+ "msg(if(check_bcrypt(@plain, @hash),"
+ " 'They match!', 'They do not match!'));\n";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "assign(@plain, 'plaintext')\nassign(@hash, bcrypt(@plain))\n"
+ "msg(if(check_bcrypt(@plain, @hash), 'They match!', 'They do not match!'))\n"
+ "msg(if(check_bcrypt('notTheRightPassword', @hash), 'They match!', 'They do not match!'))"),};
}
}
@api
public static class base64_encode extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
CByteArray ba = Static.getByteArray(args[0], t);
byte[] data = ba.asByteArrayCopy();
data = Base64.encodeBase64(data);
return CByteArray.wrap(data, t);
}
@Override
public String getName() {
return "base64_encode";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "byte_array {byteData} Encodes the given byte_array data into a base 64 byte_array.";
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "string_from_bytes(base64_encode(string_get_bytes('A string')))")
};
}
}
@api
public static class base64_decode extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
CByteArray ba = Static.getByteArray(args[0], t);
byte[] data = ba.asByteArrayCopy();
data = Base64.decodeBase64(data);
return CByteArray.wrap(data, t);
}
@Override
public String getName() {
return "base64_decode";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "byte_array {base64data} Decodes the base 64 encoded byte_array data back into the original byte_array data.";
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "string_from_bytes(base64_decode(string_get_bytes('QSBzdHJpbmc=')))")
};
}
}
@api
public static class hmac_md5 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "hmac_md5";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "string {key, val} Returns the md5 HMAC of the specified string using the provided key.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
return getHMAC("HmacMD5", t, args);
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "hmac_md5('secret_key', 'string')"),};
}
}
@api
public static class hmac_sha1 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "hmac_sha1";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "string {key, val} Returns the sha1 HMAC of the specified string using the provided key.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
return getHMAC("HmacSHA1", t, args);
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "hmac_sha1('secret_key', 'string')"),};
}
}
@api
public static class hmac_sha256 extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "hmac_sha256";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "string {key, val} Returns the sha256 HMAC of the specified string using the provided key.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
return getHMAC("HmacSHA256", t, args);
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
@Override
public ExampleScript[] examples() throws ConfigCompileException {
return new ExampleScript[]{
new ExampleScript("Basic usage", "hmac_sha256('secret_key', 'string')"),};
}
}
}