unofficial mirror of guix-patches@gnu.org 
 help / color / mirror / code / Atom feed
* [bug#44335] [PATCH 0/2] Install Chromium extensions with Guix!
@ 2020-10-30 18:29 Marius Bakke
  2020-10-30 18:32 ` [bug#44335] [PATCH 1/2] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
  2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
  0 siblings, 2 replies; 11+ messages in thread
From: Marius Bakke @ 2020-10-30 18:29 UTC (permalink / raw)
  To: 44335

Greetings Guix,

These patches lay the groundwork for packaging Chromium extensions with
Guix.  Currently only uBlock Origin has been packaged, but adding others
should be technically straight forward.  Practically it turns out
somewhat difficult because many extensions rely on the node.js ecosystem.

Patch 1/2 adds a search path mechanism to ungoogled-chromium such that
it looks for local extensions in $CHROMIUM_EXTENSION_DIRECTORY instead
of the default "/usr/share/chromium/extensions".

Patch 2/2 adds a generic MAKE-CHROMIUM-EXTENSION procedure that turns
a <package> object containing an uncompressed extension directory into
a signed .crx file visible to Chromium through a crafted JSON file.

There is one problem with the proposed patches: extensions are not
automatically updated.  That is, updating the "ublock-origin-chromium"
package will not update the browser profile even after a browser
restart.  Worse, uninstalling the extension through Chromium will leave
you no way to install it again even after it has been updated.

Not sure if that should be a blocking issue.

I'm also not sure whether (gnu packages browser-extensions) is a good
home for the helper procedures that create signing key, etc.  Thoughts?

To aid testing I have started a build of the patched ungoogled-chromium
on ci.guix.gnu.org[*].  It should be finished by tomorrow(!).

(testers can use "chromium --user-data-dir=/tmp/foo" to avoid messing
with their regular profile)

I'm currently relocating to a new city and won't be able to follow up
on this until next week.  In the meantime, feedback very welcome!

[*] on commit eee35fe3b7cb21792b90ecbf5a834b5a3fdb6f04.

Marius Bakke (2):
  gnu: ungoogled-chromium: Add search path for installed extensions.
  gnu: Add ublock-origin-chromium.

 gnu/local.mk                                  |   2 +
 gnu/packages/browser-extensions.scm           | 274 ++++++++++++++++++
 gnu/packages/chromium.scm                     |  18 +-
 .../ungoogled-chromium-search-path.patch      |  28 ++
 4 files changed, 314 insertions(+), 8 deletions(-)
 create mode 100644 gnu/packages/browser-extensions.scm
 create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

-- 
2.28.0





^ permalink raw reply	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH 1/2] gnu: ungoogled-chromium: Add search path for installed extensions.
  2020-10-30 18:29 [bug#44335] [PATCH 0/2] Install Chromium extensions with Guix! Marius Bakke
@ 2020-10-30 18:32 ` Marius Bakke
  2020-10-30 18:32   ` [bug#44335] [PATCH 2/2] gnu: Add ublock-origin-chromium Marius Bakke
  2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
  1 sibling, 1 reply; 11+ messages in thread
From: Marius Bakke @ 2020-10-30 18:32 UTC (permalink / raw)
  To: 44335

* gnu/packages/patches/ungoogled-chromium-search-path.patch: New file.
* gnu/local.mk (dist_patch_DATA): Adjust accordingly.
* gnu/packages/chromium.scm (%guix-patches): New variable.
(ungoogled-chromium-snippet): Apply %GUIX-PATCHES.
(ungoogled-chromium)[arguments]: Don't hard-code extensions directory.
[native-search-paths]: New field.
---
 gnu/local.mk                                  |  1 +
 gnu/packages/chromium.scm                     | 18 ++++++------
 .../ungoogled-chromium-search-path.patch      | 28 +++++++++++++++++++
 3 files changed, 39 insertions(+), 8 deletions(-)
 create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index 3332f10df8..4643d05049 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1641,6 +1641,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/u-boot-riscv64-fix-extlinux.patch	\
   %D%/packages/patches/ucx-tcp-iface-ioctl.patch		\
   %D%/packages/patches/udiskie-no-appindicator.patch		\
+  %D%/packages/patches/ungoogled-chromium-search-path.patch	\
   %D%/packages/patches/ungoogled-chromium-system-nspr.patch	\
   %D%/packages/patches/unison-fix-ocaml-4.08.patch		\
   %D%/packages/patches/unknown-horizons-python-3.8-distro.patch	\
diff --git a/gnu/packages/chromium.scm b/gnu/packages/chromium.scm
index f32a779805..06c57acd28 100644
--- a/gnu/packages/chromium.scm
+++ b/gnu/packages/chromium.scm
@@ -334,6 +334,10 @@
      (base32
       "18p9a7qffmy8m03nqva7maalgil13lj2mn0s56v3crbs4wk4lalj"))))
 
+(define %guix-patches
+  (list (local-file (search-patch "ungoogled-chromium-system-nspr.patch"))
+        (local-file (search-patch "ungoogled-chromium-search-path.patch"))))
+
 ;; This is a source 'snippet' that does the following:
 ;; *) Applies various patches for unbundling purposes and libstdc++ compatibility.
 ;; *) Runs the ungoogled patch-, domain substitution-, and scrubbing scripts.
@@ -356,9 +360,7 @@
                       (invoke "patch" "-p1" "--force" "--input"
                               patch "--no-backup-if-mismatch"))
                     (append '#+%debian-patches '#+%arch-patches
-                            '#+(list (local-file
-                                      (search-patch
-                                       "ungoogled-chromium-system-nspr.patch")))))
+                            '#+%guix-patches))
 
           (with-directory-excursion #+%ungoogled-origin
             (format #t "Ungooglifying...~%")
@@ -571,11 +573,6 @@
                        (find-files (string-append "third_party/webrtc/modules"
                                                   "/audio_coding/codecs/opus")))
 
-             (substitute* "chrome/common/chrome_paths.cc"
-               (("/usr/share/chromium/extensions")
-                ;; TODO: Add ~/.guix-profile.
-                "/run/current-system/profile/share/chromium/extensions"))
-
              ;; Many files try to include ICU headers from "third_party/icu/...".
              ;; Remove the "third_party/" prefix to use system headers instead.
              (substitute* (find-files "chrome" "\\.cc$")
@@ -840,6 +837,11 @@
        ("udev" ,eudev)
        ("valgrind" ,valgrind)
        ("vulkan-headers" ,vulkan-headers)))
+    (native-search-paths
+     (list (search-path-specification
+            (variable "CHROMIUM_EXTENSION_DIRECTORY")
+            (separator #f)              ;single entry
+            (files '("share/chromium/extensions")))))
 
     ;; Building Chromium takes ... a very long time.  On a single core, a busy
     ;; mid-end x86 system may need more than 24 hours to complete the build.
diff --git a/gnu/packages/patches/ungoogled-chromium-search-path.patch b/gnu/packages/patches/ungoogled-chromium-search-path.patch
new file mode 100644
index 0000000000..5ef9c56b52
--- /dev/null
+++ b/gnu/packages/patches/ungoogled-chromium-search-path.patch
@@ -0,0 +1,28 @@
+Look for system-installed extensions in CHROMIUM_EXTENSION_DIRECTORY instead
+of the hard-coded /usr/share/chromium/extensions.
+
+--- a/chrome/common/chrome_paths.cc
++++ b/chrome/common/chrome_paths.cc
+@@ -4,6 +4,7 @@
+ 
+ #include "chrome/common/chrome_paths.h"
+ 
++#include "base/environment.h"
+ #include "base/files/file_util.h"
+ #include "base/logging.h"
+ #include "base/native_library.h"
+@@ -511,7 +512,13 @@
+ #endif
+ #if defined(OS_LINUX) || defined(OS_CHROMEOS)
+     case chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS: {
+-      cur = base::FilePath(kFilepathSinglePrefExtensions);
++      std::unique_ptr<base::Environment> environment(base::Environment::Create());
++      std::string extension_dir;
++      if (environment->GetVar("CHROMIUM_EXTENSION_DIRECTORY", &extension_dir)) {
++        cur = base::FilePath(extension_dir);
++      } else {
++        cur = base::FilePath(kFilepathSinglePrefExtensions);
++      }
+       break;
+     }
+ #endif
-- 
2.28.0





^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH 2/2] gnu: Add ublock-origin-chromium.
  2020-10-30 18:32 ` [bug#44335] [PATCH 1/2] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
@ 2020-10-30 18:32   ` Marius Bakke
  0 siblings, 0 replies; 11+ messages in thread
From: Marius Bakke @ 2020-10-30 18:32 UTC (permalink / raw)
  To: 44335

* gnu/packages/browser-extensions.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.
---
 gnu/local.mk                        |   1 +
 gnu/packages/browser-extensions.scm | 278 ++++++++++++++++++++++++++++
 2 files changed, 279 insertions(+)
 create mode 100644 gnu/packages/browser-extensions.scm

diff --git a/gnu/local.mk b/gnu/local.mk
index 4643d05049..f97a0cda78 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -111,6 +111,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/packages/boost.scm			\
   %D%/packages/bootloaders.scm			\
   %D%/packages/bootstrap.scm			\
+  %D%/packages/browser-extensions.scm		\
   %D%/packages/build-tools.scm			\
   %D%/packages/busybox.scm			\
   %D%/packages/c.scm				\
diff --git a/gnu/packages/browser-extensions.scm b/gnu/packages/browser-extensions.scm
new file mode 100644
index 0000000000..b6d160bee7
--- /dev/null
+++ b/gnu/packages/browser-extensions.scm
@@ -0,0 +1,278 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages browser-extensions)
+  #:use-module (gcrypt base16)
+  #:use-module ((gcrypt hash) #:prefix hash:)
+  #:use-module (ice-9 iconv)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages check)
+  #:use-module (gnu packages chromium)
+  #:use-module (gnu packages gnupg)
+  #:use-module (gnu packages tls)
+  #:use-module (gnu packages xorg)
+  #:use-module (guix build-system trivial)
+
+  ;; Imports for extension packages.
+  #:use-module (guix git-download)
+  #:use-module (guix build-system gnu)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (gnu packages compression)
+  #:use-module (gnu packages python)
+
+  #:export (make-chromium-extension))
+
+;; Chromium requires that each package is signed with a unique key, from which
+;; the "extension ID" derives.  Currently the package name is used as seed.
+(define (make-signing-key seed)
+  "Return a derivation for a deterministic PKCS #8 private key using SEED."
+
+  (define sha256sum
+    (bytevector->base16-string (hash:sha256 (string->bytevector seed "UTF-8"))))
+
+  ;; certtool.c wants a 56 byte seed for a 2048 bit key.
+  (define size 2048)
+  (define normalized-seed (string-take sha256sum 56))
+
+  (computed-file (string-append seed "-signing-key.pem")
+                 #~(system* #$(file-append gnutls "/bin/certtool")
+                            "--generate-privkey"
+                            "--key-type=rsa"
+                            "--pkcs8"
+                            ;; Use the provable FIPS-PUB186-4 algorithm for
+                            ;; deterministic results.
+                            "--provable"
+                            "--password="
+                            "--no-text"
+                            (string-append "--bits=" #$(number->string size))
+                            (string-append "--seed=" #$normalized-seed)
+                            "--outfile" #$output)))
+
+(define* (make-crx package #:optional (chromium-output "out"))
+  ;; XXX: This is terrible.  We pull Xorg and Chromium just to pack and
+  ;; sign an extension.  This should be implemented with something lighter
+  ;; using the protobuf-based protocol (TODO: where is it documented..?).
+  (define name (package-name package))
+  (define version (package-version package))
+  (define signing-key (make-signing-key name))
+
+  (with-imported-modules '((guix build utils))
+    (computed-file
+     (string-append name "-" version ".crx")
+     #~(begin
+         (use-modules (guix build utils)
+                      (ice-9 ftw)
+                      (srfi srfi-26))
+         (let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
+               (xvfb #$(file-append xorg-server "/bin/Xvfb"))
+               (packdir (string-append "/tmp/extension/" #$name)))
+           (mkdir-p (dirname packdir))
+           (copy-recursively (ungexp package chromium-output) packdir)
+           (system (string-append xvfb " :1 &"))
+           (setenv "DISPLAY" ":1")
+           (sleep 2)  ;give Xorg some time to initialize...
+           ;; Chromium stores the current time in the .crx Zip archive.
+           ;; Sidestep that by using "faketime" for determinism.
+           ;; FIXME (core-updates): faketime is missing an absolute reference
+           ;; to 'date', hence the need to set PATH.
+           (setenv "PATH" #$(file-append coreutils "/bin"))
+           (invoke #$(file-append libfaketime "/bin/faketime")
+                   "2000-01-01 00:00:00"
+                   chromium
+                   "--user-data-dir=/tmp/signing-profile"
+                   (string-append "--pack-extension=" packdir)
+                   (string-append "--pack-extension-key=" #$signing-key))
+           (copy-file (string-append packdir ".crx") #$output))))))
+
+(define* (package->chromium-json package #:optional (chromium-output "out"))
+  "Return a derivation that creates a JSON file for PACKAGE, meant to be
+consumed by Chromium to make the installed extension visible."
+
+  (define name (package-name package))
+  (define version (package-version package))
+  (define crx (make-crx package chromium-output))
+
+  (computed-file (string-append name "-" version ".json")
+                 #~(call-with-output-file #$output
+                     (lambda (port)
+                       ;; Documentation for the JSON format can be found in
+                       ;; extensions/common/extension.h and
+                       ;; chrome/browser/extensions/external_provider_impl.cc.
+                       (format port "
+{
+  \"external_crx\": \"~a\",
+  \"external_version\": \"~a\"
+}
+"
+                               #$crx #$version)))))
+
+
+(define (key->der key)
+  "Return a derivation for a file representation of KEY in DER format."
+  (computed-file "der"
+                 #~(system* #$(file-append gnutls "/bin/certtool")
+                            "--load-privkey" #$key
+                            "--pubkey-info"
+                            "--outfile" #$output
+                            "--outder")))
+
+(define (gexp-sha256sum file)
+  "Return a derivation for a base16 representation of the SHA256 checksum
+of FILE."
+  (with-extensions (list guile-gcrypt)
+    #~(begin
+        (use-modules (gcrypt base16) (gcrypt hash))
+        (format #f (bytevector->base16-string (file-sha256 #$file))))))
+
+(define (chromium-json->profile-object json signing-key)
+  "Return a derivation that installs JSON to the destination searched by
+Chromium, using a file name (aka extension ID) derived from SIGNING-KEY."
+  (define der (key->der signing-key))
+  (define checksum (gexp-sha256sum der))
+
+  (with-imported-modules '((guix build utils))
+    (computed-file
+     "chromium-extension"
+     #~(begin
+         (use-modules (guix build utils))
+         (define (base16-string->chromium-base16 str)
+           ;; Translate STR, a hexadecimal string, to a Chromium-style
+           ;; representation using the letters a-p (where a=0, p=15).
+           (define s1 "0123456789abcdef")
+           (define s2 "abcdefghijklmnop")
+           (let loop ((chars (string->list str))
+                      (converted '()))
+             (if (null? chars)
+                 (list->string (reverse converted))
+                 (loop (cdr chars) (cons (string-ref
+                                          s2 (string-index s1 (car chars)))
+                                         converted)))))
+
+         (let ((file-name (base16-string->chromium-base16
+                           (string-take #$checksum 32)))
+               (extension-directory (string-append #$output
+                                                   "/share/chromium/extensions")))
+           (mkdir-p extension-directory)
+           (symlink #$json (string-append extension-directory "/"
+                                          file-name ".json")))))))
+
+(define* (make-chromium-extension p #:optional (output "out"))
+  "Create a signed \".crx\" file from package P, an unpacked Chromium
+extension, and return a derivation for a package that makes the packed
+extension visible to Chromium when installed to a Guix profile.
+
+OUTPUT can be used to specify which output of P contains the Chromium
+extension directory."
+  (package
+    (inherit p)
+    (name (string-append (package-name p) "-chromium"))
+    (build-system trivial-build-system)
+    (native-inputs '())
+    (inputs
+     `(("extension" ,(chromium-json->profile-object
+                      (package->chromium-json p output)
+                      (make-signing-key (package-name p))))))
+    (propagated-inputs '())
+    (outputs '("out"))
+    (arguments
+     '(#:modules ((guix build utils))
+       #:builder
+       (begin
+         (use-modules (guix build utils))
+         (copy-recursively (assoc-ref %build-inputs "extension")
+                           (assoc-ref %outputs "out")))))))
+
+\f
+
+(define uassets
+  (let ((commit "95d609786680bf4cf267b1dff3f429af88040da1"))
+    (origin
+      (method git-fetch)
+      (uri (git-reference
+            (url "https://github.com/uBlockOrigin/uAssets.git")
+            (commit commit)))
+      (file-name (git-file-name "uAssets" (string-take commit 9)))
+      (sha256
+       (base32
+        "070v2g0hyxvqv77b6z404hdlggl0sq2bfhsfl4a84k93p9pwwlrq")))))
+
+(define ublock-origin
+  (package
+    (name "ublock-origin")
+    (version "1.30.8")
+    (home-page "https://github.com/gorhill/uBlock")
+    (source (origin
+              (method git-fetch)
+              (uri (git-reference (url home-page) (commit version)))
+              (file-name (git-file-name name version))
+              (sha256
+               (base32
+                "1y4dq4p4j4qrw6gfdcbpyigqjinqq3jm8r5x9h5ariiv9s8856w7"))))
+    (build-system gnu-build-system)
+    ;; XXX: The xpi and firefox outputs are currently unused, but included
+    ;; for easy testing.
+    (outputs '("xpi" "firefox" "chromium"))
+    (arguments
+     '(#:tests? #f                      ;no tests
+       #:allowed-references ()
+       #:phases
+       (modify-phases (map (lambda (phase)
+                             (assq phase %standard-phases))
+                           '(set-paths unpack patch-source-shebangs))
+         (add-after 'unpack 'link-uassets
+           (lambda* (#:key native-inputs inputs #:allow-other-keys)
+             (symlink (string-append (assoc-ref (or native-inputs inputs)
+                                                "uassets"))
+                      "../uAssets")
+             #t))
+         (add-after 'unpack 'make-files-writable
+           (lambda _
+             ;; The build system copies some files and later tries
+             ;; modifying them.
+             (for-each make-file-writable (find-files "."))
+             #true))
+         (add-after 'patch-source-shebangs 'build-xpi
+           (lambda _
+             (invoke "./tools/make-firefox.sh" "all")))
+         (add-after 'build-xpi 'build-chromium
+           (lambda _
+             (invoke "./tools/make-chromium.sh")))
+         (add-after 'build-chromium 'install
+           (lambda* (#:key outputs #:allow-other-keys)
+             (let ((firefox (assoc-ref outputs "firefox"))
+                   (xpi (assoc-ref outputs "xpi"))
+                   (chromium (assoc-ref outputs "chromium")))
+               (install-file "dist/build/uBlock0.firefox.xpi"
+                             (string-append xpi "/lib/mozilla/extensions"))
+               (copy-recursively "dist/build/uBlock0.firefox" firefox)
+               (copy-recursively "dist/build/uBlock0.chromium" chromium)
+               #true))))))
+    (native-inputs
+     `(("python" ,python-wrapper)
+       ("uassets" ,uassets)
+       ("zip" ,zip)))
+    (synopsis "Block unwanted content from web sites")
+    (description
+     "uBlock Origin is a @dfn{wide spectrum blocker} for IceCat and
+ungoogled-chromium.")
+    (license license:gpl3+)))
+
+(define-public ublock-origin/chromium
+  (make-chromium-extension ublock-origin "chromium"))
-- 
2.28.0





^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix!
  2020-10-30 18:29 [bug#44335] [PATCH 0/2] Install Chromium extensions with Guix! Marius Bakke
  2020-10-30 18:32 ` [bug#44335] [PATCH 1/2] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
@ 2020-11-02  0:22 ` Marius Bakke
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 1/3] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
                     ` (3 more replies)
  1 sibling, 4 replies; 11+ messages in thread
From: Marius Bakke @ 2020-11-02  0:22 UTC (permalink / raw)
  To: 44335

Hello,

Here is an updated patch series that introduces a new module to deal
with the Chromium extension machinery.

The procedures have also been tweaked a little to reduce duplication.

I still don't know why extensions installed with Guix are not updated.
It could be that the update functionality has been patched out by
ungoogled-chromium.  More testing pending.

Marius Bakke (3):
  gnu: ungoogled-chromium: Add search path for installed extensions.
  Add (gnu build chromium-extension).
  gnu: Add ublock-origin-chromium.

 gnu/build/chromium-extension.scm              | 192 ++++++++++++++++++
 gnu/local.mk                                  |   3 +
 gnu/packages/browser-extensions.scm           | 100 +++++++++
 gnu/packages/chromium.scm                     |  18 +-
 .../ungoogled-chromium-search-path.patch      |  28 +++
 5 files changed, 333 insertions(+), 8 deletions(-)
 create mode 100644 gnu/build/chromium-extension.scm
 create mode 100644 gnu/packages/browser-extensions.scm
 create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

-- 
2.28.0





^ permalink raw reply	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 1/3] gnu: ungoogled-chromium: Add search path for installed extensions.
  2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
@ 2020-11-02  0:22   ` Marius Bakke
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension) Marius Bakke
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 11+ messages in thread
From: Marius Bakke @ 2020-11-02  0:22 UTC (permalink / raw)
  To: 44335

* gnu/packages/patches/ungoogled-chromium-search-path.patch: New file.
* gnu/local.mk (dist_patch_DATA): Adjust accordingly.
* gnu/packages/chromium.scm (%guix-patches): New variable.
(ungoogled-chromium-snippet): Apply %GUIX-PATCHES.
(ungoogled-chromium)[arguments]: Don't hard-code extensions directory.
[native-search-paths]: New field.
---
 gnu/local.mk                                  |  1 +
 gnu/packages/chromium.scm                     | 18 ++++++------
 .../ungoogled-chromium-search-path.patch      | 28 +++++++++++++++++++
 3 files changed, 39 insertions(+), 8 deletions(-)
 create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index d4d04c01b8..51550e80cb 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1642,6 +1642,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/u-boot-riscv64-fix-extlinux.patch	\
   %D%/packages/patches/ucx-tcp-iface-ioctl.patch		\
   %D%/packages/patches/udiskie-no-appindicator.patch		\
+  %D%/packages/patches/ungoogled-chromium-search-path.patch	\
   %D%/packages/patches/ungoogled-chromium-system-nspr.patch	\
   %D%/packages/patches/unison-fix-ocaml-4.08.patch		\
   %D%/packages/patches/unknown-horizons-python-3.8-distro.patch	\
diff --git a/gnu/packages/chromium.scm b/gnu/packages/chromium.scm
index f32a779805..06c57acd28 100644
--- a/gnu/packages/chromium.scm
+++ b/gnu/packages/chromium.scm
@@ -334,6 +334,10 @@
      (base32
       "18p9a7qffmy8m03nqva7maalgil13lj2mn0s56v3crbs4wk4lalj"))))
 
+(define %guix-patches
+  (list (local-file (search-patch "ungoogled-chromium-system-nspr.patch"))
+        (local-file (search-patch "ungoogled-chromium-search-path.patch"))))
+
 ;; This is a source 'snippet' that does the following:
 ;; *) Applies various patches for unbundling purposes and libstdc++ compatibility.
 ;; *) Runs the ungoogled patch-, domain substitution-, and scrubbing scripts.
@@ -356,9 +360,7 @@
                       (invoke "patch" "-p1" "--force" "--input"
                               patch "--no-backup-if-mismatch"))
                     (append '#+%debian-patches '#+%arch-patches
-                            '#+(list (local-file
-                                      (search-patch
-                                       "ungoogled-chromium-system-nspr.patch")))))
+                            '#+%guix-patches))
 
           (with-directory-excursion #+%ungoogled-origin
             (format #t "Ungooglifying...~%")
@@ -571,11 +573,6 @@
                        (find-files (string-append "third_party/webrtc/modules"
                                                   "/audio_coding/codecs/opus")))
 
-             (substitute* "chrome/common/chrome_paths.cc"
-               (("/usr/share/chromium/extensions")
-                ;; TODO: Add ~/.guix-profile.
-                "/run/current-system/profile/share/chromium/extensions"))
-
              ;; Many files try to include ICU headers from "third_party/icu/...".
              ;; Remove the "third_party/" prefix to use system headers instead.
              (substitute* (find-files "chrome" "\\.cc$")
@@ -840,6 +837,11 @@
        ("udev" ,eudev)
        ("valgrind" ,valgrind)
        ("vulkan-headers" ,vulkan-headers)))
+    (native-search-paths
+     (list (search-path-specification
+            (variable "CHROMIUM_EXTENSION_DIRECTORY")
+            (separator #f)              ;single entry
+            (files '("share/chromium/extensions")))))
 
     ;; Building Chromium takes ... a very long time.  On a single core, a busy
     ;; mid-end x86 system may need more than 24 hours to complete the build.
diff --git a/gnu/packages/patches/ungoogled-chromium-search-path.patch b/gnu/packages/patches/ungoogled-chromium-search-path.patch
new file mode 100644
index 0000000000..5ef9c56b52
--- /dev/null
+++ b/gnu/packages/patches/ungoogled-chromium-search-path.patch
@@ -0,0 +1,28 @@
+Look for system-installed extensions in CHROMIUM_EXTENSION_DIRECTORY instead
+of the hard-coded /usr/share/chromium/extensions.
+
+--- a/chrome/common/chrome_paths.cc
++++ b/chrome/common/chrome_paths.cc
+@@ -4,6 +4,7 @@
+ 
+ #include "chrome/common/chrome_paths.h"
+ 
++#include "base/environment.h"
+ #include "base/files/file_util.h"
+ #include "base/logging.h"
+ #include "base/native_library.h"
+@@ -511,7 +512,13 @@
+ #endif
+ #if defined(OS_LINUX) || defined(OS_CHROMEOS)
+     case chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS: {
+-      cur = base::FilePath(kFilepathSinglePrefExtensions);
++      std::unique_ptr<base::Environment> environment(base::Environment::Create());
++      std::string extension_dir;
++      if (environment->GetVar("CHROMIUM_EXTENSION_DIRECTORY", &extension_dir)) {
++        cur = base::FilePath(extension_dir);
++      } else {
++        cur = base::FilePath(kFilepathSinglePrefExtensions);
++      }
+       break;
+     }
+ #endif
-- 
2.28.0





^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension).
  2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 1/3] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
@ 2020-11-02  0:22   ` Marius Bakke
  2020-11-02  0:28     ` Marius Bakke
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 3/3] gnu: Add ublock-origin-chromium Marius Bakke
  2020-11-02  0:55   ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Leo Famulari
  3 siblings, 1 reply; 11+ messages in thread
From: Marius Bakke @ 2020-11-02  0:22 UTC (permalink / raw)
  To: 44335

* gnu/build/chromium-extension.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.
---
 gnu/build/chromium-extension.scm | 192 +++++++++++++++++++++++++++++++
 gnu/local.mk                     |   1 +
 2 files changed, 193 insertions(+)
 create mode 100644 gnu/build/chromium-extension.scm

diff --git a/gnu/build/chromium-extension.scm b/gnu/build/chromium-extension.scm
new file mode 100644
index 0000000000..35f49d788c
--- /dev/null
+++ b/gnu/build/chromium-extension.scm
@@ -0,0 +1,192 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu build chromium-extension)
+  #:use-module (gcrypt base16)
+  #:use-module ((gcrypt hash) #:prefix hash:)
+  #:use-module (ice-9 iconv)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages check)
+  #:use-module (gnu packages chromium)
+  #:use-module (gnu packages gnupg)
+  #:use-module (gnu packages tls)
+  #:use-module (gnu packages xorg)
+  #:use-module (guix build-system trivial)
+  #:export (make-chromium-extension))
+
+;;; Commentary:
+;;;
+;;; Tools to deal with Chromium extensions.
+;;;
+;;; Code:
+
+(define (make-signing-key seed)
+  "Return a derivation for a deterministic PKCS #8 private key using SEED."
+
+  (define sha256sum
+    (bytevector->base16-string (hash:sha256 (string->bytevector seed "UTF-8"))))
+
+  ;; certtool.c wants a 56 byte seed for a 2048 bit key.
+  (define size 2048)
+  (define normalized-seed (string-take sha256sum 56))
+
+  (computed-file (string-append seed "-signing-key.pem")
+                 #~(system* #$(file-append gnutls "/bin/certtool")
+                            "--generate-privkey"
+                            "--key-type=rsa"
+                            "--pkcs8"
+                            ;; Use the provable FIPS-PUB186-4 algorithm for
+                            ;; deterministic results.
+                            "--provable"
+                            "--password="
+                            "--no-text"
+                            (string-append "--bits=" #$(number->string size))
+                            (string-append "--seed=" #$normalized-seed)
+                            "--outfile" #$output)
+                 #:local-build? #t))
+
+(define* (make-crx package+output signing-key)
+  "Create a signed \".crx\" file from the unpacked Chromium extension residing
+in PACKAGE+OUTPUT, a (list package output) pair.  The extension will be signed
+with SIGNING-KEY."
+  (with-imported-modules '((guix build utils))
+    (computed-file
+     "chromium-extension.crx"
+     #~(begin
+         ;; This is not great.  We pull Xorg and Chromium just to Zip and
+         ;; sign an extension.  This should be implemented with something
+         ;; lighter.  (TODO: where is the documentation..?)
+         (use-modules (guix build utils))
+         (let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
+               (xvfb #$(file-append xorg-server "/bin/Xvfb"))
+               (packdir "/tmp/extension"))
+           (mkdir-p (dirname packdir))
+           (copy-recursively (ungexp (car package+output) (cadr package+output))
+                             packdir)
+           (system (string-append xvfb " :1 &"))
+           (setenv "DISPLAY" ":1")
+           (sleep 2)                    ;give Xorg some time to initialize...
+           ;; Chromium stores the current time in the .crx Zip archive.
+           ;; Sidestep that by using "faketime" for determinism.
+           ;; FIXME (core-updates): faketime is missing an absolute reference
+           ;; to 'date', hence the need to set PATH.
+           (setenv "PATH" #$(file-append coreutils "/bin"))
+           (invoke #$(file-append libfaketime "/bin/faketime")
+                   "2000-01-01 00:00:00"
+                   chromium
+                   "--user-data-dir=/tmp/signing-profile"
+                   (string-append "--pack-extension=" packdir)
+                   (string-append "--pack-extension-key=" #$signing-key))
+           (copy-file (string-append packdir ".crx") #$output)))
+     #:local-build? #t)))
+
+(define* (crx->chromium-json crx version)
+  "Return a derivation that creates a Chromium JSON settings file for the
+unpacked extension residing in output PACKAGE-OUTPUT of PACKAGE."
+  (computed-file "extension.json"
+                 #~(call-with-output-file #$output
+                     (lambda (port)
+                       ;; Documentation for the JSON format can be found in
+                       ;; extensions/common/extension.h and
+                       ;; chrome/browser/extensions/external_provider_impl.cc.
+                       (format port "
+{
+  \"external_crx\": \"~a\",
+  \"external_version\": \"~a\"
+}
+"
+                               #$crx #$version)))
+                 #:local-build? #t))
+
+
+(define (signing-key->public-der key)
+  "Return a derivation for a file containing the public key of KEY in DER
+format."
+  (computed-file "der"
+                 #~(system* #$(file-append gnutls "/bin/certtool")
+                            "--load-privkey" #$key
+                            "--pubkey-info"
+                            "--outfile" #$output
+                            "--outder")
+                 #:local-build? #t))
+
+(define (chromium-json->profile-object json signing-key)
+  "Return a derivation that installs JSON to the directory searched by
+Chromium, using a file name (aka extension ID) derived from SIGNING-KEY."
+  (define der (signing-key->public-der signing-key))
+
+  (with-extensions (list guile-gcrypt)
+    (with-imported-modules '((guix build utils))
+      (computed-file
+       "chromium-extension.json"
+       #~(begin
+           (use-modules (guix build utils)
+                        (gcrypt base16)
+                        (gcrypt hash))
+           (define (base16-string->chromium-base16 str)
+             ;; Translate STR, a hexadecimal string, to a Chromium-style
+             ;; representation using the letters a-p (where a=0, p=15).
+             (define s1 "0123456789abcdef")
+             (define s2 "abcdefghijklmnop")
+             (let loop ((chars (string->list str))
+                        (converted '()))
+               (if (null? chars)
+                   (list->string (reverse converted))
+                   (loop (cdr chars) (cons (string-ref
+                                            s2 (string-index s1 (car chars)))
+                                           converted)))))
+
+           (let* ((checksum (bytevector->base16-string (file-sha256 #$der)))
+                  (file-name (base16-string->chromium-base16
+                              (string-take checksum 32)))
+                  (extension-directory (string-append #$output
+                                                      "/share/chromium/extensions")))
+             (mkdir-p extension-directory)
+             (symlink #$json (string-append extension-directory "/"
+                                            file-name ".json"))))
+       #:local-build? #t))))
+
+(define* (make-chromium-extension p #:optional (output "out"))
+  "Create a Chromium extension from package P and return a package that,
+when installed, will make the extension contained in P available as a
+Chromium browser extension.  OUTPUT specifies which output of P to use."
+  (let* ((pname (package-name p))
+         (version (package-version p))
+         (signing-key (make-signing-key pname)))
+    (package
+      (inherit p)
+      (name (string-append pname "-chromium"))
+      (build-system trivial-build-system)
+      (native-inputs '())
+      (inputs
+       `(("extension" ,(chromium-json->profile-object
+                        (crx->chromium-json (make-crx (list p output)
+                                                      signing-key)
+                                            version)
+                        signing-key))))
+      (propagated-inputs '())
+      (outputs '("out"))
+      (arguments
+       '(#:modules ((guix build utils))
+         #:builder
+         (begin
+           (use-modules (guix build utils))
+           (copy-recursively (assoc-ref %build-inputs "extension")
+                             (assoc-ref %outputs "out"))))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 51550e80cb..e847f8c16a 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -657,6 +657,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/build/accounts.scm			\
   %D%/build/activation.scm			\
   %D%/build/bootloader.scm			\
+  %D%/build/chromium-extension.scm		\
   %D%/build/cross-toolchain.scm			\
   %D%/build/image.scm				\
   %D%/build/file-systems.scm			\
-- 
2.28.0





^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 3/3] gnu: Add ublock-origin-chromium.
  2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 1/3] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension) Marius Bakke
@ 2020-11-02  0:22   ` Marius Bakke
  2020-11-02  0:55   ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Leo Famulari
  3 siblings, 0 replies; 11+ messages in thread
From: Marius Bakke @ 2020-11-02  0:22 UTC (permalink / raw)
  To: 44335

* gnu/packages/browser-extensions.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
---
 gnu/local.mk                        |   1 +
 gnu/packages/browser-extensions.scm | 100 ++++++++++++++++++++++++++++
 2 files changed, 101 insertions(+)
 create mode 100644 gnu/packages/browser-extensions.scm

diff --git a/gnu/local.mk b/gnu/local.mk
index e847f8c16a..c14612cdef 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -111,6 +111,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/packages/boost.scm			\
   %D%/packages/bootloaders.scm			\
   %D%/packages/bootstrap.scm			\
+  %D%/packages/browser-extensions.scm		\
   %D%/packages/build-tools.scm			\
   %D%/packages/busybox.scm			\
   %D%/packages/c.scm				\
diff --git a/gnu/packages/browser-extensions.scm b/gnu/packages/browser-extensions.scm
new file mode 100644
index 0000000000..064a916e73
--- /dev/null
+++ b/gnu/packages/browser-extensions.scm
@@ -0,0 +1,100 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages browser-extensions)
+  #:use-module (guix packages)
+  #:use-module (guix git-download)
+  #:use-module (guix build-system gnu)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (gnu build chromium-extension)
+  #:use-module (gnu packages compression)
+  #:use-module (gnu packages python))
+
+(define uassets
+  (let ((commit "95d609786680bf4cf267b1dff3f429af88040da1"))
+    (origin
+      (method git-fetch)
+      (uri (git-reference
+            (url "https://github.com/uBlockOrigin/uAssets.git")
+            (commit commit)))
+      (file-name (git-file-name "uAssets" (string-take commit 9)))
+      (sha256
+       (base32
+        "070v2g0hyxvqv77b6z404hdlggl0sq2bfhsfl4a84k93p9pwwlrq")))))
+
+(define ublock-origin
+  (package
+    (name "ublock-origin")
+    (version "1.30.8")
+    (home-page "https://github.com/gorhill/uBlock")
+    (source (origin
+              (method git-fetch)
+              (uri (git-reference (url home-page) (commit version)))
+              (file-name (git-file-name name version))
+              (sha256
+               (base32
+                "1y4dq4p4j4qrw6gfdcbpyigqjinqq3jm8r5x9h5ariiv9s8856w7"))))
+    (build-system gnu-build-system)
+    (outputs '("xpi" "firefox" "chromium"))
+    (arguments
+     '(#:tests? #f                      ;no tests
+       #:allowed-references ()
+       #:phases
+       (modify-phases (map (lambda (phase)
+                             (assq phase %standard-phases))
+                           '(set-paths unpack patch-source-shebangs))
+         (add-after 'unpack 'link-uassets
+           (lambda* (#:key native-inputs inputs #:allow-other-keys)
+             (symlink (string-append (assoc-ref (or native-inputs inputs)
+                                                "uassets"))
+                      "../uAssets")
+             #t))
+         (add-after 'unpack 'make-files-writable
+           (lambda _
+             ;; The build system copies some files and later tries
+             ;; modifying them.
+             (for-each make-file-writable (find-files "."))
+             #true))
+         (add-after 'patch-source-shebangs 'build-xpi
+           (lambda _
+             (invoke "./tools/make-firefox.sh" "all")))
+         (add-after 'build-xpi 'build-chromium
+           (lambda _
+             (invoke "./tools/make-chromium.sh")))
+         (add-after 'build-chromium 'install
+           (lambda* (#:key outputs #:allow-other-keys)
+             (let ((firefox (assoc-ref outputs "firefox"))
+                   (xpi (assoc-ref outputs "xpi"))
+                   (chromium (assoc-ref outputs "chromium")))
+               (install-file "dist/build/uBlock0.firefox.xpi"
+                             (string-append xpi "/lib/mozilla/extensions"))
+               (copy-recursively "dist/build/uBlock0.firefox" firefox)
+               (copy-recursively "dist/build/uBlock0.chromium" chromium)
+               #true))))))
+    (native-inputs
+     `(("python" ,python-wrapper)
+       ("uassets" ,uassets)
+       ("zip" ,zip)))
+    (synopsis "Block unwanted content from web sites")
+    (description
+     "uBlock Origin is a @dfn{wide spectrum blocker} for IceCat and
+ungoogled-chromium.")
+    (license license:gpl3+)))
+
+(define-public ublock-origin/chromium
+  (make-chromium-extension ublock-origin "chromium"))
-- 
2.28.0





^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension).
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension) Marius Bakke
@ 2020-11-02  0:28     ` Marius Bakke
  0 siblings, 0 replies; 11+ messages in thread
From: Marius Bakke @ 2020-11-02  0:28 UTC (permalink / raw)
  To: 44335


[-- Attachment #1.1: Type: text/plain, Size: 215 bytes --]

Marius Bakke <marius@gnu.org> writes:

> * gnu/build/chromium-extension.scm: New file.
> * gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.

Errh, I accidentally sent an outdated patch.  Here is the new one:


[-- Attachment #1.2: 0001-Add-gnu-build-chromium-extension.patch --]
[-- Type: text/x-patch, Size: 9887 bytes --]

From 7ad719df6860c2cebdcaf73be33d1c4764c4576c Mon Sep 17 00:00:00 2001
From: Marius Bakke <marius@gnu.org>
Date: Sat, 31 Oct 2020 17:25:58 +0100
Subject: [PATCH] Add (gnu build chromium-extension).

* gnu/build/chromium-extension.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.
---
 gnu/build/chromium-extension.scm | 191 +++++++++++++++++++++++++++++++
 gnu/local.mk                     |   1 +
 2 files changed, 192 insertions(+)
 create mode 100644 gnu/build/chromium-extension.scm

diff --git a/gnu/build/chromium-extension.scm b/gnu/build/chromium-extension.scm
new file mode 100644
index 0000000000..665a94830c
--- /dev/null
+++ b/gnu/build/chromium-extension.scm
@@ -0,0 +1,191 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu build chromium-extension)
+  #:use-module (gcrypt base16)
+  #:use-module ((gcrypt hash) #:prefix hash:)
+  #:use-module (ice-9 iconv)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages check)
+  #:use-module (gnu packages chromium)
+  #:use-module (gnu packages gnupg)
+  #:use-module (gnu packages tls)
+  #:use-module (gnu packages xorg)
+  #:use-module (guix build-system trivial)
+  #:export (make-chromium-extension))
+
+;;; Commentary:
+;;;
+;;; Tools to deal with Chromium extensions.
+;;;
+;;; Code:
+
+(define (make-signing-key seed)
+  "Return a derivation for a deterministic PKCS #8 private key using SEED."
+
+  (define sha256sum
+    (bytevector->base16-string (hash:sha256 (string->bytevector seed "UTF-8"))))
+
+  ;; certtool.c wants a 56 byte seed for a 2048 bit key.
+  (define size 2048)
+  (define normalized-seed (string-take sha256sum 56))
+
+  (computed-file (string-append seed "-signing-key.pem")
+                 #~(system* #$(file-append gnutls "/bin/certtool")
+                            "--generate-privkey"
+                            "--key-type=rsa"
+                            "--pkcs8"
+                            ;; Use the provable FIPS-PUB186-4 algorithm for
+                            ;; deterministic results.
+                            "--provable"
+                            "--password="
+                            "--no-text"
+                            (string-append "--bits=" #$(number->string size))
+                            (string-append "--seed=" #$normalized-seed)
+                            "--outfile" #$output)
+                 #:local-build? #t))
+
+(define* (make-crx signing-key package #:optional (package-output "out"))
+  "Create a signed \".crx\" file from the unpacked Chromium extension residing
+in PACKAGE-OUTPUT of PACKAGE.  The extension will be signed with SIGNING-KEY."
+  (define name (package-name package))
+  (define version (package-version package))
+
+  (with-imported-modules '((guix build utils))
+    (computed-file
+     (string-append name "-" version ".crx")
+     #~(begin
+         ;; This is not great.  We pull Xorg and Chromium just to Zip and
+         ;; sign an extension.  This should be implemented with something
+         ;; lighter.  (TODO: where is the CRXv3 documentation..?)
+         (use-modules (guix build utils))
+         (let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
+               (xvfb #$(file-append xorg-server "/bin/Xvfb"))
+               (packdir "/tmp/extension"))
+           (mkdir-p (dirname packdir))
+           (copy-recursively (ungexp package package-output) packdir)
+           (system (string-append xvfb " :1 &"))
+           (setenv "DISPLAY" ":1")
+           (sleep 2)                    ;give Xorg some time to initialize...
+           ;; Chromium stores the current time in the .crx Zip archive.
+           ;; Use a fixed timestamp for deterministic behavior.
+           ;; FIXME (core-updates): faketime is missing an absolute reference
+           ;; to 'date', hence the need to set PATH.
+           (setenv "PATH" #$(file-append coreutils "/bin"))
+           (invoke #$(file-append libfaketime "/bin/faketime")
+                   "2000-01-01 00:00:00"
+                   chromium
+                   "--user-data-dir=/tmp/signing-profile"
+                   (string-append "--pack-extension=" packdir)
+                   (string-append "--pack-extension-key=" #$signing-key))
+           (copy-file (string-append packdir ".crx") #$output)))
+     #:local-build? #t)))
+
+(define* (crx->chromium-json crx version)
+  "Return a derivation that creates a Chromium JSON settings file for the
+extension given as CRX.  VERSION is used to signify the CRX version, and
+must match the version listed in the extension manifest.json."
+  ;; See chrome/browser/extensions/external_provider_impl.cc and
+  ;; extensions/common/extension.h for documentation on the JSON format.
+  (computed-file "extension.json"
+                 #~(call-with-output-file #$output
+                     (lambda (port)
+                       (format port "{
+  \"external_crx\": \"~a\",
+  \"external_version\": \"~a\"
+}
+"
+                               #$crx #$version)))
+                 #:local-build? #t))
+
+
+(define (signing-key->public-der key)
+  "Return a derivation for a file containing the public key of KEY in DER
+format."
+  (computed-file "der"
+                 #~(system* #$(file-append gnutls "/bin/certtool")
+                            "--load-privkey" #$key
+                            "--pubkey-info"
+                            "--outfile" #$output
+                            "--outder")
+                 #:local-build? #t))
+
+(define (chromium-json->profile-object json signing-key)
+  "Return a derivation that installs JSON to the directory searched by
+Chromium, using a file name (aka extension ID) derived from SIGNING-KEY."
+  (define der (signing-key->public-der signing-key))
+
+  (with-extensions (list guile-gcrypt)
+    (with-imported-modules '((guix build utils))
+      (computed-file
+       "chromium-extension"
+       #~(begin
+           (use-modules (guix build utils)
+                        (gcrypt base16)
+                        (gcrypt hash))
+           (define (base16-string->chromium-base16 str)
+             ;; Translate STR, a hexadecimal string, to a Chromium-style
+             ;; representation using the letters a-p (where a=0, p=15).
+             (define s1 "0123456789abcdef")
+             (define s2 "abcdefghijklmnop")
+             (let loop ((chars (string->list str))
+                        (converted '()))
+               (if (null? chars)
+                   (list->string (reverse converted))
+                   (loop (cdr chars) (cons (string-ref
+                                            s2 (string-index s1 (car chars)))
+                                           converted)))))
+
+           (let* ((checksum (bytevector->base16-string (file-sha256 #$der)))
+                  (file-name (base16-string->chromium-base16
+                              (string-take checksum 32)))
+                  (extension-directory (string-append #$output
+                                                      "/share/chromium/extensions")))
+             (mkdir-p extension-directory)
+             (symlink #$json (string-append extension-directory "/"
+                                            file-name ".json"))))
+       #:local-build? #t))))
+
+(define* (make-chromium-extension p #:optional (output "out"))
+  "Create a Chromium extension from package P and return a package that,
+when installed, will make the extension contained in P available as a
+Chromium browser extension.  OUTPUT specifies which output of P to use."
+  (let* ((pname (package-name p))
+         (version (package-version p))
+         (signing-key (make-signing-key pname)))
+    (package
+      (inherit p)
+      (name (string-append pname "-chromium"))
+      (build-system trivial-build-system)
+      (native-inputs '())
+      (inputs
+       `(("extension" ,(chromium-json->profile-object
+                        (crx->chromium-json (make-crx signing-key p output)
+                                            version)
+                        signing-key))))
+      (propagated-inputs '())
+      (outputs '("out"))
+      (arguments
+       '(#:modules ((guix build utils))
+         #:builder
+         (begin
+           (use-modules (guix build utils))
+           (copy-recursively (assoc-ref %build-inputs "extension")
+                             (assoc-ref %outputs "out"))))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 51550e80cb..e847f8c16a 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -657,6 +657,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/build/accounts.scm			\
   %D%/build/activation.scm			\
   %D%/build/bootloader.scm			\
+  %D%/build/chromium-extension.scm		\
   %D%/build/cross-toolchain.scm			\
   %D%/build/image.scm				\
   %D%/build/file-systems.scm			\
-- 
2.28.0


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 507 bytes --]

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix!
  2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
                     ` (2 preceding siblings ...)
  2020-11-02  0:22   ` [bug#44335] [PATCH v2 3/3] gnu: Add ublock-origin-chromium Marius Bakke
@ 2020-11-02  0:55   ` Leo Famulari
  2020-11-08 17:38     ` bug#44335: " Marius Bakke
  3 siblings, 1 reply; 11+ messages in thread
From: Leo Famulari @ 2020-11-02  0:55 UTC (permalink / raw)
  To: Marius Bakke; +Cc: 44335

On Mon, Nov 02, 2020 at 01:22:25AM +0100, Marius Bakke wrote:
> Hello,
> 
> Here is an updated patch series that introduces a new module to deal
> with the Chromium extension machinery.
> 
> The procedures have also been tweaked a little to reduce duplication.
> 
> I still don't know why extensions installed with Guix are not updated.
> It could be that the update functionality has been patched out by
> ungoogled-chromium.  More testing pending.

Really great work Marius! We trust your judgement on when the code is
"ready".




^ permalink raw reply	[flat|nested] 11+ messages in thread

* bug#44335: [PATCH v2 0/3] Install Chromium extensions with Guix!
  2020-11-02  0:55   ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Leo Famulari
@ 2020-11-08 17:38     ` Marius Bakke
  2020-11-08 17:46       ` [bug#44335] " Leo Famulari
  0 siblings, 1 reply; 11+ messages in thread
From: Marius Bakke @ 2020-11-08 17:38 UTC (permalink / raw)
  To: Leo Famulari; +Cc: 44335-done

[-- Attachment #1: Type: text/plain, Size: 1165 bytes --]

Leo Famulari <leo@famulari.name> writes:

> On Mon, Nov 02, 2020 at 01:22:25AM +0100, Marius Bakke wrote:
>> Hello,
>> 
>> Here is an updated patch series that introduces a new module to deal
>> with the Chromium extension machinery.
>> 
>> The procedures have also been tweaked a little to reduce duplication.
>> 
>> I still don't know why extensions installed with Guix are not updated.
>> It could be that the update functionality has been patched out by
>> ungoogled-chromium.  More testing pending.
>
> Really great work Marius! We trust your judgement on when the code is
> "ready".

Thanks, it's in 'master' now!  After further testing I found that
updating extensions does work, not sure what went wrong the first time
around.

Removing and reinstalling extensions is still "impossible" without
serious Chromium profile surgery.  But let's deal with that later.

I also committed a bonus extension that can send YouTube and other
streaming sites directly to the Kodi media center.

Feedback still welcome.  Enjoy the new add-on enchanced
ungoogled-chromium experience!  :-)

Now to figure out how to do the same for IceCat...

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 507 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix!
  2020-11-08 17:38     ` bug#44335: " Marius Bakke
@ 2020-11-08 17:46       ` Leo Famulari
  0 siblings, 0 replies; 11+ messages in thread
From: Leo Famulari @ 2020-11-08 17:46 UTC (permalink / raw)
  To: Marius Bakke; +Cc: 44335-done

On Sun, Nov 08, 2020 at 06:38:21PM +0100, Marius Bakke wrote:
> Thanks, it's in 'master' now!  After further testing I found that
> updating extensions does work, not sure what went wrong the first time
> around.

Great!

> I also committed a bonus extension that can send YouTube and other
> streaming sites directly to the Kodi media center.

Ah, very nice :)




^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2020-11-08 17:47 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-30 18:29 [bug#44335] [PATCH 0/2] Install Chromium extensions with Guix! Marius Bakke
2020-10-30 18:32 ` [bug#44335] [PATCH 1/2] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
2020-10-30 18:32   ` [bug#44335] [PATCH 2/2] gnu: Add ublock-origin-chromium Marius Bakke
2020-11-02  0:22 ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Marius Bakke
2020-11-02  0:22   ` [bug#44335] [PATCH v2 1/3] gnu: ungoogled-chromium: Add search path for installed extensions Marius Bakke
2020-11-02  0:22   ` [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension) Marius Bakke
2020-11-02  0:28     ` Marius Bakke
2020-11-02  0:22   ` [bug#44335] [PATCH v2 3/3] gnu: Add ublock-origin-chromium Marius Bakke
2020-11-02  0:55   ` [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix! Leo Famulari
2020-11-08 17:38     ` bug#44335: " Marius Bakke
2020-11-08 17:46       ` [bug#44335] " Leo Famulari

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).