Refactor code to comply with credo suggestions
[akkoma] / lib / pleroma / web / salmon / salmon.ex
1 defmodule Pleroma.Web.Salmon do
2 use Bitwise
3
4 def decode(salmon) do
5 {doc, _rest} = :xmerl_scan.string(to_charlist(salmon))
6
7 {:xmlObj, :string, data} = :xmerl_xpath.string('string(//me:data[1])', doc)
8 {:xmlObj, :string, sig} = :xmerl_xpath.string('string(//me:sig[1])', doc)
9 {:xmlObj, :string, alg} = :xmerl_xpath.string('string(//me:alg[1])', doc)
10 {:xmlObj, :string, encoding} = :xmerl_xpath.string('string(//me:encoding[1])', doc)
11 {:xmlObj, :string, type} = :xmerl_xpath.string('string(//me:data[1]/@type)', doc)
12
13 {:ok, data} = Base.url_decode64(to_string(data), ignore: :whitespace)
14 {:ok, sig} = Base.url_decode64(to_string(sig), ignore: :whitespace)
15 alg = to_string(alg)
16 encoding = to_string(encoding)
17 type = to_string(type)
18
19 [data, type, encoding, alg, sig]
20 end
21
22 def fetch_magic_key(salmon) do
23 [data, _, _, _, _] = decode(salmon)
24 {doc, _rest} = :xmerl_scan.string(to_charlist(data))
25 {:xmlObj, :string, uri} = :xmerl_xpath.string('string(//author[1]/uri)', doc)
26
27 uri = to_string(uri)
28 base = URI.parse(uri).host
29
30 # TODO: Find out if this endpoint is mandated by the standard.
31 {:ok, response} = HTTPoison.get(base <> "/.well-known/webfinger", ["Accept": "application/xrd+xml"], [params: [resource: uri]])
32
33 {doc, _rest} = :xmerl_scan.string(to_charlist(response.body))
34
35 {:xmlObj, :string, magickey} = :xmerl_xpath.string('string(//Link[@rel="magic-public-key"]/@href)', doc)
36 "data:application/magic-public-key," <> magickey = to_string(magickey)
37
38 magickey
39 end
40
41 def decode_and_validate(magickey, salmon) do
42 [data, type, encoding, alg, sig] = decode(salmon)
43
44 signed_text = [data, type, encoding, alg]
45 |> Enum.map(&Base.url_encode64/1)
46 |> Enum.join(".")
47
48 key = decode_key(magickey)
49
50 verify = :public_key.verify(signed_text, :sha256, sig, key)
51
52 if verify do
53 {:ok, data}
54 else
55 :error
56 end
57 end
58
59 defp decode_key("RSA." <> magickey) do
60 make_integer = fn(bin) ->
61 list = :erlang.binary_to_list(bin)
62 Enum.reduce(list, 0, fn (el, acc) -> (acc <<< 8) ||| el end)
63 end
64
65 [modulus, exponent] = magickey
66 |> String.split(".")
67 |> Enum.map(&Base.url_decode64!/1)
68 |> Enum.map(make_integer)
69
70 {:RSAPublicKey, modulus, exponent}
71 end
72 end