require File.expand_path(File.dirname(__FILE__) + '/spec_helper') require 'dionysus/digest' describe Digest do DIGESTS = { :md5 => 128, :sha1 => 160, :sha2 => 256, :sha256 => 256, :sha384 => 384, :sha512 => 512 } it 'should have the default available digests' do Digest.available_digests.length.should == Digest::DEFAULT_DIGESTS.length Digest::DEFAULT_DIGESTS.each do |dig| Digest.available_digests.should include(dig) end end describe 'registration' do before(:all) do Digest.digests.clear end after(:each) do Digest.digests.clear end after(:all) do Digest::DEFAULT_DIGESTS.each do |sym| Digest.register_digest!(sym) end end it 'should register an available digest' do Digest.register_digest(:md5).should be_a(Hash) Digest.digests[:md5].should be_a(Hash) end it 'should register an available digest with bang' do Digest.register_digest!(:md5).should be_a(Hash) Digest.digests[:md5].should be_a(Hash) end it 'should automatically interpret the class' do Digest.register_digest(:md5) Digest.digests[:md5][:klass].should == Digest::MD5 end it 'should register a digest with a class' do Digest.register_digest(:md5, :klass => Digest::SHA256) Digest.digests[:md5][:klass].should == Digest::SHA256 Digest.digests[:md5][:bit_length].should == 256 end it 'should automatically interpret the bit length' do Digest.register_digest(:md5) Digest.digests[:md5][:bit_length].should == 128 end it 'should register a digest with a class and a bit length' do Digest.register_digest(:md5, :klass => Digest::SHA256, :bit_length => 1) Digest.digests[:md5][:klass].should == Digest::SHA256 Digest.digests[:md5][:bit_length].should == 1 end it 'should quietly reject a digest if it does not exist' do Digest.register_digest(:foobar).should == nil Digest.digests.should == {} end it 'should raise an error if a digest does not exist' do lambda { Digest.register_digest!(:foobar) }.should raise_error(LoadError, 'library not found for class Digest::FOOBAR -- digest/foobar') end it 'should take an arbitrary object that responds to digest' do my_digest = mock('my_digest') my_digest.should_receive(:digest).with('1').and_return('12345') Digest.register_digest(:my_digest, :klass => my_digest) Digest.digests[:my_digest][:bit_length].should == 5 * 8 Digest.digests[:my_digest][:klass].should === my_digest Digest.digests[:my_digest][:method].should == :digest end it 'should take an arbitrary method' do my_digest = mock('my_digest') my_digest.should_receive(:do_digest).with('1').and_return('12345') Digest.register_digest(:my_digest, :klass => my_digest, :method => :do_digest) Digest.digests[:my_digest][:bit_length].should == 5 * 8 Digest.digests[:my_digest][:klass].should === my_digest Digest.digests[:my_digest][:method].should == :do_digest end end describe 'digest lengths' do DIGESTS.each do |dig, bits| it "should have binary length for #{dig}" do Digest.digest_lengths[dig].should == bits / 8 Digest.digest_lengths(:binary)[dig].should == bits / 8 Digest.digest_lengths(8)[dig].should == bits / 8 end it "should have hex length for #{dig}" do Digest.digest_lengths(:hex)[dig].should == bits / 4 Digest.digest_lengths(:hexidecimal)[dig].should == bits / 4 Digest.digest_lengths(4)[dig].should == bits / 4 end it "should have base64 length for #{dig}" do len = bits / 6 expected = (len % 4 == 0 ? len : len + (4 - len % 4)) Digest.digest_lengths(:base64)[dig].should == expected Digest.digest_lengths(6)[dig].should == expected end it "should have bit length for #{dig}" do Digest.digest_lengths(:bit)[dig].should == bits Digest.digest_lengths(1)[dig].should == bits end it "should have arbitrary length for #{dig}" do len = bits / 7 Digest.digest_lengths(7)[dig].should == len + (8 - len % 8) end end it 'should reject bit length requests for < 0' do lambda { Digest.digest_lengths(0) }.should raise_error(ArgumentError, 'Invalid encoding') lambda { Digest.digest_lengths(-1) }.should raise_error(ArgumentError, 'Invalid encoding') lambda { Digest.digest_lengths(-100) }.should raise_error(ArgumentError, 'Invalid encoding') end it 'should reject bit length requests for unknown encodings' do lambda { Digest.digest_lengths(:my_digest) }.should raise_error(ArgumentError, 'Invalid encoding') end end describe "digest helper" do DIGESTS.each do |dig, bits| it "should run the digest for #{dig}" do Digest.digest(dig, 'foobar').length.should == bits / 8 end it "should still have working original digest methods" do "Digest::#{dig.to_s.upcase}".constantize.digest('foobar').length.should == bits / 8 end end it "should fail invalid digest" do lambda { Digest.digest(:my_digest, 'foobar') }.should raise_error(LoadError) end end describe "detection" do DIGESTS.each do |dig, bits| it "should detect #{dig} in binary" do str = Digest.digest(dig, 'foobar') Digest.detect_digest(str).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest(str, :binary).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest!(str).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest!(str, :binary).should == (dig == :sha2 ? :sha256 : dig) end it "should detect #{dig} in base64" do str = Digest.digest(dig, 'foobar').encode64s Digest.detect_digest(str, :base64).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest!(str, :base64).should == (dig == :sha2 ? :sha256 : dig) end it "should detect #{dig} in hexidecimal" do str = Digest.digest(dig, 'foobar').encode_hexidecimal Digest.detect_digest(str, :hex).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest(str, :hexidecimal).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest!(str, :hex).should == (dig == :sha2 ? :sha256 : dig) Digest.detect_digest!(str, :hexidecimal).should == (dig == :sha2 ? :sha256 : dig) end end it "should return nil on unknown digest" do Digest.detect_digest('blah').should be_nil end it "should fail on unknown digest with !" do lambda { Digest.detect_digest!('blah') }.should raise_error(RuntimeError, "Unknown digest") end end end