2019-07-15 07:01:22 -06:00
# Pleroma: A lightweight social networking server
2021-01-12 23:49:20 -07:00
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
2019-07-15 07:01:22 -06:00
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.SignatureTest do
2023-08-01 04:43:50 -06:00
use Pleroma.DataCase , async : false
2023-08-04 05:50:50 -06:00
@moduletag :mocked
2019-07-15 07:01:22 -06:00
2019-07-20 07:07:51 -06:00
import ExUnit.CaptureLog
2019-07-15 07:01:22 -06:00
import Pleroma.Factory
import Tesla.Mock
2019-08-23 12:17:14 -06:00
import Mock
2019-07-15 07:01:22 -06:00
alias Pleroma.Signature
setup do
mock ( fn env -> apply ( HttpRequestMock , :request , [ env ] ) end )
:ok
end
@private_key " -----BEGIN RSA PRIVATE KEY----- \n MIIEpQIBAAKCAQEA48qb4v6kqigZutO9Ot0wkp27GIF2LiVaADgxQORZozZR63jH \n TaoOrS3Xhngbgc8SSOhfXET3omzeCLqaLNfXnZ8OXmuhJfJSU6mPUvmZ9QdT332j \n fN/g3iWGhYMf/M9ftCKh96nvFVO/tMruzS9xx7tkrfJjehdxh/3LlJMMImPtwcD7 \n kFXwyt1qZTAU6Si4oQAJxRDQXHp1ttLl3Ob829VM7IKkrVmY8TD+JSlV0jtVJPj6 \n 1J19ytKTx/7UaucYvb9HIiBpkuiy5n/irDqKLVf5QEdZoNCdojOZlKJmTLqHhzKP \n 3E9TxsUjhrf4/EqegNc/j982RvOxeu4i40zMQwIDAQABAoIBAQDH5DXjfh21i7b4 \n cXJuw0cqget617CDUhemdakTDs9yH+rHPZd3mbGDWuT0hVVuFe4vuGpmJ8c+61X0 \n RvugOlBlavxK8xvYlsqTzAmPgKUPljyNtEzQ+gz0I+3mH2jkin2rL3D+SksZZgKm \n fiYMPIQWB2WUF04gB46DDb2mRVuymGHyBOQjIx3WC0KW2mzfoFUFRlZEF+Nt8Ilw \n T+g/u0aZ1IWoszbsVFOEdghgZET0HEarum0B2Je/ozcPYtwmU10iBANGMKdLqaP/ \n j954BPunrUf6gmlnLZKIKklJj0advx0NA+cL79+zeVB3zexRYSA5o9q0WPhiuTwR \n /aedWHnBAoGBAP0sDWBAM1Y4TRAf8ZI9PcztwLyHPzfEIqzbObJJnx1icUMt7BWi \n +/RMOnhrlPGE1kMhOqSxvXYN3u+eSmWTqai2sSH5Hdw2EqnrISSTnwNUPINX7fHH \n jEkgmXQ6ixE48SuBZnb4w1EjdB/BA6/sjL+FNhggOc87tizLTkMXmMtTAoGBAOZV \n +wPuAMBDBXmbmxCuDIjoVmgSlgeRunB1SA8RCPAFAiUo3+/zEgzW2Oz8kgI+xVwM \n 33XkLKrWG1Orhpp6Hm57MjIc5MG+zF4/YRDpE/KNG9qU1tiz0UD5hOpIU9pP4bR/ \n gxgPxZzvbk4h5BfHWLpjlk8UUpgk6uxqfti48c1RAoGBALBOKDZ6HwYRCSGMjUcg \n 3NPEUi84JD8qmFc2B7Tv7h2he2ykIz9iFAGpwCIyETQsJKX1Ewi0OlNnD3RhEEAy \n l7jFGQ+mkzPSeCbadmcpYlgIJmf1KN/x7fDTAepeBpCEzfZVE80QKbxsaybd3Dp8 \n CfwpwWUFtBxr4c7J+gNhAGe/AoGAPn8ZyqkrPv9wXtyfqFjxQbx4pWhVmNwrkBPi \n Z2Qh3q4dNOPwTvTO8vjghvzIyR8rAZzkjOJKVFgftgYWUZfM5gE7T2mTkBYq8W+U \n 8LetF+S9qAM2gDnaDx0kuUTCq7t87DKk6URuQ/SbI0wCzYjjRD99KxvChVGPBHKo \n 1DjqMuECgYEAgJGNm7/lJCS2wk81whfy/ttKGsEIkyhPFYQmdGzSYC5aDc2gp1R3 \n xtOkYEvdjfaLfDGEa4UX8CHHF+w3t9u8hBtcdhMH6GYb9iv6z0VBTt4A/11HUR49 \n 3Z7TQ18Iyh3jAUCzFV9IJlLIExq5Y7P4B3ojWFBN607sDCt8BMPbDYs= \n -----END RSA PRIVATE KEY----- "
2020-03-31 22:58:48 -06:00
@public_key " -----BEGIN PUBLIC KEY----- \n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo \n C/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz \n CJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v \n eR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc \n HKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8 \n PGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9 \n QQIDAQAB \n -----END PUBLIC KEY----- \n "
2019-07-15 07:01:22 -06:00
@rsa_public_key {
:RSAPublicKey ,
24_650_000_183_914_698_290_885_268_529_673_621_967_457_234_469_123_179_408_466_269_598_577_505_928_170_923_974_132_111_403_341_217_239_999_189_084_572_368_839_502_170_501_850_920_051_662_384_964_248_315_257_926_552_945_648_828_895_432_624_227_029_881_278_113_244_073_644_360_744_504_606_177_648_469_825_063_267_913_017_309_199_785_535_546_734_904_379_798_564_556_494_962_268_682_532_371_146_333_972_821_570_577_277_375_020_977_087_539_994_500_097_107_935_618_711_808_260_846_821_077_839_605_098_669_707_417_692_791_905_543_116_911_754_774_323_678_879_466_618_738_207_538_013_885_607_095_203_516_030_057_611_111_308_904_599_045_146_148_350_745_339_208_006_497_478_057_622_336_882_506_112_530_056_970_653_403_292_123_624_453_213_574_011_183_684_739_084_105_206_483_178_943_532_208_537_215_396_831_110_268_758_639_826_369_857 ,
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
65_537
}
2019-07-17 13:18:19 -06:00
defp make_fake_signature ( key_id ) , do : " keyId= \" #{ key_id } \" "
defp make_fake_conn ( key_id ) ,
do : % Plug.Conn { req_headers : %{ " signature " = > make_fake_signature ( key_id <> " # main-key " ) } }
2019-07-15 07:01:22 -06:00
describe " fetch_public_key/1 " do
test " it returns key " do
expected_result = { :ok , @rsa_public_key }
2020-03-31 22:58:48 -06:00
user = insert ( :user , public_key : @public_key )
2019-07-15 07:01:22 -06:00
2019-07-17 13:18:19 -06:00
assert Signature . fetch_public_key ( make_fake_conn ( user . ap_id ) ) == expected_result
2019-07-15 07:01:22 -06:00
end
test " it returns error when not found user " do
2019-07-20 07:07:51 -06:00
assert capture_log ( fn ->
2020-05-01 07:54:38 -06:00
assert Signature . fetch_public_key ( make_fake_conn ( " https://test-ap-id " ) ) ==
{ :error , :error }
2019-07-20 07:07:51 -06:00
end ) =~ " [error] Could not decode user "
2019-07-15 07:01:22 -06:00
end
2020-03-31 22:58:48 -06:00
test " it returns error if public key is nil " do
user = insert ( :user , public_key : nil )
2019-07-15 07:01:22 -06:00
2019-07-23 10:47:22 -06:00
assert Signature . fetch_public_key ( make_fake_conn ( user . ap_id ) ) == { :error , :error }
2019-07-15 07:01:22 -06:00
end
end
describe " refetch_public_key/1 " do
test " it returns key " do
ap_id = " https://mastodon.social/users/lambadalambda "
2019-07-23 10:47:22 -06:00
assert Signature . refetch_public_key ( make_fake_conn ( ap_id ) ) == { :ok , @rsa_public_key }
2019-07-15 07:01:22 -06:00
end
test " it returns error when not found user " do
2019-07-20 07:07:51 -06:00
assert capture_log ( fn ->
2020-05-01 07:54:38 -06:00
{ :error , _ } = Signature . refetch_public_key ( make_fake_conn ( " https://test-ap_id " ) )
2019-07-20 07:07:51 -06:00
end ) =~ " [error] Could not decode user "
2019-07-15 07:01:22 -06:00
end
end
2023-08-07 09:17:17 -06:00
defp split_signature ( sig ) do
sig
|> String . split ( " , " )
|> Enum . map ( fn part ->
[ key , value ] = String . split ( part , " = " , parts : 2 )
[ key , String . trim ( value , ~s| " | ) ]
end )
|> Enum . sort_by ( fn [ k , _ ] -> k end )
end
# Break up a signature and check by parts
defp assert_signature_equal ( sig_a , sig_b ) when is_binary ( sig_a ) and is_binary ( sig_b ) do
parts_a = split_signature ( sig_a )
parts_b = split_signature ( sig_b )
parts_a
|> Enum . with_index ( )
|> Enum . each ( fn { part_a , index } ->
part_b = Enum . at ( parts_b , index )
assert_part_equal ( part_a , part_b )
end )
end
defp assert_part_equal ( part_a , part_b ) do
if part_a != part_b do
flunk ( " Signature check failed - expected #{ part_a } to equal #{ part_b } " )
end
end
2019-07-15 07:01:22 -06:00
describe " sign/2 " do
test " it returns signature headers " do
user =
insert ( :user , %{
ap_id : " https://mastodon.social/users/lambadalambda " ,
2019-10-06 07:22:35 -06:00
keys : @private_key
2019-07-15 07:01:22 -06:00
} )
2023-08-07 09:17:17 -06:00
headers = %{
host : " test.test " ,
" content-length " : 100
}
assert_signature_equal (
Signature . sign (
user ,
headers
) ,
" keyId= \" https://mastodon.social/users/lambadalambda # main-key \" ,algorithm= \" rsa-sha256 \" ,headers= \" content-length host \" ,signature= \" sibUOoqsFfTDerquAkyprxzDjmJm6erYc42W5w1IyyxusWngSinq5ILTjaBxFvfarvc7ci1xAi+5gkBwtshRMWm7S+Uqix24Yg5EYafXRun9P25XVnYBEIH4XQ+wlnnzNIXQkU3PU9e6D8aajDZVp3hPJNeYt1gIPOA81bROI8/glzb1SAwQVGRbqUHHHKcwR8keiR/W2h7BwG3pVRy4JgnIZRSW7fQogKedDg02gzRXwUDFDk0pr2p3q6bUWHUXNV8cZIzlMK+v9NlyFbVYBTHctAR26GIAN6Hz0eV0mAQAePHDY1mXppbA8Gpp6hqaMuYfwifcXmcc+QFm4e+n3A== \" "
)
2019-07-15 07:01:22 -06:00
end
test " it returns error " do
2019-10-06 07:22:35 -06:00
user = insert ( :user , %{ ap_id : " https://mastodon.social/users/lambadalambda " , keys : " " } )
2019-07-15 07:01:22 -06:00
assert Signature . sign (
user ,
%{ host : " test.test " , " content-length " : 100 }
) == { :error , [ ] }
end
end
2019-07-23 10:47:22 -06:00
describe " key_id_to_actor_id/1 " do
test " it properly deduces the actor id for misskey " do
assert Signature . key_id_to_actor_id ( " https://example.com/users/1234/publickey " ) ==
2020-05-01 07:54:38 -06:00
{ :ok , " https://example.com/users/1234 " }
2019-07-23 10:47:22 -06:00
end
test " it properly deduces the actor id for mastodon and pleroma " do
assert Signature . key_id_to_actor_id ( " https://example.com/users/1234 # main-key " ) ==
2020-05-01 07:54:38 -06:00
{ :ok , " https://example.com/users/1234 " }
end
2022-07-18 08:21:27 -06:00
test " it deduces the actor id for gotoSocial " do
assert Signature . key_id_to_actor_id ( " https://example.com/users/1234/main-key " ) ==
{ :ok , " https://example.com/users/1234 " }
end
2023-04-25 06:30:20 -06:00
test " it deduces the actor ID for streams " do
assert Signature . key_id_to_actor_id ( " https://example.com/users/1234?operation=getkey " ) ==
{ :ok , " https://example.com/users/1234 " }
end
2024-05-10 22:30:18 -06:00
test " it deduces the actor ID for bridgy " do
assert Signature . key_id_to_actor_id ( " https://example.com/1234 # key " ) ==
2023-08-23 17:09:00 -06:00
{ :ok , " https://example.com/1234 " }
2024-05-10 22:30:18 -06:00
end
2020-05-01 07:54:38 -06:00
test " it calls webfinger for 'acct:' accounts " do
with_mock ( Pleroma.Web.WebFinger ,
2022-12-14 05:38:48 -07:00
finger : fn _ -> { :ok , %{ " ap_id " = > " https://gensokyo.2hu/users/raymoo " } } end
2020-05-01 07:54:38 -06:00
) do
assert Signature . key_id_to_actor_id ( " acct:raymoo@gensokyo.2hu " ) ==
{ :ok , " https://gensokyo.2hu/users/raymoo " }
end
2019-07-23 10:47:22 -06:00
end
end
2019-08-23 12:17:14 -06:00
describe " signed_date " do
test " it returns formatted current date " do
with_mock ( NaiveDateTime , utc_now : fn -> ~N[ 2019-08-23 18:11:24.822233 ] end ) do
assert Signature . signed_date ( ) == " Fri, 23 Aug 2019 18:11:24 GMT "
end
end
test " it returns formatted date " do
assert Signature . signed_date ( ~N[ 2019-08-23 08:11:24.822233 ] ) ==
" Fri, 23 Aug 2019 08:11:24 GMT "
end
end
2019-07-15 07:01:22 -06:00
end