├── gson-1.7.1.jar ├── README ├── src └── com │ ├── dvanderbleek │ └── miner │ │ ├── Work.java │ │ ├── Result.java │ │ ├── Miner.java │ │ └── Converter.java │ └── lambdaworks │ └── crypto │ ├── test │ ├── CryptoTestUtil.java │ ├── SCryptUtilTest.java │ ├── SCryptTest.java │ └── PBKDFTest.java │ ├── PBKDF.java │ ├── SCryptUtil.java │ ├── SCrypt.java │ └── Base64.java ├── .classpath └── .project /gson-1.7.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanderbleek/JavaSCryptMiner/HEAD/gson-1.7.1.jar -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a miner for litecoins, written in Java. It uses the sCrypt implementation found at https://github.com/wg/scrypt 2 | Converter class from http://www.daniweb.com/software-development/java/code/216874 -------------------------------------------------------------------------------- /src/com/dvanderbleek/miner/Work.java: -------------------------------------------------------------------------------- 1 | package com.dvanderbleek.miner; 2 | //Class to make parsing JSON with GSON easier. 3 | 4 | 5 | public class Work { 6 | Result result; 7 | 8 | public Work(Result result){ 9 | this.result = result; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/com/dvanderbleek/miner/Result.java: -------------------------------------------------------------------------------- 1 | package com.dvanderbleek.miner; 2 | //This is just a class to make parsing JSON with GSON easier. 3 | public class Result { 4 | String midstate; 5 | String data; 6 | String hash1; 7 | String target; 8 | 9 | public Result(String midstate, String data, String hash1, String target){ 10 | this.midstate = midstate; 11 | this.data = data; 12 | this.hash1 = hash1; 13 | this.target = target; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JavaSCryptMiner 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/test/CryptoTestUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto.test; 4 | 5 | public class CryptoTestUtil { 6 | public static byte[] decode(String str) { 7 | byte[] bytes = new byte[str.length() / 2]; 8 | int index = 0; 9 | 10 | for (int i = 0; i < str.length(); i += 2) { 11 | int high = hexValue(str.charAt(i)); 12 | int low = hexValue(str.charAt(i + 1)); 13 | bytes[index++] = (byte) ((high << 4) + low); 14 | } 15 | 16 | return bytes; 17 | } 18 | 19 | public static int hexValue(char c) { 20 | return c >= 'a' ? c - 87 : c - 48; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/test/SCryptUtilTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto.test; 4 | 5 | import com.lambdaworks.crypto.Base64; 6 | import com.lambdaworks.crypto.SCryptUtil; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | import static org.junit.Assert.*; 10 | 11 | public class SCryptUtilTest { 12 | @Test 13 | public void scrypt() { 14 | int N = 16384; 15 | int r = 8; 16 | int p = 1; 17 | 18 | String hashed = SCryptUtil.scrypt("secret", N, r, p); 19 | String[] parts = hashed.split("\\$"); 20 | 21 | assertEquals(5, parts.length); 22 | assertEquals("s0", parts[1]); 23 | Assert.assertEquals(16, Base64.decodeFast(parts[3]).length); 24 | assertEquals(32, Base64.decodeFast(parts[4]).length); 25 | 26 | int params = Integer.valueOf(parts[2], 16); 27 | 28 | assertEquals(N, (int) Math.pow(2, params >> 16 & 0xff)); 29 | assertEquals(r, params >> 8 & 0x0f); 30 | assertEquals(p, params >> 0 & 0x0f); 31 | } 32 | 33 | @Test 34 | public void check() { 35 | String passwd = "secret"; 36 | 37 | String hashed = SCryptUtil.scrypt(passwd, 16384, 8, 1); 38 | 39 | assertTrue(SCryptUtil.check(passwd, hashed)); 40 | assertFalse(SCryptUtil.check("s3cr3t", hashed)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/PBKDF.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import java.security.GeneralSecurityException; 8 | import static java.lang.System.arraycopy; 9 | 10 | /** 11 | * An implementation of the Password-Based Key Derivation Function as specified 12 | * in RFC 2898. 13 | * 14 | * @author Will Glozer 15 | */ 16 | public class PBKDF { 17 | /** 18 | * Implementation of PBKDF2 (RFC2898). 19 | * 20 | * @param alg HMAC algorithm to use. 21 | * @param P Password. 22 | * @param S Salt. 23 | * @param c Iteration count. 24 | * @param dkLen Intended length, in octets, of the derived key. 25 | * 26 | * @return The derived key. 27 | * 28 | * @throws GeneralSecurityException 29 | */ 30 | public static byte[] pbkdf2(String alg, byte[] P, byte[] S, int c, int dkLen) throws GeneralSecurityException { 31 | Mac mac = Mac.getInstance(alg); 32 | mac.init(new SecretKeySpec(P, alg)); 33 | byte[] DK = new byte[dkLen]; 34 | pbkdf2(mac, S, c, DK, dkLen); 35 | return DK; 36 | } 37 | 38 | /** 39 | * Implementation of PBKDF2 (RFC2898). 40 | * 41 | * @param mac Pre-initialized {@link Mac} instance to use. 42 | * @param S Salt. 43 | * @param c Iteration count. 44 | * @param DK Byte array that derived key will be placed in. 45 | * @param dkLen Intended length, in octets, of the derived key. 46 | * 47 | * @throws GeneralSecurityException 48 | */ 49 | public static void pbkdf2(Mac mac, byte[] S, int c, byte[] DK, int dkLen) throws GeneralSecurityException { 50 | int hLen = mac.getMacLength(); 51 | 52 | if (dkLen > (Math.pow(2, 32) - 1) * hLen) { 53 | throw new GeneralSecurityException("Requested key length too long"); 54 | } 55 | 56 | byte[] U = new byte[hLen]; 57 | byte[] T = new byte[hLen]; 58 | byte[] block1 = new byte[S.length + 4]; 59 | 60 | int l = (int) Math.ceil((double) dkLen / hLen); 61 | int r = dkLen - (l - 1) * hLen; 62 | 63 | arraycopy(S, 0, block1, 0, S.length); 64 | 65 | for (int i = 1; i <= l; i++) { 66 | block1[S.length + 0] = (byte) (i >> 24 & 0xff); 67 | block1[S.length + 1] = (byte) (i >> 16 & 0xff); 68 | block1[S.length + 2] = (byte) (i >> 8 & 0xff); 69 | block1[S.length + 3] = (byte) (i >> 0 & 0xff); 70 | 71 | mac.update(block1); 72 | mac.doFinal(U, 0); 73 | arraycopy(U, 0, T, 0, hLen); 74 | 75 | for (int j = 1; j < c; j++) { 76 | mac.update(U); 77 | mac.doFinal(U, 0); 78 | 79 | for (int k = 0; k < hLen; k++) { 80 | T[k] ^= U[k]; 81 | } 82 | } 83 | 84 | arraycopy(T, 0, DK, (i - 1) * hLen, (i == l ? r : hLen)); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/test/SCryptTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto.test; 4 | 5 | import com.lambdaworks.crypto.SCrypt; 6 | import org.junit.Test; 7 | 8 | import static org.junit.Assert.*; 9 | import static com.lambdaworks.crypto.test.CryptoTestUtil.*; 10 | import static com.lambdaworks.crypto.SCrypt.*; 11 | 12 | public class SCryptTest { 13 | @Test 14 | public void scrypt_paper_appendix_b() throws Exception { 15 | byte[] P, S; 16 | int N, r, p, dkLen; 17 | String DK; 18 | 19 | // empty key & salt test missing because unsupported by JCE 20 | 21 | P = "password".getBytes("UTF-8"); 22 | S = "NaCl".getBytes("UTF-8"); 23 | N = 1024; 24 | r = 8; 25 | p = 16; 26 | dkLen = 64; 27 | DK = "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"; 28 | 29 | assertArrayEquals(decode(DK), SCrypt.scrypt(P, S, N, r, p, dkLen)); 30 | 31 | P = "pleaseletmein".getBytes("UTF-8"); 32 | S = "SodiumChloride".getBytes("UTF-8"); 33 | N = 16384; 34 | r = 8; 35 | p = 1; 36 | dkLen = 64; 37 | DK = "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"; 38 | 39 | assertArrayEquals(decode(DK), scrypt(P, S, N, r, p, dkLen)); 40 | 41 | P = "pleaseletmein".getBytes("UTF-8"); 42 | S = "SodiumChloride".getBytes("UTF-8"); 43 | N = 1048576; 44 | r = 8; 45 | p = 1; 46 | dkLen = 64; 47 | DK = "2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4"; 48 | 49 | assertArrayEquals(decode(DK), SCrypt.scrypt(P, S, N, r, p, dkLen)); 50 | } 51 | 52 | @Test(expected = IllegalArgumentException.class) 53 | public void scrypt_invalid_N_zero() throws Exception { 54 | byte[] P = "pleaseletmein".getBytes("UTF-8"); 55 | byte[] S = "SodiumChloride".getBytes("UTF-8"); 56 | scrypt(P, S, 0, 1, 1, 64); 57 | } 58 | 59 | @Test(expected = IllegalArgumentException.class) 60 | public void scrypt_invalid_N_odd() throws Exception { 61 | byte[] P = "pleaseletmein".getBytes("UTF-8"); 62 | byte[] S = "SodiumChloride".getBytes("UTF-8"); 63 | scrypt(P, S, 3, 1, 1, 64); 64 | } 65 | 66 | @Test(expected = IllegalArgumentException.class) 67 | public void scrypt_invalid_N_large() throws Exception { 68 | byte[] P = "pleaseletmein".getBytes("UTF-8"); 69 | byte[] S = "SodiumChloride".getBytes("UTF-8"); 70 | int r = 8; 71 | int N = Integer.MAX_VALUE / 128; 72 | scrypt(P, S, N, r, 1, 64); 73 | } 74 | 75 | // @Test(expected = IllegalArgumentException.class) 76 | // public void scrypt_invalid_r_large() throws Exception { 77 | // byte[] P = "pleaseletmein".getBytes("UTF-8"); 78 | // byte[] S = "SodiumChloride".getBytes("UTF-8"); 79 | // int N = 1024; 80 | // int r = Integer.MAX_VALUE / 128 + 1; 81 | // int p = 0; 82 | // scrypt(P, S, N, r, p, 64); 83 | // } 84 | } 85 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/SCryptUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.security.GeneralSecurityException; 7 | import java.security.SecureRandom; 8 | 9 | import static com.lambdaworks.crypto.Base64.*; 10 | 11 | /** 12 | * Simple {@link SCrypt} interface for hashing passwords using the 13 | * scrypt key derivation fuction 14 | * and comparing a plain text password to a hashed one. The hashed output is an 15 | * extended implementation of the Modular Crypt Format that also includes the scrypt 16 | * algorithm parameters. 17 | * 18 | * Format: $s0$PARAMS$SALT$KEY. 19 | * 20 | *
21 | *
PARAMS
32-bit hex integer containing log2(N) (16 bits), r (8 bits), and p (8 bits)
22 | *
SALT
base64-encoded salt
23 | *
KEY
base64-encoded derived key
24 | *
25 | * 26 | * s0 identifies version 0 of the scrypt format, using a 128-bit salt and 256-bit derived key. 27 | * 28 | * @author Will Glozer 29 | */ 30 | public class SCryptUtil { 31 | /** 32 | * Hash the supplied plaintext password and generate output in the format described 33 | * in {@link SCryptUtil}. 34 | * 35 | * @param passwd Password. 36 | * @param N CPU cost parameter. 37 | * @param r Memory cost parameter. 38 | * @param p Parallelization parameter. 39 | * 40 | * @return The hashed password. 41 | */ 42 | public static String scrypt(String passwd, int N, int r, int p) { 43 | try { 44 | byte[] salt = new byte[16]; 45 | SecureRandom.getInstance("SHA1PRNG").nextBytes(salt); 46 | 47 | byte[] derived = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); 48 | 49 | String params = Integer.toString(log2(N) << 16 | r << 8 | p, 16); 50 | 51 | StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2); 52 | sb.append("$s0$").append(params).append('$'); 53 | sb.append(encodeToChar(salt, false)).append('$'); 54 | sb.append(encodeToChar(derived, false)); 55 | 56 | return sb.toString(); 57 | } catch (UnsupportedEncodingException e) { 58 | throw new IllegalStateException("JVM doesn't support UTF-8?"); 59 | } catch (GeneralSecurityException e) { 60 | throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?"); 61 | } 62 | } 63 | 64 | /** 65 | * Compare the supplied plaintext password to a hashed password. 66 | * 67 | * @param passwd Plaintext password. 68 | * @param hashed scrypt hashed password. 69 | * 70 | * @return true if passwd matches hashed value. 71 | */ 72 | public static boolean check(String passwd, String hashed) { 73 | try { 74 | String[] parts = hashed.split("\\$"); 75 | 76 | if (parts.length != 5 || !parts[1].equals("s0")) { 77 | throw new IllegalArgumentException("Invalid hashed value"); 78 | } 79 | 80 | int params = Integer.parseInt(parts[2], 16); 81 | byte[] salt = decodeFast(parts[3].toCharArray()); 82 | byte[] derived0 = decodeFast(parts[4].toCharArray()); 83 | 84 | int N = (int) Math.pow(2, params >> 16 & 0xff); 85 | int r = params >> 8 & 0x0f; 86 | int p = params & 0x0f; 87 | 88 | byte[] derived1 = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); 89 | 90 | if (derived0.length != derived1.length) return false; 91 | 92 | int result = 0; 93 | for (int i = 0; i < derived0.length; i++) { 94 | result |= derived0[i] ^ derived1[i]; 95 | } 96 | return result == 0; 97 | } catch (UnsupportedEncodingException e) { 98 | throw new IllegalStateException("JVM doesn't support UTF-8?"); 99 | } catch (GeneralSecurityException e) { 100 | throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?"); 101 | } 102 | } 103 | 104 | private static int log2(int n) { 105 | int log = 0; 106 | if ((n & 0xffff0000 ) != 0) { n >>>= 16; log = 16; } 107 | if (n >= 256) { n >>>= 8; log += 8; } 108 | if (n >= 16 ) { n >>>= 4; log += 4; } 109 | if (n >= 4 ) { n >>>= 2; log += 2; } 110 | return log + (n >>> 1); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/test/PBKDFTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto.test; 4 | 5 | import com.lambdaworks.crypto.PBKDF; 6 | import org.junit.Test; 7 | 8 | import java.math.BigInteger; 9 | 10 | import static org.junit.Assert.*; 11 | import static com.lambdaworks.crypto.test.CryptoTestUtil.*; 12 | 13 | public class PBKDFTest { 14 | @Test 15 | public void pbkdf2_hmac_sha1_rfc6070() throws Exception { 16 | String alg = "HmacSHA1"; 17 | byte[] P, S; 18 | int c, dkLen; 19 | String DK; 20 | 21 | P = "password".getBytes("UTF-8"); 22 | S = "salt".getBytes("UTF-8"); 23 | c = 1; 24 | dkLen = 20; 25 | DK = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; 26 | 27 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 28 | 29 | P = "password".getBytes("UTF-8"); 30 | S = "salt".getBytes("UTF-8"); 31 | c = 2; 32 | dkLen = 20; 33 | DK = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; 34 | 35 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 36 | 37 | P = "password".getBytes("UTF-8"); 38 | S = "salt".getBytes("UTF-8"); 39 | c = 4096; 40 | dkLen = 20; 41 | DK = "4b007901b765489abead49d926f721d065a429c1"; 42 | 43 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 44 | 45 | P = "password".getBytes("UTF-8"); 46 | S = "salt".getBytes("UTF-8"); 47 | c = 16777216; 48 | dkLen = 20; 49 | DK = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"; 50 | 51 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 52 | 53 | P = "passwordPASSWORDpassword".getBytes("UTF-8"); 54 | S = "saltSALTsaltSALTsaltSALTsaltSALTsalt".getBytes("UTF-8"); 55 | c = 4096; 56 | dkLen = 25; 57 | DK = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; 58 | 59 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 60 | 61 | P = "pass\0word".getBytes("UTF-8"); 62 | S = "sa\0lt".getBytes("UTF-8"); 63 | c = 4096; 64 | dkLen = 16; 65 | DK = "56fa6aa75548099dcc37d7f03425e0c3"; 66 | 67 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 68 | } 69 | 70 | @Test 71 | public void pbkdf2_hmac_sha1_rfc3962() throws Exception { 72 | String alg = "HmacSHA1"; 73 | byte[] P, S; 74 | int c, dkLen; 75 | String DK; 76 | 77 | P = "password".getBytes("UTF-8"); 78 | S = "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"); 79 | c = 1; 80 | 81 | dkLen = 16; 82 | DK = "cdedb5281bb2f801565a1122b2563515"; 83 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 84 | 85 | dkLen = 32; 86 | DK = "cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837"; 87 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 88 | 89 | P = "password".getBytes("UTF-8"); 90 | S = "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"); 91 | c = 2; 92 | 93 | dkLen = 16; 94 | DK = "01dbee7f4a9e243e988b62c73cda935d"; 95 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 96 | 97 | dkLen = 32; 98 | DK = "01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"; 99 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 100 | 101 | P = "password".getBytes("UTF-8"); 102 | S = "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"); 103 | c = 1200; 104 | 105 | dkLen = 16; 106 | DK = "5c08eb61fdf71e4e4ec3cf6ba1f5512b"; 107 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 108 | 109 | dkLen = 32; 110 | DK = "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"; 111 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 112 | 113 | P = "password".getBytes("UTF-8"); 114 | S = new BigInteger("1234567878563412", 16).toByteArray(); 115 | c = 5; 116 | 117 | dkLen = 16; 118 | DK = "d1daa78615f287e6a1c8b120d7062a49"; 119 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 120 | 121 | dkLen = 32; 122 | DK = "d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"; 123 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 124 | } 125 | 126 | @Test 127 | public void pbkdf2_hmac_sha256_scrypt() throws Exception { 128 | String alg = "HmacSHA256"; 129 | byte[] P, S; 130 | int c, dkLen; 131 | String DK; 132 | 133 | P = "password".getBytes("UTF-8"); 134 | S = "salt".getBytes("UTF-8"); 135 | c = 4096; 136 | dkLen = 32; 137 | DK = "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"; 138 | 139 | assertArrayEquals(decode(DK), PBKDF.pbkdf2(alg, P, S, c, dkLen)); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/SCrypt.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import java.security.GeneralSecurityException; 8 | 9 | import static java.lang.Integer.MAX_VALUE; 10 | import static java.lang.System.arraycopy; 11 | 12 | /** 13 | * An implementation of the scrypt 14 | * key derivation function. This class will attempt to load a native library 15 | * containing the optimized C implementation from 16 | * http://www.tarsnap.com/scrypt.html and 17 | * fall back to the pure Java version if that fails. 18 | * 19 | * @author Will Glozer 20 | */ 21 | public class SCrypt { 22 | private static boolean native_library_loaded = false; 23 | 24 | 25 | 26 | /** 27 | * Implementation of the scrypt KDF. 28 | * Calls the native implementation {@link #scryptN} when the native library was successfully 29 | * loaded, otherwise calls {@link #scryptJ}. 30 | * 31 | * @param passwd Password. 32 | * @param salt Salt. 33 | * @param N CPU cost parameter. 34 | * @param r Memory cost parameter. 35 | * @param p Parallelization parameter. 36 | * @param dkLen Intended length of the derived key. 37 | * 38 | * @return The derived key. 39 | * 40 | * @throws GeneralSecurityException when HMAC_SHA256 is not available. 41 | */ 42 | public static byte[] scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException { 43 | return native_library_loaded ? scryptN(passwd, salt, N, r, p, dkLen) : scryptJ(passwd, salt, N, r, p, dkLen); 44 | } 45 | 46 | /** 47 | * Native C implementation of the scrypt KDF using 48 | * the code from http://www.tarsnap.com/scrypt.html. 49 | * 50 | * @param passwd Password. 51 | * @param salt Salt. 52 | * @param N CPU cost parameter. 53 | * @param r Memory cost parameter. 54 | * @param p Parallelization parameter. 55 | * @param dkLen Intended length of the derived key. 56 | * 57 | * @return The derived key. 58 | */ 59 | public static native byte[] scryptN(byte[] passwd, byte[] salt, long N, int r, int p, int dkLen); 60 | 61 | /** 62 | * Pure Java implementation of the scrypt KDF. 63 | * 64 | * @param passwd Password. 65 | * @param salt Salt. 66 | * @param N CPU cost parameter. 67 | * @param r Memory cost parameter. 68 | * @param p Parallelization parameter. 69 | * @param dkLen Intended length of the derived key. 70 | * 71 | * @return The derived key. 72 | * 73 | * @throws GeneralSecurityException when HMAC_SHA256 is not available. 74 | */ 75 | public static byte[] scryptJ(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException { 76 | if (N == 0 || (N & (N - 1)) != 0) throw new IllegalArgumentException("N must be > 0 and a power of 2"); 77 | 78 | if (N > MAX_VALUE / 128 / r) throw new IllegalArgumentException("Parameter N is too large"); 79 | if (r > MAX_VALUE / 128 / p) throw new IllegalArgumentException("Parameter r is too large"); 80 | 81 | Mac mac = Mac.getInstance("HmacSHA256"); 82 | mac.init(new SecretKeySpec(passwd, "HmacSHA256")); 83 | 84 | byte[] DK = new byte[dkLen]; 85 | 86 | byte[] B = new byte[128 * r * p]; 87 | byte[] XY = new byte[256 * r]; 88 | byte[] V = new byte[128 * r * N]; 89 | int i; 90 | 91 | PBKDF.pbkdf2(mac, salt, 1, B, p * 128 * r); 92 | 93 | for (i = 0; i < p; i++) { 94 | smix(B, i * 128 * r, r, N, V, XY); 95 | } 96 | 97 | PBKDF.pbkdf2(mac, B, 1, DK, dkLen); 98 | 99 | return DK; 100 | } 101 | 102 | public static void smix(byte[] B, int Bi, int r, int N, byte[] V, byte[] XY) { 103 | int Xi = 0; 104 | int Yi = 128 * r; 105 | int i; 106 | 107 | arraycopy(B, Bi, XY, Xi, 128 * r); 108 | 109 | for (i = 0; i < N; i++) { 110 | arraycopy(XY, Xi, V, i * (128 * r), 128 * r); 111 | blockmix_salsa8(XY, Xi, Yi, r); 112 | } 113 | 114 | for (i = 0; i < N; i++) { 115 | int j = integerify(XY, Xi, r) & (N - 1); 116 | blockxor(V, j * (128 * r), XY, Xi, 128 * r); 117 | blockmix_salsa8(XY, Xi, Yi, r); 118 | } 119 | 120 | arraycopy(XY, Xi, B, Bi, 128 * r); 121 | } 122 | 123 | public static void blockmix_salsa8(byte[] BY, int Bi, int Yi, int r) { 124 | byte[] X = new byte[64]; 125 | int i; 126 | 127 | arraycopy(BY, Bi + (2 * r - 1) * 64, X, 0, 64); 128 | 129 | for (i = 0; i < 2 * r; i++) { 130 | blockxor(BY, i * 64, X, 0, 64); 131 | salsa20_8(X); 132 | arraycopy(X, 0, BY, Yi + (i * 64), 64); 133 | } 134 | 135 | for (i = 0; i < r; i++) { 136 | arraycopy(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64); 137 | } 138 | 139 | for (i = 0; i < r; i++) { 140 | arraycopy(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64); 141 | } 142 | } 143 | 144 | public static int R(int a, int b) { 145 | return (a << b) | (a >>> (32 - b)); 146 | } 147 | 148 | public static void salsa20_8(byte[] B) { 149 | int[] B32 = new int[16]; 150 | int[] x = new int[16]; 151 | int i; 152 | 153 | for (i = 0; i < 16; i++) { 154 | B32[i] = (B[i * 4 + 0] & 0xff) << 0; 155 | B32[i] |= (B[i * 4 + 1] & 0xff) << 8; 156 | B32[i] |= (B[i * 4 + 2] & 0xff) << 16; 157 | B32[i] |= (B[i * 4 + 3] & 0xff) << 24; 158 | } 159 | 160 | arraycopy(B32, 0, x, 0, 16); 161 | 162 | for (i = 8; i > 0; i -= 2) { 163 | x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); 164 | x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); 165 | x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); 166 | x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); 167 | x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); 168 | x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); 169 | x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); 170 | x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); 171 | x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); 172 | x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); 173 | x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); 174 | x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); 175 | x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); 176 | x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); 177 | x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); 178 | x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); 179 | } 180 | 181 | for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i]; 182 | 183 | for (i = 0; i < 16; i++) { 184 | B[i * 4 + 0] = (byte) (B32[i] >> 0 & 0xff); 185 | B[i * 4 + 1] = (byte) (B32[i] >> 8 & 0xff); 186 | B[i * 4 + 2] = (byte) (B32[i] >> 16 & 0xff); 187 | B[i * 4 + 3] = (byte) (B32[i] >> 24 & 0xff); 188 | } 189 | } 190 | 191 | public static void blockxor(byte[] S, int Si, byte[] D, int Di, int len) { 192 | for (int i = 0; i < len; i++) { 193 | D[Di + i] ^= S[Si + i]; 194 | } 195 | } 196 | 197 | public static int integerify(byte[] B, int Bi, int r) { 198 | int n; 199 | 200 | Bi += (2 * r - 1) * 64; 201 | 202 | n = (B[Bi + 0] & 0xff) << 0; 203 | n |= (B[Bi + 1] & 0xff) << 8; 204 | n |= (B[Bi + 2] & 0xff) << 16; 205 | n |= (B[Bi + 3] & 0xff) << 24; 206 | 207 | return n; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/com/dvanderbleek/miner/Miner.java: -------------------------------------------------------------------------------- 1 | package com.dvanderbleek.miner; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.OutputStreamWriter; 7 | import java.math.BigInteger; 8 | import java.net.Authenticator; 9 | import java.net.PasswordAuthentication; 10 | import java.net.URL; 11 | import java.net.URLConnection; 12 | import java.security.GeneralSecurityException; 13 | 14 | 15 | import com.google.gson.Gson; 16 | import com.lambdaworks.crypto.SCrypt; 17 | 18 | 19 | 20 | public class Miner { 21 | 22 | /** 23 | * @param args 24 | * @throws GeneralSecurityException 25 | * @throws IOException 26 | */ 27 | public static void main(String[] args) throws GeneralSecurityException, IOException { 28 | 29 | final String rpcuser ="user"; //RPC User name (set in config) 30 | final String rpcpassword ="x"; //RPC Pass (set in config) 31 | 32 | Authenticator.setDefault(new Authenticator() {//This sets the default authenticator, with the set username and password 33 | protected PasswordAuthentication getPasswordAuthentication() { 34 | return new PasswordAuthentication (rpcuser, rpcpassword.toCharArray()); 35 | } 36 | }); 37 | while(true){ 38 | Work work = getwork(); //Gets the work from the server 39 | String data = work.result.data; //Gets the data to hash from the work 40 | String target = work.result.target;//Gets the target from the work 41 | 42 | //This chunk pulls apart the data so they can be endian switched (see the scrypt proof of work page on the wiki) 43 | String version = data.substring(0, 8); 44 | String prevhash = data.substring(8, 72); 45 | String merkle = data.substring(72, 136 ); 46 | String timestamp = data.substring(136, 144); 47 | String bits = data.substring(144, 152); 48 | String nonce = data.substring(152,160); 49 | 50 | //This chunk creates endian switched byte arrays from the data 51 | byte[] versionbit = chunkEndianSwitch(Converter.fromHexString(version)); 52 | byte[] prevhashbit = chunkEndianSwitch(Converter.fromHexString(prevhash)); 53 | byte[] merklebit = chunkEndianSwitch(Converter.fromHexString(merkle)); 54 | byte[] timestampbit = chunkEndianSwitch(Converter.fromHexString(timestamp)); 55 | byte[] bitsbit = chunkEndianSwitch(Converter.fromHexString(bits)); 56 | byte[] noncebit = chunkEndianSwitch(Converter.fromHexString(nonce)); 57 | 58 | //This chunk of code reassembles the data into a single byre array 59 | byte[] databyte = new byte[80]; 60 | System.arraycopy(versionbit, 0, databyte, 0, versionbit.length); 61 | System.arraycopy(prevhashbit, 0, databyte, 4, prevhashbit.length); 62 | System.arraycopy(merklebit, 0, databyte, 36, merklebit.length); 63 | System.arraycopy(timestampbit, 0, databyte, 68, timestampbit.length); 64 | System.arraycopy(bitsbit, 0, databyte, 72, bitsbit.length); 65 | System.arraycopy(noncebit, 0, databyte, 76, noncebit.length); 66 | 67 | //Converts the target string to a byte array for easier comparison 68 | byte[] targetbyte = Converter.fromHexString(target); 69 | targetbyte = endianSwitch(targetbyte); 70 | 71 | byte[] scrypted = doScrypt(databyte, targetbyte);//Calls sCrypt with the proper parameters, and returns the correct data 72 | byte[] databyte2 = Converter.fromHexString(data); 73 | System.arraycopy(chunkEndianSwitch(scrypted), 0, databyte2, 76, scrypted.length); 74 | 75 | work.result.data = printByteArray(databyte2); 76 | System.out.println(sendWork(work));//Send the work 77 | } 78 | } 79 | 80 | public static byte[] doScrypt(byte[] databyte, byte[] target) throws GeneralSecurityException{ 81 | //Initialize the nonce 82 | byte[] nonce = new byte[4]; 83 | nonce[0] = databyte[76] ; 84 | nonce[1] = databyte[77] ; 85 | nonce[2] = databyte[78] ; 86 | nonce[3] = databyte[79] ; 87 | boolean found = false; 88 | //Loop over and increment nonce 89 | while(!found){ 90 | //Set the bytes of the data to the nonce 91 | databyte[76] = nonce[0]; 92 | databyte[77] = nonce[1]; 93 | databyte[78] = nonce[2]; 94 | databyte[79] = nonce[3]; 95 | 96 | byte[] scrypted = (SCrypt.scryptJ(databyte,databyte, 1024, 1, 1, 32));//Scrypt the data with proper params 97 | 98 | BigInteger bigScrypt = new BigInteger(printByteArray(endianSwitch(scrypted)), 16); //Create a bigInteger to compare against the target 99 | BigInteger bigTarget = new BigInteger(printByteArray(target),16);//Create a bigInteger from the target 100 | if(bigScrypt.compareTo(bigTarget) == -1){ 101 | System.out.println(printByteArray(scrypted)); 102 | return nonce;//Compare the two bigIntegers, return the data with nonce if smaller 103 | } 104 | 105 | else incrementAtIndex(nonce, nonce.length-1); //Otherwise increment the nonce 106 | 107 | } 108 | return nonce; 109 | } 110 | 111 | 112 | public static void incrementAtIndex(byte[] array, int index) { 113 | //Short method to increment the nonce 114 | if (array[index] == Byte.MAX_VALUE) { 115 | array[index] = 0; 116 | if(index > 0) 117 | incrementAtIndex(array, index - 1); 118 | } 119 | else { 120 | array[index]++; 121 | } 122 | } 123 | 124 | 125 | public static String prettyPrintByteArray(byte[] bites){ 126 | //Method to convert a byte array to hex literal separated by bites. 127 | String str = ""; 128 | int n = 0; 129 | for(byte bite:bites){ 130 | n += 1; 131 | str = str + (Integer.toString( ( bite & 0xff ) + 0x100, 16 /* radix */ ).substring( 1 )) + " "; 132 | if((n%16) == 0) { str += "\n"; } 133 | else if((n%4) == 0) { str += " "; } 134 | } 135 | return str; 136 | } 137 | 138 | public static String printByteArray(byte[] bites){ 139 | //Method to convert a byte array to hex literal 140 | String str = ""; 141 | for(byte bite:bites){ 142 | str = str + (Integer.toString( ( bite & 0xff ) + 0x100, 16 /* radix */ ).substring( 1 )); 143 | } 144 | return str; 145 | } 146 | 147 | public static byte[] endianSwitch(byte[] bytes) { 148 | //Method to switch the endianess of a byte array 149 | byte[] bytes2 = new byte[bytes.length]; 150 | for(int i = 0; i < bytes.length; i++){ 151 | bytes2[i] = bytes[bytes.length-i-1]; 152 | } 153 | return bytes2; 154 | } 155 | 156 | public static byte[] chunkEndianSwitch(byte[] bytes) { 157 | //Method to properly switch the endianness of the header -- numbers must be treated as 32 bit chunks. Thanks to ali1234 for this. 158 | byte[] bytes2 = new byte[bytes.length]; 159 | for(int i = 0; i < bytes.length; i+=4){ 160 | bytes2[i] = bytes[i+3]; 161 | bytes2[i+1] = bytes[i+2]; 162 | bytes2[i+2] = bytes[i+1]; 163 | bytes2[i+3] = bytes[i]; 164 | } 165 | return bytes2; 166 | } 167 | 168 | 169 | public static Work getwork() throws IOException{ 170 | //Method to getwork 171 | URL url = new URL("http://127.0.0.1:9332"); 172 | URLConnection conn = url.openConnection(); 173 | conn.setDoOutput(true); 174 | conn.setDoInput(true); 175 | OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); 176 | String rpcreturn = "{\"jsonrpc\": \"1.0\" , \"method\": \"getwork\" }";//JSON RPC call for getting work 177 | wr.write(rpcreturn); 178 | wr.flush(); 179 | BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 180 | String line; 181 | 182 | line = rd.readLine(); 183 | rd.close(); 184 | Gson gson = new Gson(); 185 | Work work = gson.fromJson(line, Work.class);//Use GSON to create a work object from the response 186 | return work; 187 | } 188 | 189 | public static String sendWork(Work work) throws IOException{ 190 | //Very similar to getwork method 191 | URL url = new URL("http://127.0.0.1:9332"); 192 | URLConnection conn = url.openConnection(); 193 | conn.setDoOutput(true); 194 | conn.setDoInput(true); 195 | OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); 196 | System.out.println(work.result.data); 197 | String rpcreturn = "{\"jsonrpc\": \"1.0\" , \"method\": \"getwork\" , \"params\" : [\"" +work.result.data+ "\"]}";//RPC call with the new nonced data 198 | System.out.println(rpcreturn); 199 | wr.write(rpcreturn); 200 | wr.flush(); 201 | BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 202 | String line; 203 | line = rd.readLine(); 204 | rd.close(); 205 | return line; 206 | } 207 | } -------------------------------------------------------------------------------- /src/com/dvanderbleek/miner/Converter.java: -------------------------------------------------------------------------------- 1 | package com.dvanderbleek.miner; 2 | 3 | public class Converter { 4 | 5 | /* ========================= */ 6 | /* "primitive type --> byte[] data" Methods */ 7 | /* ========================= */ 8 | 9 | public static byte[] toByta(byte data) { 10 | return new byte[]{data}; 11 | } 12 | 13 | public static byte[] toByta(byte[] data) { 14 | return data; 15 | } 16 | 17 | /* ========================= */ 18 | 19 | public static byte[] toByta(short data) { 20 | return new byte[] { 21 | (byte)((data >> 8) & 0xff), 22 | (byte)((data >> 0) & 0xff), 23 | }; 24 | } 25 | 26 | public static byte[] toByta(short[] data) { 27 | if (data == null) return null; 28 | // ---------- 29 | byte[] byts = new byte[data.length * 2]; 30 | for (int i = 0; i < data.length; i++) 31 | System.arraycopy(toByta(data[i]), 0, byts, i * 2, 2); 32 | return byts; 33 | } 34 | 35 | /* ========================= */ 36 | 37 | public static byte[] toByta(char data) { 38 | return new byte[] { 39 | (byte)((data >> 8) & 0xff), 40 | (byte)((data >> 0) & 0xff), 41 | }; 42 | } 43 | 44 | public static byte[] toByta(char[] data) { 45 | if (data == null) return null; 46 | // ---------- 47 | byte[] byts = new byte[data.length * 2]; 48 | for (int i = 0; i < data.length; i++) 49 | System.arraycopy(toByta(data[i]), 0, byts, i * 2, 2); 50 | return byts; 51 | } 52 | 53 | /* ========================= */ 54 | 55 | public static byte[] toByta(int data) { 56 | return new byte[] { 57 | (byte)((data >> 24) & 0xff), 58 | (byte)((data >> 16) & 0xff), 59 | (byte)((data >> 8) & 0xff), 60 | (byte)((data >> 0) & 0xff), 61 | }; 62 | } 63 | 64 | public static byte[] toByta(int[] data) { 65 | if (data == null) return null; 66 | // ---------- 67 | byte[] byts = new byte[data.length * 4]; 68 | for (int i = 0; i < data.length; i++) 69 | System.arraycopy(toByta(data[i]), 0, byts, i * 4, 4); 70 | return byts; 71 | } 72 | 73 | /* ========================= */ 74 | 75 | public static byte[] toByta(long data) { 76 | return new byte[] { 77 | (byte)((data >> 56) & 0xff), 78 | (byte)((data >> 48) & 0xff), 79 | (byte)((data >> 40) & 0xff), 80 | (byte)((data >> 32) & 0xff), 81 | (byte)((data >> 24) & 0xff), 82 | (byte)((data >> 16) & 0xff), 83 | (byte)((data >> 8) & 0xff), 84 | (byte)((data >> 0) & 0xff), 85 | }; 86 | } 87 | 88 | public static byte[] toByta(long[] data) { 89 | if (data == null) return null; 90 | // ---------- 91 | byte[] byts = new byte[data.length * 8]; 92 | for (int i = 0; i < data.length; i++) 93 | System.arraycopy(toByta(data[i]), 0, byts, i * 8, 8); 94 | return byts; 95 | } 96 | 97 | /* ========================= */ 98 | 99 | public static byte[] toByta(float data) { 100 | return toByta(Float.floatToRawIntBits(data)); 101 | } 102 | 103 | public static byte[] toByta(float[] data) { 104 | if (data == null) return null; 105 | // ---------- 106 | byte[] byts = new byte[data.length * 4]; 107 | for (int i = 0; i < data.length; i++) 108 | System.arraycopy(toByta(data[i]), 0, byts, i * 4, 4); 109 | return byts; 110 | } 111 | 112 | /* ========================= */ 113 | 114 | public static byte[] toByta(double data) { 115 | return toByta(Double.doubleToRawLongBits(data)); 116 | } 117 | 118 | public static byte[] toByta(double[] data) { 119 | if (data == null) return null; 120 | // ---------- 121 | byte[] byts = new byte[data.length * 8]; 122 | for (int i = 0; i < data.length; i++) 123 | System.arraycopy(toByta(data[i]), 0, byts, i * 8, 8); 124 | return byts; 125 | } 126 | 127 | /* ========================= */ 128 | 129 | public static byte[] toByta(boolean data) { 130 | return new byte[]{(byte)(data ? 0x01 : 0x00)}; // bool -> {1 byte} 131 | } 132 | 133 | public static byte[] toByta(boolean[] data) { 134 | // Advanced Technique: The byte array containts information 135 | // about how many boolean values are involved, so the exact 136 | // array is returned when later decoded. 137 | // ---------- 138 | if (data == null) return null; 139 | // ---------- 140 | int len = data.length; 141 | byte[] lena = toByta(len); // int conversion; length array = lena 142 | byte[] byts = new byte[lena.length + (len / 8) + (len % 8 != 0 ? 1 : 0)]; 143 | // (Above) length-array-length + sets-of-8-booleans +? byte-for-remainder 144 | System.arraycopy(lena, 0, byts, 0, lena.length); 145 | // ---------- 146 | // (Below) algorithm by Matthew Cudmore: boolean[] -> bits -> byte[] 147 | for (int i = 0, j = lena.length, k = 7; i < data.length; i++) { 148 | byts[j] |= (data[i] ? 1 : 0) << k--; 149 | if (k < 0) { j++; k = 7; } 150 | } 151 | // ---------- 152 | return byts; 153 | } 154 | 155 | /* ========================= */ 156 | 157 | public static byte[] toByta(String data) { 158 | return (data == null) ? null : data.getBytes(); 159 | } 160 | 161 | public static byte[] toByta(String[] data) { 162 | // Advanced Technique: Generates an indexed byte array 163 | // which contains the array of Strings. The byte array 164 | // contains information about the number of Strings and 165 | // the length of each String. 166 | // ---------- 167 | if (data == null) return null; 168 | // ---------- flags: 169 | int totalLength = 0; // Measure length of final byte array 170 | int bytesPos = 0; // Used later 171 | // ----- arrays: 172 | byte[] dLen = toByta(data.length); // byte array of data length 173 | totalLength += dLen.length; 174 | int[] sLens = new int[data.length]; // String lengths = sLens 175 | totalLength += (sLens.length * 4); 176 | byte[][] strs = new byte[data.length][]; // array of String bytes 177 | // ----- pack strs: 178 | for (int i = 0; i < data.length; i++) { 179 | if (data[i] != null) { 180 | strs[i] = toByta(data[i]); 181 | sLens[i] = strs[i].length; 182 | totalLength += strs[i].length; 183 | } else { 184 | sLens[i] = 0; 185 | strs[i] = new byte[0]; // prevent null entries 186 | } 187 | } 188 | // ---------- 189 | byte[] bytes = new byte[totalLength]; // final array 190 | System.arraycopy(dLen, 0, bytes, 0, 4); 191 | byte[] bsLens = toByta(sLens); // byte version of String sLens 192 | System.arraycopy(bsLens, 0, bytes, 4, bsLens.length); 193 | // ----- 194 | bytesPos += 4 + bsLens.length; // mark position 195 | // ----- 196 | for (byte[] sba : strs) { 197 | System.arraycopy(sba, 0, bytes, bytesPos, sba.length); 198 | bytesPos += sba.length; 199 | } 200 | // ---------- 201 | return bytes; 202 | } 203 | 204 | /* ========================= */ 205 | /* "byte[] data --> primitive type" Methods */ 206 | /* ========================= */ 207 | 208 | public static byte toByte(byte[] data) { 209 | return (data == null || data.length == 0) ? 0x0 : data[0]; 210 | } 211 | 212 | public static byte[] toByteA(byte[] data) { 213 | return data; 214 | } 215 | 216 | /* ========================= */ 217 | 218 | public static short toShort(byte[] data) { 219 | if (data == null || data.length != 2) return 0x0; 220 | // ---------- 221 | return (short)( 222 | (0xff & data[0]) << 8 | 223 | (0xff & data[1]) << 0 224 | ); 225 | } 226 | 227 | public static short[] toShortA(byte[] data) { 228 | if (data == null || data.length % 2 != 0) return null; 229 | // ---------- 230 | short[] shts = new short[data.length / 2]; 231 | for (int i = 0; i < shts.length; i++) { 232 | shts[i] = toShort( new byte[] { 233 | data[(i*2)], 234 | data[(i*2)+1] 235 | } ); 236 | } 237 | return shts; 238 | } 239 | 240 | /* ========================= */ 241 | 242 | public static char toChar(byte[] data) { 243 | if (data == null || data.length != 2) return 0x0; 244 | // ---------- 245 | return (char)( 246 | (0xff & data[0]) << 8 | 247 | (0xff & data[1]) << 0 248 | ); 249 | } 250 | 251 | public static char[] toCharA(byte[] data) { 252 | if (data == null || data.length % 2 != 0) return null; 253 | // ---------- 254 | char[] chrs = new char[data.length / 2]; 255 | for (int i = 0; i < chrs.length; i++) { 256 | chrs[i] = toChar( new byte[] { 257 | data[(i*2)], 258 | data[(i*2)+1], 259 | } ); 260 | } 261 | return chrs; 262 | } 263 | 264 | /* ========================= */ 265 | 266 | public static int toInt(byte[] data) { 267 | if (data == null || data.length != 4) return 0x0; 268 | // ---------- 269 | return (int)( // NOTE: type cast not necessary for int 270 | (0xff & data[0]) << 24 | 271 | (0xff & data[1]) << 16 | 272 | (0xff & data[2]) << 8 | 273 | (0xff & data[3]) << 0 274 | ); 275 | } 276 | 277 | public static int[] toIntA(byte[] data) { 278 | if (data == null || data.length % 4 != 0) return null; 279 | // ---------- 280 | int[] ints = new int[data.length / 4]; 281 | for (int i = 0; i < ints.length; i++) 282 | ints[i] = toInt( new byte[] { 283 | data[(i*4)], 284 | data[(i*4)+1], 285 | data[(i*4)+2], 286 | data[(i*4)+3], 287 | } ); 288 | return ints; 289 | } 290 | 291 | /* ========================= */ 292 | 293 | public static long toLong(byte[] data) { 294 | if (data == null || data.length != 8) return 0x0; 295 | // ---------- 296 | return (long)( 297 | // (Below) convert to longs before shift because digits 298 | // are lost with ints beyond the 32-bit limit 299 | (long)(0xff & data[0]) << 56 | 300 | (long)(0xff & data[1]) << 48 | 301 | (long)(0xff & data[2]) << 40 | 302 | (long)(0xff & data[3]) << 32 | 303 | (long)(0xff & data[4]) << 24 | 304 | (long)(0xff & data[5]) << 16 | 305 | (long)(0xff & data[6]) << 8 | 306 | (long)(0xff & data[7]) << 0 307 | ); 308 | } 309 | 310 | public static long[] toLongA(byte[] data) { 311 | if (data == null || data.length % 8 != 0) return null; 312 | // ---------- 313 | long[] lngs = new long[data.length / 8]; 314 | for (int i = 0; i < lngs.length; i++) { 315 | lngs[i] = toLong( new byte[] { 316 | data[(i*8)], 317 | data[(i*8)+1], 318 | data[(i*8)+2], 319 | data[(i*8)+3], 320 | data[(i*8)+4], 321 | data[(i*8)+5], 322 | data[(i*8)+6], 323 | data[(i*8)+7], 324 | } ); 325 | } 326 | return lngs; 327 | } 328 | 329 | /* ========================= */ 330 | 331 | public static float toFloat(byte[] data) { 332 | if (data == null || data.length != 4) return 0x0; 333 | // ---------- simple: 334 | return Float.intBitsToFloat(toInt(data)); 335 | } 336 | 337 | public static float[] toFloatA(byte[] data) { 338 | if (data == null || data.length % 4 != 0) return null; 339 | // ---------- 340 | float[] flts = new float[data.length / 4]; 341 | for (int i = 0; i < flts.length; i++) { 342 | flts[i] = toFloat( new byte[] { 343 | data[(i*4)], 344 | data[(i*4)+1], 345 | data[(i*4)+2], 346 | data[(i*4)+3], 347 | } ); 348 | } 349 | return flts; 350 | } 351 | 352 | /* ========================= */ 353 | 354 | public static double toDouble(byte[] data) { 355 | if (data == null || data.length != 8) return 0x0; 356 | // ---------- simple: 357 | return Double.longBitsToDouble(toLong(data)); 358 | } 359 | 360 | public static double[] toDoubleA(byte[] data) { 361 | if (data == null) return null; 362 | // ---------- 363 | if (data.length % 8 != 0) return null; 364 | double[] dbls = new double[data.length / 8]; 365 | for (int i = 0; i < dbls.length; i++) { 366 | dbls[i] = toDouble( new byte[] { 367 | data[(i*8)], 368 | data[(i*8)+1], 369 | data[(i*8)+2], 370 | data[(i*8)+3], 371 | data[(i*8)+4], 372 | data[(i*8)+5], 373 | data[(i*8)+6], 374 | data[(i*8)+7], 375 | } ); 376 | } 377 | return dbls; 378 | } 379 | 380 | /* ========================= */ 381 | 382 | public static boolean toBoolean(byte[] data) { 383 | return (data == null || data.length == 0) ? false : data[0] != 0x00; 384 | } 385 | 386 | public static boolean[] toBooleanA(byte[] data) { 387 | // Advanced Technique: Extract the boolean array's length 388 | // from the first four bytes in the char array, and then 389 | // read the boolean array. 390 | // ---------- 391 | if (data == null || data.length < 4) return null; 392 | // ---------- 393 | int len = toInt(new byte[]{data[0], data[1], data[2], data[3]}); 394 | boolean[] bools = new boolean[len]; 395 | // ----- pack bools: 396 | for (int i = 0, j = 4, k = 7; i < bools.length; i++) { 397 | bools[i] = ((data[j] >> k--) & 0x01) == 1; 398 | if (k < 0) { j++; k = 7; } 399 | } 400 | // ---------- 401 | return bools; 402 | } 403 | 404 | /* ========================= */ 405 | 406 | public static String toString(byte[] data) { 407 | return (data == null) ? null : new String(data); 408 | } 409 | 410 | public static String[] toStringA(byte[] data) { 411 | // Advanced Technique: Extract the String array's length 412 | // from the first four bytes in the char array, and then 413 | // read the int array denoting the String lengths, and 414 | // then read the Strings. 415 | // ---------- 416 | if (data == null || data.length < 4) return null; 417 | // ---------- 418 | byte[] bBuff = new byte[4]; // Buffer 419 | // ----- 420 | System.arraycopy(data, 0, bBuff, 0, 4); 421 | int saLen = toInt(bBuff); 422 | if (data.length < (4 + (saLen * 4))) return null; 423 | // ----- 424 | bBuff = new byte[saLen * 4]; 425 | System.arraycopy(data, 4, bBuff, 0, bBuff.length); 426 | int[] sLens = toIntA(bBuff); 427 | if (sLens == null) return null; 428 | // ---------- 429 | String[] strs = new String[saLen]; 430 | for (int i = 0, dataPos = 4 + (saLen * 4); i < saLen; i++) { 431 | if (sLens[i] > 0) { 432 | if (data.length >= (dataPos + sLens[i])) { 433 | bBuff = new byte[sLens[i]]; 434 | System.arraycopy(data, dataPos, bBuff, 0, sLens[i]); 435 | dataPos += sLens[i]; 436 | strs[i] = toString(bBuff); 437 | } else return null; 438 | } 439 | } 440 | // ---------- 441 | return strs; 442 | } 443 | 444 | private static final byte[] correspondingNibble = new byte[ 'f' + 1 ]; 445 | 446 | // -------------------------- STATIC METHODS -------------------------- 447 | 448 | static 449 | { 450 | // only 0..9 A..F a..f have meaning. rest are errors. 451 | for ( int i = 0; i <= 'f'; i++ ) 452 | { 453 | correspondingNibble[ i ] = -1; 454 | } 455 | for ( int i = '0'; i <= '9'; i++ ) 456 | { 457 | correspondingNibble[ i ] = ( byte ) ( i - '0' ); 458 | } 459 | for ( int i = 'A'; i <= 'F'; i++ ) 460 | { 461 | correspondingNibble[ i ] = ( byte ) ( i - 'A' + 10 ); 462 | } 463 | for ( int i = 'a'; i <= 'f'; i++ ) 464 | { 465 | correspondingNibble[ i ] = ( byte ) ( i - 'a' + 10 ); 466 | } 467 | } 468 | 469 | /** 470 | * convert a single char to corresponding nibble using a precalculated array. 471 | * Based on code by: 472 | * Brian Marquis 473 | * Orion Group Software Engineers http://www.ogse.com 474 | * 475 | * @param c char to convert. must be 0-9 a-f A-F, no 476 | * spaces, plus or minus signs. 477 | * 478 | * @return corresponding integer 0..15 479 | * @throws IllegalArgumentException on invalid c. 480 | */ 481 | private static int charToNibble( char c ) 482 | { 483 | if ( c > 'f' ) 484 | { 485 | throw new IllegalArgumentException( "Invalid hex character: " + c ); 486 | } 487 | int nibble = correspondingNibble[ c ]; 488 | if ( nibble < 0 ) 489 | { 490 | throw new IllegalArgumentException( "Invalid hex character: " + c ); 491 | } 492 | return nibble; 493 | } 494 | 495 | /** 496 | * Convert a hex string to an unsigned byte array. 497 | * Permits upper or lower case hex. 498 | * 499 | * @param s String must have even number of characters. 500 | * and be formed only of digits 0-9 A-F or 501 | * a-f. No spaces, minus or plus signs. 502 | * 503 | * @return corresponding unsigned byte array. see http://mindprod.com/jgloss/unsigned.html 504 | */ 505 | static byte[] fromHexString( String s ) 506 | { 507 | int stringLength = s.length(); 508 | if ( ( stringLength & 0x1 ) != 0 ) 509 | { 510 | throw new IllegalArgumentException( "fromHexString requires an even number of hex characters" ); 511 | } 512 | byte[] bytes = new byte[ stringLength / 2 ]; 513 | for ( int i = 0, j = 0; i < stringLength; i += 2, j++ ) 514 | { 515 | int high = charToNibble( s.charAt( i ) ); 516 | int low = charToNibble( s.charAt( i + 1 ) ); 517 | // You can store either unsigned 0..255 or signed -128..127 bytes in a byte type. 518 | bytes[ j ] = ( byte ) ( ( high << 4 ) | low ); 519 | } 520 | return bytes; 521 | } 522 | } 523 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/Base64.java: -------------------------------------------------------------------------------- 1 | package com.lambdaworks.crypto; 2 | 3 | import java.util.Arrays; 4 | 5 | /** A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance 6 | * with RFC 2045.

7 | * On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster 8 | * on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes) 9 | * compared to sun.misc.Encoder()/Decoder().

