nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 stdenv,
4 writeText,
5 fetchFromGitHub,
6 buildcatrust,
7 blacklist ? [ ],
8 extraCertificateFiles ? [ ],
9 extraCertificateStrings ? [ ],
10
11 # Used by update.sh
12 nssOverride ? null,
13
14 # Used for tests only
15 runCommand,
16 cacert,
17 openssl,
18}:
19
20let
21 blocklist = writeText "cacert-blocklist.txt" (lib.concatStringsSep "\n" blacklist);
22 extraCertificatesBundle = writeText "cacert-extra-certificates-bundle.crt" (
23 lib.concatStringsSep "\n\n" extraCertificateStrings
24 );
25
26 srcVersion = "3.113.1";
27 version = if nssOverride != null then nssOverride.version else srcVersion;
28 meta = with lib; {
29 homepage = "https://curl.haxx.se/docs/caextract.html";
30 description = "Bundle of X.509 certificates of public Certificate Authorities (CA)";
31 platforms = platforms.all;
32 maintainers = with maintainers; [
33 fpletz
34 lukegb
35 ];
36 license = licenses.mpl20;
37 };
38 certdata = stdenv.mkDerivation {
39 pname = "nss-cacert-certdata";
40 inherit version;
41
42 src =
43 if nssOverride != null then
44 nssOverride.src
45 else
46 fetchFromGitHub {
47 owner = "nss-dev";
48 repo = "nss";
49 rev = "NSS_${lib.replaceStrings [ "." ] [ "_" ] version}_RTM";
50 hash = "sha256-Yfs9Hh98ASJe1D4qyQEXaTC2xjeDI2Cdxp5Xgy0rYdQ=";
51 };
52
53 dontBuild = true;
54
55 installPhase = ''
56 runHook preInstall
57
58 mkdir $out
59 cp lib/ckfw/builtins/certdata.txt $out
60
61 runHook postInstall
62 '';
63
64 inherit meta;
65 };
66in
67stdenv.mkDerivation {
68 pname = "nss-cacert";
69 inherit version;
70
71 src = certdata;
72
73 outputs = [
74 "out"
75 "unbundled"
76 "p11kit"
77 "hashed"
78 ];
79
80 nativeBuildInputs = [ buildcatrust ];
81
82 buildPhase = ''
83 mkdir unbundled hashed
84 buildcatrust \
85 --certdata_input certdata.txt \
86 --ca_bundle_input "${extraCertificatesBundle}" ${
87 lib.escapeShellArgs (map (arg: "${arg}") extraCertificateFiles)
88 } \
89 --blocklist "${blocklist}" \
90 --ca_bundle_output ca-bundle.crt \
91 --ca_standard_bundle_output ca-no-trust-rules-bundle.crt \
92 --ca_unpacked_output unbundled \
93 --ca_hashed_unpacked_output hashed \
94 --p11kit_output ca-bundle.trust.p11-kit
95 '';
96
97 installPhase = ''
98 install -D -t "$out/etc/ssl/certs" ca-bundle.crt
99
100 # install standard PEM compatible bundle
101 install -D -t "$out/etc/ssl/certs" ca-no-trust-rules-bundle.crt
102
103 # install p11-kit specific output to p11kit output
104 install -D -t "$p11kit/etc/ssl/trust-source" ca-bundle.trust.p11-kit
105
106 # install individual certs in unbundled output
107 install -D -t "$unbundled/etc/ssl/certs" unbundled/*.crt
108
109 # install hashed certs in hashed output
110 # use cp as install doesn't copy symlinks
111 mkdir -p $hashed/etc/ssl/certs/
112 cp -P hashed/* $hashed/etc/ssl/certs/
113 '';
114
115 setupHook = ./setup-hook.sh;
116
117 passthru = {
118 updateScript = ./update.sh;
119 tests =
120 let
121 isTrusted = ''
122 isTrusted() {
123 # isTrusted <unbundled-dir> <ca name> <ca sha256 fingerprint>
124 for f in $1/etc/ssl/certs/*.crt; do
125 if ! [[ -s "$f" ]]; then continue; fi
126 fingerprint="$(openssl x509 -in "$f" -noout -fingerprint -sha256 | cut -f2 -d=)"
127 if [[ "x$fingerprint" == "x$3" ]]; then
128 # If the certificate is treated as rejected for TLS Web Server, then we consider it untrusted.
129 if openssl x509 -in "$f" -noout -text | grep -q '^Rejected Uses:'; then
130 if openssl x509 -in "$f" -noout -text | grep -A1 '^Rejected Uses:' | grep -q 'TLS Web Server'; then
131 return 1
132 fi
133 fi
134 return 0
135 fi
136 done
137 return 1
138 }
139 '';
140 in
141 {
142 # Test that building this derivation with a blacklist works, and that UTF-8 is supported.
143 blacklist-utf8 =
144 let
145 blacklistCAToFingerprint = {
146 # "blacklist" uses the CA name from the NSS bundle, but we check for presence using the SHA256 fingerprint.
147 "CFCA EV ROOT" =
148 "5C:C3:D7:8E:4E:1D:5E:45:54:7A:04:E6:87:3E:64:F9:0C:F9:53:6D:1C:CC:2E:F8:00:F3:55:C4:C5:FD:70:FD";
149 "NetLock Arany (Class Gold) Főtanúsítvány" =
150 "6C:61:DA:C3:A2:DE:F0:31:50:6B:E0:36:D2:A6:FE:40:19:94:FB:D1:3D:F9:C8:D4:66:59:92:74:C4:46:EC:98";
151 };
152 mapBlacklist = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f blacklistCAToFingerprint);
153 in
154 runCommand "verify-the-cacert-filter-output"
155 {
156 cacert = cacert.unbundled;
157 cacertWithExcludes =
158 (cacert.override {
159 blacklist = builtins.attrNames blacklistCAToFingerprint;
160 }).unbundled;
161
162 nativeBuildInputs = [ openssl ];
163 }
164 ''
165 ${isTrusted}
166
167 # Ensure that each certificate is in the main "cacert".
168 ${mapBlacklist (
169 caName: caFingerprint: ''
170 isTrusted "$cacert" "${caName}" "${caFingerprint}" || ({
171 echo "CA fingerprint ${caFingerprint} (${caName}) is missing from the CA bundle. Consider picking a different CA for the blacklist test." >&2
172 exit 1
173 })
174 ''
175 )}
176
177 # Ensure that each certificate is NOT in the "cacertWithExcludes".
178 ${mapBlacklist (
179 caName: caFingerprint: ''
180 isTrusted "$cacertWithExcludes" "${caName}" "${caFingerprint}" && ({
181 echo "CA fingerprint ${caFingerprint} (${caName}) is present in the cacertWithExcludes bundle." >&2
182 exit 1
183 })
184 ''
185 )}
186
187 touch "$out"
188 '';
189
190 # Test that we can add additional certificates to the store, and have them be trusted.
191 extra-certificates =
192 let
193 extraCertificateStr = ''
194 -----BEGIN CERTIFICATE-----
195 MIIB5DCCAWqgAwIBAgIUItvsAYEIdYDkOIo5sdDYMcUaNuIwCgYIKoZIzj0EAwIw
196 KTEnMCUGA1UEAwweTml4T1MgY2FjZXJ0IGV4dHJhIGNlcnRpZmljYXRlMB4XDTIx
197 MDYxMjE5MDQzMFoXDTIyMDYxMjE5MDQzMFowKTEnMCUGA1UEAwweTml4T1MgY2Fj
198 ZXJ0IGV4dHJhIGNlcnRpZmljYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuP8y
199 lAm6ZyQt9v/P6gTlV/a9R+D61WjucW04kaegOhg8csiluimYodiSv0Pbgymu+Zxm
200 A3Bz9QGmytaYTiJ16083rJkwwIhqoYl7kWsLzreSTaLz87KH+rdeol59+H0Oo1Mw
201 UTAdBgNVHQ4EFgQUCxuHfvqI4YVU5M+A0+aKvd1LrdswHwYDVR0jBBgwFoAUCxuH
202 fvqI4YVU5M+A0+aKvd1LrdswDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNo
203 ADBlAjEArgxgjdNmRlSEuai0dzlktmBEDZKy2Iiul+ttSoce9ohfEVYESwO602HW
204 keVvI56vAjBCro3dc3m2TuktiKO6lQV56PUEyxko4H/sR5pnHlduCGRDlFzQKXf/
205 pMMmtj7cVb8=
206 -----END CERTIFICATE-----
207 '';
208 extraCertificateFile = ./test-cert-file.crt;
209 extraCertificatesToFingerprint = {
210 # String above
211 "NixOS cacert extra certificate string" =
212 "A3:20:D0:84:96:97:25:FF:98:B8:A9:6D:A3:7C:89:95:6E:7A:77:21:92:F3:33:E9:31:AF:5E:03:CE:A9:E5:EE";
213
214 # File
215 "NixOS cacert extra certificate file" =
216 "88:B8:BE:A7:57:AC:F1:FE:D6:98:8B:50:E0:BD:0A:AE:88:C7:DF:70:26:E1:67:5E:F5:F6:91:27:FF:02:D4:A5";
217 };
218 mapExtra = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f extraCertificatesToFingerprint);
219 in
220 runCommand "verify-the-cacert-extra-output"
221 {
222 cacert = cacert.unbundled;
223 cacertWithExtras =
224 (cacert.override {
225 extraCertificateStrings = [ extraCertificateStr ];
226 extraCertificateFiles = [ extraCertificateFile ];
227 }).unbundled;
228
229 nativeBuildInputs = [ openssl ];
230 }
231 ''
232 ${isTrusted}
233
234 # Ensure that the extra certificate is not in the main "cacert".
235 ${mapExtra (
236 extraName: extraFingerprint: ''
237 isTrusted "$cacert" "${extraName}" "${extraFingerprint}" && ({
238 echo "'extra' CA fingerprint ${extraFingerprint} (${extraName}) is present in the main CA bundle." >&2
239 exit 1
240 })
241 ''
242 )}
243
244 # Ensure that the extra certificates ARE in the "cacertWithExtras".
245 ${mapExtra (
246 extraName: extraFingerprint: ''
247 isTrusted "$cacertWithExtras" "${extraName}" "${extraFingerprint}" || ({
248 echo "CA fingerprint ${extraFingerprint} (${extraName}) is not present in the cacertWithExtras bundle." >&2
249 exit 1
250 })
251 ''
252 )}
253
254 touch "$out"
255 '';
256 };
257 };
258
259 inherit meta;
260}