@Override public byte[] sealAfterPrecomputation(final byte[] message, final Nonce nonce, final SharedKey sharedKey) { /* * The Kalium library uses the C API * which expects the first CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES bytes to be zero */ final byte[] paddedMessage = pad(message, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES); final byte[] output = new byte[paddedMessage.length]; LOGGER.info("Sealing message using public key {}", sharedKey); LOGGER.debug( "Sealing message {} using nonce {} and shared key {}", Arrays.toString(message), nonce, sharedKey ); final int sodiumResult = this.sodium.crypto_box_curve25519xsalsa20poly1305_afternm( output, paddedMessage, paddedMessage.length, nonce.getNonceBytes(), sharedKey.getKeyBytes() ); if (sodiumResult == -1) { LOGGER.warn("Could not create sealed payload using shared key {}", sharedKey); LOGGER.debug("Could not create sealed payload using shared key {}", sharedKey); throw new NaclException("Kalium could not seal the payload using the shared key"); } LOGGER.info("Created sealed payload for shared key {}", sharedKey); LOGGER.debug( "Created sealed payload {} using nonce {} and shared key {}", Arrays.toString(output), nonce, sharedKey ); /* * NaCL C API states that first crypto_secretbox_BOXZEROBYTES must be zero * but these are not part of the message, and must be stripped out */ return extract(output, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES); }
@Override public SharedKey computeSharedKey(final PublicKey publicKey, final PrivateKey privateKey) { final byte[] output = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BEFORENMBYTES]; LOGGER.info("Computing the shared key for public key {} and private key {}", publicKey, REDACTED); LOGGER.debug("Computing the shared key for public key {} and private key {}", publicKey, privateKey); final int sodiumResult = this.sodium.crypto_box_curve25519xsalsa20poly1305_beforenm( output, publicKey.getKeyBytes(), privateKey.getKeyBytes() ); if (sodiumResult == -1) { LOGGER.warn("Could not compute the shared key for pub {} and priv {}", publicKey, REDACTED); LOGGER.debug("Could not compute the shared key for pub {} and priv {}", publicKey, privateKey); throw new NaclException("Kalium could not compute the shared key"); } final SharedKey sharedKey = SharedKey.from(output); LOGGER.info("Computed shared key {} for pub {} and priv {}", sharedKey, publicKey, REDACTED); LOGGER.debug("Computed shared key {} for pub {} and priv {}", sharedKey, publicKey, privateKey); return sharedKey; }
@Override public KeyPair generateNewKeys() { final byte[] publicKey = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES]; final byte[] privateKey = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES]; LOGGER.info("Generating new keypair..."); final int sodiumResult = this.sodium.crypto_box_curve25519xsalsa20poly1305_keypair(publicKey, privateKey); if (sodiumResult == -1) { LOGGER.warn("Unable to generate a new keypair!"); throw new NaclException("Kalium could not generate a new public/private keypair"); } final PublicKey pubKey = PublicKey.from(publicKey); final PrivateKey privKey = PrivateKey.from(privateKey); LOGGER.info("Generated public key {} and private key {}", pubKey, REDACTED); LOGGER.debug("Generated public key {} and private key {}", pubKey, privKey); return new KeyPair(pubKey, privKey); }
@Override public byte[] openAfterPrecomputation(final byte[] encryptedPayload, final Nonce nonce, final SharedKey sharedKey) { LOGGER.info("Opening message using shared key {}", sharedKey); LOGGER.debug( "Opening message {} using nonce {} and shared key {}", Arrays.toString(encryptedPayload), nonce, sharedKey ); /* * NaCL C API states that first crypto_secretbox_BOXZEROBYTES must be zero * but these are not part of the ciphertext, and must be added in */ final byte[] paddedInput = pad(encryptedPayload, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES); final byte[] paddedOutput = new byte[paddedInput.length]; final int sodiumResult = this.sodium.crypto_box_curve25519xsalsa20poly1305_open_afternm( paddedOutput, paddedInput, paddedInput.length, nonce.getNonceBytes(), sharedKey.getKeyBytes() ); if (sodiumResult == -1) { LOGGER.warn("Could not open sealed payload using shared key {}", sharedKey); LOGGER.debug("Could not open sealed payload using shared key {}", sharedKey); throw new NaclException("Kalium could not open the payload using the shared key"); } LOGGER.info("Opened sealed payload for shared key {}", sharedKey); LOGGER.debug( "Opened payload {} using nonce {}, public key {} and private key {} to get result {}", Arrays.toString(encryptedPayload), nonce, sharedKey, REDACTED, Arrays.toString(paddedOutput) ); return extract(paddedOutput, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES); }
protected byte[] encryptAesGcm(byte[] publicNonce, byte[] message, byte[] additionalData) { checkLength(publicNonce, CRYPTO_AEAD_AES256GCM_NPUBBYTES); byte[] ct = zeros(message.length + CRYPTO_AEAD_AES256GCM_ABYTES); isValid(sodium().crypto_aead_aes256gcm_encrypt(ct, null, message, message.length, additionalData, additionalData.length, null, publicNonce, key), "Encryption failed"); return ct; }
protected byte[] decryptChaChaPoly(byte[] publicNonce, byte[] ciphertext, byte[] additionalData) { checkLength(publicNonce, CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES); byte[] msg = zeros(ciphertext.length - CRYPTO_AEAD_CHACHA20POLY1305_ABYTES); isValid(sodium().crypto_aead_chacha20poly1305_decrypt(msg, null, null, ciphertext, ciphertext.length, additionalData, additionalData.length, publicNonce, key), "Decryption failed. Ciphertext failed verification"); return msg; }
protected byte[] encryptAesGcm(byte[] publicNonce, byte[] message, byte[] additionalData) { checkLength(publicNonce, CRYPTO_AEAD_AES256GCM_NPUBBYTES); byte[] ct = zeros(message.length + CRYPTO_AEAD_AES256GCM_ABYTES); isValid(sodium().crypto_aead_aes256gcm_encrypt(ct, null, message, message.length, additionalData, additionalData.length, null, publicNonce, key), "Encryption failed"); return ct; }
/** * Generate random bytes * * @param n number or random bytes * @return */ public byte[] randomBytes(int n) { byte[] buffer = new byte[n]; sodium().randombytes(buffer, n); return buffer; }
public Kalium(final NaCl.Sodium sodium) { this.sodium = Objects.requireNonNull(sodium, "Kalium sodium implementation was null"); LOGGER.info("Initialising Sodium..."); this.sodium.sodium_init(); LOGGER.info("Sodium initialised"); }
@Override public SharedKey createSingleKey() { LOGGER.info("Generating random key"); final byte[] keyBytes = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES]; this.sodium.randombytes(keyBytes, keyBytes.length); final SharedKey key = SharedKey.from(keyBytes); LOGGER.info("Random key generated"); LOGGER.debug("Generated key with value {}", key); return key; }
@Override public Nonce randomNonce() { final byte[] nonceBytes = new byte[CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES]; this.sodium.randombytes(nonceBytes, nonceBytes.length); final Nonce nonce = new Nonce(nonceBytes); LOGGER.debug("Generated random nonce {}", nonce); return nonce; }
public Aead(byte[] key) { this.key = key; // both CHACHAPOLY and AESGCM use 32 byte keys checkLength(key, CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES); sodium().sodium_init(); // needs to be called here for aes256gcm_is_available() to work }
public Aead useAesGcm() { if (sodium().crypto_aead_aes256gcm_is_available() != 1) { throw new RuntimeException("AES-GCM requires hardware support"); } aesGcm = true; return this; }
public byte[] randomBytes() { byte[] buffer = new byte[DEFAULT_SIZE]; sodium().randombytes(buffer, DEFAULT_SIZE); return buffer; } }
protected byte[] decryptAesGcm(byte[] publicNonce, byte[] ciphertext, byte[] additionalData) { checkLength(publicNonce, CRYPTO_AEAD_AES256GCM_NPUBBYTES); byte[] msg = zeros(ciphertext.length - CRYPTO_AEAD_AES256GCM_ABYTES); isValid(sodium().crypto_aead_aes256gcm_decrypt(msg, null, null, ciphertext, ciphertext.length, additionalData, additionalData.length, publicNonce, key), "Decryption failed. Ciphertext failed verification"); return msg; } }
public byte[] randomBytes() { byte[] buffer = new byte[DEFAULT_SIZE]; sodium().randombytes(buffer, DEFAULT_SIZE); return buffer; } }
public KeyPair() { this.secretKey = zeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); this.publicKey = zeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); sodium().crypto_box_curve25519xsalsa20poly1305_keypair(publicKey, secretKey); }
public byte[] decrypt(byte[] nonce, byte[] ciphertext) { checkLength(nonce, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES); byte[] ct = prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ciphertext); byte[] message = new byte[ct.length]; isValid(sodium().crypto_box_curve25519xsalsa20poly1305_open_afternm( message, ct, message.length, nonce, sharedKey), "Decryption failed. Ciphertext failed verification."); return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message); }
public byte[] encrypt(byte[] nonce, byte[] message) { checkLength(nonce, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES); byte[] msg = prependZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_ZEROBYTES, message); byte[] ct = new byte[msg.length]; isValid(sodium().crypto_box_curve25519xsalsa20poly1305_afternm(ct, msg, msg.length, nonce, sharedKey), "Encryption failed"); return removeZeros(CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BOXZEROBYTES, ct); }
public Box(byte[] publicKey, byte[] privateKey) { checkLength(publicKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES); checkLength(privateKey, CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES); sharedKey = new byte[NaCl.Sodium.CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BEFORENMBYTES]; isValid(sodium().crypto_box_curve25519xsalsa20poly1305_beforenm( sharedKey, publicKey, privateKey), "Key agreement failed"); }