/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.github.encdns; import java.util.Arrays; /** * Wrapper class for libSodium (http://github.com/jedisct1/libsodium). * Needs the C part of the wrapper. * libSodium >=0.4.1 must be installed on the system. * Implementation optimised for and tested with libSodium 0.4.1. * * This class provides methods for encryption and decryption of cryptoboxes * with or without using a precomputed intermediate shared secret. If the * exchange of multiple messages with the same remote party using the same * keypair is expected, precomputed intermediate shared secrets should be cached * to increase performance. * * @author Jens Lindemann */ public class LibSodiumWrapper { /** length of public keys in bytes */ public final int PKBYTES; /** length of secret keys in bytes */ public final int SKBYTES; /** length of nonces in bytes */ public final int NONCEBYTES; /** number of 0-bytes at the beginning of a message */ public final int ZEROBYTES; /** number of 0-bytes at the beginning of a cryptobox */ public final int BOXZEROBYTES; public LibSodiumWrapper() { // Run libSodiums init function. This will test the performance of all // available crypto implementations and choose the fastest one. this.jni_init(); PKBYTES = this.jni_getPKBytes(); SKBYTES = this.jni_getSKBytes(); NONCEBYTES = this.jni_getNonceBytes(); ZEROBYTES = this.jni_getZeroBytes(); BOXZEROBYTES = this.jni_getBoxZeroBytes(); } /** * Open a cryptobox, i.e. decrypt its contents * @param cbox cryptobox to decrypt * @param rpk public key of remote party * @param sk own secret key * @param nonce nonce used to encrypt the cryptobox * @return decrypted unpadded message contained in the cryptobox */ public byte[] openCryptoBox(byte[] cbox, byte[] rpk, byte[] sk, byte[] nonce) { // add 0-padding to the beginning of cryptobox, so libSodium can process it byte[] cboxpadded = new byte[cbox.length + BOXZEROBYTES]; System.arraycopy(cbox, 0, cboxpadded, BOXZEROBYTES, cbox.length); // libSodium will return a 0-padded message. The padding is removed // before returning the message. byte[] mpadded = jni_crypto_box_open(cboxpadded, cboxpadded.length, nonce, rpk, sk); return Arrays.copyOfRange(mpadded, ZEROBYTES, mpadded.length); } /** * Encrypts a message and returns the corresponding cryptobox. * @param m message to be encrypted * @param rpk public key of remote party * @param sk own secret key * @param nonce nonce to use for encryption * @return unpadded cryptobox containing the encrypted message */ public byte[] makeCryptoBox(byte[] m, byte[] rpk, byte[] sk, byte[] nonce) { // add 0-padding to message to allow libSodium to process it byte[] mpadded = new byte[m.length + ZEROBYTES]; System.arraycopy(m, 0, mpadded, ZEROBYTES, m.length); // libSodium will return a 0-padded cryptobox. The padding is removed // before the cryptobox is returned. byte[] cboxpadded = jni_crypto_box(mpadded, mpadded.length, nonce, rpk, sk); return Arrays.copyOfRange(cboxpadded, BOXZEROBYTES, cboxpadded.length); } /** * Precomputes the intermediate shared secret. * @param pk remote party's public key * @param sk own secret key * @return intermediate shared secret for the specified keys */ public byte[] cryptoBoxBeforenm(byte[] pk, byte[] sk) { return jni_crypto_box_beforenm(pk, sk); } /** * Opens a cryptobox (i.e. decrypts the message inside it) using a * precomputed intermediate shared secret. * @param cbox cryptobox containing the encrypted message * @param k precomputed intermediate shared secret * @param nonce nonce used for encryption * @return decrypted unpadded message */ public byte[] openCryptoBoxAfternm(byte[] cbox, byte[] k, byte[] nonce) { // add 0-padding to the beginning of cryptobox, so libSodium can process it byte[] cboxpadded = new byte[cbox.length + BOXZEROBYTES]; System.arraycopy(cbox, 0, cboxpadded, BOXZEROBYTES, cbox.length); // libSodium will return a 0-padded message. The padding is removed // before returning the message. byte[] mpadded = jni_crypto_box_open_afternm(cboxpadded, cboxpadded.length, nonce, k); return Arrays.copyOfRange(mpadded, ZEROBYTES, mpadded.length); } /** * Encrypts a message using a precomputed intermediate shared secret and * returns the corresponding cryptobox. * @param m message to be encrypted * @param k precomputed intermediate shared secret * @param nonce nonce to use for encryption * @return unpadded cryptobox */ public byte[] makeCryptoBoxAfternm(byte[] m, byte[] k, byte[] nonce) { // add 0-padding to message to allow libSodium to process it byte[] mpadded = new byte[m.length + ZEROBYTES]; System.arraycopy(m, 0, mpadded, ZEROBYTES, m.length); // libSodium will return a 0-padded cryptobox. The padding is removed // before the cryptobox is returned. byte[] cboxpadded = jni_crypto_box_afternm(mpadded, mpadded.length, nonce, k); return Arrays.copyOfRange(cboxpadded, BOXZEROBYTES, cboxpadded.length); } private native void jni_init(); private native int jni_getPKBytes(); private native int jni_getSKBytes(); private native int jni_getNonceBytes(); private native int jni_getZeroBytes(); private native int jni_getBoxZeroBytes(); /** * Provides direct access to libSodium's crypto_box_keypair function for * generating a keypair. * @param pk empty byte[PKBYTES] which will be used to store the public key * @param sk empty byte[SKBYTES] which will be used to store the secret key */ public native void jni_generateKeys(byte[] pk, byte[] sk); /** * Provides direct access to libSodium's crypto_box function for creating * a cryptobox. Other than makeCryptoBox, this method will return a 0-padded * cryptobox and take a 0-padded message as a parameter * @param m 0-padded message to encrypt * @param mlen length of the message in bytes * @param n nonce to use for encryption * @param pk public key of remote party * @param sk own secret key * @return 0-padded cryptobox containing the encrypted message */ public native byte[] jni_crypto_box(byte[] m, int mlen, byte[] n, byte[] pk, byte[] sk); /** * Provides direct access to libSodium's crypto_box_open function for opening * a cryptobox. Other than openCryptoBox, this method must be called using * a 0-padded cryptobox and will return a 0-padded message. * @param c 0-padded cryptobox to decrypt * @param clen length of the cryptobox in bytes * @param n nonce used for encryption * @param pk public key of remote party * @param sk own secret key * @return 0-padded decrypted message */ public native byte[] jni_crypto_box_open(byte[] c, int clen, byte[] n, byte[] pk, byte[] sk); /** * Provides direct access to libSodium's crypto_box_beforenm function for * precomputing the intermediate shared secret. * @param pk remote party's public key * @param sk own secret key * @return intermediate shared secret corresponding to given keys */ public native byte[] jni_crypto_box_beforenm(byte[] pk, byte[] sk); /** * Provides direct access to libSodium's crypto_box_afternm for creating * a cryptobox using a precomputed intermediate shared secret. * Other than makeCryptoBoxAfternm this method will return a 0-padded * cryptobox and must be called using a 0-padded message. * @param m 0-padded message * @param mlen length of the message in bytes * @param n nonce to use for encryption * @param k intermediate shared secret * @return 0-padded cryptobox containing the message */ public native byte[] jni_crypto_box_afternm(byte[] m, long mlen, byte[] n, byte[] k); /** * Provides direct access to libSodium's crypto_box_open_afternm function * for opening a cryptobox using a precomputed intermediate shared secret. * Other than openCryptoBoxAfternm this method will return a 0-padded * message and must be called using a 0-padded cryptobox. * @param c 0-padded cryptobox to be decrypted * @param clen length of cryptobox in bytes * @param n nonce used for encryption * @param k intermediate shared secret * @return 0-padded decrypted message */ public native byte[] jni_crypto_box_open_afternm(byte[] c, long clen, byte[] n, byte[] k); }