-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add XChaCha20Poly1305 box #235
Open
vcsjones
wants to merge
1
commit into
RubyCrypto:main
Choose a base branch
from
vcsjones:xchacha20-sealedbox
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# encoding: binary | ||
# frozen_string_literal: true | ||
|
||
module RbNaCl | ||
module SecretBoxes | ||
# The XChaCha20Poly1305 class boxes and unboxes messages using the XChaCha20Poly1305 algorithm | ||
# | ||
# This class uses the given secret key to encrypt and decrypt messages. | ||
# | ||
# 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 messages encrypted. 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. | ||
class XChaCha20Poly1305 | ||
extend Sodium | ||
|
||
sodium_type :secretbox | ||
sodium_primitive :xchacha20poly1305 | ||
sodium_constant :KEYBYTES | ||
sodium_constant :NONCEBYTES | ||
sodium_constant :MACBYTES | ||
|
||
sodium_function :secretbox_xchacha20poly1305, | ||
:crypto_secretbox_xchacha20poly1305_easy, | ||
%i[pointer pointer ulong_long pointer pointer] | ||
|
||
sodium_function :secretbox_xchacha20poly1305_open, | ||
:crypto_secretbox_xchacha20poly1305_open_easy, | ||
%i[pointer pointer ulong_long pointer pointer] | ||
|
||
# Create a new XChaCha20Poly1305 Box. | ||
# | ||
# Sets up the Box with a secret key for encrypting and decrypting messages. | ||
# | ||
# @param key [String] The key to encrypt and decrypt with | ||
# | ||
# @raise [RbNaCl::LengthError] on invalid keys | ||
# | ||
# @return [RbNaCl::XChaCha20Poly1305] The new Box, ready to use | ||
def initialize(key) | ||
@key = Util.check_string(key, KEYBYTES, "Secret key") | ||
end | ||
|
||
# Encrypts a message | ||
# | ||
# Encrypts the message with the given nonce to the key set up when | ||
# initializing the class. Make sure the nonce is unique for any given | ||
# key, or you might as well just send plain text. | ||
# | ||
# This function takes care of the padding required by the NaCL C API. | ||
# | ||
# @param nonce [String] A 24-byte string containing the nonce. | ||
# @param message [String] The message to be encrypted. | ||
# | ||
# @raise [RbNaCl::LengthError] If the nonce is not valid | ||
# | ||
# @return [String] The ciphertext without the nonce prepended (BINARY encoded) | ||
def box(nonce, message) | ||
Util.check_length(nonce, nonce_bytes, "Nonce") | ||
|
||
# Libsodium appends the tag, so make space for the tag. | ||
ct = Util.zeros(message.bytesize + MACBYTES) | ||
|
||
success = self.class.secretbox_xchacha20poly1305(ct, message, message.bytesize, nonce, @key) | ||
raise CryptoError, "Encryption failed" unless success | ||
ct | ||
end | ||
alias encrypt box | ||
|
||
# Decrypts a ciphertext | ||
# | ||
# Decrypts the ciphertext with the given nonce using the key setup when | ||
# initializing the class. | ||
# | ||
# This function takes care of the padding required by the NaCL C API. | ||
# | ||
# @param nonce [String] A 24-byte string containing the nonce. | ||
# @param ciphertext [String] The message to be decrypted. | ||
# | ||
# @raise [RbNaCl::LengthError] If the nonce is not valid | ||
# @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. | ||
# | ||
# @return [String] The decrypted message (BINARY encoded) | ||
def open(nonce, ciphertext) | ||
Util.check_length(nonce, nonce_bytes, "Nonce") | ||
message = Util.zeros(ciphertext.bytesize) | ||
|
||
success = self.class.secretbox_xchacha20poly1305_open(message, ciphertext, ciphertext.bytesize, nonce, @key) | ||
raise CryptoError, "Decryption failed. Ciphertext failed verification." unless success | ||
|
||
# When the message was created it's length included the length of the tag. Slice it off | ||
# now that it passed the tag check. | ||
message.byteslice(0, message.bytesize - MACBYTES) | ||
end | ||
alias decrypt open | ||
|
||
# The crypto primitive for the XChaCha20Poly1305 instance | ||
# | ||
# @return [Symbol] The primitive used | ||
def primitive | ||
self.class.primitive | ||
end | ||
|
||
# The nonce bytes for the XChaCha20Poly1305 class | ||
# | ||
# @return [Integer] The number of bytes in a valid nonce | ||
def self.nonce_bytes | ||
NONCEBYTES | ||
end | ||
|
||
# The nonce bytes for the XChaCha20Poly1305 instance | ||
# | ||
# @return [Integer] The number of bytes in a valid nonce | ||
def nonce_bytes | ||
NONCEBYTES | ||
end | ||
|
||
# The key bytes for the XChaCha20Poly1305 class | ||
# | ||
# @return [Integer] The number of bytes in a valid key | ||
def self.key_bytes | ||
KEYBYTES | ||
end | ||
|
||
# The key bytes for the XChaCha20Poly1305 instance | ||
# | ||
# @return [Integer] The number of bytes in a valid key | ||
def key_bytes | ||
KEYBYTES | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# encoding: binary | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RbNaCl::SecretBoxes::XChaCha20Poly1305 do | ||
let(:key) { vector :secretbox_xchacha20poly1305_key } | ||
let(:box) { described_class.new(key) } | ||
|
||
context "new" do | ||
it "accepts strings" do | ||
expect { described_class.new(key) }.to_not raise_error | ||
end | ||
|
||
it "raises on a nil key" do | ||
expect { described_class.new(nil) }.to raise_error(TypeError) | ||
end | ||
|
||
it "raises on a short key" do | ||
expect { described_class.new("hello") }.to raise_error RbNaCl::LengthError | ||
end | ||
end | ||
|
||
|
||
include_examples "box" do | ||
let(:nonce) { vector :secretbox_xchacha20poly1305_nonce } | ||
let(:message) { vector :secretbox_xchacha20poly1305_message } | ||
let(:ciphertext) { vector :secretbox_xchacha20poly1305_ciphertext } | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would probably be good to note there are two different flavors of algorithm calling themselves
XChaCha20Poly1305
crypto_secretbox
/djb
-style: this is effectively thecrypto_secretbox
function but withXChaCha20
swapped forXSalsa20
, and what is being implemented heredraft-irtf-cfrg-xchacha
: uses the IETF flavor of ChaCha20