Sodium::Nonce reuse detection.
This commit is contained in:
		
							parent
							
								
									075c245011
								
							
						
					
					
						commit
						54a3cd8a8a
					
				
					 6 changed files with 56 additions and 6 deletions
				
			
		|  | @ -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| | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Didactic Drunk
						Didactic Drunk