├── .gitignore ├── scripts ├── version.sh └── build.sh ├── .travis.yml ├── src ├── main │ └── java │ │ └── org │ │ └── abstractj │ │ └── kalium │ │ ├── keys │ │ ├── Key.java │ │ ├── PublicKey.java │ │ ├── PrivateKey.java │ │ ├── VerifyKey.java │ │ ├── KeyPair.java │ │ ├── AuthenticationKey.java │ │ └── SigningKey.java │ │ ├── encoders │ │ ├── Raw.java │ │ ├── Encoder.java │ │ └── Hex.java │ │ ├── crypto │ │ ├── Random.java │ │ ├── Advanced.java │ │ ├── ShortHash.java │ │ ├── Password.java │ │ ├── SealedBox.java │ │ ├── Point.java │ │ ├── Util.java │ │ ├── SecretBox.java │ │ ├── Hash.java │ │ ├── Aead.java │ │ └── Box.java │ │ └── NaCl.java └── test │ └── java │ └── org │ └── abstractj │ └── kalium │ ├── crypto │ ├── UtilTest.java │ ├── RandomTest.java │ ├── AeadTest.java │ ├── ShortHashTest.java │ ├── PointTest.java │ ├── SealedBoxTest.java │ ├── SecretBoxTest.java │ ├── AdvancedTest.java │ ├── PasswordTest.java │ ├── BoxTest.java │ └── HashTest.java │ ├── encoders │ └── RawTest.java │ ├── keys │ ├── VerifyKeyTest.java │ ├── SigningKeyTest.java │ ├── KeyPairTest.java │ └── AuthenticationKeyTest.java │ └── fixture │ └── TestVectors.java ├── appveyor.yml ├── README.md ├── pom.xml └── LICENSE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | target/ 4 | -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export LIBSODIUM=`curl -s https://download.libsodium.org/libsodium/releases/ | sed -e 's/<[^>]*>//g' | grep -i stable | awk '{print $1}' | tail -n2 | grep -v "minisig"` 4 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . scripts/version.sh 4 | 5 | wget -c https://download.libsodium.org/libsodium/releases/$LIBSODIUM 6 | tar xzvf $LIBSODIUM 7 | cd libsodium-stable && mkdir vendor 8 | ./configure --prefix=`pwd`/vendor 9 | make && make install 10 | cd ../ 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | before_script: 4 | - ./scripts/build.sh 5 | 6 | script: 7 | - export LD_LIBRARY_PATH="/home/travis/build/abstractj/kalium/libsodium-stable/vendor/lib" 8 | - mvn clean install 9 | 10 | env: JAVA_OPTS="-Djava.library.path=/home/travis/build/abstractj/kalium/libsodium-stable/vendor/lib" 11 | 12 | jdk: 13 | - oraclejdk8 14 | - oraclejdk9 15 | - openjdk7 16 | - openjdk8 17 | 18 | after_success: 19 | - rm -rf libsodium-stable* 20 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/Key.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.keys; 2 | 3 | import static org.abstractj.kalium.encoders.Encoder.HEX; 4 | 5 | /** 6 | * Copyright 2013 Bruno Oliveira, and individual contributors 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | public interface Key { 21 | byte[] toBytes(); 22 | } 23 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | os: Windows Server 2012 3 | install: 4 | - cmd: SET JAVA_HOME=C:\Program Files (x86)\Java\jdk1.8.0 5 | - cmd: SET LIBSODIUM_VERSION=1.0.3 6 | - ps: | 7 | Add-Type -AssemblyName System.IO.Compression.FileSystem 8 | if (!(Test-Path -Path "C:\libsodium" )) { 9 | (New-Object System.Net.WebClient).DownloadFile( 10 | "https://github.com/jedisct1/libsodium/archive/$env:LIBSODIUM_VERSION.zip", 11 | 'C:\libsodium.zip' 12 | ) 13 | [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\libsodium.zip", "C:\") 14 | Rename-Item C:\libsodium-$env:LIBSODIUM_VERSION C:\libsodium 15 | } 16 | - cmd: msbuild C:\libsodium\libsodium.vcxproj /property:Configuration=ReleaseDLL /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 17 | - cmd: SET PATH=C:\libsodium\Build\ReleaseDLL\Win32;%PATH% 18 | build_script: 19 | - mvn clean install -q --batch-mode -DargLine=-Dfile.encoding=UTF-8 20 | cache: 21 | - C:\libsodium 22 | - C:\Users\appveyor\.m2 23 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/encoders/Raw.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.encoders; 18 | 19 | public class Raw implements Encoder { 20 | 21 | public byte[] decode(final String data) { 22 | return data != null ? data.getBytes(CHARSET) : null; 23 | } 24 | 25 | @Override 26 | public String encode(byte[] data) { 27 | return data != null ? new String(data, CHARSET) : null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/encoders/Encoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.encoders; 18 | 19 | import java.nio.charset.Charset; 20 | 21 | public interface Encoder { 22 | 23 | public static final Charset CHARSET = Charset.forName("US-ASCII"); 24 | 25 | public static final Hex HEX = new Hex(); 26 | public static final Raw RAW = new Raw(); 27 | 28 | public byte[] decode(String data); 29 | 30 | public String encode(final byte[] data); 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/UtilTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *
10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | public class UtilTest { 23 | @Test 24 | public void testPrependZeros() throws Exception { 25 | byte[] src = {'t', 'e', 's', 't'}; 26 | byte[] result = Util.prependZeros(3, src); 27 | Assert.assertArrayEquals(new byte[]{0, 0, 0, 't', 'e', 's', 't'}, result); 28 | } 29 | 30 | @Test(expected = RuntimeException.class) 31 | public void testDataNull() { 32 | Util.checkLength(null, 3); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Random.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import static org.abstractj.kalium.NaCl.sodium; 20 | 21 | public class Random { 22 | 23 | private static final int DEFAULT_SIZE = 32; 24 | 25 | /** 26 | * Generate random bytes 27 | * 28 | * @param n number or random bytes 29 | * @return 30 | */ 31 | public byte[] randomBytes(int n) { 32 | byte[] buffer = new byte[n]; 33 | sodium().randombytes(buffer, n); 34 | return buffer; 35 | } 36 | 37 | public byte[] randomBytes() { 38 | byte[] buffer = new byte[DEFAULT_SIZE]; 39 | sodium().randombytes(buffer, DEFAULT_SIZE); 40 | return buffer; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Advanced.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.abstractj.kalium.crypto; 17 | 18 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_STREAM_KEYBYTES; 19 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_STREAM_NONCEBYTES; 20 | import static org.abstractj.kalium.NaCl.sodium; 21 | import static org.abstractj.kalium.crypto.Util.checkLength; 22 | 23 | public class Advanced { 24 | 25 | public byte[] crypto_stream_xsalsa20_xor(byte[] message, byte[] nonce, byte[] key) { 26 | 27 | checkLength(nonce, CRYPTO_STREAM_NONCEBYTES); 28 | checkLength(key, CRYPTO_STREAM_KEYBYTES); 29 | byte[] buffer = new byte[message.length]; 30 | sodium().crypto_stream_xor(buffer, message, message.length, nonce, key); 31 | return buffer; 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/ShortHash.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import static org.abstractj.kalium.NaCl.sodium; 20 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SHORTHASH_SIPHASH24_BYTES; 21 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SHORTHASH_SIPHASH24_KEYBYTES; 22 | import static org.abstractj.kalium.crypto.Util.checkLength; 23 | import static org.abstractj.kalium.crypto.Util.isValid; 24 | 25 | public class ShortHash { 26 | 27 | public byte[] siphash24(byte[] message, byte[] key) { 28 | byte[] buffer = new byte[CRYPTO_SHORTHASH_SIPHASH24_BYTES]; 29 | checkLength(key, CRYPTO_SHORTHASH_SIPHASH24_KEYBYTES); 30 | isValid(sodium().crypto_shorthash_siphash24(buffer, message, message.length, key), "Hashing failed"); 31 | return buffer; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/PublicKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 20 | import static org.abstractj.kalium.crypto.Util.checkLength; 21 | import static org.abstractj.kalium.encoders.Encoder.HEX; 22 | 23 | public class PublicKey implements Key { 24 | 25 | private final byte[] publicKey; 26 | 27 | public PublicKey(byte[] publicKey) { 28 | this.publicKey = publicKey; 29 | checkLength(publicKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); 30 | } 31 | 32 | public PublicKey(String publicKey) { 33 | this.publicKey = HEX.decode(publicKey); 34 | } 35 | 36 | public byte[] toBytes() { 37 | return publicKey; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return HEX.encode(publicKey); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/PrivateKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES; 20 | import static org.abstractj.kalium.crypto.Util.checkLength; 21 | import static org.abstractj.kalium.encoders.Encoder.HEX; 22 | 23 | public class PrivateKey implements Key { 24 | 25 | private final byte[] secretKey; 26 | 27 | public PrivateKey(byte[] secretKey) { 28 | this.secretKey = secretKey; 29 | checkLength(secretKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); 30 | } 31 | 32 | public PrivateKey(String secretKey) { 33 | this.secretKey = HEX.decode(secretKey); 34 | checkLength(this.secretKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); 35 | } 36 | 37 | @Override 38 | public byte[] toBytes() { 39 | return secretKey; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return HEX.encode(secretKey); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Password.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.crypto; 2 | 3 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OUTBYTES; 4 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRBYTES; 5 | import static org.abstractj.kalium.NaCl.sodium; 6 | import org.abstractj.kalium.encoders.Encoder; 7 | 8 | public class Password { 9 | 10 | public Password() { 11 | } 12 | public byte[] deriveKey(int length, byte[] passwd, byte[] salt, int opslimit, long memlimit) { 13 | byte[] buffer = new byte[length]; 14 | sodium().crypto_pwhash_scryptsalsa208sha256(buffer, buffer.length, passwd, passwd.length, salt, opslimit, memlimit); 15 | return buffer; 16 | } 17 | 18 | public String hash(byte[] passwd, Encoder encoder, byte[] salt, int opslimit, long memlimit) { 19 | byte[] buffer = deriveKey(CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OUTBYTES, passwd, salt, opslimit, memlimit); 20 | return encoder.encode(buffer); 21 | } 22 | 23 | public String hash(int length, byte[] passwd, Encoder encoder, byte[] salt, int opslimit, long memlimit) { 24 | byte[] buffer = deriveKey(length, passwd, salt, opslimit, memlimit); 25 | return encoder.encode(buffer); 26 | } 27 | 28 | public String hash(byte[] passwd, Encoder encoder, int opslimit, long memlimit) { 29 | byte[] buffer = new byte[CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRBYTES]; 30 | sodium().crypto_pwhash_scryptsalsa208sha256_str(buffer, passwd, passwd.length, opslimit, memlimit); 31 | return encoder.encode(buffer); 32 | } 33 | 34 | public boolean verify(byte[] hashed_passwd, byte[] passwd) { 35 | int result = sodium().crypto_pwhash_scryptsalsa208sha256_str_verify(hashed_passwd, passwd, passwd.length); 36 | return result == 0; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/RandomTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.junit.Assert.assertEquals; 24 | import static org.junit.Assert.assertFalse; 25 | 26 | public class RandomTest { 27 | 28 | @Test 29 | public void testProducesRandomBytes() throws Exception { 30 | final int size = 16; 31 | assertEquals("Invalid random bytes", size, new Random().randomBytes(size).length); 32 | } 33 | 34 | @Test 35 | public void testProducesDefaultRandomBytes() throws Exception { 36 | final int size = 32; 37 | assertEquals("Invalid random bytes", size, new Random().randomBytes().length); 38 | } 39 | 40 | @Test 41 | public void testProducesDifferentRandomBytes() throws Exception { 42 | final int size = 16; 43 | assertFalse("Should produce different random bytes", Arrays.equals(new Random().randomBytes(size), new Random().randomBytes(size))); 44 | } 45 | 46 | @Test 47 | public void testProducesDifferentDefaultRandomBytes() throws Exception { 48 | final int size = 32; 49 | assertFalse("Should produce different random bytes", Arrays.equals(new Random().randomBytes(), new Random().randomBytes(size))); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/SealedBox.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.crypto; 2 | 3 | import org.abstractj.kalium.encoders.Encoder; 4 | 5 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_SEALBYTES; 6 | import static org.abstractj.kalium.NaCl.sodium; 7 | import static org.abstractj.kalium.crypto.Util.isValid; 8 | 9 | public class SealedBox { 10 | 11 | private byte[] publicKey; 12 | private byte[] privateKey; 13 | 14 | public SealedBox(byte[] publicKey) { 15 | this.publicKey = publicKey; 16 | this.privateKey = null; 17 | } 18 | 19 | public SealedBox(String publicKey, Encoder encoder) { 20 | this(encoder.decode(publicKey)); 21 | } 22 | 23 | public SealedBox(byte[] publicKey, byte[] privateKey) { 24 | this.publicKey = publicKey; 25 | this.privateKey = privateKey; 26 | } 27 | 28 | public SealedBox(String publicKey, String privateKey, Encoder encoder) { 29 | this(encoder.decode(publicKey), encoder.decode(privateKey)); 30 | } 31 | 32 | public byte[] encrypt(byte[] message) { 33 | if (publicKey == null) 34 | throw new RuntimeException("Encryption failed. Public key not available."); 35 | byte[] ct = new byte[message.length + CRYPTO_BOX_SEALBYTES]; 36 | isValid(sodium().crypto_box_seal( 37 | ct, message, message.length, publicKey), 38 | "Encryption failed"); 39 | return ct; 40 | } 41 | 42 | public byte[] decrypt(byte[] ciphertext) { 43 | if (privateKey == null) 44 | throw new RuntimeException("Decryption failed. Private key not available."); 45 | 46 | byte[] message = new byte[ciphertext.length - CRYPTO_BOX_SEALBYTES]; 47 | isValid(sodium().crypto_box_seal_open( 48 | message, ciphertext, ciphertext.length, publicKey, privateKey), 49 | "Decryption failed. Ciphertext failed verification"); 50 | return message; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/AeadTest.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.crypto; 2 | 3 | import org.abstractj.kalium.NaCl; 4 | import org.junit.Test; 5 | 6 | import static org.abstractj.kalium.NaCl.sodium; 7 | import static org.abstractj.kalium.encoders.Encoder.HEX; 8 | import static org.abstractj.kalium.fixture.TestVectors.*; 9 | import static org.junit.Assert.assertArrayEquals; 10 | 11 | public class AeadTest { 12 | @Test 13 | public void testEncrypt() throws Exception { 14 | byte[] key = HEX.decode(AEAD_KEY); 15 | byte[] publicNonce = HEX.decode(AEAD_NONCE); 16 | byte[] message = HEX.decode(AEAD_MESSAGE); 17 | byte[] ad = HEX.decode(AEAD_AD); 18 | 19 | Aead aead = new Aead(key); 20 | byte[] ct = aead.encrypt(publicNonce, message, ad); 21 | assertArrayEquals(HEX.decode(AEAD_CT), ct); 22 | } 23 | 24 | @Test 25 | public void testDecrypt() throws Exception { 26 | byte[] key = HEX.decode(AEAD_KEY); 27 | byte[] publicNonce = HEX.decode(AEAD_NONCE); 28 | byte[] ct = HEX.decode(AEAD_CT); 29 | byte[] ad = HEX.decode(AEAD_AD); 30 | 31 | Aead aead = new Aead(key); 32 | byte[] message = aead.decrypt(publicNonce, ct, ad); 33 | assertArrayEquals(HEX.decode(AEAD_MESSAGE), message); 34 | } 35 | 36 | @Test 37 | public void testAES256GCM() throws Exception { 38 | sodium().sodium_init(); 39 | if (sodium().crypto_aead_aes256gcm_is_available() != 1) { 40 | System.out.println("AES256-GCM is not supported"); 41 | return; 42 | } 43 | 44 | byte[] key = HEX.decode(AEAD_KEY); 45 | byte[] publicNonce = new Random().randomBytes(NaCl.Sodium.CRYPTO_AEAD_AES256GCM_NPUBBYTES); 46 | byte[] message = HEX.decode(AEAD_MESSAGE); 47 | byte[] ad = HEX.decode(AEAD_AD); 48 | 49 | Aead aead = new Aead(key).useAesGcm(); 50 | byte[] ct = aead.encrypt(publicNonce, message, ad); 51 | byte[] msg2 = aead.decrypt(publicNonce, ct, ad); 52 | assertArrayEquals(message, msg2); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Point.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.abstractj.kalium.encoders.Encoder; 20 | 21 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES; 22 | import static org.abstractj.kalium.NaCl.sodium; 23 | import static org.abstractj.kalium.crypto.Util.zeros; 24 | import static org.abstractj.kalium.encoders.Encoder.HEX; 25 | 26 | public class Point { 27 | 28 | private static final String STANDARD_GROUP_ELEMENT = "0900000000000000000000000000000000000000000000000000000000000000"; 29 | 30 | private final byte[] point; 31 | 32 | public Point() { 33 | this.point = HEX.decode(STANDARD_GROUP_ELEMENT); 34 | } 35 | 36 | public Point(byte[] point) { 37 | this.point = point; 38 | } 39 | 40 | public Point(String point, Encoder encoder) { 41 | this(encoder.decode(point)); 42 | } 43 | 44 | public Point mult(byte[] n) { 45 | byte[] result = zeros(CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES); 46 | sodium().crypto_scalarmult_curve25519(result, n, point); 47 | return new Point(result); 48 | } 49 | 50 | public Point mult(String n, Encoder encoder) { 51 | return mult(encoder.decode(n)); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return HEX.encode(point); 57 | } 58 | 59 | public byte[] toBytes() { 60 | return point; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Util.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import java.util.Arrays; 20 | 21 | public class Util { 22 | 23 | private static final int DEFAULT_SIZE = 32; 24 | 25 | public static byte[] prependZeros(int n, byte[] message) { 26 | byte[] result = new byte[n + message.length]; 27 | System.arraycopy(message, 0, result, n, message.length); 28 | return result; 29 | } 30 | 31 | public static byte[] removeZeros(int n, byte[] message) { 32 | return Arrays.copyOfRange(message, n, message.length); 33 | } 34 | 35 | public static void checkLength(byte[] data, int size) { 36 | if (data == null || data.length != size) 37 | throw new RuntimeException("Invalid size"); 38 | } 39 | 40 | public static byte[] zeros(int n) { 41 | return new byte[n]; 42 | } 43 | 44 | public static boolean isValid(int status, String message) { 45 | if (status != 0) 46 | throw new RuntimeException(message); 47 | return true; 48 | } 49 | 50 | public static byte[] slice(byte[] buffer, int start, int end) { 51 | return Arrays.copyOfRange(buffer, start, end); 52 | } 53 | 54 | public static byte[] merge(byte[] signature, byte[] message) { 55 | byte[] result = new byte[signature.length + message.length]; 56 | System.arraycopy(signature, 0, result, 0, signature.length); 57 | System.arraycopy(message, 0, result, signature.length, message.length); 58 | return result; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/VerifyKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import org.abstractj.kalium.encoders.Encoder; 20 | 21 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 22 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SIGN_ED25519_BYTES; 23 | import static org.abstractj.kalium.NaCl.sodium; 24 | import static org.abstractj.kalium.crypto.Util.checkLength; 25 | import static org.abstractj.kalium.crypto.Util.isValid; 26 | import static org.abstractj.kalium.encoders.Encoder.HEX; 27 | 28 | public class VerifyKey { 29 | 30 | private byte[] key; 31 | 32 | public VerifyKey(byte[] key) { 33 | checkLength(key, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); 34 | this.key = key; 35 | } 36 | 37 | public VerifyKey(String key, Encoder encoder) { 38 | this(encoder.decode(key)); 39 | } 40 | 41 | public boolean verify(byte[] message, byte[] signature) { 42 | checkLength(signature, CRYPTO_SIGN_ED25519_BYTES); 43 | return isValid(sodium().crypto_sign_ed25519_verify_detached(signature, message, message.length, key), "signature was forged or corrupted"); 44 | } 45 | 46 | public boolean verify(String message, String signature, Encoder encoder) { 47 | return verify(encoder.decode(message), encoder.decode(signature)); 48 | } 49 | 50 | public byte[] toBytes() { 51 | return key; 52 | } 53 | 54 | @Override 55 | public String toString(){ 56 | return HEX.encode(key); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/encoders/RawTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.encoders; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import java.util.Arrays; 23 | 24 | import static org.junit.Assert.assertTrue; 25 | import static org.junit.Assert.assertEquals; 26 | import static org.junit.Assert.assertNull; 27 | import static org.junit.Assert.fail; 28 | 29 | public class RawTest { 30 | 31 | private Encoder encoder; 32 | 33 | @Before 34 | public void setUp() { 35 | encoder = new Raw(); 36 | } 37 | 38 | @Test 39 | public void testEncode() throws Exception { 40 | String value = "hello"; 41 | assertEquals(value, encoder.encode(value.getBytes())); 42 | } 43 | 44 | @Test 45 | public void testEncodeNullString() throws Exception { 46 | byte[] value = null; 47 | try { 48 | assertNull(encoder.encode(value)); 49 | } catch (Exception e) { 50 | fail("Should not raise any exception"); 51 | } 52 | } 53 | 54 | @Test 55 | public void testDecode() throws Exception { 56 | String value = "hello"; 57 | assertTrue(Arrays.equals(encoder.decode(value), value.getBytes())); 58 | } 59 | 60 | @Test 61 | public void testDecodeNullString() throws Exception { 62 | String value = null; 63 | try { 64 | assertNull(encoder.decode(value)); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | fail("Should not raise any exception"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/ShortHashTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.abstractj.kalium.encoders.Encoder.HEX; 22 | import static org.abstractj.kalium.fixture.TestVectors.SIPHASH24_KEY; 23 | import static org.abstractj.kalium.fixture.TestVectors.SIPHASH24_MESSAGE; 24 | import static org.abstractj.kalium.fixture.TestVectors.SIPHASH24_DIGEST; 25 | import static org.abstractj.kalium.fixture.TestVectors.SIPHASH24_DIGEST_EMPTY_STRING; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | 29 | public class ShortHashTest { 30 | 31 | private final ShortHash hash = new ShortHash(); 32 | 33 | @Test 34 | public void testSiphash24() throws Exception { 35 | byte[] message = HEX.decode(SIPHASH24_MESSAGE); 36 | byte[] key = HEX.decode(SIPHASH24_KEY); 37 | String result = HEX.encode(hash.siphash24(message, key)); 38 | assertEquals("Hash is invalid", SIPHASH24_DIGEST, result); 39 | } 40 | 41 | @Test 42 | public void testSiphash24EmptyString() throws Exception { 43 | byte[] key = HEX.decode(SIPHASH24_KEY); 44 | String result = HEX.encode(hash.siphash24(new byte[0], key)); 45 | assertEquals("Hash is invalid", SIPHASH24_DIGEST_EMPTY_STRING, result); 46 | } 47 | 48 | @Test(expected = RuntimeException.class) 49 | public void testSiphash24InvalidKey() throws Exception { 50 | byte[] message = HEX.decode(SIPHASH24_MESSAGE); 51 | byte[] invalidKey = new byte[1]; 52 | hash.siphash24(message, invalidKey); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/KeyPair.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import org.abstractj.kalium.crypto.Point; 20 | import org.abstractj.kalium.encoders.Encoder; 21 | 22 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 23 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES; 24 | import static org.abstractj.kalium.NaCl.sodium; 25 | import static org.abstractj.kalium.crypto.Util.checkLength; 26 | import static org.abstractj.kalium.crypto.Util.zeros; 27 | 28 | public class KeyPair { 29 | 30 | private final byte[] publicKey; 31 | private final byte[] secretKey; 32 | 33 | public KeyPair() { 34 | this.secretKey = zeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); 35 | this.publicKey = zeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); 36 | sodium().crypto_box_curve25519xsalsa20poly1305_keypair(publicKey, secretKey); 37 | } 38 | 39 | public KeyPair(byte[] secretKey) { 40 | this.secretKey = secretKey; 41 | checkLength(this.secretKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); 42 | Point point = new Point(); 43 | this.publicKey = point.mult(secretKey).toBytes(); 44 | } 45 | 46 | public KeyPair(String secretKey, Encoder encoder) { 47 | this(encoder.decode(secretKey)); 48 | } 49 | 50 | public PublicKey getPublicKey() { 51 | return new PublicKey(publicKey); 52 | } 53 | 54 | public PrivateKey getPrivateKey() { 55 | return new PrivateKey(secretKey); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/PointTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.abstractj.kalium.encoders.Encoder.HEX; 24 | import static org.abstractj.kalium.fixture.TestVectors.ALICE_MULT_BOB; 25 | import static org.abstractj.kalium.fixture.TestVectors.ALICE_PRIVATE_KEY; 26 | import static org.abstractj.kalium.fixture.TestVectors.ALICE_PUBLIC_KEY; 27 | import static org.abstractj.kalium.fixture.TestVectors.BOB_PUBLIC_KEY; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertTrue; 30 | 31 | public class PointTest { 32 | 33 | @Test 34 | public void testMultipleIntegersWithBasePoint() throws Exception { 35 | Point point = new Point(); 36 | String mult = point.mult(ALICE_PRIVATE_KEY, HEX).toString(); 37 | assertEquals("Should return a serialized point", ALICE_PUBLIC_KEY, mult); 38 | } 39 | 40 | @Test 41 | public void testMultipleIntegersWithArbitraryPoints() throws Exception { 42 | Point point = new Point(BOB_PUBLIC_KEY, HEX); 43 | String mult = point.mult(ALICE_PRIVATE_KEY, HEX).toString(); 44 | assertEquals("Should return a valid serialized point", ALICE_MULT_BOB, mult); 45 | } 46 | 47 | @Test 48 | public void testSerializeToBytes() throws Exception { 49 | Point point = new Point(BOB_PUBLIC_KEY, HEX); 50 | assertTrue("Should serialize to bytes", Arrays.equals(HEX.decode(BOB_PUBLIC_KEY), point.toBytes())); 51 | } 52 | 53 | @Test 54 | public void testSerializeToHex() throws Exception { 55 | Point point = new Point(BOB_PUBLIC_KEY, HEX); 56 | assertEquals("Should serialize to hex", BOB_PUBLIC_KEY, point.toString()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/AuthenticationKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Cisco Systems, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import org.abstractj.kalium.encoders.Encoder; 20 | 21 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_AUTH_HMACSHA512256_BYTES; 22 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_AUTH_HMACSHA512256_KEYBYTES; 23 | import static org.abstractj.kalium.NaCl.sodium; 24 | import static org.abstractj.kalium.crypto.Util.checkLength; 25 | import static org.abstractj.kalium.crypto.Util.isValid; 26 | import static org.abstractj.kalium.encoders.Encoder.HEX; 27 | 28 | 29 | public class AuthenticationKey implements Key { 30 | 31 | private byte[] key; 32 | 33 | public AuthenticationKey(byte[] key) { 34 | this.key = key; 35 | checkLength(key, CRYPTO_AUTH_HMACSHA512256_KEYBYTES); 36 | } 37 | 38 | public AuthenticationKey(String key, Encoder encoder) { 39 | this(encoder.decode(key)); 40 | } 41 | 42 | public byte[] sign(byte[] message) { 43 | byte[] mac = new byte[CRYPTO_AUTH_HMACSHA512256_BYTES]; 44 | sodium().crypto_auth_hmacsha512256(mac, message, message.length, key); 45 | return mac; 46 | } 47 | 48 | public String sign(String message, Encoder encoder) { 49 | byte[] signature = sign(encoder.decode(message)); 50 | return encoder.encode(signature); 51 | } 52 | 53 | public boolean verify(byte[] message, byte[] signature) { 54 | checkLength(signature, CRYPTO_AUTH_HMACSHA512256_BYTES); 55 | return isValid(sodium().crypto_auth_hmacsha512256_verify(signature, message, message.length, key), "signature was forged or corrupted"); 56 | } 57 | 58 | public boolean verify(String message, String signature, Encoder encoder) { 59 | return verify(encoder.decode(message), encoder.decode(signature)); 60 | } 61 | 62 | @Override 63 | public byte[] toBytes() { 64 | return key; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return HEX.encode(key); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/SecretBox.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.abstractj.kalium.encoders.Encoder; 20 | 21 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES; 22 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES; 23 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES; 24 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES; 25 | import static org.abstractj.kalium.NaCl.sodium; 26 | import static org.abstractj.kalium.crypto.Util.checkLength; 27 | import static org.abstractj.kalium.crypto.Util.isValid; 28 | import static org.abstractj.kalium.crypto.Util.removeZeros; 29 | 30 | public class SecretBox { 31 | 32 | private byte[] key; 33 | 34 | public SecretBox(byte[] key) { 35 | this.key = key; 36 | checkLength(key, CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES); 37 | } 38 | 39 | public SecretBox(String key, Encoder encoder) { 40 | this(encoder.decode(key)); 41 | } 42 | 43 | public byte[] encrypt(byte[] nonce, byte[] message) { 44 | checkLength(nonce, CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES); 45 | byte[] msg = Util.prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message); 46 | byte[] ct = Util.zeros(msg.length); 47 | isValid(sodium().crypto_secretbox_xsalsa20poly1305(ct, msg, msg.length, 48 | nonce, key), "Encryption failed"); 49 | return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ct); 50 | } 51 | 52 | public byte[] decrypt(byte[] nonce, byte[] ciphertext) { 53 | checkLength(nonce, CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES); 54 | byte[] ct = Util.prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ciphertext); 55 | byte[] message = Util.zeros(ct.length); 56 | isValid(sodium().crypto_secretbox_xsalsa20poly1305_open(message, ct, 57 | ct.length, nonce, key), "Decryption failed. Ciphertext failed verification"); 58 | return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Hash.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.abstractj.kalium.encoders.Encoder; 20 | 21 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_GENERICHASH_BLAKE2B_BYTES; 22 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_HASH_SHA256_BYTES; 23 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_HASH_SHA512_BYTES; 24 | import static org.abstractj.kalium.NaCl.sodium; 25 | 26 | public class Hash { 27 | 28 | public byte[] sha256(byte[] message) { 29 | byte[] buffer = new byte[CRYPTO_HASH_SHA256_BYTES]; 30 | sodium().crypto_hash_sha256(buffer, message, message.length); 31 | return buffer; 32 | } 33 | 34 | public byte[] sha512(byte[] message) { 35 | byte[] buffer = new byte[CRYPTO_HASH_SHA512_BYTES]; 36 | sodium().crypto_hash_sha512(buffer, message, message.length); 37 | return buffer; 38 | } 39 | 40 | public String sha256(String message, Encoder encoder) { 41 | byte[] hash = sha256(message.getBytes()); 42 | return encoder.encode(hash); 43 | } 44 | 45 | public String sha512(String message, Encoder encoder) { 46 | byte[] hash = sha512(message.getBytes()); 47 | return encoder.encode(hash); 48 | } 49 | 50 | 51 | public byte[] blake2(byte[] message) throws UnsupportedOperationException { 52 | byte[] buffer = new byte[CRYPTO_GENERICHASH_BLAKE2B_BYTES]; 53 | sodium().crypto_generichash_blake2b(buffer, CRYPTO_GENERICHASH_BLAKE2B_BYTES, message, message.length, null, 0); 54 | return buffer; 55 | } 56 | 57 | public String blake2(String message, Encoder encoder) throws UnsupportedOperationException { 58 | byte[] hash = blake2(message.getBytes()); 59 | return encoder.encode(hash); 60 | } 61 | 62 | public byte[] blake2(byte[] message, byte[] key, byte[] salt, byte[] personal) throws UnsupportedOperationException { 63 | byte[] buffer = new byte[CRYPTO_GENERICHASH_BLAKE2B_BYTES]; 64 | sodium().crypto_generichash_blake2b_salt_personal(buffer, CRYPTO_GENERICHASH_BLAKE2B_BYTES, 65 | message, message.length, 66 | key, key.length, 67 | salt, personal); 68 | return buffer; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/SealedBoxTest.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.crypto; 2 | 3 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 4 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES; 5 | import static org.abstractj.kalium.NaCl.sodium; 6 | import static org.junit.Assert.assertArrayEquals; 7 | 8 | import java.security.SecureRandom; 9 | import org.abstractj.kalium.keys.KeyPair; 10 | import org.junit.Test; 11 | 12 | public class SealedBoxTest { 13 | 14 | @Test 15 | public void testEncryptDecrypt() throws Exception { 16 | SecureRandom r = new SecureRandom(); 17 | KeyPair keyPair = new KeyPair(new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES]); 18 | byte[] sk = keyPair.getPrivateKey().toBytes(); 19 | byte[] pk = keyPair.getPublicKey().toBytes(); 20 | byte[] m = new byte[r.nextInt(1000)]; 21 | 22 | r.nextBytes(m); 23 | SealedBox sb = new SealedBox(pk); 24 | byte[] c = sb.encrypt(m); 25 | 26 | SealedBox sb2 = new SealedBox(pk, sk); 27 | byte[] m2 = sb2.decrypt(c); 28 | assertArrayEquals(m, m2); 29 | } 30 | 31 | @Test 32 | public void testEncryptDecryptMultPublicKeys() throws Exception { 33 | SecureRandom r = new SecureRandom(); 34 | KeyPair keyPair = new KeyPair(new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES]); 35 | byte[] sk = keyPair.getPrivateKey().toBytes(); 36 | byte[] pk1 = keyPair.getPublicKey().toBytes(); 37 | byte[] pk2 = new byte[pk1.length]; 38 | 39 | sodium().crypto_scalarmult_base(pk2, sk); 40 | 41 | byte[] m = new byte[r.nextInt(1000)]; 42 | r.nextBytes(m); 43 | 44 | SealedBox sb1 = new SealedBox(pk1); 45 | byte[] c1 = sb1.encrypt(m); 46 | 47 | SealedBox sb2 = new SealedBox(pk2); 48 | byte[] c2 = sb2.encrypt(m); 49 | 50 | SealedBox sb3 = new SealedBox(pk1, sk); 51 | byte[] m2 = sb3.decrypt(c1); 52 | byte[] m3 = sb3.decrypt(c2); 53 | assertArrayEquals(m, m2); 54 | assertArrayEquals(m2, m3); 55 | } 56 | 57 | @Test(expected = RuntimeException.class) 58 | public void testDecryptFailsFlippedKeys() throws Exception { 59 | SecureRandom r = new SecureRandom(); 60 | byte[] pk = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES]; 61 | byte[] sk = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES]; 62 | byte[] m = new byte[r.nextInt(1000)]; 63 | 64 | sodium().crypto_box_curve25519xsalsa20poly1305_keypair(pk, sk); 65 | r.nextBytes(m); 66 | 67 | SealedBox sb = new SealedBox(pk); 68 | byte[] c = sb.encrypt(m); 69 | SealedBox sb2 = new SealedBox(sk, pk); 70 | sb2.decrypt(c); 71 | } 72 | 73 | @Test(expected = RuntimeException.class) 74 | public void testDecryptFailsWithNull() throws Exception { 75 | SecureRandom r = new SecureRandom(); 76 | byte[] pk = null; 77 | byte[] sk = null; 78 | byte[] m = new byte[r.nextInt(1000)]; 79 | 80 | SealedBox sb = new SealedBox(pk); 81 | byte[] c = sb.encrypt(m); 82 | SealedBox sb2 = new SealedBox(sk, pk); 83 | sb2.decrypt(c); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/keys/SigningKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import jnr.ffi.byref.LongLongByReference; 20 | import org.abstractj.kalium.crypto.Random; 21 | import org.abstractj.kalium.crypto.Util; 22 | import org.abstractj.kalium.encoders.Encoder; 23 | 24 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 25 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES; 26 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_SIGN_ED25519_BYTES; 27 | import static org.abstractj.kalium.NaCl.sodium; 28 | import static org.abstractj.kalium.crypto.Util.checkLength; 29 | import static org.abstractj.kalium.crypto.Util.isValid; 30 | import static org.abstractj.kalium.crypto.Util.slice; 31 | import static org.abstractj.kalium.crypto.Util.zeros; 32 | import static org.abstractj.kalium.encoders.Encoder.HEX; 33 | 34 | public class SigningKey { 35 | 36 | private final byte[] seed; 37 | private final byte[] secretKey; 38 | private final VerifyKey verifyKey; 39 | 40 | public SigningKey(byte[] seed) { 41 | checkLength(seed, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); 42 | this.seed = seed; 43 | this.secretKey = zeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES * 2); 44 | byte[] publicKey = zeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); 45 | isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed), 46 | "Failed to generate a key pair"); 47 | 48 | this.verifyKey = new VerifyKey(publicKey); 49 | } 50 | 51 | public SigningKey() { 52 | this(new Random().randomBytes(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES)); 53 | } 54 | 55 | public SigningKey(String seed, Encoder encoder) { 56 | this(encoder.decode(seed)); 57 | } 58 | 59 | public VerifyKey getVerifyKey() { 60 | return this.verifyKey; 61 | } 62 | 63 | public byte[] sign(byte[] message) { 64 | byte[] signature = new byte[CRYPTO_SIGN_ED25519_BYTES]; 65 | LongLongByReference bufferLen = new LongLongByReference(0); 66 | sodium().crypto_sign_ed25519_detached(signature, bufferLen, message, message.length, secretKey); 67 | return signature; 68 | } 69 | 70 | public String sign(String message, Encoder encoder) { 71 | byte[] signature = sign(encoder.decode(message)); 72 | return encoder.encode(signature); 73 | } 74 | 75 | public byte[] toBytes() { 76 | return seed; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return HEX.encode(seed); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/SecretBoxTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.abstractj.kalium.encoders.Encoder.HEX; 24 | import static org.abstractj.kalium.fixture.TestVectors.BOX_CIPHERTEXT; 25 | import static org.abstractj.kalium.fixture.TestVectors.BOX_MESSAGE; 26 | import static org.abstractj.kalium.fixture.TestVectors.BOX_NONCE; 27 | import static org.abstractj.kalium.fixture.TestVectors.SECRET_KEY; 28 | import static org.junit.Assert.assertTrue; 29 | import static org.junit.Assert.fail; 30 | 31 | public class SecretBoxTest { 32 | 33 | @Test 34 | public void testAcceptStrings() throws Exception { 35 | try { 36 | new SecretBox(SECRET_KEY, HEX); 37 | } catch (Exception e) { 38 | fail("SecretBox should accept strings"); 39 | } 40 | } 41 | 42 | @Test(expected = RuntimeException.class) 43 | public void testNullKey() throws Exception { 44 | byte[] key = null; 45 | new SecretBox(key); 46 | fail("Should raise an exception"); 47 | } 48 | 49 | @Test(expected = RuntimeException.class) 50 | public void testShortKey() throws Exception { 51 | String key = "hello"; 52 | new SecretBox(key.getBytes()); 53 | fail("Should raise an exception"); 54 | } 55 | 56 | @Test 57 | public void testEncrypt() throws Exception { 58 | SecretBox box = new SecretBox(SECRET_KEY, HEX); 59 | 60 | byte[] nonce = HEX.decode(BOX_NONCE); 61 | byte[] message = HEX.decode(BOX_MESSAGE); 62 | byte[] ciphertext = HEX.decode(BOX_CIPHERTEXT); 63 | 64 | byte[] result = box.encrypt(nonce, message); 65 | assertTrue("failed to generate ciphertext", Arrays.equals(result, ciphertext)); 66 | } 67 | 68 | @Test 69 | public void testDecrypt() throws Exception { 70 | 71 | SecretBox box = new SecretBox(SECRET_KEY, HEX); 72 | 73 | byte[] nonce = HEX.decode(BOX_NONCE); 74 | byte[] expectedMessage = HEX.decode(BOX_MESSAGE); 75 | byte[] ciphertext = box.encrypt(nonce, expectedMessage); 76 | 77 | byte[] message = box.decrypt(nonce, ciphertext); 78 | 79 | assertTrue("failed to decrypt ciphertext", Arrays.equals(message, expectedMessage)); 80 | } 81 | 82 | @Test(expected = RuntimeException.class) 83 | public void testDecryptCorruptedCipherText() throws Exception { 84 | SecretBox box = new SecretBox(SECRET_KEY, HEX); 85 | byte[] nonce = HEX.decode(BOX_NONCE); 86 | byte[] message = HEX.decode(BOX_MESSAGE); 87 | byte[] ciphertext = box.encrypt(nonce, message); 88 | ciphertext[23] = ' '; 89 | 90 | box.decrypt(nonce, ciphertext); 91 | fail("Should raise an exception"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/keys/VerifyKeyTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.junit.Assert.assertTrue; 24 | import static org.junit.Assert.assertEquals; 25 | import static org.abstractj.kalium.encoders.Encoder.HEX; 26 | import static org.abstractj.kalium.fixture.TestVectors.SIGN_MESSAGE; 27 | import static org.abstractj.kalium.fixture.TestVectors.SIGN_PRIVATE; 28 | import static org.abstractj.kalium.fixture.TestVectors.SIGN_PUBLIC; 29 | import static org.abstractj.kalium.fixture.TestVectors.SIGN_SIGNATURE; 30 | import static org.junit.Assert.fail; 31 | 32 | public class VerifyKeyTest { 33 | 34 | @Test 35 | public void testVerifyCorrectRawSignature() throws Exception { 36 | byte[] rawSignature = HEX.decode(SIGN_SIGNATURE); 37 | byte[] rawMessage = HEX.decode(SIGN_MESSAGE); 38 | byte[] rawPublicKey = HEX.decode(SIGN_PUBLIC); 39 | VerifyKey verifyKey = new VerifyKey(rawPublicKey); 40 | assertTrue(verifyKey.verify(rawMessage, rawSignature)); 41 | } 42 | 43 | @Test 44 | public void testVerifyCorrectHexSignature() throws Exception { 45 | byte[] rawPublicKey = HEX.decode(SIGN_PUBLIC); 46 | VerifyKey verifyKey = new VerifyKey(rawPublicKey); 47 | verifyKey.verify(SIGN_MESSAGE, SIGN_SIGNATURE, HEX); 48 | assertTrue(verifyKey.verify(SIGN_MESSAGE, SIGN_SIGNATURE, HEX)); 49 | } 50 | 51 | @Test 52 | public void testDetectBadSignature() throws Exception { 53 | try { 54 | String badSignature = SIGN_SIGNATURE.concat("0000"); 55 | VerifyKey verifyKey = new VerifyKey(SIGN_PUBLIC, HEX); 56 | verifyKey.verify(SIGN_MESSAGE, badSignature, HEX); 57 | fail("Should an exception on bad signatures"); 58 | } catch (Exception e) { 59 | assertTrue(true); 60 | } 61 | } 62 | 63 | @Test 64 | public void testSerializeToBytes() throws Exception { 65 | byte[] rawPublic = HEX.decode(SIGN_PUBLIC); 66 | VerifyKey verifyKey = new VerifyKey(SIGN_PUBLIC, HEX); 67 | verifyKey.verify(SIGN_MESSAGE, SIGN_SIGNATURE, HEX); 68 | assertTrue(Arrays.equals(verifyKey.toBytes(), rawPublic)); 69 | } 70 | 71 | @Test 72 | public void testSerializeToString() throws Exception { 73 | SigningKey key = new SigningKey(SIGN_PRIVATE, HEX); 74 | VerifyKey verifyKey = new VerifyKey(SIGN_PUBLIC, HEX); 75 | verifyKey.verify(SIGN_MESSAGE, SIGN_SIGNATURE, HEX); 76 | assertEquals(verifyKey.toString(), SIGN_PUBLIC); 77 | } 78 | 79 | @Test 80 | public void testInitializeFromHex() throws Exception { 81 | VerifyKey verifyKey = new VerifyKey(SIGN_PUBLIC, HEX); 82 | assertTrue(verifyKey.verify(SIGN_MESSAGE, SIGN_SIGNATURE, HEX)); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/AdvancedTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | import static org.junit.Assert.assertNotEquals; 23 | 24 | public class AdvancedTest { 25 | 26 | @Test 27 | public void testXsalsa20HappyFlow() { 28 | Random random = new Random(); 29 | Advanced advanced = new Advanced(); 30 | byte[] nonce = random.randomBytes(24); 31 | byte[] key = random.randomBytes(32); 32 | String pwd = "This is a test message :-)..."; 33 | byte[] plaintext = pwd.getBytes(); 34 | byte[] ciphertext = advanced.crypto_stream_xsalsa20_xor(plaintext, nonce, key); // encrypt 35 | plaintext = advanced.crypto_stream_xsalsa20_xor(ciphertext, nonce, key); // decrypt 36 | assertEquals(pwd, new String(plaintext)); 37 | } 38 | 39 | @Test 40 | public void testXsalsa20IncorrectNonce() { 41 | Random random = new Random(); 42 | Advanced advanced = new Advanced(); 43 | byte[] nonce = random.randomBytes(24); 44 | byte[] incorrectNonce = random.randomBytes(24); 45 | byte[] key = random.randomBytes(32); 46 | String pwd = "This is a test message :-)..."; 47 | byte[] plaintext = pwd.getBytes(); 48 | byte[] ciphertext = advanced.crypto_stream_xsalsa20_xor(plaintext, nonce, key); // encrypt 49 | plaintext = advanced.crypto_stream_xsalsa20_xor(ciphertext, incorrectNonce, key); // decrypt 50 | assertNotEquals(pwd, new String(plaintext)); 51 | } 52 | 53 | @Test 54 | public void testXsalsa20IncorrectKey() { 55 | Random random = new Random(); 56 | Advanced advanced = new Advanced(); 57 | byte[] nonce = random.randomBytes(24); 58 | byte[] key = random.randomBytes(32); 59 | byte[] incorrectKey = random.randomBytes(32); 60 | String pwd = "This is a test message :-)..."; 61 | byte[] plaintext = pwd.getBytes(); 62 | byte[] ciphertext = advanced.crypto_stream_xsalsa20_xor(plaintext, nonce, key); // encrypt 63 | plaintext = advanced.crypto_stream_xsalsa20_xor(ciphertext, nonce, incorrectKey); // decrypt 64 | assertNotEquals(pwd, new String(plaintext)); 65 | } 66 | 67 | @Test 68 | public void testXsalsa20IncorrectKeyAndIncorrectNonce() { 69 | Random random = new Random(); 70 | Advanced advanced = new Advanced(); 71 | byte[] nonce = random.randomBytes(24); 72 | byte[] incorrectNonce = random.randomBytes(24); 73 | byte[] key = random.randomBytes(32); 74 | byte[] incorrectKey = random.randomBytes(32); 75 | String pwd = "This is a test message :-)..."; 76 | byte[] plaintext = pwd.getBytes(); 77 | byte[] ciphertext = advanced.crypto_stream_xsalsa20_xor(plaintext, nonce, key); // encrypt 78 | plaintext = advanced.crypto_stream_xsalsa20_xor(ciphertext, incorrectNonce, incorrectKey); // decrypt 79 | assertNotEquals(pwd, new String(plaintext)); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](http://www.apache.org/licenses/LICENSE-2.0) 2 | [](https://maven-badges.herokuapp.com/maven-central/org.abstractj.kalium/kalium) 3 | [](https://travis-ci.org/abstractj/kalium) 4 | [](https://ci.appveyor.com/project/abstractj/kalium/branch/master) 5 | [](https://saythanks.io/to/abstractj) 6 | 7 | # kalium - Java binding to the Networking and Cryptography (NaCl) library 8 | 9 | A Java binding to [Networking and Cryptography](http://nacl.cr.yp.to/) library by [Daniel J. Bernstein](http://cr.yp.to/djb.html). All the hard work of making a portable NaCl API version was done by [Frank Denis](https://github.com/jedisct1) on [libsodium](https://github.com/jedisct1/libsodium) and kalium was totally inspired by [Tony Arcieri's](https://github.com/tarcieri) work with [RbNaCl](https://github.com/cryptosphere/rbnacl). 10 | 11 | ## Requirements 12 | 13 | * JDK 6 or [higher](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 14 | * [Apache Maven](http://maven.apache.org/guides/getting-started/) 15 | 16 | ## Installation 17 | 18 | ### libsodium 19 | 20 | kalium is implemented using [jnr-ffi](https://github.com/jnr/jnr-ffi) to bind the shared libraries from [libsodium](https://github.com/jedisct1/libsodium). For a more detailed explanation, please refer to [RbNaCl's documentation](https://github.com/cryptosphere/rbnacl/blob/master/README.md). 21 | 22 | #### OSX 23 | OS X users can get libsodium via [homebrew](http://mxcl.github.com/homebrew/) with: 24 | 25 | brew install libsodium 26 | 27 | #### Windows 28 | Windows users will need to provide the pre-build binaries from `libsodium`. 29 | 30 | - Download `libsodium` from https://download.libsodium.org/libsodium/releases/ 31 | - Choose the version of `libsodium` you wish to use 32 | - The archives follow the following pattern: libsodium-{version}-msvc.zip 33 | - From the archive find the artifacts compiled for your architecture and then the MSVC tool set of your choice 34 | - For example: `v141 // these were compiled against the MSVC v141 (i.e. Visual Studio 2017)` 35 | - Extract from the archive the `dll` library files into **one** of the following locations: 36 | - into the `lib` at the root of the working directory directory of your project. 37 | - into a location that is included in your `PATH` environment variable. 38 | 39 | For example, on Windows 10 machine with a x64 architecture: 40 | ``` 41 | {archive root} 42 | └───x64 43 | ... 44 | └───Release 45 | ... 46 | └───v141 47 | ... 48 | └───dynamic <- copy the library files from this locaiton. 49 | ``` 50 | 51 | ### kalium installation 52 | 53 | Add as a [Maven dependency](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.abstractj.kalium%22%20AND%20a%3A%22kalium%22) to your project. 54 | 55 | ### FAQ 56 | 57 | #### Is Android supported? 58 | No. 59 | 60 | #### Would be nice to have some documentation. Do you have some? 61 | 62 | Look at the libsodium docs, they are self explanatory. Or, contribute with docs. 63 | 64 | #### I'm experiencing some issues on Windows. Do you have any idea? 65 | 66 | I'm sorry but I'm completely clueless about Windows environment, but if you have any suggestions or PR changes. They will be more than welcome. 67 | 68 | ### Notes 69 | 70 | Kalium is the effort of a **really** small group of people, feedback, bug reports and patches are always welcome. 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Aead.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.crypto; 2 | 3 | import org.abstractj.kalium.encoders.Encoder; 4 | 5 | import static org.abstractj.kalium.NaCl.Sodium.*; 6 | import static org.abstractj.kalium.NaCl.sodium; 7 | import static org.abstractj.kalium.crypto.Util.*; 8 | 9 | public class Aead { 10 | 11 | private byte[] key; 12 | 13 | private boolean aesGcm = false; 14 | 15 | public Aead(byte[] key) { 16 | this.key = key; 17 | // both CHACHAPOLY and AESGCM use 32 byte keys 18 | checkLength(key, CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES); 19 | sodium().sodium_init(); // needs to be called here for aes256gcm_is_available() to work 20 | 21 | } 22 | 23 | public Aead(String key, Encoder encoder) { 24 | this(encoder.decode(key)); 25 | } 26 | 27 | public Aead useAesGcm() { 28 | if (sodium().crypto_aead_aes256gcm_is_available() != 1) { 29 | throw new RuntimeException("AES-GCM requires hardware support"); 30 | } 31 | 32 | aesGcm = true; 33 | return this; 34 | } 35 | 36 | public byte[] encrypt(byte[] publicNonce, byte[] message, byte[] additionalData) { 37 | return aesGcm ? 38 | encryptAesGcm(publicNonce, message, additionalData) : 39 | encryptChaChaPoly(publicNonce, message, additionalData); 40 | } 41 | 42 | protected byte[] encryptChaChaPoly(byte[] publicNonce, byte[] message, byte[] additionalData) { 43 | checkLength(publicNonce, CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES); 44 | byte[] ct = zeros(message.length + CRYPTO_AEAD_CHACHA20POLY1305_ABYTES); 45 | isValid(sodium().crypto_aead_chacha20poly1305_encrypt(ct, null, 46 | message, message.length, additionalData, 47 | additionalData.length, null, publicNonce, key), 48 | "Encryption failed"); 49 | 50 | return ct; 51 | } 52 | 53 | protected byte[] encryptAesGcm(byte[] publicNonce, byte[] message, byte[] additionalData) { 54 | checkLength(publicNonce, CRYPTO_AEAD_AES256GCM_NPUBBYTES); 55 | byte[] ct = zeros(message.length + CRYPTO_AEAD_AES256GCM_ABYTES); 56 | isValid(sodium().crypto_aead_aes256gcm_encrypt(ct, null, 57 | message, message.length, additionalData, 58 | additionalData.length, null, publicNonce, key), 59 | "Encryption failed"); 60 | 61 | return ct; 62 | } 63 | 64 | public byte[] decrypt(byte[] publicNonce, byte[] ciphertext, byte[] additionalData) { 65 | return aesGcm ? 66 | decryptAesGcm(publicNonce, ciphertext, additionalData) : 67 | decryptChaChaPoly(publicNonce, ciphertext, additionalData); 68 | } 69 | 70 | protected byte[] decryptChaChaPoly(byte[] publicNonce, byte[] ciphertext, byte[] additionalData) { 71 | checkLength(publicNonce, CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES); 72 | byte[] msg = zeros(ciphertext.length - CRYPTO_AEAD_CHACHA20POLY1305_ABYTES); 73 | isValid(sodium().crypto_aead_chacha20poly1305_decrypt(msg, null, 74 | null, ciphertext, ciphertext.length, additionalData, 75 | additionalData.length, publicNonce, key), 76 | "Decryption failed. Ciphertext failed verification"); 77 | 78 | return msg; 79 | } 80 | 81 | protected byte[] decryptAesGcm(byte[] publicNonce, byte[] ciphertext, byte[] additionalData) { 82 | checkLength(publicNonce, CRYPTO_AEAD_AES256GCM_NPUBBYTES); 83 | byte[] msg = zeros(ciphertext.length - CRYPTO_AEAD_AES256GCM_ABYTES); 84 | isValid(sodium().crypto_aead_aes256gcm_decrypt(msg, null, 85 | null, ciphertext, ciphertext.length, additionalData, 86 | additionalData.length, publicNonce, key), 87 | "Decryption failed. Ciphertext failed verification"); 88 | 89 | return msg; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/crypto/PasswordTest.java: -------------------------------------------------------------------------------- 1 | package org.abstractj.kalium.crypto; 2 | 3 | import org.abstractj.kalium.NaCl; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertTrue; 7 | import static org.abstractj.kalium.encoders.Encoder.HEX; 8 | import static org.abstractj.kalium.fixture.TestVectors.*; 9 | import static org.abstractj.kalium.fixture.TestVectors.PWHASH_MESSAGE; 10 | import static org.abstractj.kalium.fixture.TestVectors.PWHASH_SALT; 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.fail; 13 | 14 | /** 15 | * Created by abstractj on 8/5/15. 16 | */ 17 | public class PasswordTest { 18 | 19 | private final Password password = new Password(); 20 | 21 | @Test 22 | public void testPWHash(){ 23 | String result = password.hash(PWHASH_MESSAGE.getBytes(), 24 | HEX, 25 | PWHASH_SALT.getBytes(), 26 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE, 27 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE); 28 | assertEquals("Hash is invalid", PWHASH_DIGEST, result); 29 | } 30 | 31 | @Test 32 | public void testPWHashEmptyString(){ 33 | String result = password.hash("".getBytes(), 34 | HEX, 35 | PWHASH_SALT.getBytes(), 36 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE, 37 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE); 38 | assertEquals("Hash is invalid", PWHASH_DIGEST_EMPTY_STRING, result); 39 | } 40 | 41 | @Test 42 | public void testPWHashNullByte() { 43 | try { 44 | password.hash("\0".getBytes(), 45 | HEX, 46 | PWHASH_SALT.getBytes(), 47 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE, 48 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE); 49 | } catch (Exception e) { 50 | fail("Should not raise any exception on null byte"); 51 | } 52 | } 53 | 54 | @Test 55 | public void testPWHashStorage(){ 56 | String result = password.hash(PWHASH_MESSAGE.getBytes(), 57 | HEX, 58 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE, 59 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE); 60 | byte[] hashed = HEX.decode(result); 61 | 62 | // Must return true 63 | boolean verified1 = password.verify(hashed, PWHASH_MESSAGE.getBytes()); 64 | assertTrue("Invalid password", verified1); 65 | 66 | // Must return false since it's an invalid 67 | boolean verified2 = password.verify(hashed, ("i" + PWHASH_MESSAGE).getBytes()); 68 | assertTrue("Valid password", !verified2); 69 | } 70 | 71 | @Test 72 | public void testPWHashKeyDerivation() { 73 | String result = password.hash(NaCl.Sodium.CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES, 74 | PWHASH_MESSAGE.getBytes(), 75 | HEX, 76 | PWHASH_SALT.getBytes(), 77 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE, 78 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE); 79 | byte[] hashed = HEX.decode(result); 80 | 81 | // Must receive expected size 82 | assertEquals(NaCl.Sodium.CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES, hashed.length); 83 | } 84 | 85 | @Test 86 | public void testPWHashKeyDerivationBytes() { 87 | byte[] key = password.deriveKey(NaCl.Sodium.CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES, 88 | PWHASH_MESSAGE.getBytes(), 89 | PWHASH_SALT.getBytes(), 90 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE, 91 | NaCl.Sodium.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE); 92 | 93 | // Must receive expected size 94 | assertEquals(NaCl.Sodium.CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES, key.length); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/org/abstractj/kalium/crypto/Box.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.crypto; 18 | 19 | import org.abstractj.kalium.NaCl; 20 | import org.abstractj.kalium.encoders.Encoder; 21 | import org.abstractj.kalium.keys.PrivateKey; 22 | import org.abstractj.kalium.keys.PublicKey; 23 | 24 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES; 25 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES; 26 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 27 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES; 28 | import static org.abstractj.kalium.NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES; 29 | import static org.abstractj.kalium.NaCl.sodium; 30 | import static org.abstractj.kalium.crypto.Util.checkLength; 31 | import static org.abstractj.kalium.crypto.Util.isValid; 32 | import static org.abstractj.kalium.crypto.Util.prependZeros; 33 | import static org.abstractj.kalium.crypto.Util.removeZeros; 34 | 35 | /** 36 | * Based on Curve25519XSalsa20Poly1305 and Box classes from rbnacl 37 | */ 38 | public class Box { 39 | 40 | private final byte[] sharedKey; 41 | 42 | public Box(byte[] publicKey, byte[] privateKey) { 43 | checkLength(publicKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); 44 | checkLength(privateKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); 45 | 46 | sharedKey = new byte[NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BEFORENMBYTES]; 47 | isValid(sodium().crypto_box_curve25519xsalsa20poly1305_beforenm( 48 | sharedKey, publicKey, privateKey), "Key agreement failed"); 49 | } 50 | 51 | public Box(PublicKey publicKey, PrivateKey privateKey) { 52 | this(publicKey.toBytes(), privateKey.toBytes()); 53 | } 54 | 55 | public Box(String publicKey, String privateKey, Encoder encoder) { 56 | this(encoder.decode(publicKey), encoder.decode(privateKey)); 57 | } 58 | 59 | public byte[] encrypt(byte[] nonce, byte[] message) { 60 | checkLength(nonce, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES); 61 | byte[] msg = prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message); 62 | byte[] ct = new byte[msg.length]; 63 | isValid(sodium().crypto_box_curve25519xsalsa20poly1305_afternm(ct, msg, 64 | msg.length, nonce, sharedKey), "Encryption failed"); 65 | return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ct); 66 | } 67 | 68 | public byte[] encrypt(String nonce, String message, Encoder encoder) { 69 | return encrypt(encoder.decode(nonce), encoder.decode(message)); 70 | } 71 | 72 | public byte[] decrypt(byte[] nonce, byte[] ciphertext) { 73 | checkLength(nonce, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES); 74 | byte[] ct = prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ciphertext); 75 | byte[] message = new byte[ct.length]; 76 | isValid(sodium().crypto_box_curve25519xsalsa20poly1305_open_afternm( 77 | message, ct, message.length, nonce, sharedKey), 78 | "Decryption failed. Ciphertext failed verification."); 79 | return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message); 80 | } 81 | 82 | public byte[] decrypt(String nonce, String ciphertext, Encoder encoder) { 83 | return decrypt(encoder.decode(nonce), encoder.decode(ciphertext)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/keys/SigningKeyTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.abstractj.kalium.encoders.Encoder.HEX; 24 | import static org.abstractj.kalium.fixture.TestVectors.*; 25 | import static org.junit.Assert.assertEquals; 26 | import static org.junit.Assert.assertTrue; 27 | import static org.junit.Assert.fail; 28 | 29 | public class SigningKeyTest { 30 | 31 | @Test 32 | public void testGenerateSigninKey() throws Exception { 33 | try { 34 | new SigningKey(); 35 | } catch (Exception e) { 36 | fail("Should return a valid key size"); 37 | } 38 | } 39 | 40 | @Test 41 | public void testAcceptsRawValidKey() throws Exception { 42 | try { 43 | byte[] rawKey = HEX.decode(SIGN_PRIVATE); 44 | new SigningKey(rawKey); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | fail("Should return a valid key size"); 48 | } 49 | } 50 | 51 | @Test 52 | public void testAcceptsHexValidKey() throws Exception { 53 | try { 54 | new SigningKey(SIGN_PRIVATE, HEX); 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | fail("Should return a valid key size"); 58 | } 59 | } 60 | 61 | @Test 62 | public void testCreateHexValidKey() throws Exception { 63 | try { 64 | new SigningKey(SIGN_PRIVATE, HEX).toString(); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | fail("Should return a valid key size"); 68 | } 69 | } 70 | 71 | @Test 72 | public void testCreateByteValidKey() throws Exception { 73 | try { 74 | new SigningKey(SIGN_PRIVATE, HEX).toBytes(); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | fail("Should return a valid key size"); 78 | } 79 | } 80 | 81 | @Test(expected = RuntimeException.class) 82 | public void testRejectNullKey() throws Exception { 83 | byte[] key = null; 84 | new SigningKey(key); 85 | fail("Should reject null keys"); 86 | } 87 | 88 | @Test(expected = RuntimeException.class) 89 | public void testRejectShortKey() throws Exception { 90 | byte[] key = "short".getBytes(); 91 | new SigningKey(key); 92 | fail("Should reject short keys"); 93 | } 94 | 95 | @Test 96 | public void testSignMessageAsBytes() throws Exception { 97 | byte[] rawKey = HEX.decode(SIGN_PRIVATE); 98 | byte[] signatureRaw = HEX.decode(SIGN_SIGNATURE); 99 | SigningKey key = new SigningKey(rawKey); 100 | byte[] signedMessage = key.sign(HEX.decode(SIGN_MESSAGE)); 101 | assertTrue("Message sign has failed", Arrays.equals(signatureRaw, signedMessage)); 102 | } 103 | 104 | @Test 105 | public void testSignMessageAsHex() throws Exception { 106 | SigningKey key = new SigningKey(SIGN_PRIVATE, HEX); 107 | String signature = key.sign(SIGN_MESSAGE, HEX); 108 | assertEquals("Message sign has failed", SIGN_SIGNATURE, signature); 109 | } 110 | 111 | @Test 112 | public void testSerializesToHex() throws Exception { 113 | try { 114 | SigningKey key = new SigningKey(SIGN_PRIVATE, HEX); 115 | assertEquals("Correct sign key expected", SIGN_PRIVATE, key.toString()); 116 | } catch (Exception e) { 117 | fail("Should return a valid key size"); 118 | } 119 | } 120 | 121 | @Test 122 | public void testSerializesToBytes() throws Exception { 123 | try { 124 | byte[] rawKey = HEX.decode(SIGN_PRIVATE); 125 | SigningKey key = new SigningKey(SIGN_PRIVATE, HEX); 126 | assertTrue("Correct sign key expected", Arrays.equals(rawKey, 127 | key.toBytes())); 128 | } catch (Exception e) { 129 | fail("Should return a valid key size"); 130 | } 131 | } 132 | 133 | @Test 134 | public void testAccessVerifyKey() { 135 | SigningKey key = new SigningKey(SIGN_PRIVATE, HEX); 136 | VerifyKey v = key.getVerifyKey(); 137 | assertEquals(v.toString(), SIGN_PUBLIC); 138 | } 139 | 140 | @Test 141 | public void testRoundTrip() { 142 | SigningKey key = new SigningKey(SIGN_PRIVATE, HEX); 143 | String signature = key.sign(SIGN_MESSAGE, HEX); 144 | key.getVerifyKey().verify(SIGN_MESSAGE, signature, HEX); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/keys/KeyPairTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.keys; 18 | 19 | import org.junit.Ignore; 20 | import org.junit.Test; 21 | 22 | import java.util.Arrays; 23 | 24 | import static org.abstractj.kalium.encoders.Encoder.HEX; 25 | import static org.abstractj.kalium.fixture.TestVectors.BOB_PRIVATE_KEY; 26 | import static org.abstractj.kalium.fixture.TestVectors.BOB_PUBLIC_KEY; 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertTrue; 29 | import static org.junit.Assert.fail; 30 | 31 | public class KeyPairTest { 32 | 33 | @Test 34 | public void testGenerateKeyPair() { 35 | try { 36 | KeyPair key = new KeyPair(); 37 | assertTrue(key.getPrivateKey() != null); 38 | assertTrue(key.getPublicKey() != null); 39 | } catch (Exception e) { 40 | fail("Should return a valid key size"); 41 | } 42 | } 43 | 44 | @Test 45 | public void testAcceptsValidKey() { 46 | try { 47 | byte[] rawKey = HEX.decode(BOB_PRIVATE_KEY); 48 | new KeyPair(rawKey); 49 | } catch (Exception e) { 50 | fail("Should not raise any exception"); 51 | } 52 | } 53 | 54 | @Test 55 | public void testAcceptsHexEncodedKey() { 56 | try { 57 | new KeyPair(BOB_PRIVATE_KEY, HEX); 58 | } catch (Exception e) { 59 | fail("Should not raise any exception"); 60 | } 61 | } 62 | 63 | @Test(expected = RuntimeException.class) 64 | public void testRejectNullKey() throws Exception { 65 | byte[] privateKey = null; 66 | new KeyPair(privateKey); 67 | fail("Should reject null keys"); 68 | } 69 | 70 | @Test(expected = RuntimeException.class) 71 | public void testRejectShortKey() throws Exception { 72 | byte[] privateKey = "short".getBytes(); 73 | new KeyPair(privateKey); 74 | fail("Should reject null keys"); 75 | } 76 | 77 | @Test 78 | public void testGeneratePublicKey() throws Exception { 79 | try { 80 | byte[] pk = HEX.decode(BOB_PRIVATE_KEY); 81 | KeyPair key = new KeyPair(pk); 82 | assertTrue(key.getPublicKey() != null); 83 | } catch (Exception e) { 84 | fail("Should return a valid key size"); 85 | } 86 | } 87 | 88 | @Test 89 | public void testPrivateKeyToString() throws Exception { 90 | try { 91 | KeyPair key = new KeyPair(BOB_PRIVATE_KEY, HEX); 92 | assertEquals("Correct private key expected", BOB_PRIVATE_KEY, key.getPrivateKey().toString()); 93 | } catch (Exception e) { 94 | fail("Should return a valid key size"); 95 | } 96 | } 97 | 98 | @Test 99 | public void testPrivateKeyToBytes() throws Exception { 100 | try { 101 | KeyPair key = new KeyPair(BOB_PRIVATE_KEY, HEX); 102 | assertTrue("Correct private key expected", Arrays.equals(HEX.decode(BOB_PUBLIC_KEY), 103 | key.getPublicKey().toBytes())); 104 | } catch (Exception e) { 105 | fail("Should return a valid key size"); 106 | } 107 | } 108 | 109 | @Test 110 | public void testPublicKeyToString() throws Exception { 111 | try { 112 | KeyPair key = new KeyPair(BOB_PRIVATE_KEY, HEX); 113 | assertEquals("Correct public key expected", BOB_PUBLIC_KEY, key.getPublicKey().toString()); 114 | } catch (Exception e) { 115 | fail("Should return a valid key size"); 116 | } 117 | } 118 | 119 | 120 | /** 121 | * TODO: This unit test is a friendly reminder to be investigated 122 | * @see 23 | * This class is thread-safe. 24 | * 25 | * @version $Id$ 26 | * @since 1.1 27 | */ 28 | public class Hex implements Encoder { 29 | 30 | /** 31 | * Used to build output as Hex 32 | */ 33 | private static final char[] DIGITS_LOWER = 34 | {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 35 | 36 | /** 37 | * Used to build output as Hex 38 | */ 39 | private static final char[] DIGITS_UPPER = 40 | {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 41 | 42 | /** 43 | * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 44 | * returned array will be half the length of the passed array, as it takes two characters to represent any given 45 | * byte. An exception is thrown if the passed char array has an odd number of elements. 46 | * 47 | * @param data An array of characters containing hexadecimal digits 48 | * @return A byte array containing binary data decoded from the supplied char array. 49 | */ 50 | private static byte[] decodeHex(final char[] data) { 51 | 52 | final int len = (data == null) ? 0 : data.length; 53 | 54 | if ((len & 0x01) != 0) { 55 | throw new RuntimeException("Odd number of characters."); 56 | } 57 | 58 | final byte[] out = new byte[len >> 1]; 59 | 60 | // two characters form the hex value. 61 | for (int i = 0, j = 0; j < len; i++) { 62 | int f = toDigit(data[j], j) << 4; 63 | j++; 64 | f = f | toDigit(data[j], j); 65 | j++; 66 | out[i] = (byte) (f & 0xFF); 67 | } 68 | 69 | return out; 70 | } 71 | 72 | @Override 73 | public byte[] decode(final String value) { 74 | 75 | char[] data = value != null ? value.toCharArray() : new char[0]; 76 | return decodeHex(data); 77 | } 78 | 79 | /** 80 | * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 81 | * The returned array will be double the length of the passed array, as it takes two characters to represent any 82 | * given byte. 83 | * 84 | * @param data a byte[] to convert to Hex characters 85 | * @return A char[] containing hexadecimal characters 86 | */ 87 | private static char[] encodeHex(final byte[] data) { 88 | return encodeHex(data, true); 89 | } 90 | 91 | /** 92 | * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 93 | * The returned array will be double the length of the passed array, as it takes two characters to represent any 94 | * given byte. 95 | * 96 | * @param data a byte[] to convert to Hex characters 97 | * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 98 | * @return A char[] containing hexadecimal characters 99 | * @since 1.4 100 | */ 101 | private static char[] encodeHex(final byte[] data, final boolean toLowerCase) { 102 | return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 103 | } 104 | 105 | /** 106 | * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 107 | * The returned array will be double the length of the passed array, as it takes two characters to represent any 108 | * given byte. 109 | * 110 | * @param data a byte[] to convert to Hex characters 111 | * @param toDigits the output alphabet 112 | * @return A char[] containing hexadecimal characters 113 | * @since 1.4 114 | */ 115 | private static char[] encodeHex(final byte[] data, final char[] toDigits) { 116 | final int l = data.length; 117 | final char[] out = new char[l << 1]; 118 | // two characters form the hex value. 119 | for (int i = 0, j = 0; i < l; i++) { 120 | out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 121 | out[j++] = toDigits[0x0F & data[i]]; 122 | } 123 | return out; 124 | } 125 | 126 | /** 127 | * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 128 | * String will be double the length of the passed array, as it takes two characters to represent any given byte. 129 | * 130 | * @param data a byte[] to convert to Hex characters 131 | * @return A String containing hexadecimal characters 132 | * @since 1.4 133 | */ 134 | @Override 135 | public String encode(final byte[] data) { 136 | return new String(encodeHex(data)); 137 | } 138 | 139 | /** 140 | * Converts a hexadecimal character to an integer. 141 | * 142 | * @param ch A character to convert to an integer digit 143 | * @param index The index of the character in the source 144 | * @return An integer 145 | */ 146 | private static int toDigit(final char ch, final int index) { 147 | final int digit = Character.digit(ch, 16); 148 | if (digit == -1) { 149 | throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); 150 | } 151 | return digit; 152 | } 153 | 154 | /** 155 | * Returns a string representation of the object, which includes the charset name. 156 | * 157 | * @return a string representation of the object. 158 | */ 159 | @Override 160 | public String toString() { 161 | return super.toString() + "[charsetName=" + CHARSET + "]"; 162 | } 163 | } -------------------------------------------------------------------------------- /src/test/java/org/abstractj/kalium/fixture/TestVectors.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Bruno Oliveira, and individual contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.abstractj.kalium.fixture; 18 | 19 | public class TestVectors { 20 | 21 | /** 22 | * HMAC SHA512256 test vectors 23 | */ 24 | 25 | public static final String AUTH_KEY = "eea6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff880"; 26 | public static final String AUTH_MESSAGE = "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a" + 27 | "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738" + 28 | "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da" + 29 | "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74" + 30 | "e355a5"; 31 | public static final String AUTH_HMAC_SHA512256 = "b2a31b8d4e01afcab2ee545b5caf4e3d212a99d7b3a116a97cec8e83c32e107d"; 32 | 33 | /** 34 | * SHA256 test vectors 35 | */ 36 | 37 | public static final String SHA256_MESSAGE = "My Bonnie lies over the ocean, my Bonnie lies over the sea"; 38 | public static final String SHA256_DIGEST = "d281d10296b7bde20df3f3f4a6d1bdb513f4aa4ccb0048c7b2f7f5786b4bcb77"; 39 | public static final String SHA256_DIGEST_EMPTY_STRING = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; 40 | 41 | /** 42 | * SHA512 test vectors 43 | */ 44 | 45 | public static final String SHA512_MESSAGE = "My Bonnie lies over the ocean, Oh bring back my Bonnie to me"; 46 | public static final String SHA512_DIGEST = "2823e0373001b5f3aa6db57d07bc588324917fc221dd27975613942d7f2e19bf4" + 47 | "44654c8b9f4f9cb908ef15f2304522e60e9ced3fdec66e34bc2afb52be6ad1c"; 48 | public static final String SHA512_DIGEST_EMPTY_STRING = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921" + 49 | "d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; 50 | 51 | /** 52 | * Blake2 test vectors 53 | */ 54 | 55 | public static final String Blake2_MESSAGE = "The quick brown fox jumps over the lazy dog"; 56 | public static final String Blake2_DIGEST = "01718cec35cd3d796dd00020e0bfecb473ad23457d063b75eff29c0ffa2e58a9"; 57 | public static final String Blake2_DIGEST_EMPTY_STRING = "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"; 58 | public static final String Blake2_KEY = "This is a super secret key. Ssshh!"; 59 | public static final String Blake2_SALT = "0123456789abcdef"; 60 | public static final String Blake2_PERSONAL = "fedcba9876543210"; 61 | public static final String Blake2_DIGEST_WITH_SALT_PERSONAL = "108e81d0c7b0487de45c54554ea35b427f886b098d792497c6a803bbac7a5f7c"; 62 | 63 | /** 64 | * SipHash-2-4 test vectors 65 | */ 66 | 67 | public static final String SIPHASH24_KEY = "000102030405060708090a0b0c0d0e0f"; 68 | public static final String SIPHASH24_MESSAGE = "000102030405060708090a0b0c0d0e"; 69 | public static final String SIPHASH24_DIGEST = "e545be4961ca29a1"; 70 | public static final String SIPHASH24_DIGEST_EMPTY_STRING = "310e0edd47db6f72"; 71 | 72 | /** 73 | * pwhash test vectors 74 | * */ 75 | 76 | public static final String PWHASH_MESSAGE = "Correct Horse Battery Staple"; 77 | public static final String PWHASH_SALT = "[<~A 32-bytes salt for scrypt~>]"; 78 | public static final String PWHASH_DIGEST = "a2ec8a8ee744e0ff2c26d4fc198ddf7c0cd1460b5b6729e0d8518b6577c69acd412491f0913737e64c5c9136c04731545e756e0a9be35f55337e446c6bbc3a3f"; 79 | public static final String PWHASH_DIGEST_EMPTY_STRING = "f8b1543b940c7898ce90261d07f5193cb0570081e47b01610c043b8091666e12585ab9844edb189624c2ba662ca7478cfbed9f38fe1066b7ce583a3321470424"; 80 | 81 | 82 | /** 83 | * Curve25519 test vectors 84 | */ 85 | 86 | public static final String BOB_PRIVATE_KEY = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"; 87 | public static final String BOB_PUBLIC_KEY = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"; 88 | 89 | public static final String ALICE_PRIVATE_KEY = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"; 90 | public static final String ALICE_PUBLIC_KEY = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"; 91 | public static final String ALICE_MULT_BOB = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"; 92 | 93 | public static final String BOX_NONCE = "69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37"; 94 | public static final String BOX_MESSAGE = "be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffc" + 95 | "e5ecbaaf33bd751a1ac728d45e6c61296cdc3c01233561f41db66cce314adb31" + 96 | "0e3be8250c46f06dceea3a7fa1348057e2f6556ad6b1318a024a838f21af1fde" + 97 | "048977eb48f59ffd4924ca1c60902e52f0a089bc76897040e082f93776384864" + 98 | "5e0705"; 99 | public static final String BOX_CIPHERTEXT = "f3ffc7703f9400e52a7dfb4b3d3305d98e993b9f48681273c29650ba32fc76ce" + 100 | "48332ea7164d96a4476fb8c531a1186ac0dfc17c98dce87b4da7f011ec48c972" + 101 | "71d2c20f9b928fe2270d6fb863d51738b48eeee314a7cc8ab932164548e526ae" + 102 | "90224368517acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b3" + 103 | "7973f622a43d14a6599b1f654cb45a74e355a5"; 104 | 105 | public static final String SECRET_KEY = "1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389"; 106 | 107 | public static final String SIGN_PRIVATE = "b18e1d0045995ec3d010c387ccfeb984d783af8fbb0f40fa7db126d889f6dadd"; 108 | public static final String SIGN_MESSAGE = "916c7d1d268fc0e77c1bef238432573c39be577bbea0998936add2b50a653171" + 109 | "ce18a542b0b7f96c1691a3be6031522894a8634183eda38798a0c5d5d79fbd01" + 110 | "dd04a8646d71873b77b221998a81922d8105f892316369d5224c9983372d2313" + 111 | "c6b1f4556ea26ba49d46e8b561e0fc76633ac9766e68e21fba7edca93c4c7460" + 112 | "376d7f3ac22ff372c18f613f2ae2e856af40"; 113 | public static final String SIGN_SIGNATURE = "6bd710a368c1249923fc7a1610747403040f0cc30815a00f9ff548a896bbda0b" + 114 | "4eb2ca19ebcf917f0f34200a9edbad3901b64ab09cc5ef7b9bcc3c40c0ff7509"; 115 | public static final String SIGN_PUBLIC = "77f48b59caeda77751ed138b0ec667ff50f8768c25d48309a8f386a2bad187fb"; 116 | 117 | 118 | 119 | /** 120 | * AEAD test vectors 121 | */ 122 | 123 | public static final String AEAD_KEY = "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007"; 124 | public static final String AEAD_MESSAGE = "86d09974840bded2a5ca"; 125 | public static final String AEAD_NONCE = "cd7cf67be39c794a"; 126 | public static final String AEAD_AD = "87e229d4500845a079c0"; 127 | public static final String AEAD_CT = "e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6"; 128 | } 129 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 |