Skip to content

Bcrypt password string to byte conversion #15934

Open
@straight-shoota

Description

@straight-shoota

While reviewing #15931 I noticed some oddities with the conversion from String to Bytes in Bcrypt.new and .hash_secret.

# Creates a new `Crypto::Bcrypt` object from the given *password* with *salt* and *cost*.
#
# ```
# require "crypto/bcrypt"
#
# password = Crypto::Bcrypt.new "secret", "salt_of_16_chars"
# password.digest
# ```
def self.new(password : String, salt : String, cost = DEFAULT_COST)
# We make a clone here to we don't keep a mutable reference to the original string
passwordb = password.to_unsafe.to_slice(password.bytesize + 1).clone # include leading 0
saltb = Base64.decode(salt, SALT_SIZE)
new(passwordb, saltb, cost)
end

The first oddity is that .clone call. There's a code comment that claims this is to avoid keeping a mutable reference to the original string. But AFAIK BCrypt does not mutate the password slice. It exposes it as #password which might make it available for other things. But it's still a read-only slice. So I don't think there's great danger here. We should be able to avoid the extra allocation.
The API docs for the other overload, which accepts Bytes explicitly demonstrates its use with "secret".to_slice.

Also, why does the byte slice include the trailing zero?
At first glance, this seems wrong because trailing zeros are an implementation detail of String.
I'd expect this to cause compatibility issues with other implementations. But this actually seems to work... Is a trailing zero expected in bcrypt passwords?

Related: #15276

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:bugA bug in the code. Does not apply to documentation, specs, etc.topic:stdlib:crypto

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions