code: public Optional<byte[]> open(byte[] nonce, byte[] ciphertext) {
      final XSalsa20Engine xsalsa20 = new XSalsa20Engine();
      final Poly1305 poly1305 = new Poly1305();
  
      // initialize XSalsa20
      xsalsa20.init(false, new ParametersWithIV(new KeyParameter(key), nonce));
  
      // generate mac subkey
      final byte[] sk = new byte[Keys.KEY_LEN];
      xsalsa20.processBytes(sk, 0, sk.length, sk, 0);
  
      // hash ciphertext
      poly1305.init(new KeyParameter(sk));
      final int len = Math.max(ciphertext.length - poly1305.getMacSize(), 0);
      poly1305.update(ciphertext, poly1305.getMacSize(), len);
      final byte[] calculatedMAC = new byte[poly1305.getMacSize()];
      poly1305.doFinal(calculatedMAC, 0);
  
      // extract mac
      final byte[] presentedMAC = new byte[poly1305.getMacSize()];
      System.arraycopy(
          ciphertext, 0, presentedMAC, 0, Math.min(ciphertext.length, poly1305.getMacSize()));
  
      // compare macs
      if (!MessageDigest.isEqual(calculatedMAC, presentedMAC)) {
        return Optional.empty();
      }
  
      // decrypt ciphertext
      final byte[] plaintext = new byte[len];
      xsalsa20.processBytes(ciphertext, poly1305.getMacSize(), plaintext.length, plaintext, 0);
      return Optional.of(plaintext);
    }
docs: Decrypt a ciphertext using the given key and nonce.
  
  @param nonce a 24-byte nonce
  @param ciphertext the encrypted message
  @return an {@link Optional} of the original plaintext, or if either the key, nonce, or
  ciphertext was modified, an empty {@link Optional}
  @see #nonce(byte[])
  @see #nonce()