chromium-extension.scm 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (gnu build chromium-extension)
  19. #:use-module (guix gexp)
  20. #:use-module (guix packages)
  21. #:use-module (gnu packages chromium)
  22. #:use-module (gnu packages gnupg)
  23. #:use-module (gnu packages tls)
  24. #:use-module (gnu packages xorg)
  25. #:use-module (guix build-system trivial)
  26. #:export (make-chromium-extension))
  27. ;;; Commentary:
  28. ;;;
  29. ;;; Tools to deal with Chromium extensions.
  30. ;;;
  31. ;;; Code:
  32. (define (make-signing-key seed)
  33. "Return a derivation for a deterministic PKCS #8 private key using SEED."
  34. (computed-file
  35. (string-append seed "-signing-key.pem")
  36. (with-extensions (list guile-gcrypt)
  37. #~(begin
  38. (use-modules (gcrypt base16) (gcrypt hash) (ice-9 iconv))
  39. (let* ((sha256sum (bytevector->base16-string
  40. (sha256 (string->bytevector #$seed "UTF-8"))))
  41. ;; certtool.c wants a 56 byte seed for a 2048 bit key.
  42. (key-size 2048)
  43. (normalized-seed (string-take sha256sum 56)))
  44. (system* #$(file-append gnutls "/bin/certtool")
  45. "--generate-privkey"
  46. "--key-type=rsa"
  47. "--pkcs8"
  48. ;; Use the provable FIPS-PUB186-4 algorithm for
  49. ;; deterministic results.
  50. "--provable"
  51. "--password="
  52. "--no-text"
  53. (string-append "--bits=" (number->string key-size))
  54. (string-append "--seed=" normalized-seed)
  55. "--outfile" #$output))))
  56. #:local-build? #t))
  57. (define* (make-crx signing-key package #:optional (package-output "out"))
  58. "Create a signed \".crx\" file from the unpacked Chromium extension residing
  59. in PACKAGE-OUTPUT of PACKAGE. The extension will be signed with SIGNING-KEY."
  60. (define name (package-name package))
  61. (define version (package-version package))
  62. (computed-file
  63. (string-append name "-" version ".crx")
  64. (with-imported-modules '((guix build utils))
  65. #~(begin
  66. ;; This is not great. We pull Xorg and Chromium just to Zip and
  67. ;; sign an extension. This should be implemented with something
  68. ;; lighter. (TODO: where is the CRXv3 documentation..?)
  69. (use-modules (guix build utils))
  70. (let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
  71. (xvfb #$(file-append xorg-server "/bin/Xvfb"))
  72. (packdir (string-append (getcwd) "/extension")))
  73. (mkdir packdir)
  74. (copy-recursively (ungexp package package-output) packdir
  75. ;; Ensure consistent file modification times.
  76. #:keep-mtime? #t)
  77. (system (string-append xvfb " :1 &"))
  78. (setenv "DISPLAY" ":1")
  79. (sleep 2) ;give Xorg some time to initialize...
  80. (invoke chromium
  81. "--user-data-dir=chromium-profile"
  82. (string-append "--pack-extension=" packdir)
  83. (string-append "--pack-extension-key=" #$signing-key))
  84. (copy-file (string-append packdir ".crx") #$output))))
  85. #:local-build? #t))
  86. (define (crx->chromium-json crx version)
  87. "Return a derivation that creates a Chromium JSON settings file for the
  88. extension given as CRX. VERSION is used to signify the CRX version, and
  89. must match the version listed in the extension manifest.json."
  90. ;; See chrome/browser/extensions/external_provider_impl.cc and
  91. ;; extensions/common/extension.h for documentation on the JSON format.
  92. (computed-file "extension.json"
  93. #~(call-with-output-file #$output
  94. (lambda (port)
  95. (format port "{
  96. \"external_crx\": \"~a\",
  97. \"external_version\": \"~a\"
  98. }
  99. "
  100. #$crx #$version)))
  101. #:local-build? #t))
  102. (define (signing-key->public-der key)
  103. "Return a derivation for a file containing the public key of KEY in DER
  104. format."
  105. (computed-file "der"
  106. #~(system* #$(file-append gnutls "/bin/certtool")
  107. "--load-privkey" #$key
  108. "--pubkey-info"
  109. "--outfile" #$output
  110. "--outder")
  111. #:local-build? #t))
  112. (define (file-sha256sum file)
  113. (with-extensions (list guile-gcrypt)
  114. #~(begin
  115. (use-modules (gcrypt base16) (gcrypt hash))
  116. (bytevector->base16-string (file-sha256 #$file)))))
  117. (define* (make-chromium-extension pkg #:optional (pkg-output "out"))
  118. "Create a Chromium extension from package PKG and return a package that,
  119. when installed, will make the extension contained in PKG available as a
  120. Chromium browser extension. PKG-OUTPUT specifies which output of PKG to use."
  121. (let* ((name (package-name pkg))
  122. (version (package-version pkg))
  123. (private-key (make-signing-key name))
  124. (public-key (signing-key->public-der private-key))
  125. (checksum (file-sha256sum public-key))
  126. (crx (make-crx private-key pkg pkg-output))
  127. (json (crx->chromium-json crx version)))
  128. (package
  129. (inherit pkg)
  130. (name (string-append name "-chromium"))
  131. (source #f)
  132. (native-inputs '())
  133. (inputs '())
  134. (propagated-inputs '())
  135. (outputs '("out"))
  136. (build-system trivial-build-system)
  137. (arguments
  138. (list #:modules '((guix build utils))
  139. #:builder
  140. #~(begin
  141. (use-modules (guix build utils))
  142. (define (base16-char->chromium-base16 char)
  143. ;; Translate CHAR, a hexadecimal character, to a Chromium-style
  144. ;; representation using the letters a-p (where a=0, p=15).
  145. (string-ref "abcdefghijklmnop"
  146. (string-index "0123456789abcdef" char)))
  147. (let ((file-name (string-map base16-char->chromium-base16
  148. (string-take #$checksum 32)))
  149. (extension-directory
  150. (string-append #$output
  151. "/share/chromium/extensions")))
  152. (mkdir-p extension-directory)
  153. (symlink #$json (string-append extension-directory "/"
  154. file-name ".json")))))))))