1 package net.sumaris.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 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
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
67
68
69 public String getPublicKey() {
70 return pubKey;
71 }
72
73
74
75
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
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 }