Class: RbNaCl::Boxes::Curve25519XSalsa20Poly1305

Inherits:
Object
  • Object
show all
Extended by:
Sodium
Defined in:
lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb,
lib/rbnacl/boxes/curve25519xsalsa20poly1305/public_key.rb,
lib/rbnacl/boxes/curve25519xsalsa20poly1305/private_key.rb

Overview

The Box class boxes and unboxes messages between a pair of keys

This class uses the given public and secret keys to derive a shared key, which is used with the nonce given to encrypt the given messages and decrypt the given ciphertexts. The same shared key will generated from both pairing of keys, so given two keypairs belonging to alice (pkalice, skalice) and bob(pkbob, skbob), the key derived from (pkalice, skbob) with equal that from (pkbob, skalice). This is how the system works:

It is VITALLY important that the nonce is a nonce, i.e. it is a number used only once for any given pair of keys. If you fail to do this, you compromise the privacy of the the messages encrypted. Also, bear in mind the property mentioned just above. Give your nonces a different prefix, or have one side use an odd counter and one an even counter. Just make sure they are different.

The ciphertexts generated by this class include a 16-byte authenticator which is checked as part of the decryption. An invalid authenticator will cause the unbox function to raise. The authenticator is not a signature. Once you've looked in the box, you've demonstrated the ability to create arbitrary valid messages, so messages you send are repudiable. For non-repudiable messages, sign them before or after encryption.

Examples:

# On bob's system
bobkey = RbNaCl::PrivateKey.generate
#=> #<RbNaCl::PrivateKey ...>

# send bobkey.public_key to alice
# receive alice's public key, alicepk
# NB: This is actually the hard part of the system.  How to do it securely
# is left as an exercise to for the reader.
alice_pubkey = "..."

# make a box
alicebob_box = RbNaCl::Box.new(alice_pubkey, bobkey)
#=> #<RbNaCl::Box ...>

# encrypt a message to alice
cipher_text = alicebob_box.box("A bad example of a nonce", "Hello, Alice!")
#=> "..." # a string of bytes, 29 bytes long

# send ["A bad example of a nonce", cipher_text] to alice
# note that nonces don't have to be secret
# receive [nonce, cipher_text_to_bob] from alice

# decrypt the reply
# Alice has been a little more sensible than bob, and has a random nonce
# that is too fiddly to type here.  But there are other choices than just
# random
plain_text = alicebob_box.open(nonce, cipher_text_to_bob)
#=> "Hey there, Bob!"

# we have a new message!
# But Eve has tampered with this message, by flipping some bytes around!
# [nonce2, cipher_text_to_bob_honest_love_eve]
alicebob_box.open(nonce2, cipher_text_to_bob_honest_love_eve)

# BOOM!
# Bob gets a RbNaCl::CryptoError to deal with!

Defined Under Namespace

Classes: PrivateKey, PublicKey

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Sodium

sodium_constant, sodium_function, sodium_function_with_return_code, sodium_primitive, sodium_type

Constructor Details

#initialize(public_key, private_key) ⇒ RbNaCl::Box

Create a new Box

Sets up the Box for deriving the shared key and encrypting and decrypting messages.

Parameters:

Raises:


101
102
103
104
105
# File 'lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb', line 101

def initialize(public_key, private_key)
  @public_key   = public_key.is_a?(PublicKey) ? public_key : PublicKey.new(public_key)
  @private_key  = private_key.is_a?(PrivateKey) ? private_key : PrivateKey.new(private_key)
  raise IncorrectPrimitiveError unless @public_key.primitive == primitive && @private_key.primitive == primitive
end

Class Method Details

.nonce_bytesInteger

The nonce bytes for the box class

Returns:

  • (Integer)

    The number of bytes in a valid nonce


169
170
171
# File 'lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb', line 169

def self.nonce_bytes
  NONCEBYTES
end

Instance Method Details

#box(nonce, message) ⇒ String Also known as: encrypt

Encrypts a message

Encrypts the message with the given nonce to the keypair set up when initializing the class. Make sure the nonce is unique for any given keypair, or you might as well just send plain text.

This function takes care of the padding required by the NaCL C API.

Parameters:

  • nonce (String)

    A 24-byte string containing the nonce.

  • message (String)

    The message to be encrypted.

Returns:

  • (String)

    The ciphertext without the nonce prepended (BINARY encoded)

Raises:


121
122
123
124
125
126
127
128
129
130
# File 'lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb', line 121

def box(nonce, message)
  Util.check_length(nonce, nonce_bytes, "Nonce")
  msg = Util.prepend_zeros(ZEROBYTES, message)
  ct  = Util.zeros(msg.bytesize)

  success = self.class.box_curve25519xsalsa20poly1305_afternm(ct, msg, msg.bytesize, nonce, beforenm)
  raise CryptoError, "Encryption failed" unless success

  Util.remove_zeros(BOXZEROBYTES, ct)
end

#nonce_bytesInteger

The nonce bytes for the box instance

Returns:

  • (Integer)

    The number of bytes in a valid nonce


176
177
178
# File 'lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb', line 176

def nonce_bytes
  NONCEBYTES
end

#open(nonce, ciphertext) ⇒ String Also known as: decrypt

Decrypts a ciphertext

Decrypts the ciphertext with the given nonce using the keypair setup when initializing the class.

This function takes care of the padding required by the NaCL C API.

Parameters:

  • nonce (String)

    A 24-byte string containing the nonce.

  • ciphertext (String)

    The message to be decrypted.

Returns:

  • (String)

    The decrypted message (BINARY encoded)

Raises:


147
148
149
150
151
152
153
154
155
156
# File 'lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb', line 147

def open(nonce, ciphertext)
  Util.check_length(nonce, nonce_bytes, "Nonce")
  ct = Util.prepend_zeros(BOXZEROBYTES, ciphertext)
  message = Util.zeros(ct.bytesize)

  success = self.class.box_curve25519xsalsa20poly1305_open_afternm(message, ct, ct.bytesize, nonce, beforenm)
  raise CryptoError, "Decryption failed. Ciphertext failed verification." unless success

  Util.remove_zeros(ZEROBYTES, message)
end

#primitiveSymbol

The crypto primitive for the box class

Returns:

  • (Symbol)

    The primitive used


162
163
164
# File 'lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb', line 162

def primitive
  self.class.primitive
end