chromium-extension.scm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
  3. ;;; Copyright © 2022 Nicolas Graves <ngraves@ngraves.fr>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gnu build chromium-extension)
  20. #:use-module (guix gexp)
  21. #:use-module (guix packages)
  22. #:use-module (gnu packages gnupg)
  23. #:use-module (gnu packages tls)
  24. #:use-module (gnu packages node-xyz)
  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. (use-modules (guix build utils))
  67. (let ((crx3 #+(file-append node-crx3 "/bin/crx3"))
  68. (packdir (string-append (getcwd) "/extension")))
  69. (mkdir packdir)
  70. (copy-recursively (ungexp package package-output) packdir
  71. ;; Ensure consistent file modification times.
  72. #:keep-mtime? #t)
  73. (invoke crx3 "--keyPath" #$signing-key packdir)
  74. (copy-file (string-append packdir ".crx") #$output))))
  75. #:local-build? #t))
  76. (define (crx->chromium-json crx version)
  77. "Return a derivation that creates a Chromium JSON settings file for the
  78. extension given as CRX. VERSION is used to signify the CRX version, and
  79. must match the version listed in the extension manifest.json."
  80. ;; See chrome/browser/extensions/external_provider_impl.cc and
  81. ;; extensions/common/extension.h for documentation on the JSON format.
  82. (computed-file "extension.json"
  83. #~(call-with-output-file #$output
  84. (lambda (port)
  85. (format port "{
  86. \"external_crx\": \"~a\",
  87. \"external_version\": \"~a\"
  88. }
  89. "
  90. #$crx #$version)))
  91. #:local-build? #t))
  92. (define (signing-key->public-der key)
  93. "Return a derivation for a file containing the public key of KEY in DER
  94. format."
  95. (computed-file "der"
  96. #~(system* #$(file-append gnutls "/bin/certtool")
  97. "--load-privkey" #$key
  98. "--pubkey-info"
  99. "--outfile" #$output
  100. "--outder")
  101. #:local-build? #t))
  102. (define (file-sha256sum file)
  103. (with-extensions (list guile-gcrypt)
  104. #~(begin
  105. (use-modules (gcrypt base16) (gcrypt hash))
  106. (bytevector->base16-string (file-sha256 #$file)))))
  107. (define* (make-chromium-extension pkg #:optional (pkg-output "out"))
  108. "Create a Chromium extension from package PKG and return a package that,
  109. when installed, will make the extension contained in PKG available as a
  110. Chromium browser extension. PKG-OUTPUT specifies which output of PKG to use."
  111. (let* ((name (package-name pkg))
  112. (version (package-version pkg))
  113. (private-key (make-signing-key name))
  114. (public-key (signing-key->public-der private-key))
  115. (checksum (file-sha256sum public-key))
  116. (crx (make-crx private-key pkg pkg-output))
  117. (json (crx->chromium-json crx version)))
  118. (package
  119. (inherit pkg)
  120. (name (string-append name "-chromium"))
  121. (source #f)
  122. (native-inputs '())
  123. (inputs '())
  124. (propagated-inputs '())
  125. (outputs '("out"))
  126. (build-system trivial-build-system)
  127. (arguments
  128. (list #:modules '((guix build utils))
  129. #:builder
  130. #~(begin
  131. (use-modules (guix build utils))
  132. (define (base16-char->chromium-base16 char)
  133. ;; Translate CHAR, a hexadecimal character, to a Chromium-style
  134. ;; representation using the letters a-p (where a=0, p=15).
  135. (string-ref "abcdefghijklmnop"
  136. (string-index "0123456789abcdef" char)))
  137. (let ((file-name (string-map base16-char->chromium-base16
  138. (string-take #$checksum 32)))
  139. (extension-directory
  140. (string-append #$output
  141. "/share/chromium/extensions")))
  142. (mkdir-p extension-directory)
  143. (symlink #$json (string-append extension-directory "/"
  144. file-name ".json")))))))))