Search code examples
pythonrlibsodiumpynacl

64 bytes keys in PyNaCl


Is there a way to use 64 bytes keys in PyNaCl?

I am working with the signature functions in PyNaCl. Where I can sign messages and verify them using asymmetric cryptography. As far as I know the basic implementation of signing uses 32 bytes keys.

Now: I also want to use the same keys that I used in Python to sign messages in R. Therefore, I use the sodium package. This package uses 64 bytes keys (and the same algorithm).

Is there a way to get either package to use 32 or 64 bytes keys so that I can then reuse the keys in the other language?

Thanks!


Solution

  • Okay. Turns out, the problem here was my understanding of the signing algorithm.

    The PyNaCl library relies more heavily on the seed that is used to create the private key. This seed is 32 bytes and can be used instead of the 64 bytes key.

    The sodium package uses the 64 bytes key and only allows you to specify this key directly. But not the seed.

    Therefore, what I really want to do is when creating a private key, saving the key and the seed. With the key I can use the sodium package in R and with the seed I can reconstruct the key for the PyNaCl library.

    Update (2020-11-03)

    My specific usecase creates keys in Python and then reads them in both Python and R. I will illustrate this behaviour.

    What I found out is that the PyNaCl library exposes the seed if you take an instance of SigningKey and cast it to bytes but it still has a representation of the actual signing key as an attribute _signing_key:

    from nacl.signing import SigningKey
    signing_key = SigningKey.generate()
    
    # This gives you the 32 bytes seed
    bytes(signing_key)
    
    # This gives you the 64 bytes actual key
    signing_key._signing_key
    

    (Really, seed and key are interchangeable here. If you have the seed, you are able to calculate the key from it. From what I know it is an implementation detail and up to the library what it exposes to you. Using the 32 bytes seed is of course more efficient since you need to store only 32 bytes instead of 64.)

    Then, I proceeded to save both the 64 bytes key as well as the 32 bytes seed. I can later import the 32 bytes seed in Python again using the PyNaCl library. In R, I can import the key the following way:

    keyFile <- "path/to/the/key"
    key.char <- readChar(keyFile, file.info(keyFile)$size)
    key <- base64enc::base64decode(key.char)
    
    # Sign messages using this key with the sodium library
    sodium::sig_sign(charToRaw("your private message"), key)