Skip to content

Instantly share code, notes, and snippets.

@ttuan
Last active October 29, 2024 15:26
Show Gist options
  • Save ttuan/0bec9f0cd3797b13cffdb0adc535ad7d to your computer and use it in GitHub Desktop.
Save ttuan/0bec9f0cd3797b13cffdb0adc535ad7d to your computer and use it in GitHub Desktop.
Firebase SCrypt in Ruby. This file is Ruby version for this code: https://github.com/JaakkoL/firebase-scrypt-python/blob/master/firebasescrypt/firebasescrypt.py
# MIT License
# Copyright (c) 2021 ttuan
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
require 'base64'
require 'scrypt'
def generate_derived_key password, salt, salt_separator, rounds, mem_cost
# Generates derived key from known parameters
n = 2 ** mem_cost
p = 1
r = rounds
# We're only using first 32 bytes of the derived key to match expected key length.
derived_key_length = 32
user_salt = Base64.decode64(salt).b
salt_separator = Base64.decode64(salt_separator).b
password = password.b
# https://stackoverflow.com/questions/40409876/encrypting-a-private-key-in-ruby-using-aes-128-ctr-scrypt
SCrypt::Engine.scrypt(password, (user_salt + salt_separator), n, r, p, derived_key_length)
end
# https://gist.github.com/treble37/55459f5f0f218ab9b5ebe74b325f4a41#file-gistfile1-L33-L43
def encrypt signer_key, derived_key
# https://github.com/spreedly/gala/blob/master/lib/gala/payment_token.rb#L113-L116
iv = 0.chr * 16
cipher = OpenSSL::Cipher::AES256.new :CTR
cipher.encrypt
cipher.iv = iv
cipher.key = derived_key
cipher.update(signer_key) + cipher.final
end
def verify_password password, known_hash, salt, salt_separator, signer_key, rounds, mem_cost
derived_key = generate_derived_key password, salt, salt_separator, rounds, mem_cost
signer_key = Base64.decode64(signer_key).b
result = encrypt(signer_key, derived_key)
# We use `strict_encode64` instead of `encode64` to ignore new line in result
# https://stackoverflow.com/questions/2620975/strange-n-in-base64-encoded-string-in-ruby
password_hash = Base64.strict_encode64(result).encode('utf-8')
# Please change this line to `Rack::Utils.secure_compare(a, b)` or `ActiveSupport::SecurityUtils.secure_compare(a, b)`
# If we use `==` method, it could lead to timing attacks.
# https://github.com/mailgun/documentation/issues/133
password_hash == known_hash
end
# Testing
salt_separator = "Bw=="
signer_key = "jxspr8Ki0RYycVU8zykbdLGjFQ3McFUH0uiiTvC8pVMXAn210wjLNmdZJzxUECKbm0QsEmYUSDzZvpjeJ9WmXA=="
rounds= 8
mem_cost=14
password = "user1password"
salt = "42xEC+ixf3L2lw=="
password_hash="lSrfV15cpx95/sZS2W9c9Kp6i/LVgQNDNC/qzrCnh1SAyZvqmZqAjTdn3aoItz+VHjoZilo78198JAdRuid5lQ=="
is_valid = verify_password(password, password_hash, salt, salt_separator, signer_key, rounds, mem_cost)
puts is_valid
@obskein
Copy link

obskein commented Mar 1, 2024

Can you put an MIT license on this?

@ttuan
Copy link
Author

ttuan commented Mar 1, 2024

@obskein I added MIT license 🚀

May I ask the reason why you need me put MIT license? 🤔 (just questioning)

@obskein
Copy link

obskein commented Mar 1, 2024

Licenses are good for software development, they provide clarity for people and how software can and should be used. Thank you ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment