View Javadoc
1   package net.sumaris.core.util.crypto;
2   
3   /*
4    * #%L
5    * Duniter4j :: Core API
6    * %%
7    * Copyright (C) 2014 - 2015 EIS
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  
26  import com.lambdaworks.crypto.SCrypt;
27  import jnr.ffi.byref.LongLongByReference;
28  import net.sumaris.core.exception.SumarisTechnicalException;
29  import net.sumaris.shared.exception.ErrorCodes;
30  import org.abstractj.kalium.crypto.Util;
31  
32  import java.security.GeneralSecurityException;
33  
34  import static org.abstractj.kalium.NaCl.Sodium.*;
35  import static org.abstractj.kalium.NaCl.sodium;
36  import static org.abstractj.kalium.crypto.Util.*;
37  
38  
39  public class SecretBox {
40  
41  	// Length of the key
42  	private static int SEED_LENGTH = 32;
43  	private static int SCRYPT_PARAMS_N = 4096;
44  	private static int SCRYPT_PARAMS_r = 16;
45  	private static int SCRYPT_PARAMS_p = 1;
46  
47  	private final String pubKey;
48      private final byte[] secretKey;
49      private final byte[] seed;
50  
51  	public SecretBox(String salt, String password) {
52  		this(computeSeedFromSaltAndPassword(salt, password));
53  	}
54  	
55  	public SecretBox(byte[] seed) {
56  		checkLength(seed, SEED_LENGTH);
57  		this.seed = seed;
58          this.secretKey = CryptoUtils.zeros(CRYPTO_SIGN_ED25519_SECRETKEYBYTES);
59          byte[] publicKey = CryptoUtils.zeros(CRYPTO_SIGN_ED25519_PUBLICKEYBYTES);
60          isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
61                  "Failed to generate a key pair");
62          this.pubKey = Base58.encode(publicKey);
63  	}
64  	
65  	/**
66  	 * Retrun the public key, encode in Base58
67  	 * @return
68  	 */
69  	public String getPublicKey() {
70  		return pubKey;
71  	}
72  	
73  	/**
74  	 * Return the secret key, encode in Base58
75  	 * @return
76  	 */
77  	public String getSecretKey() {
78  		return Base58.encode(secretKey);
79  	}
80  	
81  	public String sign(String message) {
82  		byte[] messageBinary = CryptoUtils.decodeUTF8(message);
83  		return CryptoUtils.encodeBase64(
84  				sign(messageBinary)
85  				);
86  	}
87  	
88  	public byte[] sign(byte[] message) {
89          byte[] signature = Util.prependZeros(CRYPTO_SIGN_ED25519_BYTES, message);
90          LongLongByReference bufferLen = new LongLongByReference(0);
91          sodium().crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
92          signature = Util.slice(signature, 0, CRYPTO_SIGN_ED25519_BYTES);
93          
94          checkLength(signature, CRYPTO_SIGN_ED25519_BYTES);
95          return signature;
96      }
97  
98  
99      public byte[] encrypt(byte[] nonce, byte[] message) {
100         checkLength(nonce, CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES);
101         byte[] msg = Util.prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message);
102         byte[] ct = Util.zeros(msg.length);
103         isValid(sodium().crypto_secretbox_xsalsa20poly1305(ct, msg, msg.length,
104                 nonce, seed), "Encryption failed");
105         return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, ct);
106     }
107 
108     public byte[] decrypt(byte[] nonce, byte[] ciphertext) {
109         checkLength(nonce, CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES);
110         byte[] ct = Util.prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ciphertext);
111         byte[] message = Util.zeros(ct.length);
112         isValid(sodium().crypto_secretbox_xsalsa20poly1305_open(message, ct,
113                 ct.length, nonce, seed), "Decryption failed. Ciphertext failed verification");
114         return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message);
115     }
116     
117 	/* -- Internal methods -- */
118 	
119 	public static byte[] computeSeedFromSaltAndPassword(String salt, String password) {
120 		try {
121 			byte[] seed = SCrypt.scrypt(
122 					CryptoUtils.decodeAscii(password),
123 					CryptoUtils.decodeAscii(salt),
124 					SCRYPT_PARAMS_N, SCRYPT_PARAMS_r,
125 					SCRYPT_PARAMS_p, SEED_LENGTH);
126 			return seed;
127 		} catch (GeneralSecurityException e) {
128 			throw new SumarisTechnicalException(
129 					"Unable to salt password, using Scrypt library", e);
130 		}
131 	}
132 	
133 	
134 }