forked from mastodon/mastodon
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update
devise-two-factor
to version 5.0.0 (mastodon#28325)
Co-authored-by: Claire <[email protected]>
- Loading branch information
1 parent
309f352
commit 1e7d5d2
Showing
12 changed files
with
163 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# frozen_string_literal: true | ||
|
||
# TODO: This file is here for legacy support during devise-two-factor upgrade. | ||
# It should be removed after all records have been migrated. | ||
|
||
module LegacyOtpSecret | ||
extend ActiveSupport::Concern | ||
|
||
private | ||
|
||
# Decrypt and return the `encrypted_otp_secret` attribute which was used in | ||
# prior versions of devise-two-factor | ||
# @return [String] The decrypted OTP secret | ||
def legacy_otp_secret | ||
return nil unless self[:encrypted_otp_secret] | ||
return nil unless self.class.otp_secret_encryption_key | ||
|
||
hmac_iterations = 2000 # a default set by the Encryptor gem | ||
key = self.class.otp_secret_encryption_key | ||
salt = Base64.decode64(encrypted_otp_secret_salt) | ||
iv = Base64.decode64(encrypted_otp_secret_iv) | ||
|
||
raw_cipher_text = Base64.decode64(encrypted_otp_secret) | ||
# The last 16 bytes of the ciphertext are the authentication tag - we use | ||
# Galois Counter Mode which is an authenticated encryption mode | ||
cipher_text = raw_cipher_text[0..-17] | ||
auth_tag = raw_cipher_text[-16..-1] # rubocop:disable Style/SlicingWithRange | ||
|
||
# this alrorithm lifted from | ||
# https://github.com/attr-encrypted/encryptor/blob/master/lib/encryptor.rb#L54 | ||
|
||
# create an OpenSSL object which will decrypt the AES cipher with 256 bit | ||
# keys in Galois Counter Mode (GCM). See | ||
# https://ruby.github.io/openssl/OpenSSL/Cipher.html | ||
cipher = OpenSSL::Cipher.new('aes-256-gcm') | ||
|
||
# tell the cipher we want to decrypt. Symmetric algorithms use a very | ||
# similar process for encryption and decryption, hence the same object can | ||
# do both. | ||
cipher.decrypt | ||
|
||
# Use a Password-Based Key Derivation Function to generate the key actually | ||
# used for encryptoin from the key we got as input. | ||
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(key, salt, hmac_iterations, cipher.key_len) | ||
|
||
# set the Initialization Vector (IV) | ||
cipher.iv = iv | ||
|
||
# The tag must be set after calling Cipher#decrypt, Cipher#key= and | ||
# Cipher#iv=, but before calling Cipher#final. After all decryption is | ||
# performed, the tag is verified automatically in the call to Cipher#final. | ||
# | ||
# If the auth_tag does not verify, then #final will raise OpenSSL::Cipher::CipherError | ||
cipher.auth_tag = auth_tag | ||
|
||
# auth_data must be set after auth_tag has been set when decrypting See | ||
# https://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-auth_data-3D | ||
# we are not adding any authenticated data but OpenSSL docs say this should | ||
# still be called. | ||
cipher.auth_data = '' | ||
|
||
# #update is (somewhat confusingly named) the method which actually | ||
# performs the decryption on the given chunk of data. Our OTP secret is | ||
# short so we only need to call it once. | ||
# | ||
# It is very important that we call #final because: | ||
# | ||
# 1. The authentication tag is checked during the call to #final | ||
# 2. Block based cipher modes (e.g. CBC) work on fixed size chunks. We need | ||
# to call #final to get it to process the last chunk properly. The output | ||
# of #final should be appended to the decrypted value. This isn't | ||
# required for streaming cipher modes but including it is a best practice | ||
# so that your code will continue to function correctly even if you later | ||
# change to a block cipher mode. | ||
cipher.update(cipher_text) + cipher.final | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
class AddOtpSecretToUser < ActiveRecord::Migration[7.1] | ||
def change | ||
add_column :users, :otp_secret, :string | ||
end | ||
end |
39 changes: 39 additions & 0 deletions
39
db/post_migrate/20240307180905_migrate_devise_two_factor_secrets.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
|
||
class MigrateDeviseTwoFactorSecrets < ActiveRecord::Migration[7.1] | ||
disable_ddl_transaction! | ||
|
||
class MigrationUser < ApplicationRecord | ||
self.table_name = :users | ||
|
||
devise :two_factor_authenticatable, | ||
otp_secret_encryption_key: Rails.configuration.x.otp_secret | ||
|
||
include LegacyOtpSecret # Must be after the above `devise` line in order to override the legacy method | ||
end | ||
|
||
def up | ||
MigrationUser.reset_column_information | ||
|
||
users_with_otp_enabled.find_each do |user| | ||
# Gets the new value on already-updated users | ||
# Falls back to legacy value on not-yet-migrated users | ||
otp_secret = user.otp_secret | ||
|
||
Rails.logger.debug { "Processing #{user.email}" } | ||
|
||
# This is a no-op for migrated users and updates format for not migrated | ||
user.update!(otp_secret: otp_secret) | ||
end | ||
end | ||
|
||
def down | ||
raise ActiveRecord::IrreversibleMigration | ||
end | ||
|
||
private | ||
|
||
def users_with_otp_enabled | ||
MigrationUser.where(otp_required_for_login: true, otp_secret: nil) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters