Skip to content

Commit d403bb9

Browse files
committed
Rely on OS certificates for downloading Hex/Rebar manifests
1 parent 1cbc493 commit d403bb9

File tree

7 files changed

+26
-373
lines changed

7 files changed

+26
-373
lines changed

lib/mix/lib/mix/local.ex

Lines changed: 18 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,6 @@
55
defmodule Mix.Local do
66
@moduledoc false
77

8-
@public_keys_html "https://builds.hex.pm/installs/public_keys.html"
9-
10-
@in_memory_key """
11-
-----BEGIN PUBLIC KEY-----
12-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAslPz1mAfyAvRv8W8xOdv
13-
HQMbDJkDKfRhsL4JBGwGH7qw0xh+TbaUlNaM3pF+i8VUjS/4FeXjT/OAUEAHu5Y2
14-
rBVlx00QcH8Dpbyf+H73XiCs0MXnTSecqDgzx6i6NMi8knklHT7yHySHtuuPmPuN
15-
Po8QTKolCKftwPE/iNDeyZfwufd+hTCoCQdoTVcB01SElfNtvKRtoKbx35q80IPr
16-
rOcGsALmr58+bWqCTY/51kFeRxzrPJ5LdcLU/AebyWddD4IUfPDxk16jTiCagMWA
17-
JPSwo8NUrWDIBbD+rEUp06y0ek276rG5Tzm/3Bma56RN/u6nAqBTBE8F2Hu2QBKj
18-
lQIDAQAB
19-
-----END PUBLIC KEY-----
20-
"""
21-
228
@doc """
239
Returns the name for an archive or an escript, based on the project config.
2410
@@ -167,40 +153,16 @@ defmodule Mix.Local do
167153
168154
Used to install both Rebar and Hex from S3.
169155
"""
170-
def find_matching_versions_from_signed_csv!(name, version, path) do
171-
ensure_applications!()
172-
csv = read_unsafe_path!(name, path)
173-
174-
signature =
175-
read_unsafe_path!(name, path <> ".signed")
176-
|> String.replace("\n", "")
177-
|> Base.decode64!()
178-
179-
verified? =
180-
Enum.any?(public_keys(), fn {id, key} ->
181-
:public_key.verify(csv, :sha512, signature, decode_pk!(id, key))
182-
end)
183-
184-
if verified? do
185-
result =
186-
csv
187-
|> parse_csv()
188-
|> find_latest_eligible_version(version)
189-
190-
result || Mix.raise("Could not find a version of #{name} matching: #{version}")
191-
else
192-
Mix.raise(
193-
"Could not install #{name} because Mix could not verify authenticity " <>
194-
"of metadata file at #{inspect(path)}. This may happen because a proxy or some " <>
195-
"entity is interfering with the download or because you don't have a " <>
196-
"public key to verify the download.\n\nYou may try again later or check " <>
197-
"if a new public key has been released in our public keys page: #{@public_keys_html}"
198-
)
199-
end
156+
def find_matching_versions!(name, version, path) do
157+
name
158+
|> read_path!(path)
159+
|> parse_csv()
160+
|> find_latest_eligible_version(version)
161+
|> Kernel.||(Mix.raise("Could not find a version of #{name} matching: #{version}"))
200162
end
201163

202-
defp read_unsafe_path!(name, path) do
203-
case Mix.Utils.read_path(path, unsafe_uri: true) do
164+
defp read_path!(name, path) do
165+
case Mix.Utils.read_path(path) do
204166
{:ok, contents} ->
205167
contents
206168

@@ -242,78 +204,20 @@ defmodule Mix.Local do
242204
|> find_version(artifact_version, elixir_version)
243205
end
244206

245-
defp find_version(entries, _artifact_version = nil, elixir_version) do
246-
Enum.find_value(entries, &find_by_elixir_version(&1, elixir_version))
247-
end
248-
249207
defp find_version(entries, artifact_version, elixir_version) do
250-
Enum.find_value(entries, &find_by_artifact_version(&1, artifact_version, elixir_version))
251-
end
252-
253-
defp find_by_elixir_version([artifact_version, digest | versions], elixir_version) do
254-
if version = Enum.find(versions, &(Version.compare(&1, elixir_version) != :gt)) do
255-
{version, artifact_version, digest}
256-
end
257-
end
258-
259-
defp find_by_artifact_version(
260-
[artifact_version, digest | versions],
261-
artifact_version,
262-
elixir_version
263-
) do
264-
if version = Enum.find(versions, &(Version.compare(&1, elixir_version) != :gt)) do
265-
{version, artifact_version, digest}
266-
end
267-
end
268-
269-
defp find_by_artifact_version(_entry, _artifact_version, _elixir_version) do
270-
nil
271-
end
272-
273-
## Public keys
274-
275-
@doc """
276-
Returns the file system path for public keys.
277-
"""
278-
def public_keys_path, do: Path.join(Mix.Utils.mix_home(), "public_keys")
279-
280-
@doc """
281-
Returns all public keys as a list.
282-
"""
283-
def public_keys do
284-
path = public_keys_path()
285-
286-
[{"in-memory public key for Elixir v#{System.version()}", @in_memory_key}] ++
287-
case File.ls(path) do
288-
{:ok, keys} -> Enum.map(keys, &{&1, File.read!(Path.join(path, &1))})
289-
{:error, _} -> []
208+
entries =
209+
if artifact_version do
210+
Enum.filter(entries, &(hd(&1) == artifact_version))
211+
else
212+
entries
290213
end
291-
end
292214

293-
@doc """
294-
Decodes a public key and raises if the key is invalid.
295-
"""
296-
def decode_pk!(id, key) do
297-
ensure_applications!()
298-
299-
try do
300-
[rsa_public_key] = :public_key.pem_decode(key)
301-
:public_key.pem_entry_decode(rsa_public_key)
302-
rescue
303-
_ ->
304-
Mix.raise("""
305-
Could not decode public key: #{id}. The public key contents are shown below.
306-
307-
#{key}
308-
309-
Public keys must be valid and be in the PEM format
310-
""")
311-
end
215+
Enum.find_value(entries, &find_by_elixir_version(&1, elixir_version))
312216
end
313217

314-
defp ensure_applications! do
315-
Mix.ensure_application!(:crypto)
316-
Mix.ensure_application!(:asn1)
317-
Mix.ensure_application!(:public_key)
218+
defp find_by_elixir_version([artifact_version, digest, hex_elixir_version | _], elixir_version) do
219+
if Version.compare(hex_elixir_version, elixir_version) != :gt do
220+
{hex_elixir_version, artifact_version, digest}
221+
end
318222
end
319223
end

lib/mix/lib/mix/tasks/local.hex.ex

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,7 @@ defmodule Mix.Tasks.Local.Hex do
7373
hex_url = Mix.Hex.url()
7474

7575
{elixir_version, hex_version, sha512} =
76-
Mix.Local.find_matching_versions_from_signed_csv!(
77-
"Hex",
78-
version,
79-
hex_url <> @hex_list_path
80-
)
76+
Mix.Local.find_matching_versions!("Hex", version, hex_url <> @hex_list_path)
8177

8278
url =
8379
(hex_url <> @hex_archive_path)

lib/mix/lib/mix/tasks/local.public_keys.ex

Lines changed: 0 additions & 90 deletions
This file was deleted.

lib/mix/lib/mix/tasks/local.rebar.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ defmodule Mix.Tasks.Local.Rebar do
115115
list_url = hex_url <> list_url
116116

117117
{elixir_version, rebar_version, sha512} =
118-
Mix.Local.find_matching_versions_from_signed_csv!("Rebar", _version = nil, list_url)
118+
Mix.Local.find_matching_versions!("Rebar", _version = nil, list_url)
119119

120120
url =
121121
(hex_url <> escript_url)

lib/mix/lib/mix/utils.ex

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -625,9 +625,7 @@ defmodule Mix.Utils do
625625
## Options
626626
627627
* `:sha512` - checks against the given SHA-512 checksum. Returns
628-
`{:checksum, message}` in case it fails. This option is required
629-
for URLs unless the `:unsafe_uri` is given (WHICH IS NOT RECOMMENDED
630-
unless another security mechanism is in place, such as private keys)
628+
`{:checksum, message}` in case it fails
631629
632630
* `:timeout` - times out the request after the given milliseconds.
633631
Returns `{:remote, timeout_message}` if it fails. Defaults to 60
@@ -645,8 +643,7 @@ defmodule Mix.Utils do
645643
url?(path) ->
646644
task =
647645
Task.async(fn ->
648-
with :ok <- require_checksum(opts),
649-
{:ok, binary} <- read_httpc(path) do
646+
with {:ok, binary} <- read_httpc(path) do
650647
checksum(binary, opts)
651648
end
652649
end)
@@ -670,19 +667,6 @@ defmodule Mix.Utils do
670667

671668
@checksums [:sha512]
672669

673-
defp require_checksum(opts) do
674-
cond do
675-
Keyword.take(opts, @checksums) != [] ->
676-
:ok
677-
678-
Keyword.get(opts, :unsafe_uri) ->
679-
:ok
680-
681-
true ->
682-
{:checksum, "fetching from URIs require a checksum to be given"}
683-
end
684-
end
685-
686670
defp checksum(binary, opts) do
687671
Enum.find_value(@checksums, {:ok, binary}, fn hash ->
688672
with expected when expected != nil <- opts[hash],

0 commit comments

Comments
 (0)