--- /dev/null
+defmodule Pleroma.Web.Salmon do
+ use Bitwise
+
+ def decode_and_validate(magickey, salmon) do
+ {doc, _rest} = :xmerl_scan.string(to_charlist(salmon))
+
+ {:xmlObj, :string, data} = :xmerl_xpath.string('string(//me:data[1])', doc)
+ {:xmlObj, :string, sig} = :xmerl_xpath.string('string(//me:sig[1])', doc)
+ {:xmlObj, :string, alg} = :xmerl_xpath.string('string(//me:alg[1])', doc)
+ {:xmlObj, :string, encoding} = :xmerl_xpath.string('string(//me:encoding[1])', doc)
+ {:xmlObj, :string, type} = :xmerl_xpath.string('string(//me:data[1]/@type)', doc)
+
+
+ {:ok, data} = Base.url_decode64(to_string(data), ignore: :whitespace)
+ {:ok, sig} = Base.url_decode64(to_string(sig), ignore: :whitespace)
+ alg = to_string(alg)
+ encoding = to_string(encoding)
+ type = to_string(type)
+
+ signed_text = [data, type, encoding, alg]
+ |> Enum.map(&Base.url_encode64/1)
+ |> Enum.join(".")
+
+ key = decode_key(magickey)
+
+ verify = :public_key.verify(signed_text, :sha256, sig, key)
+
+ if verify do
+ {:ok, data}
+ else
+ :error
+ end
+ end
+
+ defp decode_key("RSA." <> magickey) do
+ make_integer = fn(bin) ->
+ list = :erlang.binary_to_list(bin)
+ Enum.reduce(list, 0, fn (el, acc) -> (acc <<< 8) ||| el end)
+ end
+
+ [modulus, exponent] = magickey
+ |> String.split(".")
+ |> Enum.map(&Base.url_decode64!/1)
+ |> Enum.map(make_integer)
+
+ {:RSAPublicKey, modulus, exponent}
+ end
+end
--- /dev/null
+defmodule Pleroma.Web.Salmon.SalmonTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.Salmon
+
+ @magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
+
+ @wrong_magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAA"
+
+ test "decodes a salmon" do
+ {:ok, salmon} = File.read("test/fixtures/salmon.xml")
+ {:ok, doc} = Salmon.decode_and_validate(@magickey, salmon)
+ assert Regex.match?(~r/xml/, doc)
+ end
+
+ test "errors on wrong magic key" do
+ {:ok, salmon} = File.read("test/fixtures/salmon.xml")
+ assert Salmon.decode_and_validate(@wrong_magickey, salmon) == :error
+ end
+end