initial commit after rename
21
AndroidManifest.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="net.chaoswebs.snow10">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||||
|
<activity android:name=".MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
8
LICENSE
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
Copyright (c) 2017 Kevin Froman
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
36
README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Snow2
|
||||||
|
|
||||||
|
Obfuscate & Encrypt Text as White-Space
|
||||||
|
|
||||||
|
Demo: [https://ChaosWebs.net/snow2](https://chaoswebs.net/snow2/)
|
||||||
|
|
||||||
|
![snow image](snow.jpg)
|
||||||
|
|
||||||
|
# About
|
||||||
|
|
||||||
|
Snow2 is a web app inspired by the [original snow program](http://darkside.com.au/snow/) to use whitespace/zero-width characters to obfuscate messages within the whitespace of other content.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
MIT license, see LICENSE file.
|
||||||
|
|
||||||
|
# Warnings
|
||||||
|
|
||||||
|
* No steganography is perfect, your message may be discovered especially by skilled adversaries.
|
||||||
|
* Some text editors (like vim) show zero width characters
|
||||||
|
* Some clients/places may strip whitespace characters
|
||||||
|
* Use encryption if you really need to protect your messages. Use a good password but keep in mind that no encryption is perfect (Snow2 uses cryptojs AES)
|
||||||
|
* It is best to save the site offline to avoid your messages being intercepted by possible malicious code sent from (my) web server
|
||||||
|
* The larger your message, the more obvious it is that it is there.
|
||||||
|
|
||||||
|
# Roadmap (to do)
|
||||||
|
|
||||||
|
* Ability to automatically spread/retrieve message content into paragraphs of "cover" text
|
||||||
|
|
||||||
|
* CLI version
|
||||||
|
|
||||||
|
* Android app
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
Feel free to make a pull request if you feel that you have improved the software.
|
35
assets/aes.min.js
vendored
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
CryptoJS v3.1.2
|
||||||
|
code.google.com/p/crypto-js
|
||||||
|
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||||
|
code.google.com/p/crypto-js/wiki/License
|
||||||
|
*/
|
||||||
|
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
|
||||||
|
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
|
||||||
|
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
|
||||||
|
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
|
||||||
|
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
|
||||||
|
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
|
||||||
|
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
|
||||||
|
(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
|
||||||
|
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
|
||||||
|
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
|
||||||
|
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
|
||||||
|
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
|
||||||
|
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
|
||||||
|
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
|
||||||
|
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
|
||||||
|
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
|
||||||
|
l)}})();
|
||||||
|
CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
|
||||||
|
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
|
||||||
|
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
|
||||||
|
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
|
||||||
|
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
|
||||||
|
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
|
||||||
|
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
|
||||||
|
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
|
||||||
|
(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
|
||||||
|
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
|
||||||
|
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
|
||||||
|
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();
|
5
assets/bootstrap-theme.min.css
vendored
Normal file
5
assets/bootstrap.min.css
vendored
Normal file
7
assets/bootstrap.min.js
vendored
Normal file
7
assets/clipboard.min.js
vendored
Normal file
4
assets/font-awesome.min.css
vendored
Normal file
BIN
assets/fonts/FontAwesome.otf
Normal file
BIN
assets/fonts/fontawesome-webfont.eot
Normal file
2671
assets/fonts/fontawesome-webfont.svg
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
assets/fonts/fontawesome-webfont.ttf
Normal file
BIN
assets/fonts/fontawesome-webfont.woff
Normal file
BIN
assets/fonts/fontawesome-webfont.woff2
Normal file
66
assets/help.html
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<!-- Copyright (c) 2017 Kevin Froman MIT (expat) license -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<meta name='description' content="Perform Whitespace steganography with encryption and zero width characters">
|
||||||
|
<meta name='author' content='Kevin Froman'>
|
||||||
|
<title>Snow10</title>
|
||||||
|
<link rel="stylesheet" href="./bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="./bootstrap-theme.min.css">
|
||||||
|
<link rel="stylesheet" href="./font-awesome.min.css">
|
||||||
|
<script src="./jquery.min.js"></script>
|
||||||
|
<script src="./bootstrap.min.js"></script>
|
||||||
|
<script src="./clipboard.min.js"></script>
|
||||||
|
<script src='./aes.min.js'></script>
|
||||||
|
<link rel='stylesheet' href='./theme.min.css'>
|
||||||
|
<style>p{ font-family: serif; }</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class='container'>
|
||||||
|
|
||||||
|
<div class='page-header'>
|
||||||
|
<h1 class='center'>Snow10 ☃</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class='center'><a href='./index.html' class='btn btn-primary'>Go Back</a></p>
|
||||||
|
|
||||||
|
<h1>About</h1>
|
||||||
|
<p>Snow10 is a free and open-source web and Android app for converting messages into invisible whitespace characters and back again.</p>
|
||||||
|
<br>
|
||||||
|
<img src='./snow.jpg' alt='snow image'>
|
||||||
|
<br><br>
|
||||||
|
<p><a href='https://github.com/beardog108/snow10/'>It is available on Github</a>.</p>
|
||||||
|
<h1>Encoding Basics</h1>
|
||||||
|
<p>Snow10 messages are meant to be embedded elsewhere, specifically within or by standard blocks of text.</p>
|
||||||
|
<p>Messages with the 'zero width characters' setting work in most places that accept unicode input.</p>
|
||||||
|
<p>Simply enter the message you want to hide <b>(waring: larger messages are more noticeable, and really massive messages may be dangerous/not work)</b></p>
|
||||||
|
<p>You can uncheck 'use zero width characters' but this is more noticeable in some cases and is more likely to be stripped out.</p>
|
||||||
|
<p>Then, enter a decent password in both passwords fields to encrypt your message (or disable encryption, but note that doing so will let anyone read your message.)</p>
|
||||||
|
<p>Press 'encode' and wait for the output window to show, then, press 'copy to clipboard'.</p>
|
||||||
|
<p>You can now paste your message inside Twitter DMs, social media posts, word documents, or elsewhere.</p>
|
||||||
|
|
||||||
|
<h2>Decoding Basics</h2>
|
||||||
|
|
||||||
|
<p>To retrieve a Snow10 message, copy paste it into the text field.</p>
|
||||||
|
<p>It is important to get the full message, & if not using 'zero width characters', then remove any non-message text. <b>This can be difficult, but failing to do so many result in an incomplete or corrupted message.</b></p>
|
||||||
|
<p>If the message is encrypted, enter the password (you only need to enter it into the first field to decode).</p>
|
||||||
|
<p>If the message is not encrypted, uncheck 'use encryption'</p>
|
||||||
|
<p>Then, press decode.</p>
|
||||||
|
<p>If all went right, then you should see the decoded message.</p>
|
||||||
|
|
||||||
|
<h2>Example</h2>
|
||||||
|
|
||||||
|
<p>Here is an example of Snow10's use.</p>
|
||||||
|
|
||||||
|
<input type='text' readonly value='Put this into Snow10' onclick='this.select();'>
|
||||||
|
<br><br>
|
||||||
|
<p class='center'><a href='./index.html' class='btn btn-primary'>Go Back</a></p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<footer class='center'>made with <i class='fa fa-heart'></i> by <a href='https://chaoswebs.net/'>Beardog</a></footer>
|
||||||
|
<script src='./main.js'></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
74
assets/index.html
Executable file
@ -0,0 +1,74 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<!-- Copyright (c) 2017 Kevin Froman MIT (expat) license -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<meta name='description' content="Perform Whitespace steganography with encryption and zero width characters">
|
||||||
|
<meta name='author' content='Kevin Froman'>
|
||||||
|
<title>Snow10</title>
|
||||||
|
<link rel="stylesheet" href="./bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="./bootstrap-theme.min.css">
|
||||||
|
<link rel="stylesheet" href="./font-awesome.min.css">
|
||||||
|
<script src="./jquery.min.js"></script>
|
||||||
|
<script src="./bootstrap.min.js"></script>
|
||||||
|
<script src="./clipboard.min.js"></script>
|
||||||
|
<script src="./jquery.bootstrap-growl.min.js"></script>
|
||||||
|
<script src='./aes.min.js'></script>
|
||||||
|
<link rel='stylesheet' href='./theme.min.css'>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="modal" id="outputModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title center" id="myModalLabel">Your Text Is Ready!</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<br>
|
||||||
|
<pre id='output'></pre>
|
||||||
|
<div class='center'><button class="btn btn-success" data-clipboard-action="copy" data-clipboard-target="#output">Copy to Clipboard</button></div>
|
||||||
|
<div id='copyFeedback' class='center'></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal" id='modalClose'>Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='container'>
|
||||||
|
|
||||||
|
<div class='page-header'>
|
||||||
|
<h1 class='center'>Snow10 ☃</h1>
|
||||||
|
</div>
|
||||||
|
<p class='center'><a href='help.html'>Help! I'm confused!</a></p>
|
||||||
|
<div id='dataArea'>
|
||||||
|
<textarea id='text' placeholder=''></textarea>
|
||||||
|
</div>
|
||||||
|
<div class='center'><button class='btn btn-sm btn-default dataItem' id='clearInputButton'>Clear Input <i class='fa fa-cross'></i></button></div>
|
||||||
|
<br><br>
|
||||||
|
<div class='center'>
|
||||||
|
<label>Use Zero-Width Characters <input type='checkbox' id='useZeroWidthCharacters' checked></label>
|
||||||
|
<br><br>
|
||||||
|
<label>Use Encryption <input type='checkbox' id='useEncrypt'></label>
|
||||||
|
<br><br>
|
||||||
|
<span id='encryptArea'>
|
||||||
|
<input type='password' id='password' placeholder='Encryption password' class='dataItem'>
|
||||||
|
<br>
|
||||||
|
<input type='password' id='confirmPass' placeholder='Confirm password' class='dataItem'>
|
||||||
|
<br>
|
||||||
|
</span>
|
||||||
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
|
<button type="button" class="btn btn-primary" id="encode">Encode <i class='fa fa-lock'></i></button>
|
||||||
|
<button type="button" class="btn btn-primary" id="decode">Decode <i class='fa fa-unlock'></i></button>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class='center'>made with <i class='fa fa-heart'></i> by <a href='https://chaoswebs.net/'>Beardog</a></footer>
|
||||||
|
<script src='./main.js'></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
assets/jquery.bootstrap-growl.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(){var c;c=jQuery;c.bootstrapGrowl=function(f,a){var b,e,d;a=c.extend({},c.bootstrapGrowl.default_options,a);b=c("<div>");b.attr("class","bootstrap-growl alert");a.type&&b.addClass("alert-"+a.type);a.allow_dismiss&&(b.addClass("alert-dismissible"),b.append('<button class="close" data-dismiss="alert" type="button"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>'));b.append(f);a.top_offset&&(a.offset={from:"top",amount:a.top_offset});d=a.offset.amount;c(".bootstrap-growl").each(function(){return d= Math.max(d,parseInt(c(this).css(a.offset.from))+c(this).outerHeight()+a.stackup_spacing)});e={position:"body"===a.ele?"fixed":"absolute",margin:0,"z-index":"9999",display:"none"};e[a.offset.from]=d+"px";b.css(e);"auto"!==a.width&&b.css("width",a.width+"px");c(a.ele).append(b);switch(a.align){case "center":b.css({left:"50%","margin-left":"-"+b.outerWidth()/2+"px"});break;case "left":b.css("left","20px");break;default:b.css("right","20px")}b.fadeIn();0<a.delay&&b.delay(a.delay).fadeOut(function(){return c(this).alert("close")}); return b};c.bootstrapGrowl.default_options={ele:"body",type:"info",offset:{from:"top",amount:20},align:"right",width:250,delay:4E3,allow_dismiss:!0,stackup_spacing:10}}).call(this);
|
5
assets/jquery.min.js
vendored
Normal file
185
assets/main.js
Executable file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2017 Kevin Froman MIT (expat) license
|
||||||
|
*/
|
||||||
|
var clipboard = new Clipboard('.btn');
|
||||||
|
|
||||||
|
var zero = '';
|
||||||
|
var one = '';
|
||||||
|
|
||||||
|
var z_zero = '';
|
||||||
|
var z_one = '';
|
||||||
|
|
||||||
|
var w_zero = ' ';
|
||||||
|
var w_one = '\t';
|
||||||
|
|
||||||
|
|
||||||
|
function showError(msg){
|
||||||
|
$.bootstrapGrowl(msg, {type: 'danger'})
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(str) {
|
||||||
|
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceAll(str, find, replace) {
|
||||||
|
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
clipboard.on('success', function(e) {
|
||||||
|
$('#copyFeedback').css('display', 'inherit');
|
||||||
|
$('#copyFeedback').css('color', 'green');
|
||||||
|
$('#copyFeedback').html('Copied!');
|
||||||
|
e.clearSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
clipboard.on('error', function(e) {
|
||||||
|
$('#copyFeedback').css('display', 'inherit');
|
||||||
|
$('#copyFeedback').css('color', 'red');
|
||||||
|
$('#copyFeedback').html('Your browser doesn\'t seem to support automatic copying. Get a better one.');
|
||||||
|
e.clearSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($('#useEncrypt').is(':checked') == false)
|
||||||
|
{
|
||||||
|
$('#encryptArea').css('display', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#modalClose').click(function(){
|
||||||
|
$('#copyFeedback').css('display', 'none');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("#output").on("click", function () {
|
||||||
|
$(this).select();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#useZeroWidthCharacters').click(function(){
|
||||||
|
|
||||||
|
if (zero == w_zero)
|
||||||
|
{
|
||||||
|
zero = z_zero;
|
||||||
|
one = z_one;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zero = w_zero;
|
||||||
|
one = z_one;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/* based on stackoverflow.com/questions/14430633/how-to-convert-text-to-binary-code-in-javascript */
|
||||||
|
function binToText(str) {
|
||||||
|
var str = replaceAll(replaceAll(str, one, "1"), zero, "0");
|
||||||
|
if(str.match(/[10]{8}/g)){
|
||||||
|
var wordFromBinary = str.match(/([10]{8}|\s+)/g).map(function(fromBinary){
|
||||||
|
return String.fromCharCode(parseInt(fromBinary, 2) );
|
||||||
|
}).join('');
|
||||||
|
return wordFromBinary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* based on stackoverflow.com/questions/21354235/converting-binary-to-text-using-javascript */
|
||||||
|
function textToBin(text) {
|
||||||
|
var output = '';
|
||||||
|
var length = text.length,
|
||||||
|
output = [];
|
||||||
|
for (var i = 0;i < length; i++) {
|
||||||
|
var bin = text[i].charCodeAt().toString(2);
|
||||||
|
output.push(Array(8-bin.length+1).join("0") + bin);
|
||||||
|
}
|
||||||
|
return output.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#useEncrypt').click(function(){
|
||||||
|
if (! this.checked)
|
||||||
|
{
|
||||||
|
$('#encryptArea').css('display', 'none');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$('#encryptArea').css('display', 'inherit');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function verifyPass(mode)
|
||||||
|
{
|
||||||
|
if ($('#password').val() == '')
|
||||||
|
{
|
||||||
|
showError('You must provide a password.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 'encrypt')
|
||||||
|
{
|
||||||
|
if ($('#password').val() != $('#confirmPass').val())
|
||||||
|
{
|
||||||
|
showError('Passwords must match.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#encode').click(function(){
|
||||||
|
go('encode');
|
||||||
|
});
|
||||||
|
$('#decode').click(function(){
|
||||||
|
go('decode');
|
||||||
|
});
|
||||||
|
|
||||||
|
function go(mode) {
|
||||||
|
var output = '';
|
||||||
|
|
||||||
|
var input = $('#text').val();
|
||||||
|
|
||||||
|
if (input == '') { return false; }
|
||||||
|
|
||||||
|
|
||||||
|
// If we're encoding:
|
||||||
|
if (mode == 'encode')
|
||||||
|
{
|
||||||
|
// If we should use encryption, encrypt first:
|
||||||
|
if ($('#useEncrypt').is(':checked'))
|
||||||
|
{
|
||||||
|
// verify password first
|
||||||
|
if (verifyPass('encrypt'))
|
||||||
|
{
|
||||||
|
input = CryptoJS.AES.encrypt(input, $('#password').val()).toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// convert result to binary
|
||||||
|
output = textToBin(encodeURIComponent(input));
|
||||||
|
$('#output').text(replaceAll(replaceAll(output.toString(), "1", one), "0", zero));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sanitized = "";
|
||||||
|
var split = input.split("");
|
||||||
|
for (var i = 0; i < split.length; i++)
|
||||||
|
if(split[i] == one || split[i] == zero)
|
||||||
|
sanitized = sanitized + split[i]
|
||||||
|
var output = decodeURIComponent(binToText(sanitized));
|
||||||
|
if ($('#useEncrypt').is(':checked'))
|
||||||
|
{
|
||||||
|
if (verifyPass('decrypt'))
|
||||||
|
{
|
||||||
|
output = CryptoJS.AES.decrypt(output, $('#password').val()).toString(CryptoJS.enc.Utf8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#output').text(output.toString());
|
||||||
|
}
|
||||||
|
$('#outputModal').modal();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#clearInputButton').click(function(){
|
||||||
|
$('#text').val('');
|
||||||
|
});
|
BIN
assets/snow.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
57
assets/theme.css
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
body
|
||||||
|
{
|
||||||
|
background-color: #d8d8d8;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.center
|
||||||
|
{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dataArea
|
||||||
|
{
|
||||||
|
margin-left: 3em;
|
||||||
|
margin-right: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#one
|
||||||
|
{
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataItem
|
||||||
|
{
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer
|
||||||
|
{
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#copyFeedback
|
||||||
|
{
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outputDecode, #copyFeedback{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outputDecode{
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, h1, h2 {
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
1
assets/theme.min.css
vendored
Executable file
@ -0,0 +1 @@
|
|||||||
|
body{background-color:#d8d8d8;color:black}.center{text-align:center}#dataArea{margin-left:3em;margin-right:3em}#text{width:100%;max-width:100%;min-height:300px}#one{margin-bottom:1em}.dataItem{margin:1em}footer{margin-top:1em;margin-bottom:2em}#copyFeedback{margin-top:1em;margin-bottom:1em}#outputDecode,#copyFeedback{display:none}#outputDecode{overflow:scroll}p,h1,h2{user-select:none;-webkit-user-select:none}
|
58
java/net/chaoswebs/snow10/MainActivity.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package net.chaoswebs.snow10;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
// Copyright (c) 2017 Kevin Froman MIT (expat) license
|
||||||
|
|
||||||
|
private WebView view;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
view = new WebView(this);
|
||||||
|
view.setWebViewClient(new WebViewClient(){
|
||||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
if (url != null && url.startsWith("http://") || url.startsWith("https://")) {
|
||||||
|
view.getContext().startActivity(
|
||||||
|
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
view.getSettings().setJavaScriptEnabled(true);
|
||||||
|
view.loadUrl("file:///android_asset/index.html");
|
||||||
|
setContentView(view);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if ((keyCode == KeyEvent.KEYCODE_BACK) && view.canGoBack()) {
|
||||||
|
//if Back key pressed and webview can navigate to previous page
|
||||||
|
view.goBack();
|
||||||
|
// go back to previous page
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finish();
|
||||||
|
// finish the activity
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
res/layout/activity_main.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--Copyright (c) 2017 Kevin Froman MIT (expat) license-->
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/activity_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin" tools:context="net.chaoswebs.snow10.MainActivity">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="If you can read this, you probably don't have webview installed" />
|
||||||
|
</RelativeLayout>
|
BIN
res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 14 KiB |
6
res/values-w820dp/dimens.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||||
|
(such as screen margins) for screens with more than 820dp of available width. This
|
||||||
|
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||||
|
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||||
|
</resources>
|
6
res/values/colors.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
5
res/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
</resources>
|
3
res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Snow10</string>
|
||||||
|
</resources>
|
11
res/values/styles.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|