10 | * 11 | * On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and 12 | * about 50% faster for decoding large arrays. This implementation is about twice as fast on very small 13 | * arrays (< 30 bytes). If source/destination is a String this 14 | * version is about three times as fast due to the fact that the Commons Codec result has to be recoded 15 | * to a String from byte[], which is very expensive.

16 | * 17 | * This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only 18 | * allocates the resulting array. This produces less garbage and it is possible to handle arrays twice 19 | * as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown 20 | * whether Sun's sun.misc.Encoder()/Decoder() produce temporary arrays but since performance 21 | * is quite low it probably does.

22 | * 23 | * The encoder produces the same output as the Sun one except that the Sun's encoder appends 24 | * a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the 25 | * length and is probably a side effect. Both are in conformance with RFC 2045 though.
26 | * Commons codec seem to always att a trailing line separator.

27 | * 28 | * Note! 29 | * The encode/decode method pairs (types) come in three versions with the exact same algorithm and 30 | * thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different 31 | * format types. The methods not used can simply be commented out.

32 | * 33 | * There is also a "fast" version of all decode methods that works the same way as the normal ones, but 34 | * har a few demands on the decoded input. Normally though, these fast verions should be used if the source if 35 | * the input is known and it hasn't bee tampered with.

36 | * 37 | * If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com. 38 | * 39 | * Licence (BSD): 40 | * ============== 41 | * 42 | * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com) 43 | * All rights reserved. 44 | * 45 | * Redistribution and use in source and binary forms, with or without modification, 46 | * are permitted provided that the following conditions are met: 47 | * Redistributions of source code must retain the above copyright notice, this list 48 | * of conditions and the following disclaimer. 49 | * Redistributions in binary form must reproduce the above copyright notice, this 50 | * list of conditions and the following disclaimer in the documentation and/or other 51 | * materials provided with the distribution. 52 | * Neither the name of the MiG InfoCom AB nor the names of its contributors may be 53 | * used to endorse or promote products derived from this software without specific 54 | * prior written permission. 55 | * 56 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 57 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 58 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 59 | * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 60 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 61 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 62 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 63 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 64 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 65 | * OF SUCH DAMAGE. 66 | * 67 | * @version 2.2 68 | * @author Mikael Grev 69 | * Date: 2004-aug-02 70 | * Time: 11:31:11 71 | */ 72 | 73 | public class Base64 74 | { 75 | private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); 76 | private static final int[] IA = new int[256]; 77 | static { 78 | Arrays.fill(IA, -1); 79 | for (int i = 0, iS = CA.length; i < iS; i++) 80 | IA[CA[i]] = i; 81 | IA['='] = 0; 82 | } 83 | 84 | // **************************************************************************************** 85 | // * char[] version 86 | // **************************************************************************************** 87 | 88 | /** Encodes a raw byte array into a BASE64 char[] representation i accordance with RFC 2045. 89 | * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. 90 | * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
91 | * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a 92 | * little faster. 93 | * @return A BASE64 encoded array. Never null. 94 | */ 95 | public final static char[] encodeToChar(byte[] sArr, boolean lineSep) 96 | { 97 | // Check special case 98 | int sLen = sArr != null ? sArr.length : 0; 99 | if (sLen == 0) 100 | return new char[0]; 101 | 102 | int eLen = (sLen / 3) * 3; // Length of even 24-bits. 103 | int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count 104 | int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array 105 | char[] dArr = new char[dLen]; 106 | 107 | // Encode even 24-bits 108 | for (int s = 0, d = 0, cc = 0; s < eLen;) { 109 | // Copy next three bytes into lower 24 bits of int, paying attension to sign. 110 | int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); 111 | 112 | // Encode the int into four chars 113 | dArr[d++] = CA[(i >>> 18) & 0x3f]; 114 | dArr[d++] = CA[(i >>> 12) & 0x3f]; 115 | dArr[d++] = CA[(i >>> 6) & 0x3f]; 116 | dArr[d++] = CA[i & 0x3f]; 117 | 118 | // Add optional line separator 119 | if (lineSep && ++cc == 19 && d < dLen - 2) { 120 | dArr[d++] = '\r'; 121 | dArr[d++] = '\n'; 122 | cc = 0; 123 | } 124 | } 125 | 126 | // Pad and encode last bits if source isn't even 24 bits. 127 | int left = sLen - eLen; // 0 - 2. 128 | if (left > 0) { 129 | // Prepare the int 130 | int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); 131 | 132 | // Set last four chars 133 | dArr[dLen - 4] = CA[i >> 12]; 134 | dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; 135 | dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; 136 | dArr[dLen - 1] = '='; 137 | } 138 | return dArr; 139 | } 140 | 141 | /** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with 142 | * and without line separators. 143 | * @param sArr The source array. null or length 0 will return an empty array. 144 | * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters 145 | * (including '=') isn't divideable by 4. (I.e. definitely corrupted). 146 | */ 147 | public final static byte[] decode(char[] sArr) 148 | { 149 | // Check special case 150 | int sLen = sArr != null ? sArr.length : 0; 151 | if (sLen == 0) 152 | return new byte[0]; 153 | 154 | // Count illegal characters (including '\r', '\n') to know what size the returned array will be, 155 | // so we don't have to reallocate & copy it later. 156 | int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) 157 | for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. 158 | if (IA[sArr[i]] < 0) 159 | sepCnt++; 160 | 161 | // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. 162 | if ((sLen - sepCnt) % 4 != 0) 163 | return null; 164 | 165 | int pad = 0; 166 | for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) 167 | if (sArr[i] == '=') 168 | pad++; 169 | 170 | int len = ((sLen - sepCnt) * 6 >> 3) - pad; 171 | 172 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 173 | 174 | for (int s = 0, d = 0; d < len;) { 175 | // Assemble three bytes into an int from four "valid" characters. 176 | int i = 0; 177 | for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. 178 | int c = IA[sArr[s++]]; 179 | if (c >= 0) 180 | i |= c << (18 - j * 6); 181 | else 182 | j--; 183 | } 184 | // Add the bytes 185 | dArr[d++] = (byte) (i >> 16); 186 | if (d < len) { 187 | dArr[d++]= (byte) (i >> 8); 188 | if (d < len) 189 | dArr[d++] = (byte) i; 190 | } 191 | } 192 | return dArr; 193 | } 194 | 195 | /** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as 196 | * fast as {@link #decode(char[])}. The preconditions are:
197 | * + The array must have a line length of 76 chars OR no line separators at all (one line).
198 | * + Line separator must be "\r\n", as specified in RFC 2045 199 | * + The array must not contain illegal characters within the encoded string
200 | * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
201 | * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. 202 | * @return The decoded array of bytes. May be of length 0. 203 | */ 204 | public final static byte[] decodeFast(char[] sArr) 205 | { 206 | // Check special case 207 | int sLen = sArr.length; 208 | if (sLen == 0) 209 | return new byte[0]; 210 | 211 | int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. 212 | 213 | // Trim illegal chars from start 214 | while (sIx < eIx && IA[sArr[sIx]] < 0) 215 | sIx++; 216 | 217 | // Trim illegal chars from end 218 | while (eIx > 0 && IA[sArr[eIx]] < 0) 219 | eIx--; 220 | 221 | // get the padding count (=) (0, 1 or 2) 222 | int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. 223 | int cCnt = eIx - sIx + 1; // Content count including possible separators 224 | int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; 225 | 226 | int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes 227 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 228 | 229 | // Decode all but the last 0 - 2 bytes. 230 | int d = 0; 231 | for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { 232 | // Assemble three bytes into an int from four "valid" characters. 233 | int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; 234 | 235 | // Add the bytes 236 | dArr[d++] = (byte) (i >> 16); 237 | dArr[d++] = (byte) (i >> 8); 238 | dArr[d++] = (byte) i; 239 | 240 | // If line separator, jump over it. 241 | if (sepCnt > 0 && ++cc == 19) { 242 | sIx += 2; 243 | cc = 0; 244 | } 245 | } 246 | 247 | if (d < len) { 248 | // Decode last 1-3 bytes (incl '=') into 1-3 bytes 249 | int i = 0; 250 | for (int j = 0; sIx <= eIx - pad; j++) 251 | i |= IA[sArr[sIx++]] << (18 - j * 6); 252 | 253 | for (int r = 16; d < len; r -= 8) 254 | dArr[d++] = (byte) (i >> r); 255 | } 256 | 257 | return dArr; 258 | } 259 | 260 | // **************************************************************************************** 261 | // * byte[] version 262 | // **************************************************************************************** 263 | 264 | /** Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. 265 | * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. 266 | * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
267 | * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a 268 | * little faster. 269 | * @return A BASE64 encoded array. Never null. 270 | */ 271 | public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) 272 | { 273 | // Check special case 274 | int sLen = sArr != null ? sArr.length : 0; 275 | if (sLen == 0) 276 | return new byte[0]; 277 | 278 | int eLen = (sLen / 3) * 3; // Length of even 24-bits. 279 | int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count 280 | int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array 281 | byte[] dArr = new byte[dLen]; 282 | 283 | // Encode even 24-bits 284 | for (int s = 0, d = 0, cc = 0; s < eLen;) { 285 | // Copy next three bytes into lower 24 bits of int, paying attension to sign. 286 | int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); 287 | 288 | // Encode the int into four chars 289 | dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; 290 | dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; 291 | dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; 292 | dArr[d++] = (byte) CA[i & 0x3f]; 293 | 294 | // Add optional line separator 295 | if (lineSep && ++cc == 19 && d < dLen - 2) { 296 | dArr[d++] = '\r'; 297 | dArr[d++] = '\n'; 298 | cc = 0; 299 | } 300 | } 301 | 302 | // Pad and encode last bits if source isn't an even 24 bits. 303 | int left = sLen - eLen; // 0 - 2. 304 | if (left > 0) { 305 | // Prepare the int 306 | int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); 307 | 308 | // Set last four chars 309 | dArr[dLen - 4] = (byte) CA[i >> 12]; 310 | dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; 311 | dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; 312 | dArr[dLen - 1] = '='; 313 | } 314 | return dArr; 315 | } 316 | 317 | /** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with 318 | * and without line separators. 319 | * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. 320 | * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters 321 | * (including '=') isn't divideable by 4. (I.e. definitely corrupted). 322 | */ 323 | public final static byte[] decode(byte[] sArr) 324 | { 325 | // Check special case 326 | int sLen = sArr.length; 327 | 328 | // Count illegal characters (including '\r', '\n') to know what size the returned array will be, 329 | // so we don't have to reallocate & copy it later. 330 | int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) 331 | for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. 332 | if (IA[sArr[i] & 0xff] < 0) 333 | sepCnt++; 334 | 335 | // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. 336 | if ((sLen - sepCnt) % 4 != 0) 337 | return null; 338 | 339 | int pad = 0; 340 | for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;) 341 | if (sArr[i] == '=') 342 | pad++; 343 | 344 | int len = ((sLen - sepCnt) * 6 >> 3) - pad; 345 | 346 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 347 | 348 | for (int s = 0, d = 0; d < len;) { 349 | // Assemble three bytes into an int from four "valid" characters. 350 | int i = 0; 351 | for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. 352 | int c = IA[sArr[s++] & 0xff]; 353 | if (c >= 0) 354 | i |= c << (18 - j * 6); 355 | else 356 | j--; 357 | } 358 | 359 | // Add the bytes 360 | dArr[d++] = (byte) (i >> 16); 361 | if (d < len) { 362 | dArr[d++]= (byte) (i >> 8); 363 | if (d < len) 364 | dArr[d++] = (byte) i; 365 | } 366 | } 367 | 368 | return dArr; 369 | } 370 | 371 | 372 | /** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as 373 | * fast as {@link #decode(byte[])}. The preconditions are:
374 | * + The array must have a line length of 76 chars OR no line separators at all (one line).
375 | * + Line separator must be "\r\n", as specified in RFC 2045 376 | * + The array must not contain illegal characters within the encoded string
377 | * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
378 | * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. 379 | * @return The decoded array of bytes. May be of length 0. 380 | */ 381 | public final static byte[] decodeFast(byte[] sArr) 382 | { 383 | // Check special case 384 | int sLen = sArr.length; 385 | if (sLen == 0) 386 | return new byte[0]; 387 | 388 | int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. 389 | 390 | // Trim illegal chars from start 391 | while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) 392 | sIx++; 393 | 394 | // Trim illegal chars from end 395 | while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) 396 | eIx--; 397 | 398 | // get the padding count (=) (0, 1 or 2) 399 | int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. 400 | int cCnt = eIx - sIx + 1; // Content count including possible separators 401 | int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; 402 | 403 | int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes 404 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 405 | 406 | // Decode all but the last 0 - 2 bytes. 407 | int d = 0; 408 | for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { 409 | // Assemble three bytes into an int from four "valid" characters. 410 | int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; 411 | 412 | // Add the bytes 413 | dArr[d++] = (byte) (i >> 16); 414 | dArr[d++] = (byte) (i >> 8); 415 | dArr[d++] = (byte) i; 416 | 417 | // If line separator, jump over it. 418 | if (sepCnt > 0 && ++cc == 19) { 419 | sIx += 2; 420 | cc = 0; 421 | } 422 | } 423 | 424 | if (d < len) { 425 | // Decode last 1-3 bytes (incl '=') into 1-3 bytes 426 | int i = 0; 427 | for (int j = 0; sIx <= eIx - pad; j++) 428 | i |= IA[sArr[sIx++]] << (18 - j * 6); 429 | 430 | for (int r = 16; d < len; r -= 8) 431 | dArr[d++] = (byte) (i >> r); 432 | } 433 | 434 | return dArr; 435 | } 436 | 437 | // **************************************************************************************** 438 | // * String version 439 | // **************************************************************************************** 440 | 441 | /** Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. 442 | * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. 443 | * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
444 | * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a 445 | * little faster. 446 | * @return A BASE64 encoded array. Never null. 447 | */ 448 | public final static String encodeToString(byte[] sArr, boolean lineSep) 449 | { 450 | // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. 451 | return new String(encodeToChar(sArr, lineSep)); 452 | } 453 | 454 | /** Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with 455 | * and without line separators.
456 | * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That 457 | * will create a temporary array though. This version will use str.charAt(i) to iterate the string. 458 | * @param str The source string. null or length 0 will return an empty array. 459 | * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters 460 | * (including '=') isn't divideable by 4. (I.e. definitely corrupted). 461 | */ 462 | public final static byte[] decode(String str) 463 | { 464 | // Check special case 465 | int sLen = str != null ? str.length() : 0; 466 | if (sLen == 0) 467 | return new byte[0]; 468 | 469 | // Count illegal characters (including '\r', '\n') to know what size the returned array will be, 470 | // so we don't have to reallocate & copy it later. 471 | int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) 472 | for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. 473 | if (IA[str.charAt(i)] < 0) 474 | sepCnt++; 475 | 476 | // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. 477 | if ((sLen - sepCnt) % 4 != 0) 478 | return null; 479 | 480 | // Count '=' at end 481 | int pad = 0; 482 | for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) 483 | if (str.charAt(i) == '=') 484 | pad++; 485 | 486 | int len = ((sLen - sepCnt) * 6 >> 3) - pad; 487 | 488 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 489 | 490 | for (int s = 0, d = 0; d < len;) { 491 | // Assemble three bytes into an int from four "valid" characters. 492 | int i = 0; 493 | for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. 494 | int c = IA[str.charAt(s++)]; 495 | if (c >= 0) 496 | i |= c << (18 - j * 6); 497 | else 498 | j--; 499 | } 500 | // Add the bytes 501 | dArr[d++] = (byte) (i >> 16); 502 | if (d < len) { 503 | dArr[d++]= (byte) (i >> 8); 504 | if (d < len) 505 | dArr[d++] = (byte) i; 506 | } 507 | } 508 | return dArr; 509 | } 510 | 511 | /** Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as 512 | * fast as {@link #decode(String)}. The preconditions are:
513 | * + The array must have a line length of 76 chars OR no line separators at all (one line).
514 | * + Line separator must be "\r\n", as specified in RFC 2045 515 | * + The array must not contain illegal characters within the encoded string
516 | * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
517 | * @param s The source string. Length 0 will return an empty array. null will throw an exception. 518 | * @return The decoded array of bytes. May be of length 0. 519 | */ 520 | public final static byte[] decodeFast(String s) 521 | { 522 | // Check special case 523 | int sLen = s.length(); 524 | if (sLen == 0) 525 | return new byte[0]; 526 | 527 | int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. 528 | 529 | // Trim illegal chars from start 530 | while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) 531 | sIx++; 532 | 533 | // Trim illegal chars from end 534 | while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) 535 | eIx--; 536 | 537 | // get the padding count (=) (0, 1 or 2) 538 | int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. 539 | int cCnt = eIx - sIx + 1; // Content count including possible separators 540 | int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; 541 | 542 | int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes 543 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 544 | 545 | // Decode all but the last 0 - 2 bytes. 546 | int d = 0; 547 | for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { 548 | // Assemble three bytes into an int from four "valid" characters. 549 | int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; 550 | 551 | // Add the bytes 552 | dArr[d++] = (byte) (i >> 16); 553 | dArr[d++] = (byte) (i >> 8); 554 | dArr[d++] = (byte) i; 555 | 556 | // If line separator, jump over it. 557 | if (sepCnt > 0 && ++cc == 19) { 558 | sIx += 2; 559 | cc = 0; 560 | } 561 | } 562 | 563 | if (d < len) { 564 | // Decode last 1-3 bytes (incl '=') into 1-3 bytes 565 | int i = 0; 566 | for (int j = 0; sIx <= eIx - pad; j++) 567 | i |= IA[s.charAt(sIx++)] << (18 - j * 6); 568 | 569 | for (int r = 16; d < len; r -= 8) 570 | dArr[d++] = (byte) (i >> r); 571 | } 572 | 573 | return dArr; 574 | } 575 | } 576 | --------------------------------------------------------------------------------