mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Apply syntax_tree formatting to lib/*
This commit is contained in:
@@ -35,7 +35,7 @@ module Webauthn
|
||||
ChallengeSession.new(
|
||||
challenge: SecureRandom.hex(30),
|
||||
rp_id: Discourse.current_hostname,
|
||||
rp_name: SiteSetting.title
|
||||
rp_name: SiteSetting.title,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
require 'cose'
|
||||
require "cose"
|
||||
|
||||
module Webauthn
|
||||
class SecurityKeyAuthenticationService < SecurityKeyBaseValidationService
|
||||
|
||||
##
|
||||
# See https://w3c.github.io/webauthn/#sctn-verifying-assertion for
|
||||
# the steps followed here. Memoized methods are called in their
|
||||
# place in the step flow to make the process clearer.
|
||||
def authenticate_security_key
|
||||
return false if @params.blank? || (!@params.is_a?(Hash) && !@params.is_a?(ActionController::Parameters))
|
||||
if @params.blank? || (!@params.is_a?(Hash) && !@params.is_a?(ActionController::Parameters))
|
||||
return false
|
||||
end
|
||||
|
||||
# 3. Identify the user being authenticated and verify that this user is the
|
||||
# owner of the public key credential source credentialSource identified by credential.id:
|
||||
security_key = UserSecurityKey.find_by(credential_id: @params[:credentialId])
|
||||
raise(NotFoundError, I18n.t('webauthn.validation.not_found_error')) if security_key.blank?
|
||||
raise(OwnershipError, I18n.t('webauthn.validation.ownership_error')) if security_key.user != @current_user
|
||||
raise(NotFoundError, I18n.t("webauthn.validation.not_found_error")) if security_key.blank?
|
||||
if security_key.user != @current_user
|
||||
raise(OwnershipError, I18n.t("webauthn.validation.ownership_error"))
|
||||
end
|
||||
|
||||
# 4. Using credential.id (or credential.rawId, if base64url encoding is inappropriate for your use case),
|
||||
# look up the corresponding credential public key and let credentialPublicKey be that credential public key.
|
||||
@@ -67,18 +70,24 @@ module Webauthn
|
||||
cose_algorithm = COSE::Algorithm.find(cose_key.alg)
|
||||
|
||||
if cose_algorithm.blank?
|
||||
Rails.logger.error("Unknown COSE algorithm encountered. alg: #{cose_key.alg}. user_id: #{@current_user.id}. params: #{@params.inspect}")
|
||||
raise(UnknownCOSEAlgorithmError, I18n.t('webauthn.validation.unknown_cose_algorithm_error'))
|
||||
Rails.logger.error(
|
||||
"Unknown COSE algorithm encountered. alg: #{cose_key.alg}. user_id: #{@current_user.id}. params: #{@params.inspect}",
|
||||
)
|
||||
raise(UnknownCOSEAlgorithmError, I18n.t("webauthn.validation.unknown_cose_algorithm_error"))
|
||||
end
|
||||
|
||||
if !cose_key.to_pkey.verify(cose_algorithm.hash_function, signature, auth_data + client_data_hash)
|
||||
raise(PublicKeyError, I18n.t('webauthn.validation.public_key_error'))
|
||||
if !cose_key.to_pkey.verify(
|
||||
cose_algorithm.hash_function,
|
||||
signature,
|
||||
auth_data + client_data_hash,
|
||||
)
|
||||
raise(PublicKeyError, I18n.t("webauthn.validation.public_key_error"))
|
||||
end
|
||||
|
||||
# Success! Update the last used at time for the key.
|
||||
security_key.update(last_used: Time.zone.now)
|
||||
rescue OpenSSL::PKey::PKeyError
|
||||
raise(PublicKeyError, I18n.t('webauthn.validation.public_key_error'))
|
||||
raise(PublicKeyError, I18n.t("webauthn.validation.public_key_error"))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -9,29 +9,32 @@ module Webauthn
|
||||
end
|
||||
|
||||
def validate_webauthn_type(type_to_check)
|
||||
return if client_data['type'] == type_to_check
|
||||
raise(InvalidTypeError, I18n.t('webauthn.validation.invalid_type_error'))
|
||||
return if client_data["type"] == type_to_check
|
||||
raise(InvalidTypeError, I18n.t("webauthn.validation.invalid_type_error"))
|
||||
end
|
||||
|
||||
def validate_challenge
|
||||
return if challenge_match?
|
||||
raise(ChallengeMismatchError, I18n.t('webauthn.validation.challenge_mismatch_error'))
|
||||
raise(ChallengeMismatchError, I18n.t("webauthn.validation.challenge_mismatch_error"))
|
||||
end
|
||||
|
||||
def validate_origin
|
||||
return if origin_match?
|
||||
raise(InvalidOriginError, I18n.t('webauthn.validation.invalid_origin_error'))
|
||||
raise(InvalidOriginError, I18n.t("webauthn.validation.invalid_origin_error"))
|
||||
end
|
||||
|
||||
def validate_rp_id_hash
|
||||
return if rp_id_hash_match?
|
||||
raise(InvalidRelyingPartyIdError, I18n.t('webauthn.validation.invalid_relying_party_id_error'))
|
||||
raise(
|
||||
InvalidRelyingPartyIdError,
|
||||
I18n.t("webauthn.validation.invalid_relying_party_id_error"),
|
||||
)
|
||||
end
|
||||
|
||||
def validate_user_verification
|
||||
flags = auth_data[32].unpack("b*")[0].split('')
|
||||
return if flags[0] == '1'
|
||||
raise(UserVerificationError, I18n.t('webauthn.validation.user_verification_error'))
|
||||
flags = auth_data[32].unpack("b*")[0].split("")
|
||||
return if flags[0] == "1"
|
||||
raise(UserVerificationError, I18n.t("webauthn.validation.user_verification_error"))
|
||||
end
|
||||
|
||||
private
|
||||
@@ -49,11 +52,11 @@ module Webauthn
|
||||
end
|
||||
|
||||
def challenge_match?
|
||||
Base64.decode64(client_data['challenge']) == @challenge_params[:challenge]
|
||||
Base64.decode64(client_data["challenge"]) == @challenge_params[:challenge]
|
||||
end
|
||||
|
||||
def origin_match?
|
||||
client_data['origin'] == @challenge_params[:origin]
|
||||
client_data["origin"] == @challenge_params[:origin]
|
||||
end
|
||||
|
||||
def rp_id_hash_match?
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
require 'cbor'
|
||||
require 'cose'
|
||||
require "cbor"
|
||||
require "cose"
|
||||
|
||||
module Webauthn
|
||||
class SecurityKeyRegistrationService < SecurityKeyBaseValidationService
|
||||
|
||||
##
|
||||
# See https://w3c.github.io/webauthn/#sctn-registering-a-new-credential for
|
||||
# the registration steps followed here. Memoized methods are called in their
|
||||
@@ -50,8 +49,14 @@ module Webauthn
|
||||
# https://w3c.github.io/webauthn/#table-attestedCredentialData
|
||||
# See https://www.iana.org/assignments/cose/cose.xhtml#algorithms for supported algorithm
|
||||
# codes.
|
||||
credential_public_key, credential_public_key_bytes, credential_id = extract_public_key_and_credential_from_attestation(auth_data)
|
||||
raise(UnsupportedPublicKeyAlgorithmError, I18n.t('webauthn.validation.unsupported_public_key_algorithm_error')) if ::Webauthn::SUPPORTED_ALGORITHMS.exclude?(credential_public_key.alg)
|
||||
credential_public_key, credential_public_key_bytes, credential_id =
|
||||
extract_public_key_and_credential_from_attestation(auth_data)
|
||||
if ::Webauthn::SUPPORTED_ALGORITHMS.exclude?(credential_public_key.alg)
|
||||
raise(
|
||||
UnsupportedPublicKeyAlgorithmError,
|
||||
I18n.t("webauthn.validation.unsupported_public_key_algorithm_error"),
|
||||
)
|
||||
end
|
||||
|
||||
# 14. Verify that the values of the client extension outputs in clientExtensionResults and the authenticator
|
||||
# extension outputs in the extensions in authData are as expected, considering the client extension input
|
||||
@@ -67,8 +72,12 @@ module Webauthn
|
||||
# name [WebAuthn-Registries].
|
||||
# 16. Verify that attStmt is a correct attestation statement, conveying a valid attestation signature,
|
||||
# by using the attestation statement format fmt’s verification procedure given attStmt, authData and hash.
|
||||
if ::Webauthn::VALID_ATTESTATION_FORMATS.exclude?(attestation['fmt']) || attestation['fmt'] != 'none'
|
||||
raise(UnsupportedAttestationFormatError, I18n.t('webauthn.validation.unsupported_attestation_format_error'))
|
||||
if ::Webauthn::VALID_ATTESTATION_FORMATS.exclude?(attestation["fmt"]) ||
|
||||
attestation["fmt"] != "none"
|
||||
raise(
|
||||
UnsupportedAttestationFormatError,
|
||||
I18n.t("webauthn.validation.unsupported_attestation_format_error"),
|
||||
)
|
||||
end
|
||||
|
||||
#==================================================
|
||||
@@ -92,7 +101,9 @@ module Webauthn
|
||||
# the registration, e.g. while deleting the older registration.
|
||||
encoded_credential_id = Base64.strict_encode64(credential_id)
|
||||
endcoded_public_key = Base64.strict_encode64(credential_public_key_bytes)
|
||||
raise(CredentialIdInUseError, I18n.t('webauthn.validation.credential_id_in_use_error')) if UserSecurityKey.exists?(credential_id: encoded_credential_id)
|
||||
if UserSecurityKey.exists?(credential_id: encoded_credential_id)
|
||||
raise(CredentialIdInUseError, I18n.t("webauthn.validation.credential_id_in_use_error"))
|
||||
end
|
||||
|
||||
# 20. If the attestation statement attStmt verified successfully and is found to be trustworthy,
|
||||
# then register the new credential with the account that was denoted in options.user, by
|
||||
@@ -103,10 +114,10 @@ module Webauthn
|
||||
credential_id: encoded_credential_id,
|
||||
public_key: endcoded_public_key,
|
||||
name: @params[:name],
|
||||
factor_type: UserSecurityKey.factor_types[:second_factor]
|
||||
factor_type: UserSecurityKey.factor_types[:second_factor],
|
||||
)
|
||||
rescue CBOR::UnpackError, CBOR::TypeError, CBOR::MalformedFormatError, CBOR::StackError
|
||||
raise MalformedAttestationError, I18n.t('webauthn.validation.malformed_attestation_error')
|
||||
raise MalformedAttestationError, I18n.t("webauthn.validation.malformed_attestation_error")
|
||||
end
|
||||
|
||||
private
|
||||
@@ -116,7 +127,7 @@ module Webauthn
|
||||
end
|
||||
|
||||
def auth_data
|
||||
@auth_data ||= attestation['authData']
|
||||
@auth_data ||= attestation["authData"]
|
||||
end
|
||||
|
||||
def extract_public_key_and_credential_from_attestation(auth_data)
|
||||
@@ -128,9 +139,12 @@ module Webauthn
|
||||
|
||||
attested_credential_data_start_position = rp_id_length + flags_length + sign_count_length # 37
|
||||
attested_credential_data_length = auth_data.size - attested_credential_data_start_position
|
||||
attested_credential_data = auth_data[
|
||||
attested_credential_data_start_position..(attested_credential_data_start_position + attested_credential_data_length - 1)
|
||||
]
|
||||
attested_credential_data =
|
||||
auth_data[
|
||||
attested_credential_data_start_position..(
|
||||
attested_credential_data_start_position + attested_credential_data_length - 1
|
||||
)
|
||||
]
|
||||
|
||||
# see https://w3c.github.io/webauthn/#attested-credential-data for lengths
|
||||
# of data for extraction
|
||||
@@ -139,9 +153,10 @@ module Webauthn
|
||||
credential_id = attested_credential_data[18..(18 + credential_id_length - 1)]
|
||||
|
||||
public_key_start_position = 18 + credential_id_length
|
||||
public_key_bytes = attested_credential_data[
|
||||
public_key_start_position..(public_key_start_position + attested_credential_data.size - 1)
|
||||
]
|
||||
public_key_bytes =
|
||||
attested_credential_data[
|
||||
public_key_start_position..(public_key_start_position + attested_credential_data.size - 1)
|
||||
]
|
||||
public_key = COSE::Key.deserialize(public_key_bytes)
|
||||
|
||||
[public_key, public_key_bytes, credential_id]
|
||||
|
||||
Reference in New Issue
Block a user