-1

I am trying to decipher the data presented in this format (password for decryption - 123123123, data taken from my phantom wallet):

{
  "encrypted": "Cj7psrZSx4oyzdMHpmemhHT21HrRzQxZ9Qyk9v3g3sg3",
  "nonce": "Gs1UtjfmNJ48rwqNPwfv5MMxcwFYpvtsd",
  "kdf": "pbkdf2",
  "salt": "JHjFEhDPmLFsRVshHYMprr",
  "iterations": 10000,
  "digest": "sha256"
}

I wrote the following code to decode this data, but when decoding nonce via Base64 I get the error "Last unit does not have enough valid bits". I thought that nonce should be trimmed, but then the error "Tag mismatch" appears.

public static void main(String[] args) throws Exception {
    String encrypted = "Cj7psrZSx4oyzdMHpmemhHT21HrRzQxZ9Qyk9v3g3sg3";
    String nonce = "Gs1UtjfmNJ48rwqNPwfv5MMxcwFYpvtsd";
    String salt = "JHjFEhDPmLFsRVshHYMprr";

    SecretKey key = getAESKeyFromPassword("123123123".toCharArray(), salt.getBytes());
    byte[] encryptedBytes = Base64.getDecoder().decode(encrypted);
    byte[] nonceBytes = Base64.getDecoder().decode(nonce);
    String decrypted = decryptAES(encryptedBytes, key, nonceBytes);
}

private static SecretKey getAESKeyFromPassword(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(password, salt, 10000, 256);
    return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
}

private static String decryptAES(byte[] cText, SecretKey secret, byte[] nonce) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(128, nonce));
    byte[] plainText = cipher.doFinal(cText);
    return new String(plainText, UTF_8);
}

Maybe someone knows how to work with this nonce?

  • 1
    Just a guess - as the nonce are of length 24 (after Base64 decoding) - are you sure that the algorithm is AES-GCM ? I think it is **XChaCha20-Poly1305** that has a nonce length of 24 bytes. – Michael Fehr Feb 28 '23 at 07:33
  • @michael-fehr, can you tell me how to decode nonce correctly? I use java.util.Base64.Decoder, but I get error "Last unit does not have enough valid bits". I also tried to use Base64 from bouncycastle, but I get error there too. – Alexander Prag Feb 28 '23 at 07:42
  • 1
    The nonce is simply not a valid Base64 and therefore cannot be Base64 decoded. Possibly the data is corrupted or a different encoding is used. Without more detailed information about the encryption, we can only guess here. Can you post the encryption code or a link to the documentation? – Topaco Feb 28 '23 at 08:25

1 Answers1

1

If this is base64 then there is something strange going on. Base64 encodes bytes of size 8 using characters representing 6 bits. There are 33 characters in the nonce, which means that would encode a maximum of 33 * 6 = 198 bits. That's 24 bytes + 6 bits. Now if it was base 64 then the last character of 6 bits wouldn't be needed, or you would be missing 2 bits. So probably it is another encoding (which is also likely because none of the other symbols such as +, /, - or _ are used.

More than a little searching shows that Solana wallets are using base 58 encoding / decoding. You can find an (unverified) encoder / decoder for Java here. In that case the decoding shows a more logical 24 bytes for the nonce. It might be base 62 as well, although that seems to be used less frequently.

Usually GCM uses a nonce of 12 bytes which probably means that the decryption algorithm is off as well; even with base58 encoding I was unable to verify the authentication tag. As mentioned in the comments, there aren't that many ciphers with a 24 byte nonce, I'd guess it is XSalsa20 or XChaCha20 (where the X actually hints at the larger nonce size), probably combined with Poly1305 to authenticate the ciphertext.

As you can see, the guesses quickly mount, which is why you should probably try and see where the protocol is specified instead. For instance, even if the ciphertext is authenticated, then the algorithm is somewhat daunting to implement. If it isn't, then you'd have the problem that you may not be able to verify the "plaintext" at all. For instance, if it is an EC private key then it is essentially a randomized byte array.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263