2017-07-14 20:41:49 +02:00
# frozen_string_literal: true
# Implemented according to HTTP signatures (Draft 6)
# <https://tools.ietf.org/html/draft-cavage-http-signatures-06>
module SignatureVerification
extend ActiveSupport :: Concern
2019-07-09 03:27:35 +02:00
include DomainControlHelper
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
EXPIRATION_WINDOW_LIMIT = 12 . hours
CLOCK_SKEW_MARGIN = 1 . hour
class SignatureVerificationError < StandardError ; end
class SignatureParamsParser < Parslet :: Parser
rule ( :token ) { match ( " [0-9a-zA-Z! # $%&'*+.^_`|~-] " ) . repeat ( 1 ) . as ( :token ) }
rule ( :quoted_string ) { str ( '"' ) >> ( qdtext | quoted_pair ) . repeat . as ( :quoted_string ) >> str ( '"' ) }
# qdtext and quoted_pair are not exactly according to spec but meh
rule ( :qdtext ) { match ( '[^\\\\"]' ) }
rule ( :quoted_pair ) { str ( '\\' ) >> any }
rule ( :bws ) { match ( '\s' ) . repeat }
rule ( :param ) { ( token . as ( :key ) >> bws >> str ( '=' ) >> bws >> ( token | quoted_string ) . as ( :value ) ) . as ( :param ) }
rule ( :comma ) { bws >> str ( ',' ) >> bws }
# Old versions of node-http-signature add an incorrect "Signature " prefix to the header
rule ( :buggy_prefix ) { str ( 'Signature ' ) }
rule ( :params ) { buggy_prefix . maybe >> ( param >> ( comma >> param ) . repeat ) . as ( :params ) }
root ( :params )
end
class SignatureParamsTransformer < Parslet :: Transform
2022-12-15 17:11:58 +01:00
rule ( params : subtree ( :param ) ) do
( param . is_a? ( Array ) ? param : [ param ] ) . each_with_object ( { } ) { | ( key , value ) , hash | hash [ key ] = value }
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
end
rule ( param : { key : simple ( :key ) , value : simple ( :val ) } ) do
[ key , val ]
end
rule ( quoted_string : simple ( :string ) ) do
string . to_s
end
rule ( token : simple ( :string ) ) do
string . to_s
end
end
2022-09-21 22:45:57 +02:00
def require_account_signature!
2023-01-18 16:47:56 +01:00
render json : signature_verification_failure_reason , status : signature_verification_failure_code unless signed_request_account
2019-07-11 20:11:09 +02:00
end
2022-09-21 22:45:57 +02:00
def require_actor_signature!
2023-01-18 16:47:56 +01:00
render json : signature_verification_failure_reason , status : signature_verification_failure_code unless signed_request_actor
2022-09-21 22:45:57 +02:00
end
2017-07-14 20:41:49 +02:00
def signed_request?
request . headers [ 'Signature' ] . present?
end
2017-10-03 23:21:19 +02:00
def signature_verification_failure_reason
2019-07-11 20:11:09 +02:00
@signature_verification_failure_reason
end
def signature_verification_failure_code
@signature_verification_failure_code || 401
2017-10-03 23:21:19 +02:00
end
2019-08-18 18:03:56 +02:00
def signature_key_id
signature_params [ 'keyId' ]
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
rescue SignatureVerificationError
nil
2019-08-18 18:03:56 +02:00
end
2017-07-14 20:41:49 +02:00
def signed_request_account
2022-09-21 22:45:57 +02:00
signed_request_actor . is_a? ( Account ) ? signed_request_actor : nil
end
def signed_request_actor
return @signed_request_actor if defined? ( @signed_request_actor )
2017-07-14 20:41:49 +02:00
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
raise SignatureVerificationError , 'Request not signed' unless signed_request?
raise SignatureVerificationError , 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters?
raise SignatureVerificationError , 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless %w( rsa-sha256 hs2019 ) . include? ( signature_algorithm )
raise SignatureVerificationError , 'Signed request date outside acceptable time window' unless matches_time_window?
2017-07-14 20:41:49 +02:00
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
verify_signature_strength!
2020-11-01 23:38:31 +01:00
verify_body_digest!
2017-07-14 20:41:49 +02:00
2022-09-21 22:45:57 +02:00
actor = actor_from_key_id ( signature_params [ 'keyId' ] )
2017-07-14 20:41:49 +02:00
2022-09-21 22:45:57 +02:00
raise SignatureVerificationError , " Public key not found for key #{ signature_params [ 'keyId' ] } " if actor . nil?
2017-07-14 20:41:49 +02:00
signature = Base64 . decode64 ( signature_params [ 'signature' ] )
2024-01-03 12:29:26 +01:00
compare_signed_string = build_signed_string ( include_query_string : true )
2017-07-14 20:41:49 +02:00
2022-09-21 22:45:57 +02:00
return actor unless verify_signature ( actor , signature , compare_signed_string ) . nil?
2017-09-28 17:50:14 +02:00
2024-01-03 12:29:26 +01:00
# Compatibility quirk with older Mastodon versions
compare_signed_string = build_signed_string ( include_query_string : false )
return actor unless verify_signature ( actor , signature , compare_signed_string ) . nil?
2022-09-21 22:45:57 +02:00
actor = stoplight_wrap_request { actor_refresh_key! ( actor ) }
2019-01-07 21:45:13 +01:00
2023-01-18 16:47:56 +01:00
raise SignatureVerificationError , " Could not refresh public key #{ signature_params [ 'keyId' ] } " if actor . nil?
2019-01-07 21:45:13 +01:00
2024-01-03 12:29:26 +01:00
compare_signed_string = build_signed_string ( include_query_string : true )
return actor unless verify_signature ( actor , signature , compare_signed_string ) . nil?
# Compatibility quirk with older Mastodon versions
compare_signed_string = build_signed_string ( include_query_string : false )
2022-09-21 22:45:57 +02:00
return actor unless verify_signature ( actor , signature , compare_signed_string ) . nil?
2019-01-07 21:45:13 +01:00
2023-01-18 16:47:56 +01:00
fail_with! " Verification failed for #{ actor . to_log_human_identifier } #{ actor . uri } using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256) " , signed_string : compare_signed_string , signature : signature_params [ 'signature' ]
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
rescue SignatureVerificationError = > e
2022-09-20 23:30:26 +02:00
fail_with! e . message
rescue HTTP :: Error , OpenSSL :: SSL :: SSLError = > e
fail_with! " Failed to fetch remote data: #{ e . message } "
2022-09-21 14:48:35 +02:00
rescue Mastodon :: UnexpectedResponseError
2022-09-20 23:30:26 +02:00
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
rescue Stoplight :: Error :: RedLight
fail_with! 'Fetching attempt skipped because of recent connection failure'
2017-07-14 20:41:49 +02:00
end
2017-08-09 23:54:14 +02:00
def request_body
@request_body || = request . raw_post
end
2017-07-14 20:41:49 +02:00
private
2023-01-18 16:47:56 +01:00
def fail_with! ( message , ** options )
2023-09-06 12:17:22 +02:00
Rails . logger . debug { " Signature verification failed: #{ message } " }
2023-08-29 10:29:07 +02:00
2023-01-18 16:47:56 +01:00
@signature_verification_failure_reason = { error : message } . merge ( options )
2022-09-21 22:45:57 +02:00
@signed_request_actor = nil
2022-09-20 23:30:26 +02:00
end
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
def signature_params
@signature_params || = begin
raw_signature = request . headers [ 'Signature' ]
tree = SignatureParamsParser . new . parse ( raw_signature )
SignatureParamsTransformer . new . apply ( tree )
end
rescue Parslet :: ParseFailed
raise SignatureVerificationError , 'Error parsing signature parameters'
end
def signature_algorithm
signature_params . fetch ( 'algorithm' , 'hs2019' )
end
def signed_headers
2023-03-16 02:34:00 +01:00
signature_params . fetch ( 'headers' , signature_algorithm == 'hs2019' ? '(created)' : 'date' ) . downcase . split
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
end
def verify_signature_strength!
raise SignatureVerificationError , 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers . include? ( 'date' ) || signed_headers . include? ( '(created)' )
raise SignatureVerificationError , 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers . include? ( Request :: REQUEST_TARGET ) || signed_headers . include? ( 'digest' )
2020-11-01 23:38:31 +01:00
raise SignatureVerificationError , 'Mastodon requires the Host header to be signed when doing a GET request' if request . get? && ! signed_headers . include? ( 'host' )
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
raise SignatureVerificationError , 'Mastodon requires the Digest header to be signed when doing a POST request' if request . post? && ! signed_headers . include? ( 'digest' )
end
2020-11-01 23:38:31 +01:00
def verify_body_digest!
return unless signed_headers . include? ( 'digest' )
2021-02-26 17:40:27 +01:00
raise SignatureVerificationError , 'Digest header missing' unless request . headers . key? ( 'Digest' )
2020-11-01 23:38:31 +01:00
digests = request . headers [ 'Digest' ] . split ( ',' ) . map { | digest | digest . split ( '=' , 2 ) } . map { | key , value | [ key . downcase , value ] }
sha256 = digests . assoc ( 'sha-256' )
raise SignatureVerificationError , " Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{ digests . map ( & :first ) . join ( ', ' ) } " if sha256 . nil?
2022-09-20 23:30:26 +02:00
return if body_digest == sha256 [ 1 ]
digest_size = begin
Base64 . strict_decode64 ( sha256 [ 1 ] . strip ) . length
rescue ArgumentError
raise SignatureVerificationError , " Invalid Digest value. The provided Digest value is not a valid base64 string. Given digest: #{ sha256 [ 1 ] } "
end
raise SignatureVerificationError , " Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{ sha256 [ 1 ] } " if digest_size != 32
2023-02-20 06:58:28 +01:00
2022-09-20 23:30:26 +02:00
raise SignatureVerificationError , " Invalid Digest value. Computed SHA-256 digest: #{ body_digest } ; given: #{ sha256 [ 1 ] } "
2020-11-01 23:38:31 +01:00
end
2022-09-21 22:45:57 +02:00
def verify_signature ( actor , signature , compare_signed_string )
if actor . keypair . public_key . verify ( OpenSSL :: Digest . new ( 'SHA256' ) , signature , compare_signed_string )
@signed_request_actor = actor
@signed_request_actor
2019-01-07 21:45:13 +01:00
end
rescue OpenSSL :: PKey :: RSAError
nil
end
2024-01-03 12:29:26 +01:00
def build_signed_string ( include_query_string : true )
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
signed_headers . map do | signed_header |
2023-05-04 05:51:18 +02:00
case signed_header
when Request :: REQUEST_TARGET
2024-01-03 12:29:26 +01:00
if include_query_string
" #{ Request :: REQUEST_TARGET } : #{ request . method . downcase } #{ request . original_fullpath } "
else
# Current versions of Mastodon incorrectly omit the query string from the (request-target) pseudo-header.
# Therefore, temporarily support such incorrect signatures for compatibility.
# TODO: remove eventually some time after release of the fixed version
" #{ Request :: REQUEST_TARGET } : #{ request . method . downcase } #{ request . path } "
end
2023-05-04 05:51:18 +02:00
when '(created)'
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
raise SignatureVerificationError , 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'
raise SignatureVerificationError , 'Pseudo-header (created) used but corresponding argument missing' if signature_params [ 'created' ] . blank?
" (created): #{ signature_params [ 'created' ] } "
2023-05-04 05:51:18 +02:00
when '(expires)'
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
raise SignatureVerificationError , 'Invalid pseudo-header (expires) for rsa-sha256' unless signature_algorithm == 'hs2019'
raise SignatureVerificationError , 'Pseudo-header (expires) used but corresponding argument missing' if signature_params [ 'expires' ] . blank?
" (expires): #{ signature_params [ 'expires' ] } "
2017-07-14 20:41:49 +02:00
else
" #{ signed_header } : #{ request . headers [ to_header_name ( signed_header ) ] } "
end
end . join ( " \n " )
end
2018-10-12 00:15:55 +02:00
def matches_time_window?
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
created_time = nil
expires_time = nil
2018-10-12 00:15:55 +02:00
begin
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
if signature_algorithm == 'hs2019' && signature_params [ 'created' ] . present?
created_time = Time . at ( signature_params [ 'created' ] . to_i ) . utc
elsif request . headers [ 'Date' ] . present?
created_time = Time . httpdate ( request . headers [ 'Date' ] ) . utc
end
expires_time = Time . at ( signature_params [ 'expires' ] . to_i ) . utc if signature_params [ 'expires' ] . present?
2023-01-18 16:47:56 +01:00
rescue ArgumentError = > e
raise SignatureVerificationError , " Invalid Date header: #{ e . message } "
2018-10-12 00:15:55 +02:00
end
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
expires_time || = created_time + 5 . minutes unless created_time . nil?
expires_time = [ expires_time , created_time + EXPIRATION_WINDOW_LIMIT ] . min unless created_time . nil?
return false if created_time . present? && created_time > Time . now . utc + CLOCK_SKEW_MARGIN
return false if expires_time . present? && Time . now . utc > expires_time + CLOCK_SKEW_MARGIN
true
2018-10-12 00:15:55 +02:00
end
2017-08-09 23:54:14 +02:00
def body_digest
2020-11-01 23:38:31 +01:00
@body_digest || = Digest :: SHA256 . base64digest ( request_body )
2017-08-09 23:54:14 +02:00
end
2017-07-14 20:41:49 +02:00
def to_header_name ( name )
2023-02-08 02:25:20 +01:00
name . split ( '-' ) . map ( & :capitalize ) . join ( '-' )
2017-07-14 20:41:49 +02:00
end
Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft
https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html
- add support for the “hs2019” signature algorithm (assumed to be equivalent
to RSA-SHA256, since we do not have a mechanism to specify the algorithm
within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
header to accepting signatures created up to 1 hour in the future and
expiring up to 1 hour in the past (but only allowing expiration dates up to
12 hours after the creation date)
This doesn't conform with the current draft, as it doesn't permit accounting
for clock skew.
This, however, should be addressed in a next version of the draft:
https://github.com/httpwg/http-extensions/pull/1235
* Add additional signature requirements
* Rewrite signature params parsing using Parslet
* Make apparent which signature algorithm Mastodon on verification failure
Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.
* Add workaround for PeerTube's invalid signature header
The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.
This commit adds a workaround for that.
* Fix `signature_key_id` raising an exception
Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.
This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.
* Move extra HTTP signature helper methods to private methods
* Relax (request-target) requirement to (request-target) || digest
This lets requests from Plume work without lowering security significantly.
2020-08-24 18:21:07 +02:00
def missing_required_signature_parameters?
signature_params [ 'keyId' ] . blank? || signature_params [ 'signature' ] . blank?
2017-08-09 23:54:14 +02:00
end
2022-09-21 22:45:57 +02:00
def actor_from_key_id ( key_id )
2019-07-11 20:11:09 +02:00
domain = key_id . start_with? ( 'acct:' ) ? key_id . split ( '@' ) . last : key_id
if domain_not_allowed? ( domain )
@signature_verification_failure_code = 403
return
end
2017-08-09 23:54:14 +02:00
if key_id . start_with? ( 'acct:' )
2023-05-02 21:07:45 +02:00
stoplight_wrap_request { ResolveAccountService . new . call ( key_id . delete_prefix ( 'acct:' ) , suppress_errors : false ) }
2017-08-09 23:54:14 +02:00
elsif ! ActivityPub :: TagManager . instance . local_uri? ( key_id )
2022-09-21 22:45:57 +02:00
account = ActivityPub :: TagManager . instance . uri_to_actor ( key_id )
2024-02-01 15:56:46 +01:00
account || = stoplight_wrap_request { ActivityPub :: FetchRemoteKeyService . new . call ( key_id , suppress_errors : false ) }
2017-08-21 22:57:34 +02:00
account
2017-08-09 23:54:14 +02:00
end
2022-09-20 23:30:26 +02:00
rescue Mastodon :: PrivateNetworkAddressError = > e
raise SignatureVerificationError , " Requests to private network addresses are disallowed (tried to query #{ e . host } ) "
2022-09-21 22:45:57 +02:00
rescue Mastodon :: HostValidationError , ActivityPub :: FetchRemoteActorService :: Error , ActivityPub :: FetchRemoteKeyService :: Error , Webfinger :: Error = > e
2022-09-20 23:30:26 +02:00
raise SignatureVerificationError , e . message
2017-07-14 20:41:49 +02:00
end
2019-01-07 21:45:13 +01:00
2019-05-23 15:22:39 +02:00
def stoplight_wrap_request ( & block )
Stoplight ( " source: #{ request . remote_ip } " , & block )
. with_threshold ( 1 )
. with_cool_off_time ( 5 . minutes . seconds )
2019-08-18 18:03:56 +02:00
. with_error_handler { | error , handle | error . is_a? ( HTTP :: Error ) || error . is_a? ( OpenSSL :: SSL :: SSLError ) ? handle . call ( error ) : raise ( error ) }
2019-05-23 15:22:39 +02:00
. run
end
2022-09-21 22:45:57 +02:00
def actor_refresh_key! ( actor )
return if actor . local? || ! actor . activitypub?
return actor . refresh! if actor . respond_to? ( :refresh! ) && actor . possibly_stale?
ActivityPub :: FetchRemoteActorService . new . call ( actor . uri , only_key : true , suppress_errors : false )
2022-09-20 23:30:26 +02:00
rescue Mastodon :: PrivateNetworkAddressError = > e
raise SignatureVerificationError , " Requests to private network addresses are disallowed (tried to query #{ e . host } ) "
2022-09-21 22:45:57 +02:00
rescue Mastodon :: HostValidationError , ActivityPub :: FetchRemoteActorService :: Error , Webfinger :: Error = > e
2022-09-20 23:30:26 +02:00
raise SignatureVerificationError , e . message
2019-01-07 21:45:13 +01:00
end
2017-07-14 20:41:49 +02:00
end