Sodium::Nonce reuse detection.

master
Didactic Drunk 2019-08-06 14:30:16 -07:00
parent 075c245011
commit 54a3cd8a8a
6 changed files with 56 additions and 6 deletions

View File

@ -99,6 +99,20 @@ describe Sodium::CryptoBox::SecretKey do
String.new(decrypted).should eq(data)
end
it "can't encrypt twice using the same nonce" do
data = "Hello World!".to_slice
alice = Sodium::CryptoBox::SecretKey.new
bob = Sodium::CryptoBox::SecretKey.new
alice.box bob.public_key do |box|
encrypted, nonce = box.encrypt data
expect_raises Sodium::Nonce::Error::Reused do
box.encrypt data, nonce: nonce
end
end
end
it "PyNaCl combined test vectors" do
combined_test_vectors.each do |vec|
box_from_vec(vec) do |box1, box2, nonce, plaintext, ciphertext|

View File

@ -40,6 +40,17 @@ describe Sodium::SecretBox do
end
end
it "can't encrypt twice using the same nonce" do
box = Sodium::SecretBox.new
message = "foobar"
encrypted, nonce = box.encrypt message
expect_raises(Sodium::Nonce::Error::Reused) do
box.encrypt message.to_slice, nonce: nonce
end
end
it "PyNaCl combined test vectors" do
combined_test_vectors.each do |vec|
box, nonce, plaintext, ciphertext = box_from_test_vector vec

View File

@ -35,7 +35,8 @@ module Sodium
# Encrypts data and returns {ciphertext, nonce}
#
# Optionally supply a destination buffer.
def encrypt(src : Bytes, dst = Bytes.new(src.bytesize + MAC_SIZE), nonce = Nonce.new) : {Bytes, Nonce}
def encrypt(src : Bytes, dst = Bytes.new(src.bytesize + MAC_SIZE), nonce = Nonce.random) : {Bytes, Nonce}
nonce.used!
if LibSodium.crypto_box_easy_afternm(dst, src, src.bytesize, nonce.to_slice, @key.to_slice) != 0
raise Error.new("crypto_box_easy")
end

View File

@ -49,6 +49,8 @@ module Sodium
fun sodium_memcmp(Pointer(LibC::UChar), Pointer(LibC::UChar), LibC::SizeT) : LibC::Int
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
fun sodium_increment(Pointer(LibC::UChar), LibC::SizeT) : Nil
fun sodium_malloc(LibC::SizeT) : Pointer(LibC::UChar)
fun sodium_free(Pointer(LibC::UChar)) : Nil

View File

@ -3,9 +3,16 @@ require "random/secure"
module Sodium
class Nonce
NONCE_SIZE = LibSodium::NONCE_SIZE
class Error < Sodium::Error
class Reused < Error
end
end
NONCE_SIZE = LibSodium::NONCE_SIZE.to_i
getter? used
@used = false
getter bytes : Bytes
delegate to_slice, to: @bytes
def initialize(@bytes : Bytes)
@ -14,8 +21,22 @@ module Sodium
end
end
def initialize
@bytes = Random::Secure.random_bytes(NONCE_SIZE)
def self.random
self.new Random::Secure.random_bytes(NONCE_SIZE)
end
def self.zero
self.new Bytes.new(NONCE_SIZE)
end
def increment
LibSodium.sodium_increment @bytes, @bytes.bytesize
@used = false
end
def used!
raise Error::Reused.new("attempted nonce reuse") if @used
@used = true
end
end
end

View File

@ -54,10 +54,11 @@ module Sodium
# Encrypts data and returns {ciphertext, nonce}
#
# Optionally supply a destination buffer.
def encrypt(src : Bytes, dst : Bytes = Bytes.new(src.bytesize + MAC_SIZE), nonce : Nonce = Nonce.new) : {Bytes, Nonce}
def encrypt(src : Bytes, dst : Bytes = Bytes.new(src.bytesize + MAC_SIZE), nonce : Nonce = Nonce.random) : {Bytes, Nonce}
if dst.bytesize != (src.bytesize + MAC_SIZE)
raise ArgumentError.new("dst.bytesize must be src.bytesize + MAC_SIZE, got #{dst.bytesize}")
end
nonce.used!
if LibSodium.crypto_secretbox_easy(dst, src, src.bytesize, nonce.to_slice, self.to_slice) != 0
raise Sodium::Error.new("crypto_secretbox_easy")
end