1 package org.duniter.core.util.crypto;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
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
70
71
72 public String getPublicKey() {
73 return pubKey;
74 }
75
76
77
78
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
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 }