View Javadoc
1   package org.duniter.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 org.duniter.core.exception.TechnicalException;
28  import jnr.ffi.byref.LongLongByReference;
29  import org.abstractj.kalium.crypto.Util;
30  
31  import java.security.GeneralSecurityException;
32  
33  import static org.duniter.core.util.crypto.CryptoUtils.zeros;
34  import static org.abstractj.kalium.NaCl.Sodium.*;
35  import static org.abstractj.kalium.NaCl.sodium;
36  import static org.abstractj.kalium.crypto.Util.checkLength;
37  import static org.abstractj.kalium.crypto.Util.isValid;
38  import static org.abstractj.kalium.crypto.Util.removeZeros;
39  
40  
41  public class SecretBox {
42  
43  	// Length of the key
44  	private static int SEED_LENGTH = 32;
45  	private static int SIGNATURE_BYTES = 64;
46  	private static int SCRYPT_PARAMS_N = 4096;
47  	private static int SCRYPT_PARAMS_r = 16;
48  	private static int SCRYPT_PARAMS_p = 1;
49  
50  	private final String pubKey;
51      private final byte[] secretKey;
52      private final byte[] seed;
53  
54  	public SecretBox(String salt, String password) {
55  		this(computeSeedFromSaltAndPassword(salt, password));
56  	}
57  	
58  	public SecretBox(byte[] seed) {
59  		checkLength(seed, SEED_LENGTH);
60  		this.seed = seed;
61          this.secretKey = CryptoUtils.zeros(SECRETKEY_BYTES * 2);
62          byte[] publicKey = CryptoUtils.zeros(PUBLICKEY_BYTES);
63          isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
64                  "Failed to generate a key pair");
65          this.pubKey = Base58.encode(publicKey);
66  	}
67  	
68  	/**
69  	 * Retrun the public key, encode in Base58
70  	 * @return
71  	 */
72  	public String getPublicKey() {
73  		return pubKey;
74  	}
75  	
76  	/**
77  	 * Return the secret key, encode in Base58
78  	 * @return
79  	 */
80  	public String getSecretKey() {
81  		return Base58.encode(secretKey);
82  	}
83  	
84  	public String sign(String message) {
85  		byte[] messageBinary = CryptoUtils.decodeUTF8(message);
86  		return CryptoUtils.encodeBase64(
87  				sign(messageBinary)
88  				);
89  	}
90  	
91  	public byte[] sign(byte[] message) {
92          byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
93          LongLongByReference bufferLen = new LongLongByReference(0);
94          sodium().crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
95          signature = Util.slice(signature, 0, SIGNATURE_BYTES);
96          
97          checkLength(signature, SIGNATURE_BYTES);
98          return signature;
99      }
100 
101 
102     public byte[] encrypt(byte[] nonce, byte[] message) {
103         checkLength(nonce, XSALSA20_POLY1305_SECRETBOX_NONCEBYTES);
104         byte[] msg = Util.prependZeros(ZERO_BYTES, message);
105         byte[] ct = Util.zeros(msg.length);
106         isValid(sodium().crypto_secretbox_xsalsa20poly1305(ct, msg, msg.length,
107                 nonce, seed), "Encryption failed");
108         return removeZeros(BOXZERO_BYTES, ct);
109     }
110 
111     public byte[] decrypt(byte[] nonce, byte[] ciphertext) {
112         checkLength(nonce, XSALSA20_POLY1305_SECRETBOX_NONCEBYTES);
113         byte[] ct = Util.prependZeros(BOXZERO_BYTES, ciphertext);
114         byte[] message = Util.zeros(ct.length);
115         isValid(sodium().crypto_secretbox_xsalsa20poly1305_open(message, ct,
116                 ct.length, nonce, seed), "Decryption failed. Ciphertext failed verification");
117         return removeZeros(ZERO_BYTES, message);
118     }
119     
120 	/* -- Internal methods -- */
121 	
122 	public static byte[] computeSeedFromSaltAndPassword(String salt, String password) {
123 		try {
124 			byte[] seed = SCrypt.scrypt(
125 					CryptoUtils.decodeAscii(password),
126 					CryptoUtils.decodeAscii(salt),
127 					SCRYPT_PARAMS_N, SCRYPT_PARAMS_r,
128 					SCRYPT_PARAMS_p, SEED_LENGTH);
129 			return seed;
130 		} catch (GeneralSecurityException e) {
131 			throw new TechnicalException(
132 					"Unable to salt password, using Scrypt library", e);
133 		}
134 	}
135 	
136 	
137 }