Sodium::Nonce reuse detection.
parent
075c245011
commit
54a3cd8a8a
|
@ -99,6 +99,20 @@ describe Sodium::CryptoBox::SecretKey do
|
||||||
String.new(decrypted).should eq(data)
|
String.new(decrypted).should eq(data)
|
||||||
end
|
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
|
it "PyNaCl combined test vectors" do
|
||||||
combined_test_vectors.each do |vec|
|
combined_test_vectors.each do |vec|
|
||||||
box_from_vec(vec) do |box1, box2, nonce, plaintext, ciphertext|
|
box_from_vec(vec) do |box1, box2, nonce, plaintext, ciphertext|
|
||||||
|
|
|
@ -40,6 +40,17 @@ describe Sodium::SecretBox do
|
||||||
end
|
end
|
||||||
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
|
it "PyNaCl combined test vectors" do
|
||||||
combined_test_vectors.each do |vec|
|
combined_test_vectors.each do |vec|
|
||||||
box, nonce, plaintext, ciphertext = box_from_test_vector vec
|
box, nonce, plaintext, ciphertext = box_from_test_vector vec
|
||||||
|
|
|
@ -35,7 +35,8 @@ module Sodium
|
||||||
# Encrypts data and returns {ciphertext, nonce}
|
# Encrypts data and returns {ciphertext, nonce}
|
||||||
#
|
#
|
||||||
# Optionally supply a destination buffer.
|
# 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
|
if LibSodium.crypto_box_easy_afternm(dst, src, src.bytesize, nonce.to_slice, @key.to_slice) != 0
|
||||||
raise Error.new("crypto_box_easy")
|
raise Error.new("crypto_box_easy")
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,6 +49,8 @@ module Sodium
|
||||||
fun sodium_memcmp(Pointer(LibC::UChar), Pointer(LibC::UChar), LibC::SizeT) : LibC::Int
|
fun sodium_memcmp(Pointer(LibC::UChar), Pointer(LibC::UChar), LibC::SizeT) : LibC::Int
|
||||||
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
|
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_malloc(LibC::SizeT) : Pointer(LibC::UChar)
|
||||||
fun sodium_free(Pointer(LibC::UChar)) : Nil
|
fun sodium_free(Pointer(LibC::UChar)) : Nil
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,16 @@ require "random/secure"
|
||||||
|
|
||||||
module Sodium
|
module Sodium
|
||||||
class Nonce
|
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
|
delegate to_slice, to: @bytes
|
||||||
|
|
||||||
def initialize(@bytes : Bytes)
|
def initialize(@bytes : Bytes)
|
||||||
|
@ -14,8 +21,22 @@ module Sodium
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def self.random
|
||||||
@bytes = Random::Secure.random_bytes(NONCE_SIZE)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,10 +54,11 @@ module Sodium
|
||||||
# Encrypts data and returns {ciphertext, nonce}
|
# Encrypts data and returns {ciphertext, nonce}
|
||||||
#
|
#
|
||||||
# Optionally supply a destination buffer.
|
# 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)
|
if dst.bytesize != (src.bytesize + MAC_SIZE)
|
||||||
raise ArgumentError.new("dst.bytesize must be src.bytesize + MAC_SIZE, got #{dst.bytesize}")
|
raise ArgumentError.new("dst.bytesize must be src.bytesize + MAC_SIZE, got #{dst.bytesize}")
|
||||||
end
|
end
|
||||||
|
nonce.used!
|
||||||
if LibSodium.crypto_secretbox_easy(dst, src, src.bytesize, nonce.to_slice, self.to_slice) != 0
|
if LibSodium.crypto_secretbox_easy(dst, src, src.bytesize, nonce.to_slice, self.to_slice) != 0
|
||||||
raise Sodium::Error.new("crypto_secretbox_easy")
|
raise Sodium::Error.new("crypto_secretbox_easy")
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue