This commit is contained in:
Benoit Daloze 2023-10-30 16:44:54 +01:00
parent b09618e566
commit 7d8cfa0a40

View File

@ -1,207 +1,209 @@
require_relative '../../../spec_helper' require_relative '../../../spec_helper'
require 'openssl' require 'openssl'
describe "OpenSSL::KDF.scrypt" do guard -> { OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000 } do
before :each do describe "OpenSSL::KDF.scrypt" do
@defaults = { before :each do
salt: "\x00".b * 16, @defaults = {
N: 2**14, salt: "\x00".b * 16,
r: 8, N: 2**14,
p: 1, r: 8,
length: 32 p: 1,
} length: 32
end }
end
it "creates the same value with the same input" do it "creates the same value with the same input" do
key = OpenSSL::KDF.scrypt("secret", **@defaults) key = OpenSSL::KDF.scrypt("secret", **@defaults)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "supports nullbytes embedded into the password" do it "supports nullbytes embedded into the password" do
key = OpenSSL::KDF.scrypt("sec\x00ret".b, **@defaults) key = OpenSSL::KDF.scrypt("sec\x00ret".b, **@defaults)
key.should == "\xF9\xA4\xA0\xF1p\xF4\xF0\xCAT\xB4v\xEB\r7\x88N\xF7\x15]Ns\xFCwt4a\xC9\xC6\xA7\x13\x81&".b key.should == "\xF9\xA4\xA0\xF1p\xF4\xF0\xCAT\xB4v\xEB\r7\x88N\xF7\x15]Ns\xFCwt4a\xC9\xC6\xA7\x13\x81&".b
end end
it "coerces the password into a String using #to_str" do it "coerces the password into a String using #to_str" do
pass = mock("pass") pass = mock("pass")
pass.should_receive(:to_str).and_return("secret") pass.should_receive(:to_str).and_return("secret")
key = OpenSSL::KDF.scrypt(pass, **@defaults) key = OpenSSL::KDF.scrypt(pass, **@defaults)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "coerces the salt into a String using #to_str" do it "coerces the salt into a String using #to_str" do
salt = mock("salt") salt = mock("salt")
salt.should_receive(:to_str).and_return("\x00".b * 16) salt.should_receive(:to_str).and_return("\x00".b * 16)
key = OpenSSL::KDF.scrypt("secret", **@defaults, salt: salt) key = OpenSSL::KDF.scrypt("secret", **@defaults, salt: salt)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "coerces the N into an Integer using #to_int" do it "coerces the N into an Integer using #to_int" do
n = mock("N") n = mock("N")
n.should_receive(:to_int).and_return(2**14) n.should_receive(:to_int).and_return(2**14)
key = OpenSSL::KDF.scrypt("secret", **@defaults, N: n) key = OpenSSL::KDF.scrypt("secret", **@defaults, N: n)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "coerces the r into an Integer using #to_int" do it "coerces the r into an Integer using #to_int" do
r = mock("r") r = mock("r")
r.should_receive(:to_int).and_return(8) r.should_receive(:to_int).and_return(8)
key = OpenSSL::KDF.scrypt("secret", **@defaults, r: r) key = OpenSSL::KDF.scrypt("secret", **@defaults, r: r)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "coerces the p into an Integer using #to_int" do it "coerces the p into an Integer using #to_int" do
p = mock("p") p = mock("p")
p.should_receive(:to_int).and_return(1) p.should_receive(:to_int).and_return(1)
key = OpenSSL::KDF.scrypt("secret", **@defaults, p: p) key = OpenSSL::KDF.scrypt("secret", **@defaults, p: p)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "coerces the length into an Integer using #to_int" do it "coerces the length into an Integer using #to_int" do
length = mock("length") length = mock("length")
length.should_receive(:to_int).and_return(32) length.should_receive(:to_int).and_return(32)
key = OpenSSL::KDF.scrypt("secret", **@defaults, length: length) key = OpenSSL::KDF.scrypt("secret", **@defaults, length: length)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D\x17}\xF2\x84T\xD4)\xC2>\xFE\x93\xE3\xF4".b
end end
it "accepts an empty password" do it "accepts an empty password" do
key = OpenSSL::KDF.scrypt("", **@defaults) key = OpenSSL::KDF.scrypt("", **@defaults)
key.should == "\xAA\xFC\xF5^E\x94v\xFFk\xE6\xF0vR\xE7\x13\xA7\xF5\x15'\x9A\xE4C\x9Dn\x18F_E\xD2\v\e\xB3".b key.should == "\xAA\xFC\xF5^E\x94v\xFFk\xE6\xF0vR\xE7\x13\xA7\xF5\x15'\x9A\xE4C\x9Dn\x18F_E\xD2\v\e\xB3".b
end end
it "accepts an empty salt" do it "accepts an empty salt" do
key = OpenSSL::KDF.scrypt("secret", **@defaults, salt: "") key = OpenSSL::KDF.scrypt("secret", **@defaults, salt: "")
key.should == "\x96\xACDl\xCB3/aN\xB0F\x8A#\xD7\x92\xD2O\x1E\v\xBB\xCE\xC0\xAA\xB9\x0F]\xB09\xEA8\xDD\e".b key.should == "\x96\xACDl\xCB3/aN\xB0F\x8A#\xD7\x92\xD2O\x1E\v\xBB\xCE\xC0\xAA\xB9\x0F]\xB09\xEA8\xDD\e".b
end end
it "accepts a zero length" do it "accepts a zero length" do
key = OpenSSL::KDF.scrypt("secret", **@defaults, length: 0) key = OpenSSL::KDF.scrypt("secret", **@defaults, length: 0)
key.should.empty? key.should.empty?
end end
it "accepts an arbitrary length" do it "accepts an arbitrary length" do
key = OpenSSL::KDF.scrypt("secret", **@defaults, length: 19) key = OpenSSL::KDF.scrypt("secret", **@defaults, length: 19)
key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D".b key.should == "h\xB2k\xDF]\xDA\xE1.-(\xCF\xAC\x91D\x8F\xC2a\x9C\x9D".b
end end
it "raises a TypeError when password is not a String and does not respond to #to_str" do it "raises a TypeError when password is not a String and does not respond to #to_str" do
-> { -> {
OpenSSL::KDF.scrypt(Object.new, **@defaults) OpenSSL::KDF.scrypt(Object.new, **@defaults)
}.should raise_error(TypeError, "no implicit conversion of Object into String") }.should raise_error(TypeError, "no implicit conversion of Object into String")
end end
it "raises a TypeError when salt is not a String and does not respond to #to_str" do it "raises a TypeError when salt is not a String and does not respond to #to_str" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, salt: Object.new) OpenSSL::KDF.scrypt("secret", **@defaults, salt: Object.new)
}.should raise_error(TypeError, "no implicit conversion of Object into String") }.should raise_error(TypeError, "no implicit conversion of Object into String")
end end
it "raises a TypeError when N is not an Integer and does not respond to #to_int" do it "raises a TypeError when N is not an Integer and does not respond to #to_int" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, N: Object.new) OpenSSL::KDF.scrypt("secret", **@defaults, N: Object.new)
}.should raise_error(TypeError, "no implicit conversion of Object into Integer") }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
end end
it "raises a TypeError when r is not an Integer and does not respond to #to_int" do it "raises a TypeError when r is not an Integer and does not respond to #to_int" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, r: Object.new) OpenSSL::KDF.scrypt("secret", **@defaults, r: Object.new)
}.should raise_error(TypeError, "no implicit conversion of Object into Integer") }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
end end
it "raises a TypeError when p is not an Integer and does not respond to #to_int" do it "raises a TypeError when p is not an Integer and does not respond to #to_int" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, p: Object.new) OpenSSL::KDF.scrypt("secret", **@defaults, p: Object.new)
}.should raise_error(TypeError, "no implicit conversion of Object into Integer") }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
end end
it "raises a TypeError when length is not an Integer and does not respond to #to_int" do it "raises a TypeError when length is not an Integer and does not respond to #to_int" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, length: Object.new) OpenSSL::KDF.scrypt("secret", **@defaults, length: Object.new)
}.should raise_error(TypeError, "no implicit conversion of Object into Integer") }.should raise_error(TypeError, "no implicit conversion of Object into Integer")
end end
it "treats salt as a required keyword" do it "treats salt as a required keyword" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults.except(:salt)) OpenSSL::KDF.scrypt("secret", **@defaults.except(:salt))
}.should raise_error(ArgumentError, 'missing keyword: :salt') }.should raise_error(ArgumentError, 'missing keyword: :salt')
end end
it "treats N as a required keyword" do it "treats N as a required keyword" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults.except(:N)) OpenSSL::KDF.scrypt("secret", **@defaults.except(:N))
}.should raise_error(ArgumentError, 'missing keyword: :N') }.should raise_error(ArgumentError, 'missing keyword: :N')
end end
it "treats r as a required keyword" do it "treats r as a required keyword" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults.except(:r)) OpenSSL::KDF.scrypt("secret", **@defaults.except(:r))
}.should raise_error(ArgumentError, 'missing keyword: :r') }.should raise_error(ArgumentError, 'missing keyword: :r')
end end
it "treats p as a required keyword" do it "treats p as a required keyword" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults.except(:p)) OpenSSL::KDF.scrypt("secret", **@defaults.except(:p))
}.should raise_error(ArgumentError, 'missing keyword: :p') }.should raise_error(ArgumentError, 'missing keyword: :p')
end end
it "treats length as a required keyword" do it "treats length as a required keyword" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults.except(:length)) OpenSSL::KDF.scrypt("secret", **@defaults.except(:length))
}.should raise_error(ArgumentError, 'missing keyword: :length') }.should raise_error(ArgumentError, 'missing keyword: :length')
end end
it "treats all keywords as required" do it "treats all keywords as required" do
-> { -> {
OpenSSL::KDF.scrypt("secret") OpenSSL::KDF.scrypt("secret")
}.should raise_error(ArgumentError, 'missing keywords: :salt, :N, :r, :p, :length') }.should raise_error(ArgumentError, 'missing keywords: :salt, :N, :r, :p, :length')
end end
it "requires N to be a power of 2" do it "requires N to be a power of 2" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, N: 2**14 - 1) OpenSSL::KDF.scrypt("secret", **@defaults, N: 2**14 - 1)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
end end
it "requires N to be at least 2" do it "requires N to be at least 2" do
key = OpenSSL::KDF.scrypt("secret", **@defaults, N: 2) key = OpenSSL::KDF.scrypt("secret", **@defaults, N: 2)
key.should == "\x06A$a\xA9!\xBE\x01\x85\xA7\x18\xBCEa\x82\xC5\xFEl\x93\xAB\xBD\xF7\x8B\x84\v\xFC\eN\xEBQ\xE6\xD2".b key.should == "\x06A$a\xA9!\xBE\x01\x85\xA7\x18\xBCEa\x82\xC5\xFEl\x93\xAB\xBD\xF7\x8B\x84\v\xFC\eN\xEBQ\xE6\xD2".b
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, N: 1) OpenSSL::KDF.scrypt("secret", **@defaults, N: 1)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, N: 0) OpenSSL::KDF.scrypt("secret", **@defaults, N: 0)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, N: -1) OpenSSL::KDF.scrypt("secret", **@defaults, N: -1)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
end end
it "requires r to be positive" do it "requires r to be positive" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, r: 0) OpenSSL::KDF.scrypt("secret", **@defaults, r: 0)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, r: -1) OpenSSL::KDF.scrypt("secret", **@defaults, r: -1)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
end end
it "requires p to be positive" do it "requires p to be positive" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, p: 0) OpenSSL::KDF.scrypt("secret", **@defaults, p: 0)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, p: -1) OpenSSL::KDF.scrypt("secret", **@defaults, p: -1)
}.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/) }.should raise_error(OpenSSL::KDF::KDFError, /EVP_PBE_scrypt/)
end end
it "requires length to be not negative" do it "requires length to be not negative" do
-> { -> {
OpenSSL::KDF.scrypt("secret", **@defaults, length: -1) OpenSSL::KDF.scrypt("secret", **@defaults, length: -1)
}.should raise_error(ArgumentError, "negative string size (or size too big)") }.should raise_error(ArgumentError, "negative string size (or size too big)")
end
end end
end end