package milkman.plugin.privatebin; import java.io.InputStreamReader; import java.util.Base64; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.SimpleScriptContext; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; import milkman.plugin.privatebin.PrivateBinApi.PrivateBinDataV1; /** * uses sjcl for de-/encryption. i cannot find another way to make it work in browser as well. * this is slower for obvious reasons than JavaDeEncryptor * * @author peter * */ public class SjclDeEncryptor implements DeEncryptor { private static final int AES_KEY_LENGTH = 256; private ScriptEngine engine; private SimpleScriptContext ctx; public SjclDeEncryptor() { init(); } @SneakyThrows private void init() { engine = new ScriptEngineManager().getEngineByName("nashorn"); ctx = new SimpleScriptContext(); ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); engine.eval(new InputStreamReader(getClass().getResourceAsStream("/js/sjcl-1.0.7.js")), ctx); engine.eval( "function encrypt(pass,data){try{return sjcl.encrypt(pass,data, {mode:'gcm',ks:256,ts:128});}catch(e){return e;}}", ctx); engine.eval( "function decrypt(pass,data){try{return sjcl.decrypt(pass,data);}catch(e){return e;}}", ctx); } @Override public PrivateBinDataV1 encrypt(String strToEncrypt) throws Exception { byte[] key = DeEncryptor.generateRandomKey(AES_KEY_LENGTH); return encodeInSjcl(strToEncrypt, Base64.getEncoder().encodeToString(key)); } @Override public String decrypt(PrivateBinDataV1 data, String secret64) throws Exception { return decodeSjcl(data, secret64 ); } private PrivateBinDataV1 encodeInSjcl(String strToEncrypt, String secret64) throws Exception { ObjectMapper m = new ObjectMapper(); Bindings bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE); bindings.put("secret", secret64); bindings.put("data", strToEncrypt); Object evaluation = engine.eval("encrypt(secret,data)", ctx); if (evaluation instanceof Bindings) { Bindings eval = (Bindings) evaluation; if (eval.containsKey("message")) { System.err.println(); throw new RuntimeException("Failed to encrypt: " + eval.get("message")); } } PrivateBinDataV1 data = m.readValue((String)evaluation, PrivateBinDataV1.class); data.setSecret(secret64); return data; } private String decodeSjcl(PrivateBinDataV1 data, String secret64) throws Exception { ObjectMapper m = new ObjectMapper(); Bindings bindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE); bindings.put("secret", secret64); // bindings.put("data", "\"" + m.writeValueAsString(data).replace("\"", "\\\"") + "\""); bindings.put("data", m.writeValueAsString(data)); Object evaluation = engine.eval("decrypt(secret,data)", ctx); if (evaluation instanceof Bindings) { Bindings eval = (Bindings) evaluation; if (eval.containsKey("message")) { System.err.println(); throw new RuntimeException("Failed to decrypt: " + eval.get("message")); } } return (String)evaluation; } }