Search code examples
javac#cryptographylibsodiumnacl-cryptography

Cannot open "SecretBox" generated by TweetNacl (Java) with "Libsodium-net" (C#)


I'm having problems opening a Nacl SecretBox (generated in java using the TweetNaclFast library) in C# using the libsodium-net library. I also can't do it the other way around (open a libsodium-net generated box using TweetNaclFast).

In the following example i'll create a SecretBox using TweetNaclFast (Java) and try to open it with libsodium-net (C#)

Creating the SecretBox (Java)

    String secretMessage = "Hello Stack overflow!";
    byte[] messageBytes = secretMessage.getBytes("UTF-8");
    byte[] keyBytes = secureRandomGenerator(); //returns 32 random bytes (256 bits)
    byte[] nonceBytes = TweetNaclFast.makeSecretBoxNonce();

    byte[] boxBytes = new TweetNaclFast.SecretBox(keyBytes).box(messageBytes,nonceBytes);

    System.out.println("Base64 box -> "+Base64.encodeBase64String(boxBytes));
    System.out.println("Base64 key -> "+Base64.encodeBase64String(keyBytes));
    System.out.println("Base64 nonce -> "+Base64.encodeBase64String(nonceBytes));

Creation Output

Base64 box -> iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==
Base64 key -> FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=
Base64 nonce -> 2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV

Opening the SecretBox (C#)

string box = "iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==";
string key = "FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=";
string nonce = "2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV";


        try
        {
            byte[] message = Sodium.SecretBox.Open(
            Convert.FromBase64String(box),
            Convert.FromBase64String(nonce),
            Convert.FromBase64String(key));

            Console.WriteLine(Encoding.UTF8.GetString(message));
        }
        catch (CryptographicException e)
        {
            Console.WriteLine(e.Message);
            Console.WriteLine(e.StackTrace);
        }

Open Output

Failed to open SecretBox
   at Sodium.SecretBox.Open(Byte[] cipherText, Byte[] nonce, Byte[] key)

Any idea about what i might be doing wrong?


EDIT

I guess the problem is with one of the libraries (libsodium-net most likely). If i create a SecretBox with the same variables i get a different box...

Creating a Secret Box with TweetNaclFast

String message = "Hello Stack overflow!";
String key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
String nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";

SecretBox box = new SecretBox(Base64.decodeBase64(key));
byte[] cipherText = box.box(message.getBytes("UTF-8"), Base64.decodeBase64(nonce));

RETURNS: yDCt/kOLFUWPZpV3deVNUZaH0ZHLVmj9Nvm8QlbVKPe1a/INDw==

Creating a Secret Box with libsodium-net

string message = "Hello Stack overflow!";
string key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
string nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";

byte[] box = Sodium.SecretBox.Create(Encoding.UTF8.GetBytes(message), 
                                             Convert.FromBase64String(nonce), 
                                             Convert.FromBase64String(key));

Console.WriteLine(Convert.ToBase64String(box));

RETURNS: AAAAAAAAAAAAAAAAAAAAAMgwrf5DixVFj2aVd3XlTVGWh9GRy1Zo/Tb5vEJW1Sj3tWvyDQ8=


Solution

  • Sodium.SecretBox.Create uses the original NaCl crypto_box() API, which requires extra padding before the message and the ciphertext.

    This API is a bit confusing and is rarely useful except in C. Even in C, people using it end up writing wrappers to prepend or get rid of the padding.

    The box and secretbox constructions, as exposed by most APIs, do not require extra padding. The ciphertext is directly returned without 16 extra bytes before. The message can be directly given without prepending 16 nul bytes.

    TweetNaclFast does not require any padding, but libsodium-net apparently does.

    The extra 16 bytes before the ciphertext you are observing with libsodium-net do not contain any useful information. It's just a bunch of zeros. You can safely strip them, and add them later when calling Sodium.SecretBox.Open.

    Note that unlike Sodium.SecretBox, Sodium.PublicKeyBox doesn't require padding.