// Copyright (c) 2014 Tom Zhou<iwebpp@gmail.com> package com.iwebpp.crypto; import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import java.util.concurrent.atomic.AtomicLong; /* * @description * TweetNacl.c Java porting * */ public final class TweetNacl { private final static String TAG = "TweetNacl"; /* * @description * Box algorithm, Public-key authenticated encryption * */ public static final class Box { private final static String TAG = "Box"; private AtomicLong nonce; private byte [] theirPublicKey; private byte [] mySecretKey; private byte [] sharedKey; public Box(byte [] theirPublicKey, byte [] mySecretKey) { this(theirPublicKey, mySecretKey, 68); } public Box(byte [] theirPublicKey, byte [] mySecretKey, long nonce) { this.theirPublicKey = theirPublicKey; this.mySecretKey = mySecretKey; this.nonce = new AtomicLong(nonce); // generate pre-computed shared key before(); } public void setNonce(long nonce) { this.nonce.set(nonce); } public long getNonce() { return this.nonce.get(); } public long incrNonce() { return this.nonce.incrementAndGet(); } private byte[] generateNonce() { // generate nonce long nonce = this.nonce.get(); byte [] n = new byte[nonceLength]; for (int i = 0; i < nonceLength; i += 8) { n[i+0] = (byte) (nonce>>> 0); n[i+1] = (byte) (nonce>>> 8); n[i+2] = (byte) (nonce>>>16); n[i+3] = (byte) (nonce>>>24); n[i+4] = (byte) (nonce>>>32); n[i+5] = (byte) (nonce>>>40); n[i+6] = (byte) (nonce>>>48); n[i+7] = (byte) (nonce>>>56); } return n; } /* * @description * Encrypt and authenticates message using peer's public key, * our secret key, and the given nonce, which must be unique * for each distinct message for a key pair. * * Returns an encrypted and authenticated message, * which is nacl.box.overheadLength longer than the original message. * */ ///public byte_buf_t box(byte [] message) { public byte [] box(byte [] message) { return box(message, generateNonce()); } /* * @description * Encrypt and authenticates message using peer's public key, * our secret key, and the explicitly provided nonce. * Caller is responsible for ensuring that nonce is unique * for each distinct message for a key pair. * * Returns an encrypted and authenticated message, * which is nacl.box.overheadLength longer than the original message. * */ ///public byte_buf_t box(byte [] message) { public byte [] box(byte [] message, byte [] theNonce) { // check message if (!(message!=null && message.length>0 && theNonce!=null && theNonce.length==nonceLength)) return null; // message buffer byte [] m = new byte[message.length + zerobytesLength]; // cipher buffer byte [] c = new byte[m.length]; for (int i = 0; i < message.length; i ++) m[i+zerobytesLength] = message[i]; if (0 != crypto_box(c, m, m.length, theNonce, theirPublicKey, mySecretKey)) return null; // wrap byte_buf_t on c offset@boxzerobytesLength ///return new byte_buf_t(c, boxzerobytesLength, c.length-boxzerobytesLength); byte [] ret = new byte[c.length-boxzerobytesLength]; for (int i = 0; i < ret.length; i ++) ret[i] = c[i+boxzerobytesLength]; return ret; } /* * @description * Authenticates and decrypts the given box with peer's public key, * our secret key, and the given nonce. * * Returns the original message, or null if authentication fails. * */ public byte [] open(byte [] box) { return open(box, generateNonce()); } /* * @description * Authenticates and decrypts the given box with peer's public key, * our secret key, and the explicitly provided nonce. * * Returns the original message, or null if authentication fails. * */ public byte [] open(byte [] box, byte [] theNonce) { // check message if (!(box!=null && box.length>boxzerobytesLength && theNonce!=null && theNonce.length==nonceLength)) return null; // cipher buffer byte [] c = new byte[box.length + boxzerobytesLength]; // message buffer byte [] m = new byte[c.length]; for (int i = 0; i < box.length; i++) c[i+boxzerobytesLength] = box[i]; if (0 != crypto_box_open(m, c, c.length, theNonce, theirPublicKey, mySecretKey)) return null; // wrap byte_buf_t on m offset@zerobytesLength ///return new byte_buf_t(m, zerobytesLength, m.length-zerobytesLength); byte [] ret = new byte[m.length-zerobytesLength]; for (int i = 0; i < ret.length; i ++) ret[i] = m[i+zerobytesLength]; return ret; } /* * @description * Returns a precomputed shared key * which can be used in nacl.box.after and nacl.box.open.after. * */ public byte [] before() { if (this.sharedKey == null) { this.sharedKey = new byte[sharedKeyLength]; crypto_box_beforenm(this.sharedKey, this.theirPublicKey, this.mySecretKey); } return this.sharedKey; } /* * @description * Same as nacl.box, but uses a shared key precomputed with nacl.box.before. * */ public byte [] after(byte [] message) { return after(message, generateNonce()); } /* * @description * Same as nacl.box, but uses a shared key precomputed with nacl.box.before * and explicitly provided nonce * */ public byte [] after(byte [] message, byte [] theNonce) { // check message if (!(message!=null && message.length>0 && theNonce!=null && theNonce.length==nonceLength)) return null; // message buffer byte [] m = new byte[message.length + zerobytesLength]; // cipher buffer byte [] c = new byte[m.length]; for (int i = 0; i < message.length; i ++) m[i+zerobytesLength] = message[i]; if (0 != crypto_box_afternm(c, m, m.length, theNonce, sharedKey)) return null; // wrap byte_buf_t on c offset@boxzerobytesLength ///return new byte_buf_t(c, boxzerobytesLength, c.length-boxzerobytesLength); byte [] ret = new byte[c.length-boxzerobytesLength]; for (int i = 0; i < ret.length; i ++) ret[i] = c[i+boxzerobytesLength]; return ret; } /* * @description * Same as nacl.box.open, * but uses a shared key pre-computed with nacl.box.before. * */ public byte [] open_after(byte [] box) { return open_after(box, generateNonce()); } /* * @description * Same as nacl.box.open, * but uses a shared key pre-computed with nacl.box.before, * and explicitly passed nonce * */ public byte [] open_after(byte [] box, byte [] theNonce) { // check message if (!(box!=null && box.length>boxzerobytesLength && theNonce!=null && theNonce.length==nonceLength)) return null; // cipher buffer byte [] c = new byte[box.length + boxzerobytesLength]; // message buffer byte [] m = new byte[c.length]; for (int i = 0; i < box.length; i++) c[i+boxzerobytesLength] = box[i]; if (crypto_box_open_afternm(m, c, c.length, theNonce, sharedKey) != 0) return null; // wrap byte_buf_t on m offset@zerobytesLength ///return new byte_buf_t(m, zerobytesLength, m.length-zerobytesLength); byte [] ret = new byte[m.length-zerobytesLength]; for (int i = 0; i < ret.length; i ++) ret[i] = m[i+zerobytesLength]; return ret; } /* * @description * Length of public key in bytes. * */ public static final int publicKeyLength = 32; /* * @description * Length of secret key in bytes. * */ public static final int secretKeyLength = 32; /* * @description * Length of precomputed shared key in bytes. * */ public static final int sharedKeyLength = 32; /* * @description * Length of nonce in bytes. * */ public static final int nonceLength = 24; /* * @description * zero bytes in case box * */ public static final int zerobytesLength = 32; /* * @description * zero bytes in case open box * */ public static final int boxzerobytesLength = 16; /* * @description * Length of overhead added to box compared to original message. * */ public static final int overheadLength = 16; public static class KeyPair { private byte [] publicKey; private byte [] secretKey; public KeyPair() { publicKey = new byte[publicKeyLength]; secretKey = new byte[secretKeyLength]; } public byte [] getPublicKey() { return publicKey; } public byte [] getSecretKey() { return secretKey; } } /* * @description * Generates a new random key pair for box and * returns it as an object with publicKey and secretKey members: * */ public static KeyPair keyPair() { KeyPair kp = new KeyPair(); crypto_box_keypair(kp.getPublicKey(), kp.getSecretKey()); return kp; } public static KeyPair keyPair_fromSecretKey(byte [] secretKey) { KeyPair kp = new KeyPair(); byte [] sk = kp.getSecretKey(); byte [] pk = kp.getPublicKey(); // copy sk for (int i = 0; i < sk.length; i ++) sk[i] = secretKey[i]; crypto_scalarmult_base(pk, sk); return kp; } } /* * @description * Secret Box algorithm, secret key * */ public static class SecretBox { private final static String TAG = "SecretBox"; private AtomicLong nonce; private byte [] key; public SecretBox(byte [] key) { this(key, 68); } public SecretBox(byte [] key, long nonce) { this.key = key; this.nonce = new AtomicLong(nonce); } public void setNonce(long nonce) { this.nonce.set(nonce); } public long getNonce() { return this.nonce.get(); } public long incrNonce() { return this.nonce.incrementAndGet(); } private byte[] generateNonce() { // generate nonce long nonce = this.nonce.get(); byte [] n = new byte[nonceLength]; for (int i = 0; i < nonceLength; i += 8) { n[i+0] = (byte) (nonce>>> 0); n[i+1] = (byte) (nonce>>> 8); n[i+2] = (byte) (nonce>>>16); n[i+3] = (byte) (nonce>>>24); n[i+4] = (byte) (nonce>>>32); n[i+5] = (byte) (nonce>>>40); n[i+6] = (byte) (nonce>>>48); n[i+7] = (byte) (nonce>>>56); } return n; } /* * @description * Encrypt and authenticates message using the key and the nonce. * The nonce must be unique for each distinct message for this key. * * Returns an encrypted and authenticated message, * which is nacl.secretbox.overheadLength longer than the original message. * */ ///public byte_buf_t box(byte [] message) { public byte [] box(byte [] message) { return box(message, generateNonce()); } /* * @description * Encrypt and authenticates message using the key * and the explicitly passed nonce. * The nonce must be unique for each distinct message for this key. * * Returns an encrypted and authenticated message, * which is nacl.secretbox.overheadLength longer than the original message. * */ ///public byte_buf_t box(byte [] message) { public byte [] box(byte [] message, byte [] theNonce) { // check message if (!(message!=null && message.length>0 && theNonce!=null && theNonce.length==nonceLength)) return null; // message buffer byte [] m = new byte[message.length + zerobytesLength]; // cipher buffer byte [] c = new byte[m.length]; for (int i = 0; i < message.length; i ++) m[i+zerobytesLength] = message[i]; if (0 != crypto_secretbox(c, m, m.length, theNonce, key)) return null; // TBD optimizing ... // wrap byte_buf_t on c offset@boxzerobytesLength ///return new byte_buf_t(c, boxzerobytesLength, c.length-boxzerobytesLength); byte [] ret = new byte[c.length-boxzerobytesLength]; for (int i = 0; i < ret.length; i ++) ret[i] = c[i+boxzerobytesLength]; return ret; } /* * @description * Authenticates and decrypts the given secret box * using the key and the nonce. * * Returns the original message, or null if authentication fails. * */ public byte [] open(byte [] box) { return open(box, generateNonce()); } /* * @description * Authenticates and decrypts the given secret box * using the key and the explicitly passed nonce. * * Returns the original message, or null if authentication fails. * */ public byte [] open(byte [] box, byte [] theNonce) { // check message if (!(box!=null && box.length>boxzerobytesLength && theNonce!=null && theNonce.length==nonceLength)) return null; // cipher buffer byte [] c = new byte[box.length + boxzerobytesLength]; // message buffer byte [] m = new byte[c.length]; for (int i = 0; i < box.length; i++) c[i+boxzerobytesLength] = box[i]; if (0 != crypto_secretbox_open(m, c, c.length, theNonce, key)) return null; // wrap byte_buf_t on m offset@zerobytesLength ///return new byte_buf_t(m, zerobytesLength, m.length-zerobytesLength); byte [] ret = new byte[m.length-zerobytesLength]; for (int i = 0; i < ret.length; i ++) ret[i] = m[i+zerobytesLength]; return ret; } /* * @description * Length of key in bytes. * */ public static final int keyLength = 32; /* * @description * Length of nonce in bytes. * */ public static final int nonceLength = 24; /* * @description * Length of overhead added to secret box compared to original message. * */ public static final int overheadLength = 16; /* * @description * zero bytes in case box * */ public static final int zerobytesLength = 32; /* * @description * zero bytes in case open box * */ public static final int boxzerobytesLength = 16; } /* * @description * Scalar multiplication, Implements curve25519. * */ public static final class ScalarMult { private final static String TAG = "ScalarMult"; /* * @description * Multiplies an integer n by a group element p and * returns the resulting group element. * */ public static byte [] scalseMult(byte [] n, byte [] p) { if (!(n.length==scalarLength && p.length==groupElementLength)) return null; byte [] q = new byte [scalarLength]; crypto_scalarmult(q, n, p); return q; } /* * @description * Multiplies an integer n by a standard group element and * returns the resulting group element. * */ public static byte [] scalseMult_base(byte [] n) { if (!(n.length==scalarLength)) return null; byte [] q = new byte [scalarLength]; crypto_scalarmult_base(q, n); return q; } /* * @description * Length of scalar in bytes. * */ public static final int scalarLength = 32; /* * @description * Length of group element in bytes. * */ public static final int groupElementLength = 32; } /* * @description * Hash algorithm, Implements SHA-512. * */ public static final class Hash { private final static String TAG = "Hash"; /* * @description * Returns SHA-512 hash of the message. * */ public static byte[] sha512(byte [] message) { if (!(message!=null && message.length>0)) return null; byte [] out = new byte[hashLength]; crypto_hash(out, message); return out; } public static byte[] sha512(String message) throws UnsupportedEncodingException { return sha512(message.getBytes("utf-8")); } /* * @description * Length of hash in bytes. * */ public static final int hashLength = 64; } /* * @description * Signature algorithm, Implements ed25519. * */ public static final class Signature { private final static String TAG = "Signature"; private byte [] theirPublicKey; private byte [] mySecretKey; public Signature(byte [] theirPublicKey, byte [] mySecretKey) { this.theirPublicKey = theirPublicKey; this.mySecretKey = mySecretKey; } /* * @description * Signs the message using the secret key and returns a signed message. * */ public byte [] sign(byte [] message) { // signed message byte [] sm = new byte[message.length + signatureLength]; crypto_sign(sm, -1, message, message.length, mySecretKey); return sm; } /* * @description * Verifies the signed message and returns the message without signature. * Returns null if verification failed. * */ public byte [] open(byte [] signedMessage) { // check sm length if (!(signedMessage!=null && signedMessage.length>signatureLength)) return null; // temp buffer byte [] tmp = new byte[signedMessage.length]; if (0 != crypto_sign_open(tmp, -1, signedMessage, signedMessage.length, theirPublicKey)) return null; // message byte [] msg = new byte[signedMessage.length-signatureLength]; for (int i = 0; i < msg.length; i ++) msg[i] = signedMessage[i+signatureLength]; return msg; } /* * @description * Signs the message using the secret key and returns a signature. * */ public byte [] detached(byte [] message) { return null; } /* * @description * Verifies the signature for the message and * returns true if verification succeeded or false if it failed. * */ public boolean detached_verify(byte [] message, byte [] signature) { return false; } /* * @description * Generates new random key pair for signing and * returns it as an object with publicKey and secretKey members * */ public static class KeyPair { private byte [] publicKey; private byte [] secretKey; public KeyPair() { publicKey = new byte[publicKeyLength]; secretKey = new byte[secretKeyLength]; } public byte [] getPublicKey() { return publicKey; } public byte [] getSecretKey() { return secretKey; } } /* * @description * Signs the message using the secret key and returns a signed message. * */ public static KeyPair keyPair() { KeyPair kp = new KeyPair(); crypto_sign_keypair(kp.getPublicKey(), kp.getSecretKey(), false); return kp; } public static KeyPair keyPair_fromSecretKey(byte [] secretKey) { KeyPair kp = new KeyPair(); byte [] pk = kp.getPublicKey(); byte [] sk = kp.getSecretKey(); // copy sk for (int i = 0; i < kp.getSecretKey().length; i ++) sk[i] = secretKey[i]; // copy pk from sk for (int i = 0; i < kp.getPublicKey().length; i ++) pk[i] = secretKey[32+i]; // hard-copy return kp; } public static KeyPair keyPair_fromSeed(byte [] seed) { KeyPair kp = new KeyPair(); byte [] pk = kp.getPublicKey(); byte [] sk = kp.getSecretKey(); // copy sk for (int i = 0; i < seedLength; i ++) sk[i] = seed[i]; // generate pk from sk crypto_sign_keypair(pk, sk, true); return kp; } /* * @description * Length of signing public key in bytes. * */ public static final int publicKeyLength = 32; /* * @description * Length of signing secret key in bytes. * */ public static final int secretKeyLength = 64; /* * @description * Length of seed for nacl.sign.keyPair.fromSeed in bytes. * */ public static final int seedLength = 32; /* * @description * Length of signature in bytes. * */ public static final int signatureLength = 64; } //////////////////////////////////////////////////////////////////////////////////// /* * @description * Codes below are ported from TweetNacl.c/TweetNacl.h * */ private static final byte [] _0 = new byte[16]; private static final byte [] _9 = new byte[32]; static { for (int i = 0; i < _0.length; i ++) _0[i] = 0; for (int i = 0; i < _9.length; i ++) _9[i] = 0; _9[0] = 9; } private static final long [] gf0 = new long[16]; private static final long [] gf1 = new long[16]; private static final long [] _121665 = new long[16]; static { for (int i = 0; i < gf0.length; i ++) gf0[i] = 0; for (int i = 0; i < gf1.length; i ++) gf1[i] = 0; gf1[0] = 1; for (int i = 0; i < _121665.length; i ++) _121665[i] = 0; _121665[0] = 0xDB41; _121665[1] = 1; } private static final long [] D = new long [] { 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203 }; private static final long [] D2 = new long [] { 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406 }; private static final long [] X = new long [] { 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169 }; private static final long [] Y = new long [] { 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666 }; private static final long [] I = new long [] { 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83 }; private static int L32(int x, int c) { return (x << c) | ((x&0xffffffff) >>> (32 - c)); } private static int ld32(byte [] x, final int xoff, final int xlen) { int u = (x[3+xoff]&0xff); u = (u<<8)|(x[2+xoff]&0xff); u = (u<<8)|(x[1+xoff]&0xff); return (u<<8)|(x[0+xoff]&0xff); } private static long dl64(byte [] x, final int xoff, final int xlen) { int i; long u=0; for (i = 0; i < 8; i ++) u=(u<<8)|(x[i+xoff]&0xff); return u; } private static void st32(byte [] x, final int xoff, final int xlen, int u) { int i; for (i = 0; i < 4; i ++) { x[i+xoff] = (byte)(u&0xff); u >>>= 8; } } private static void ts64(byte [] x, final int xoff, final int xlen, long u) { int i; for (i = 7;i >= 0;--i) { x[i+xoff] = (byte)(u&0xff); u >>>= 8; } } private static int vn( byte [] x, final int xoff, final int xlen, byte [] y, final int yoff, final int ylen, int n) { int i,d = 0; for (i = 0; i < n; i ++) d |= (x[i+xoff]^y[i+yoff]) & 0xff; return (1 & ((d - 1) >>> 8)) - 1; } private static int crypto_verify_16( byte [] x, final int xoff, final int xlen, byte [] y, final int yoff, final int ylen) { return vn(x,xoff,xlen,y,yoff,ylen,16); } public static int crypto_verify_16(byte [] x, byte [] y) { return crypto_verify_16(x, 0, x.length, y, 0, y.length); } private static int crypto_verify_32( byte [] x, final int xoff, final int xlen, byte [] y, final int yoff, final int ylen) { return vn(x,xoff,xlen,y,yoff,ylen,32); } public static int crypto_verify_32(byte [] x, byte [] y) { return crypto_verify_32(x, 0, x.length, y, 0, y.length); } private static void core(byte [] out, byte [] in, byte [] k, byte [] c, int h) { int [] w = new int[16], x = new int[16], y = new int[16], t = new int[4]; int i,j,m; for (i = 0; i < 4; i ++) { x[5*i] = ld32(c, 4*i, 4); x[1+i] = ld32(k, 4*i, 4); x[6+i] = ld32(in, 4*i, 4); x[11+i] = ld32(k, 16+4*i, 4); } for (i = 0; i < 16; i ++) y[i] = x[i]; for (i = 0; i < 20; i ++) { for (j = 0; j < 4; j ++) { for (m = 0; m < 4; m ++) t[m] = x[(5*j+4*m)%16]; t[1] ^= L32(t[0]+t[3], 7); t[2] ^= L32(t[1]+t[0], 9); t[3] ^= L32(t[2]+t[1],13); t[0] ^= L32(t[3]+t[2],18); for (m = 0; m < 4; m ++) w[4*j+(j+m)%4] = t[m]; } for (m = 0; m < 16; m ++) x[m] = w[m]; } if (h != 0) { for (i = 0; i < 16; i ++) x[i] += y[i]; for (i = 0; i < 4; i ++) { x[5*i] -= ld32(c, 4*i, 4); x[6+i] -= ld32(in, 4*i, 4); } for (i = 0; i < 4; i ++) { st32(out, 4*i, 4, x[5*i]); st32(out, 16+4*i, 4, x[6+i]); } } else for (i = 0; i < 16; i ++) st32(out, 4*i, 4, x[i] + y[i]); ///String dbgt = ""; ///for (int dbg = 0; dbg < out.length; dbg ++) dbgt += " "+out[dbg]; ///L/og.d(TAG, "core -> "+dbgt); } public static int crypto_core_salsa20(byte [] out, byte [] in, byte [] k, byte [] c) { core(out,in,k,c,0); ///String dbgt = ""; ///for (int dbg = 0; dbg < out.length; dbg ++) dbgt += " "+out[dbg]; ///L/og.d(TAG, "crypto_core_salsa20 -> "+dbgt); return 0; } public static int crypto_core_hsalsa20(byte [] out, byte [] in, byte [] k, byte [] c) { core(out,in,k,c,1); ///String dbgt = ""; ///for (int dbg = 0; dbg < out.length; dbg ++) dbgt += " "+out[dbg]; ///L/og.d(TAG, "crypto_core_hsalsa20 -> "+dbgt); return 0; } private static final byte[] sigma = { 101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107 }; /*static { try { sigma = "expand 32-byte k".getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }*/ private static int crypto_stream_salsa20_xor(byte [] c, byte [] m, long b, byte [] n,final int noff,final int nlen, byte [] k) { byte[] z = new byte[16], x = new byte[64]; int u,i; if (0==b) return 0; for (i = 0; i < 16; i ++) z[i] = 0; for (i = 0; i < 8; i ++) z[i] = n[i+noff]; int coffset = 0; int moffset = 0; while (b >= 64) { crypto_core_salsa20(x,z,k,sigma); for (i = 0; i < 64; i ++) c[i+coffset] = (byte) (((m!=null?m[i+moffset]:0) ^ x[i]) & 0xff); u = 1; for (i = 8;i < 16;++i) { u += (int) (z[i]&0xff); z[i] = (byte) (u&0xff); u >>>= 8; } b -= 64; coffset += 64; if (m!=null) moffset += 64; } if (b!=0) { crypto_core_salsa20(x,z,k,sigma); for (i = 0; i < b; i ++) c[i+coffset] = (byte) (((m!=null?m[i+moffset]:0) ^ x[i]) & 0xff); } return 0; } public static int crypto_stream_salsa20_xor(byte [] c, byte [] m, long b, byte [] n, byte [] k) { return crypto_stream_salsa20_xor(c, m, b, n,0,n.length, k); } private static int crypto_stream_salsa20(byte [] c, long d, byte [] n,final int noff,final int nlen, byte [] k) { return crypto_stream_salsa20_xor(c,null,d, n,noff,nlen, k); } public static int crypto_stream_salsa20(byte [] c, long d, byte [] n, byte [] k) { return crypto_stream_salsa20(c, d, n,0,n.length, k); } public static int crypto_stream(byte [] c, long d, byte [] n, byte [] k) { byte[] s = new byte[32]; crypto_core_hsalsa20(s,n,k,sigma); return crypto_stream_salsa20(c,d, n,16,n.length-16, s); } public static int crypto_stream_xor(byte []c,byte []m,long d,byte []n,byte []k) { byte[] s = new byte[32]; crypto_core_hsalsa20(s,n,k,sigma); return crypto_stream_salsa20_xor(c,m,d, n,16,n.length-16, s); } /* !!! Use TweetNaclFast.java onetimeauth function private static void add1305(int [] h,int [] c) { int j; int u = 0; for (j = 0; j < 17; j ++) { u = (u + ((h[j] + c[j]) | 0)) | 0; h[j] = u & 255; u >>>= 8; } } private final static int minusp[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 }; private static int crypto_onetimeauth( byte[] out,final int outoff,final int outlen, byte[] m,final int moff,final int mlen, long n, byte [] k) { int i,j; int s,u; int [] x = new int[17], r = new int [17], h = new int[17], c = new int [17], g = new int[17]; for (j = 0; j < 17; j ++) r[j] = h[j] = 0; for (j = 0; j < 16; j ++) r[j] = k[j] & 0xff; r[3]&=15; r[4]&=252; r[7]&=15; r[8]&=252; r[11]&=15; r[12]&=252; r[15]&=15; int moffset = moff; while (n > 0) { for (j = 0; j < 17; j ++) c[j] = 0; for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j+moffset] & 0xff; c[j] = 1; moffset += j; n -= j; add1305(h,c); for (i = 0; i < 17; i ++) { x[i] = 0; for (j = 0; j < 17; j ++) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]); for (j = 0; j < 17; j++) x[i] = (x[i] + (h[j] * ((j <= i) ? r[i - j] : ((320 * r[i + 17 - j])|0))) | 0) | 0; } for (i = 0; i < 17; i ++) h[i] = x[i]; u = 0; for (j = 0; j < 16; j ++) { u = (u + h[j]) | 0; h[j] = u & 255; u >>>= 8; } u = (u + h[16]) | 0; h[16] = u & 3; u = (5 * (u >>> 2)) | 0; for (j = 0; j < 16; j ++) { u = (u + h[j]) | 0; h[j] = u & 255; u >>>= 8; } u = (u + h[16]) | 0; h[16] = u; } for (j = 0; j < 17; j ++) g[j] = h[j]; add1305(h,minusp); s = (-(h[16] >>> 7) | 0); for (j = 0; j < 17; j ++) h[j] ^= s & (g[j] ^ h[j]); for (j = 0; j < 16; j ++) c[j] = k[j + 16] & 0xff; c[16] = 0; add1305(h,c); for (j = 0; j < 16; j ++) out[j+outoff] = (byte) (h[j]&0xff); return 0; }*/ /* * Port of Andrew Moon's Poly1305-donna-16. Public domain. * https://github.com/floodyberry/poly1305-donna */ public static class poly1305 { private byte[] buffer; private int[] r; private int[] h; private int[] pad; private int leftover; private int fin; public poly1305(byte [] key) { this.buffer = new byte[16]; this.r = new int[10]; this.h = new int[10]; this.pad = new int[8]; this.leftover = 0; this.fin = 0; int t0, t1, t2, t3, t4, t5, t6, t7; t0 = key[ 0] & 0xff | (key[ 1] & 0xff) << 8; this.r[0] = ( t0 ) & 0x1fff; t1 = key[ 2] & 0xff | (key[ 3] & 0xff) << 8; this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff; t2 = key[ 4] & 0xff | (key[ 5] & 0xff) << 8; this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03; t3 = key[ 6] & 0xff | (key[ 7] & 0xff) << 8; this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff; t4 = key[ 8] & 0xff | (key[ 9] & 0xff) << 8; this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff; this.r[5] = ((t4 >>> 1)) & 0x1ffe; t5 = key[10] & 0xff | (key[11] & 0xff) << 8; this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff; t6 = key[12] & 0xff | (key[13] & 0xff) << 8; this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81; t7 = key[14] & 0xff | (key[15] & 0xff) << 8; this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff; this.r[9] = ((t7 >>> 5)) & 0x007f; this.pad[0] = key[16] & 0xff | (key[17] & 0xff) << 8; this.pad[1] = key[18] & 0xff | (key[19] & 0xff) << 8; this.pad[2] = key[20] & 0xff | (key[21] & 0xff) << 8; this.pad[3] = key[22] & 0xff | (key[23] & 0xff) << 8; this.pad[4] = key[24] & 0xff | (key[25] & 0xff) << 8; this.pad[5] = key[26] & 0xff | (key[27] & 0xff) << 8; this.pad[6] = key[28] & 0xff | (key[29] & 0xff) << 8; this.pad[7] = key[30] & 0xff | (key[31] & 0xff) << 8; } public poly1305 blocks(byte [] m, int mpos, int bytes) { int hibit = this.fin!=0 ? 0 : (1 << 11); int t0, t1, t2, t3, t4, t5, t6, t7, c; int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; int h0 = this.h[0], h1 = this.h[1], h2 = this.h[2], h3 = this.h[3], h4 = this.h[4], h5 = this.h[5], h6 = this.h[6], h7 = this.h[7], h8 = this.h[8], h9 = this.h[9]; int r0 = this.r[0], r1 = this.r[1], r2 = this.r[2], r3 = this.r[3], r4 = this.r[4], r5 = this.r[5], r6 = this.r[6], r7 = this.r[7], r8 = this.r[8], r9 = this.r[9]; while (bytes >= 16) { t0 = m[mpos+ 0] & 0xff | (m[mpos+ 1] & 0xff) << 8; h0 += ( t0 ) & 0x1fff; t1 = m[mpos+ 2] & 0xff | (m[mpos+ 3] & 0xff) << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff; t2 = m[mpos+ 4] & 0xff | (m[mpos+ 5] & 0xff) << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff; t3 = m[mpos+ 6] & 0xff | (m[mpos+ 7] & 0xff) << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff; t4 = m[mpos+ 8] & 0xff | (m[mpos+ 9] & 0xff) << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff; h5 += ((t4 >>> 1)) & 0x1fff; t5 = m[mpos+10] & 0xff | (m[mpos+11] & 0xff) << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff; t6 = m[mpos+12] & 0xff | (m[mpos+13] & 0xff) << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff; t7 = m[mpos+14] & 0xff | (m[mpos+15] & 0xff) << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff; h9 += ((t7 >>> 5)) | hibit; c = 0; d0 = c; d0 += h0 * r0; d0 += h1 * (5 * r9); d0 += h2 * (5 * r8); d0 += h3 * (5 * r7); d0 += h4 * (5 * r6); c = (d0 >>> 13); d0 &= 0x1fff; d0 += h5 * (5 * r5); d0 += h6 * (5 * r4); d0 += h7 * (5 * r3); d0 += h8 * (5 * r2); d0 += h9 * (5 * r1); c += (d0 >>> 13); d0 &= 0x1fff; d1 = c; d1 += h0 * r1; d1 += h1 * r0; d1 += h2 * (5 * r9); d1 += h3 * (5 * r8); d1 += h4 * (5 * r7); c = (d1 >>> 13); d1 &= 0x1fff; d1 += h5 * (5 * r6); d1 += h6 * (5 * r5); d1 += h7 * (5 * r4); d1 += h8 * (5 * r3); d1 += h9 * (5 * r2); c += (d1 >>> 13); d1 &= 0x1fff; d2 = c; d2 += h0 * r2; d2 += h1 * r1; d2 += h2 * r0; d2 += h3 * (5 * r9); d2 += h4 * (5 * r8); c = (d2 >>> 13); d2 &= 0x1fff; d2 += h5 * (5 * r7); d2 += h6 * (5 * r6); d2 += h7 * (5 * r5); d2 += h8 * (5 * r4); d2 += h9 * (5 * r3); c += (d2 >>> 13); d2 &= 0x1fff; d3 = c; d3 += h0 * r3; d3 += h1 * r2; d3 += h2 * r1; d3 += h3 * r0; d3 += h4 * (5 * r9); c = (d3 >>> 13); d3 &= 0x1fff; d3 += h5 * (5 * r8); d3 += h6 * (5 * r7); d3 += h7 * (5 * r6); d3 += h8 * (5 * r5); d3 += h9 * (5 * r4); c += (d3 >>> 13); d3 &= 0x1fff; d4 = c; d4 += h0 * r4; d4 += h1 * r3; d4 += h2 * r2; d4 += h3 * r1; d4 += h4 * r0; c = (d4 >>> 13); d4 &= 0x1fff; d4 += h5 * (5 * r9); d4 += h6 * (5 * r8); d4 += h7 * (5 * r7); d4 += h8 * (5 * r6); d4 += h9 * (5 * r5); c += (d4 >>> 13); d4 &= 0x1fff; d5 = c; d5 += h0 * r5; d5 += h1 * r4; d5 += h2 * r3; d5 += h3 * r2; d5 += h4 * r1; c = (d5 >>> 13); d5 &= 0x1fff; d5 += h5 * r0; d5 += h6 * (5 * r9); d5 += h7 * (5 * r8); d5 += h8 * (5 * r7); d5 += h9 * (5 * r6); c += (d5 >>> 13); d5 &= 0x1fff; d6 = c; d6 += h0 * r6; d6 += h1 * r5; d6 += h2 * r4; d6 += h3 * r3; d6 += h4 * r2; c = (d6 >>> 13); d6 &= 0x1fff; d6 += h5 * r1; d6 += h6 * r0; d6 += h7 * (5 * r9); d6 += h8 * (5 * r8); d6 += h9 * (5 * r7); c += (d6 >>> 13); d6 &= 0x1fff; d7 = c; d7 += h0 * r7; d7 += h1 * r6; d7 += h2 * r5; d7 += h3 * r4; d7 += h4 * r3; c = (d7 >>> 13); d7 &= 0x1fff; d7 += h5 * r2; d7 += h6 * r1; d7 += h7 * r0; d7 += h8 * (5 * r9); d7 += h9 * (5 * r8); c += (d7 >>> 13); d7 &= 0x1fff; d8 = c; d8 += h0 * r8; d8 += h1 * r7; d8 += h2 * r6; d8 += h3 * r5; d8 += h4 * r4; c = (d8 >>> 13); d8 &= 0x1fff; d8 += h5 * r3; d8 += h6 * r2; d8 += h7 * r1; d8 += h8 * r0; d8 += h9 * (5 * r9); c += (d8 >>> 13); d8 &= 0x1fff; d9 = c; d9 += h0 * r9; d9 += h1 * r8; d9 += h2 * r7; d9 += h3 * r6; d9 += h4 * r5; c = (d9 >>> 13); d9 &= 0x1fff; d9 += h5 * r4; d9 += h6 * r3; d9 += h7 * r2; d9 += h8 * r1; d9 += h9 * r0; c += (d9 >>> 13); d9 &= 0x1fff; c = (((c << 2) + c)) | 0; c = (c + d0) | 0; d0 = c & 0x1fff; c = (c >>> 13); d1 += c; h0 = d0; h1 = d1; h2 = d2; h3 = d3; h4 = d4; h5 = d5; h6 = d6; h7 = d7; h8 = d8; h9 = d9; mpos += 16; bytes -= 16; } this.h[0] = h0; this.h[1] = h1; this.h[2] = h2; this.h[3] = h3; this.h[4] = h4; this.h[5] = h5; this.h[6] = h6; this.h[7] = h7; this.h[8] = h8; this.h[9] = h9; return this; } public poly1305 finish(byte [] mac, int macpos) { int [] g = new int[10]; int c, mask, f, i; if (this.leftover != 0) { i = this.leftover; this.buffer[i++] = 1; for (; i < 16; i++) this.buffer[i] = 0; this.fin = 1; this.blocks(this.buffer, 0, 16); } c = this.h[1] >>> 13; this.h[1] &= 0x1fff; for (i = 2; i < 10; i++) { this.h[i] += c; c = this.h[i] >>> 13; this.h[i] &= 0x1fff; } this.h[0] += (c * 5); c = this.h[0] >>> 13; this.h[0] &= 0x1fff; this.h[1] += c; c = this.h[1] >>> 13; this.h[1] &= 0x1fff; this.h[2] += c; g[0] = this.h[0] + 5; c = g[0] >>> 13; g[0] &= 0x1fff; for (i = 1; i < 10; i++) { g[i] = this.h[i] + c; c = g[i] >>> 13; g[i] &= 0x1fff; } g[9] -= (1 << 13); g[9] &= 0xffff; mask = (g[9] >>> ((2 * 8) - 1)) - 1; mask &= 0xffff; for (i = 0; i < 10; i++) g[i] &= mask; mask = ~mask; for (i = 0; i < 10; i++) this.h[i] = (this.h[i] & mask) | g[i]; this.h[0] = ((this.h[0] ) | (this.h[1] << 13) ) & 0xffff; this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10) ) & 0xffff; this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7) ) & 0xffff; this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4) ) & 0xffff; this.h[4] = ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff; this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11) ) & 0xffff; this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8) ) & 0xffff; this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5) ) & 0xffff; f = this.h[0] + this.pad[0]; this.h[0] = f & 0xffff; for (i = 1; i < 8; i++) { f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0; this.h[i] = f & 0xffff; } mac[macpos+ 0] = (byte) ((this.h[0] >>> 0) & 0xff); mac[macpos+ 1] = (byte) ((this.h[0] >>> 8) & 0xff); mac[macpos+ 2] = (byte) ((this.h[1] >>> 0) & 0xff); mac[macpos+ 3] = (byte) ((this.h[1] >>> 8) & 0xff); mac[macpos+ 4] = (byte) ((this.h[2] >>> 0) & 0xff); mac[macpos+ 5] = (byte) ((this.h[2] >>> 8) & 0xff); mac[macpos+ 6] = (byte) ((this.h[3] >>> 0) & 0xff); mac[macpos+ 7] = (byte) ((this.h[3] >>> 8) & 0xff); mac[macpos+ 8] = (byte) ((this.h[4] >>> 0) & 0xff); mac[macpos+ 9] = (byte) ((this.h[4] >>> 8) & 0xff); mac[macpos+10] = (byte) ((this.h[5] >>> 0) & 0xff); mac[macpos+11] = (byte) ((this.h[5] >>> 8) & 0xff); mac[macpos+12] = (byte) ((this.h[6] >>> 0) & 0xff); mac[macpos+13] = (byte) ((this.h[6] >>> 8) & 0xff); mac[macpos+14] = (byte) ((this.h[7] >>> 0) & 0xff); mac[macpos+15] = (byte) ((this.h[7] >>> 8) & 0xff); return this; } public poly1305 update(byte [] m, int mpos, int bytes) { int i, want; if (this.leftover != 0) { want = (16 - this.leftover); if (want > bytes) want = bytes; for (i = 0; i < want; i++) this.buffer[this.leftover + i] = m[mpos+i]; bytes -= want; mpos += want; this.leftover += want; if (this.leftover < 16) return this; this.blocks(buffer, 0, 16); this.leftover = 0; } if (bytes >= 16) { want = bytes - (bytes % 16); this.blocks(m, mpos, want); mpos += want; bytes -= want; } if (bytes != 0) { for (i = 0; i < bytes; i++) this.buffer[this.leftover + i] = m[mpos+i]; this.leftover += bytes; } return this; } } private static int crypto_onetimeauth( byte[] out,final int outpos,final int outlen, byte[] m,final int mpos,final int mlen, int n, byte [] k) { poly1305 s = new poly1305(k); s.update(m, mpos, n); s.finish(out, outpos); /*String dbgt = ""; for (int dbg = 0; dbg < out.length-outpos; dbg ++) dbgt += " "+out[dbg+outpos]; Log.d(TAG, "crypto_onetimeauth -> "+dbgt); */ return 0; } public static int crypto_onetimeauth(byte [] out, byte [] m, int n , byte [] k) { return crypto_onetimeauth(out,0,out.length, m,0,m.length, n, k); } private static int crypto_onetimeauth_verify( byte[] h,final int hoff,final int hlen, byte[] m,final int moff,final int mlen, int n, byte [] k) { byte[] x = new byte[16]; crypto_onetimeauth(x,0,x.length, m,moff,mlen, n,k); return crypto_verify_16(h,hoff,hlen, x,0,x.length); } public static int crypto_onetimeauth_verify(byte [] h, byte [] m, int n, byte [] k) { return crypto_onetimeauth_verify(h,0,h.length, m,0,m.length, n, k); } public static int crypto_onetimeauth_verify(byte [] h, byte [] m, byte [] k) { return crypto_onetimeauth_verify(h, m, m!=null? m.length:0, k); } public static int crypto_secretbox(byte [] c, byte [] m, int d, byte [] n, byte [] k) { int i; if (d < 32) return -1; crypto_stream_xor(c,m,d,n,k); crypto_onetimeauth(c,16,c.length-16, c,32,c.length-32, d - 32, c); ///for (i = 0; i < 16; i ++) c[i] = 0; return 0; } public static int crypto_secretbox_open(byte []m,byte []c,int d,byte []n,byte []k) { int i; byte[] x = new byte[32]; if (d < 32) return -1; crypto_stream(x,32,n,k); if (crypto_onetimeauth_verify(c,16,16, c,32,c.length-32, d-32, x) != 0) return -1; crypto_stream_xor(m,c,d,n,k); ///for (i = 0; i < 32; i ++) m[i] = 0; return 0; } private static void set25519(long [] r, long [] a) { int i; for (i = 0; i < 16; i ++) r[i]=a[i]; } private static void car25519(long [] o,final int ooff,final int olen) { int i; long c; ///String dbgt = ""; ///for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o.get(dbg); ///L/og.d(TAG, "car25519 pre -> "+dbgt); for (i = 0; i < 16; i ++) { o[i+ooff] += (1L<<16); c = o[i+ooff]>>16; o[(i+1)*((i<15) ? 1 : 0)+ooff] += c-1+37*(c-1)*((i==15) ? 1 : 0); o[i+ooff] -= (c<<16); } ///dbgt = ""; ///for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o.get(dbg); ///L/og.d(TAG, "car25519 -> "+dbgt); } private static void sel25519( long[] p,final int poff,final int plen, long[] q,final int qoff,final int qlen, int b) { int i; long t,c=~(b-1); for (i = 0; i < 16; i ++) { t = c & (p[i+poff] ^ q[i+qoff]); p[i+poff] ^= t; q[i+qoff] ^= t; } ///String dbgt = ""; ///for (int dbg = 0; dbg < p.length; dbg ++) dbgt += " "+p.get(dbg); ///L/og.d(TAG, "sel25519 -> "+dbgt); } private static void pack25519(byte [] o, long [] n,final int noff,final int nlen) { int i,j,b; long [] m = new long[16], t = new long[16]; for (i = 0; i < 16; i ++) t[i] = n[i+noff]; car25519(t,0,t.length); car25519(t,0,t.length); car25519(t,0,t.length); for (j = 0; j < 2; j ++) { m[0]=t[0]-0xffed; for(i=1;i<15;i++) { m[i]=t[i]-0xffff-((m[i-1] >> 16)&1); m[i-1]&=0xffff; } m[15]=t[15]-0x7fff-((m[14] >> 16)&1); b=(int) ((m[15] >> 16)&1); m[14]&=0xffff; sel25519(t,0,t.length, m,0,m.length, 1-b); } for (i = 0; i < 16; i ++) { o[2*i]=(byte) (t[i]&0xff); o[2*i+1]=(byte) (t[i] >> 8); } ///String dbgt = ""; ///for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o[dbg]; ///L/og.d(TAG, "pack25519 -> "+dbgt); } private static int neq25519(long [] a, long [] b) { byte[] c = new byte[32], d = new byte[32]; pack25519(c, a,0,a.length); pack25519(d, b,0,b.length); return crypto_verify_32(c,0,c.length, d,0,d.length); } private static byte par25519(long [] a) { byte[] d = new byte[32]; pack25519(d, a,0,a.length); return (byte) (d[0]&1); } private static void unpack25519(long [] o, byte [] n) { int i; for (i = 0; i < 16; i ++) o[i]=(n[2*i]&0xff)+((long)((n[2*i+1]<<8)&0xffff)); o[15]&=0x7fff; ///String dbgt = ""; ///for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o[dbg]; ///L/og.d(TAG, "unpack25519 -> "+dbgt); } private static void A( long [] o,final int ooff,final int olen, long [] a,final int aoff,final int alen, long [] b,final int boff,final int blen) { int i; for (i = 0; i < 16; i ++) o[i+ooff] = a[i+aoff] + b[i+boff]; } private static void Z( long [] o,final int ooff,final int olen, long [] a,final int aoff,final int alen, long [] b,final int boff,final int blen) { int i; for (i = 0; i < 16; i ++) o[i+ooff] = a[i+aoff] - b[i+boff]; } private static void M( long [] o,final int ooff,final int olen, long [] a,final int aoff,final int alen, long [] b,final int boff,final int blen) { int i,j; long [] t = new long[31]; for (i = 0; i < 31; i ++) t[i]=0; for (i = 0; i < 16; i ++) for (j = 0; j < 16; j ++) t[i+j]+=a[i+aoff]*b[j+boff]; for (i = 0; i < 15; i ++) t[i]+=38*t[i+16]; for (i = 0; i < 16; i ++) o[i+ooff]=t[i]; car25519(o,ooff,olen); car25519(o,ooff,olen); ///String dbgt = ""; ///for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o.get(dbg); ///L/og.d(TAG, "M -> "+dbgt); } private static void S( long [] o,final int ooff,final int olen, long [] a,final int aoff,final int alen) { M(o,ooff,olen, a,aoff,alen, a,aoff,alen); } private static void inv25519( long [] o,final int ooff,final int olen, long [] i,final int ioff,final int ilen) { long [] c = new long[16]; int a; for (a = 0; a < 16; a ++) c[a]=i[a+ioff]; for(a=253;a>=0;a--) { S(c,0,c.length, c,0,c.length); if(a!=2&&a!=4) M(c,0,c.length, c,0,c.length, i,ioff,ilen); } for (a = 0; a < 16; a ++) o[a+ooff] = c[a]; ///String dbgt = ""; ///for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o.get(dbg); ///L/og.d(TAG, "inv25519 -> "+dbgt); } private static void pow2523(long [] o,long [] i) { long [] c = new long[16]; int a; for (a = 0; a < 16; a ++) c[a]=i[a]; for(a=250;a>=0;a--) { S(c,0,c.length, c,0,c.length); if(a!=1) M(c,0,c.length, c,0,c.length, i,0,i.length); } for (a = 0; a < 16; a ++) o[a]=c[a]; } public static int crypto_scalarmult(byte []q,byte []n,byte []p) { byte[] z = new byte[32]; long[] x = new long[80]; int r,i; long [] a = new long[16], b = new long[16], c = new long[16], d = new long[16], e = new long[16], f = new long[16]; for (i = 0; i < 31; i ++) z[i]=n[i]; z[31]=(byte) (((n[31]&127)|64) & 0xff); z[0]&=248; unpack25519(x,p); for (i = 0; i < 16; i ++) { b[i]=x[i]; d[i]=a[i]=c[i]=0; } a[0]=d[0]=1; for(i=254;i>=0;--i) { r=(z[i>>>3]>>>(i&7))&1; sel25519(a,0,a.length, b,0,b.length, r); sel25519(c,0,c.length, d,0,d.length, r); A(e,0,e.length, a,0,a.length, c,0,c.length); Z(a,0,a.length, a,0,a.length, c,0,c.length); A(c,0,c.length, b,0,b.length, d,0,d.length); Z(b,0,b.length, b,0,b.length, d,0,d.length); S(d,0,d.length, e,0,e.length); S(f,0,f.length, a,0,a.length); M(a,0,a.length, c,0,c.length, a,0,a.length); M(c,0,c.length, b,0,b.length, e,0,e.length); A(e,0,e.length, a,0,a.length, c,0,c.length); Z(a,0,a.length, a,0,a.length, c,0,c.length); S(b,0,b.length, a,0,a.length); Z(c,0,c.length, d,0,d.length, f,0,f.length); M(a,0,a.length, c,0,c.length, _121665,0,_121665.length); A(a,0,a.length, a,0,a.length, d,0,d.length); M(c,0,c.length, c,0,c.length, a,0,a.length); M(a,0,a.length, d,0,d.length, f,0,f.length); M(d,0,d.length, b,0,b.length, x,0,x.length); S(b,0,b.length, e,0,e.length); sel25519(a,0,a.length, b,0,b.length, r); sel25519(c,0,c.length, d,0,d.length, r); } for (i = 0; i < 16; i ++) { x[i+16]=a[i]; x[i+32]=c[i]; x[i+48]=b[i]; x[i+64]=d[i]; } inv25519(x, 32, x.length-32, x, 32, x.length-32); M(x,16,x.length-16, x,16,x.length-16, x,32,x.length-32); pack25519(q, x,16,x.length-16); ///String dbgt = ""; ///for (int dbg = 0; dbg < q.length; dbg ++) dbgt += " "+q[dbg]; ///L/og.d(TAG, "crypto_scalarmult -> "+dbgt); return 0; } public static int crypto_scalarmult_base(byte []q,byte []n) { return crypto_scalarmult(q,n,_9); } public static int crypto_box_keypair(byte [] y, byte [] x) { randombytes(x,32); return crypto_scalarmult_base(y,x); } public static int crypto_box_beforenm(byte []k,byte []y,byte []x) { byte[] s = new byte[32]; crypto_scalarmult(s,x,y); ///String dbgt = ""; ///for (int dbg = 0; dbg < s.length; dbg ++) dbgt += " "+s[dbg]; ///L/og.d(TAG, "crypto_box_beforenm -> "+dbgt); return crypto_core_hsalsa20(k,_0,s,sigma); } public static int crypto_box_afternm(byte []c,byte []m,int d,byte []n,byte []k) { return crypto_secretbox(c,m,d,n,k); } public static int crypto_box_open_afternm(byte []m,byte []c,int d,byte []n,byte []k) { return crypto_secretbox_open(m,c,d,n,k); } public static int crypto_box(byte []c,byte []m,int d,byte []n,byte []y,byte []x) { byte[] k = new byte[32]; ///L/og.d(TAG, "crypto_box start ..."); crypto_box_beforenm(k,y,x); return crypto_box_afternm(c,m,d,n,k); } public static int crypto_box_open(byte []m,byte []c,int d,byte []n,byte []y,byte []x) { byte[] k = new byte[32]; crypto_box_beforenm(k,y,x); return crypto_box_open_afternm(m,c,d,n,k); } private static long R(long x,int c) { return (x >>> c) | (x << (64 - c)); } private static long Ch( long x,long y,long z) { return (x & y) ^ (~x & z); } private static long Maj(long x,long y,long z) { return (x & y) ^ (x & z) ^ (y & z); } private static long Sigma0(long x) { return R(x,28) ^ R(x,34) ^ R(x,39); } private static long Sigma1(long x) { return R(x,14) ^ R(x,18) ^ R(x,41); } private static long sigma0(long x) { return R(x, 1) ^ R(x, 8) ^ (x >>> 7); } private static long sigma1(long x) { return R(x,19) ^ R(x,61) ^ (x >>> 6); } private static final long K[] = { 0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL, 0x3956c25bf348b538L, 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, 0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L, 0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 0xc19bf174cf692694L, 0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, 0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L, 0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L, 0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L, 0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL, 0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 0x92722c851482353bL, 0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L, 0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L, 0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L, 0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, 0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL, 0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL, 0xca273eceea26619cL, 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, 0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL, 0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL, 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L }; // TBD... long length n ///int crypto_hashblocks(byte [] x, byte [] m, long n) private static int crypto_hashblocks(byte [] x, byte [] m,final int moff,final int mlen, int n) { long [] z = new long [8], b = new long [8], a = new long [8], w = new long [16]; long t; int i,j; for (i = 0; i < 8; i ++) z[i] = a[i] = dl64(x, 8*i, x.length-8*i); int moffset = moff; while (n >= 128) { for (i = 0; i < 16; i ++) w[i] = dl64(m, 8*i+moffset, mlen-8*i); for (i = 0; i < 80; i ++) { for (j = 0; j < 8; j ++) b[j] = a[j]; t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16]; b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]); b[3] += t; for (j = 0; j < 8; j ++) a[(j+1)%8] = b[j]; if (i%16 == 15) for (j = 0; j < 16; j ++) w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]); } for (i = 0; i < 8; i ++) { a[i] += z[i]; z[i] = a[i]; } moffset += 128; n -= 128; } for (i = 0; i < 8; i ++) ts64(x,8*i,x.length-8*i, z[i]); return n; } public static int crypto_hashblocks(byte [] x, byte [] m, int n) { return crypto_hashblocks(x, m,0,m.length, n); } private final static byte iv[] = { 0x6a,0x09,(byte) 0xe6,0x67,(byte) 0xf3,(byte) 0xbc,(byte) 0xc9,0x08, (byte) 0xbb,0x67,(byte) 0xae,(byte) 0x85,(byte) 0x84,(byte) 0xca,(byte) 0xa7,0x3b, 0x3c,0x6e,(byte) 0xf3,0x72,(byte) 0xfe,(byte) 0x94,(byte) 0xf8,0x2b, (byte) 0xa5,0x4f,(byte) 0xf5,0x3a,0x5f,0x1d,0x36,(byte) 0xf1, 0x51,0x0e,0x52,0x7f,(byte) 0xad,(byte) 0xe6,(byte) 0x82,(byte) 0xd1, (byte) 0x9b,0x05,0x68,(byte) 0x8c,0x2b,0x3e,0x6c,0x1f, 0x1f,(byte) 0x83,(byte) 0xd9,(byte) 0xab,(byte) 0xfb,0x41,(byte) 0xbd,0x6b, 0x5b,(byte) 0xe0,(byte) 0xcd,0x19,0x13,0x7e,0x21,0x79 } ; // TBD 64bits of n ///int crypto_hash(byte [] out, byte [] m, long n) private static int crypto_hash(byte [] out, byte [] m,final int moff,final int mlen, int n) { byte[] h = new byte[64], x = new byte [256]; long b = n; int i; for (i = 0; i < 64; i ++) h[i] = iv[i]; crypto_hashblocks(h, m,moff,mlen, n); ///m += n; n &= 127; ///m -= n; for (i = 0; i < 256; i ++) x[i] = 0; for (i = 0; i < n; i ++) x[i] = m[i+moff]; x[n] = (byte) 128; n = 256-128*(n<112?1:0); x[n-9] = (byte) (b >>> 61); ts64(x,n-8,x.length-(n-8), b<<3); crypto_hashblocks(h, x,0,x.length, n); for (i = 0; i < 64; i ++) out[i] = h[i]; return 0; } public static int crypto_hash(byte [] out, byte [] m, int n) { return crypto_hash(out, m,0,m.length, n); } public static int crypto_hash(byte [] out, byte [] m) { return crypto_hash(out, m, m!=null? m.length : 0); } // gf: long[16] ///private static void add(gf p[4],gf q[4]) private static void add(long [] p[], long [] q[]) { long [] a = new long[16]; long [] b = new long[16]; long [] c = new long[16]; long [] d = new long[16]; long [] t = new long[16]; long [] e = new long[16]; long [] f = new long[16]; long [] g = new long[16]; long [] h = new long[16]; long [] p0 = p[0]; long [] p1 = p[1]; long [] p2 = p[2]; long [] p3 = p[3]; long [] q0 = q[0]; long [] q1 = q[1]; long [] q2 = q[2]; long [] q3 = q[3]; Z(a,0,a.length, p1,0,p1.length, p0,0,p0.length); Z(t,0,t.length, q1,0,q1.length, q0,0,q0.length); M(a,0,a.length, a,0,a.length, t,0,t.length); A(b,0,b.length, p0,0,p0.length, p1,0,p1.length); A(t,0,t.length, q0,0,q0.length, q1,0,q1.length); M(b,0,b.length, b,0,b.length, t,0,t.length); M(c,0,c.length, p3,0,p3.length, q3,0,q3.length); M(c,0,c.length, c,0,c.length, D2,0,D2.length); M(d,0,d.length, p2,0,p2.length, q2,0,q2.length); A(d,0,d.length, d,0,d.length, d,0,d.length); Z(e,0,e.length, b,0,b.length, a,0,a.length); Z(f,0,f.length, d,0,d.length, c,0,c.length); A(g,0,g.length, d,0,d.length, c,0,c.length); A(h,0,h.length, b,0,b.length, a,0,a.length); M(p0,0,p0.length, e,0,e.length, f,0,f.length); M(p1,0,p1.length, h,0,h.length, g,0,g.length); M(p2,0,p2.length, g,0,g.length, f,0,f.length); M(p3,0,p3.length, e,0,e.length, h,0,h.length); } private static void cswap(long [] p[], long [] q[], byte b) { int i; for (i = 0; i < 4; i ++) sel25519(p[i],0,p[i].length, q[i],0,q[i].length, b); } private static void pack(byte [] r, long [] p[]) { long [] tx = new long[16]; long [] ty = new long[16]; long [] zi = new long[16]; inv25519(zi,0,zi.length, p[2],0,p[2].length); M(tx,0,tx.length, p[0],0,p[0].length, zi,0,zi.length); M(ty,0,ty.length, p[1],0,p[1].length, zi,0,zi.length); pack25519(r, ty,0,ty.length); r[31] ^= par25519(tx) << 7; } private static void scalarmult(long [] p[], long [] q[], byte[] s,final int soff,final int slen) { int i; set25519(p[0],gf0); set25519(p[1],gf1); set25519(p[2],gf1); set25519(p[3],gf0); for (i = 255;i >= 0;--i) { byte b = (byte) ((s[i/8+soff] >> (i&7))&1); cswap(p,q,b); add(q,p); add(p,p); cswap(p,q,b); } ///String dbgt = ""; ///for (int dbg = 0; dbg < p.length; dbg ++) for (int dd = 0; dd < p[dbg].length; dd ++) dbgt += " "+p[dbg][dd]; ///L/og.d(TAG, "scalarmult -> "+dbgt); } private static void scalarbase(long [] p[], byte[] s,final int soff,final int slen) { long [] [] q = new long [4] []; q[0] = new long [16]; q[1] = new long [16]; q[2] = new long [16]; q[3] = new long [16]; set25519(q[0],X); set25519(q[1],Y); set25519(q[2],gf1); M(q[3],0,q[3].length, X,0,X.length, Y,0,Y.length); scalarmult(p,q, s,soff,slen); } public static int crypto_sign_keypair(byte [] pk, byte [] sk, boolean seeded) { byte [] d = new byte[64]; long [] [] p = new long [4] []; p[0] = new long [16]; p[1] = new long [16]; p[2] = new long [16]; p[3] = new long [16]; int i; if (!seeded) randombytes(sk, 32); crypto_hash(d, sk,0,sk.length, 32); d[0] &= 248; d[31] &= 127; d[31] |= 64; scalarbase(p, d,0,d.length); pack(pk, p); for (i = 0; i < 32; i++) sk[i+32] = pk[i]; return 0; } private static final long L[] = { 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10 }; private static void modL(byte[] r,final int roff,final int rlen, long x[]) { long carry; int i, j; for (i = 63;i >= 32;--i) { carry = 0; for (j = i - 32;j < i - 12;++j) { x[j] += carry - 16 * x[i] * L[j - (i - 32)]; carry = (x[j] + 128) >> 8; x[j] -= carry << 8; } x[j] += carry; x[i] = 0; } carry = 0; for (j = 0; j < 32; j ++) { x[j] += carry - (x[31] >> 4) * L[j]; carry = x[j] >> 8; x[j] &= 255; } for (j = 0; j < 32; j ++) x[j] -= carry * L[j]; for (i = 0; i < 32; i ++) { x[i+1] += x[i] >> 8; r[i+roff] = (byte) (x[i] & 255); } } private static void reduce(byte [] r) { long[] x = new long [64]; int i; for (i = 0; i < 64; i ++) x[i] = (long) (r[i]&0xff); for (i = 0; i < 64; i ++) r[i] = 0; modL(r,0,r.length, x); } // TBD... 64bits of n ///int crypto_sign(byte [] sm, long * smlen, byte [] m, long n, byte [] sk) public static int crypto_sign(byte [] sm, long dummy /* *smlen not used*/, byte [] m, int/*long*/ n, byte [] sk) { byte[] d = new byte[64], h = new byte[64], r = new byte[64]; int i, j; long [] x = new long[64]; long [] [] p = new long [4] []; p[0] = new long [16]; p[1] = new long [16]; p[2] = new long [16]; p[3] = new long [16]; crypto_hash(d, sk,0,sk.length, 32); d[0] &= 248; d[31] &= 127; d[31] |= 64; ///*smlen = n+64; for (i = 0; i < n; i ++) sm[64 + i] = m[i]; for (i = 0; i < 32; i ++) sm[32 + i] = d[32 + i]; crypto_hash(r, sm,32,sm.length-32, n+32); reduce(r); scalarbase(p, r,0,r.length); pack(sm,p); for (i = 0; i < 32; i ++) sm[i+32] = sk[i+32]; crypto_hash(h, sm,0,sm.length, n + 64); reduce(h); for (i = 0; i < 64; i ++) x[i] = 0; for (i = 0; i < 32; i ++) x[i] = (long) (r[i]&0xff); for (i = 0; i < 32; i ++) for (j = 0; j < 32; j ++) x[i+j] += (h[i]&0xff) * (long) (d[j]&0xff); modL(sm,32,sm.length-32, x); return 0; } private static int unpackneg(long [] r[], byte p[]) { long [] t = new long [16]; long [] chk = new long [16]; long [] num = new long [16]; long [] den = new long [16]; long [] den2 = new long [16]; long [] den4 = new long [16]; long [] den6 = new long [16]; set25519(r[2],gf1); unpack25519(r[1], p); S(num,0,num.length, r[1],0,r[1].length); M(den,0,den.length, num,0,num.length, D,0,D.length); Z(num,0,num.length, num,0,num.length, r[2],0,r[2].length); A(den,0,den.length, r[2],0,r[2].length, den,0,den.length); S(den2,0,den2.length, den,0,den.length); S(den4,0,den4.length, den2,0,den2.length); M(den6,0,den6.length, den4,0,den4.length, den2,0,den2.length); M(t,0,t.length, den6,0,den6.length, num,0,num.length); M(t,0,t.length, t,0,t.length, den,0,den.length); pow2523(t, t); M(t,0,t.length, t,0,t.length, num,0,num.length); M(t,0,t.length, t,0,t.length, den,0,den.length); M(t,0,t.length, t,0,t.length, den,0,den.length); M(r[0],0,r[0].length, t,0,t.length, den,0,den.length); S(chk,0,chk.length, r[0],0,r[0].length); M(chk,0,chk.length, chk,0,chk.length, den,0,den.length); if (neq25519(chk, num)!=0) M(r[0],0,r[0].length, r[0],0,r[0].length, I,0,I.length); S(chk,0,chk.length, r[0],0,r[0].length); M(chk,0,chk.length, chk,0,chk.length, den,0,den.length); if (neq25519(chk, num)!=0) return -1; if (par25519(r[0]) == (p[31]>>7)) Z(r[0],0,r[0].length, gf0,0,gf0.length, r[0],0,r[0].length); M(r[3],0,r[3].length, r[0],0,r[0].length, r[1],0,r[1].length); return 0; } /// TBD 64bits of mlen ///int crypto_sign_open(byte []m,long *mlen,byte []sm,long n,byte []pk) public static int crypto_sign_open(byte [] m, long dummy /* *mlen not used*/, byte [] sm, int/*long*/ n, byte []pk) { int i; byte[] t = new byte[32], h = new byte[64]; long [] [] p = new long [4] []; p[0] = new long [16]; p[1] = new long [16]; p[2] = new long [16]; p[3] = new long [16]; long [] [] q = new long [4] []; q[0] = new long [16]; q[1] = new long [16]; q[2] = new long [16]; q[3] = new long [16]; ///*mlen = -1; if (n < 64) return -1; if (unpackneg(q,pk)!=0) return -1; for (i = 0; i < n; i ++) m[i] = sm[i]; for (i = 0; i < 32; i ++) m[i+32] = pk[i]; crypto_hash(h, m,0,m.length, n); reduce(h); scalarmult(p,q, h,0,h.length); scalarbase(q, sm,32,sm.length-32); add(p,q); pack(t,p); n -= 64; if (crypto_verify_32(sm,0,sm.length, t,0,t.length)!=0) { // optimizing it ///for (i = 0; i < n; i ++) m[i] = 0; return -1; } // TBD optimizing ... ///for (i = 0; i < n; i ++) m[i] = sm[i + 64]; ///*mlen = n; return 0; } /* * @description * Java SecureRandom generator * */ private static final SecureRandom jrandom = new SecureRandom(); public static void randombytes(byte [] x, int len) { int ret = len % 8; long rnd; for (int i = 0; i < len-ret; i += 8) { rnd = jrandom.nextLong(); x[i+0] = (byte) (rnd >>> 0); x[i+1] = (byte) (rnd >>> 8); x[i+2] = (byte) (rnd >>> 16); x[i+3] = (byte) (rnd >>> 24); x[i+4] = (byte) (rnd >>> 32); x[i+5] = (byte) (rnd >>> 40); x[i+6] = (byte) (rnd >>> 48); x[i+7] = (byte) (rnd >>> 56); } if (ret > 0) { rnd = jrandom.nextLong(); for (int i = len-ret; i < len; i ++) x[i] = (byte) (rnd >>> 8*i); } } }