| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 |
|
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 |
|
| 26 | (define-module (gnu build chromium-extension)
|
| 27 | #:use-module (guix gexp)
|
| 28 | #:use-module (guix packages)
|
| 29 | #:use-module (gnu packages gnupg)
|
| 30 | #:use-module (gnu packages tls)
|
| 31 | #:use-module (gnu packages node-xyz)
|
| 32 | #:use-module (guix build-system trivial)
|
| 33 | #:export (make-chromium-extension))
|
| 34 |
|
| 35 | (define (make-signing-key seed)
|
| 36 | "Return a derivation for a deterministic PKCS #8 private key using SEED."
|
| 37 | (computed-file
|
| 38 | (string-append seed "-signing-key.pem")
|
| 39 | (with-extensions (list guile-gcrypt)
|
| 40 | #~(begin
|
| 41 | (use-modules (gcrypt base16) (gcrypt hash) (ice-9 iconv))
|
| 42 | (let* ((sha256sum (bytevector->base16-string
|
| 43 | (sha256 (string->bytevector #$seed "UTF-8"))))
|
| 44 | |
| 45 | (key-size 2048)
|
| 46 | (normalized-seed (string-take sha256sum 56)))
|
| 47 |
|
| 48 | (system* #$(file-append gnutls "/bin/certtool")
|
| 49 | "--generate-privkey"
|
| 50 | "--key-type=rsa"
|
| 51 | "--pkcs8"
|
| 52 | |
| 53 | |
| 54 | "--provable"
|
| 55 | "--password="
|
| 56 | "--no-text"
|
| 57 | (string-append "--bits=" (number->string key-size))
|
| 58 | (string-append "--seed=" normalized-seed)
|
| 59 | "--outfile" #$output))))
|
| 60 | #:local-build? #t))
|
| 61 |
|
| 62 | (define* (make-crx signing-key package #:optional (package-output "out"))
|
| 63 | "Create a signed \".crx\" file from the unpacked Chromium extension residing
|
| 64 | in PACKAGE-OUTPUT of PACKAGE. The extension will be signed with SIGNING-KEY."
|
| 65 | (define name (package-name package))
|
| 66 | (define version (package-version package))
|
| 67 |
|
| 68 | (computed-file
|
| 69 | (string-append name "-" version ".crx")
|
| 70 | (with-imported-modules '((guix build utils))
|
| 71 | #~(begin
|
| 72 | (use-modules (guix build utils))
|
| 73 | (let ((crx3 #+(file-append node-crx3 "/bin/crx3"))
|
| 74 | (packdir (string-append (getcwd) "/extension")))
|
| 75 | (mkdir packdir)
|
| 76 | (copy-recursively (ungexp package package-output) packdir
|
| 77 | |
| 78 | #:keep-mtime? #t)
|
| 79 | (invoke crx3 "--keyPath" #$signing-key packdir)
|
| 80 | (copy-file (string-append packdir ".crx") #$output))))
|
| 81 | #:local-build? #t))
|
| 82 |
|
| 83 | (define (crx->chromium-json crx version)
|
| 84 | "Return a derivation that creates a Chromium JSON settings file for the
|
| 85 | extension given as CRX. VERSION is used to signify the CRX version, and
|
| 86 | must match the version listed in the extension manifest.json."
|
| 87 | |
| 88 | |
| 89 | (computed-file "extension.json"
|
| 90 | #~(call-with-output-file #$output
|
| 91 | (lambda (port)
|
| 92 | (format port "{
|
| 93 | \"external_crx\": \"~a\",
|
| 94 | \"external_version\": \"~a\"
|
| 95 | }
|
| 96 | "
|
| 97 | #$crx #$version)))
|
| 98 | #:local-build? #t))
|
| 99 |
|
| 100 |
|
| 101 | (define (signing-key->public-der key)
|
| 102 | "Return a derivation for a file containing the public key of KEY in DER
|
| 103 | format."
|
| 104 | (computed-file "der"
|
| 105 | #~(system* #$(file-append gnutls "/bin/certtool")
|
| 106 | "--load-privkey" #$key
|
| 107 | "--pubkey-info"
|
| 108 | "--outfile" #$output
|
| 109 | "--outder")
|
| 110 | #:local-build? #t))
|
| 111 |
|
| 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 |
|
| 118 | (define* (make-chromium-extension pkg #:optional (pkg-output "out"))
|
| 119 | "Create a Chromium extension from package PKG and return a package that,
|
| 120 | when installed, will make the extension contained in PKG available as a
|
| 121 | Chromium browser extension. PKG-OUTPUT specifies which output of PKG to use."
|
| 122 | (let* ((name (package-name pkg))
|
| 123 | (version (package-version pkg)))
|
| 124 | (package
|
| 125 | (inherit pkg)
|
| 126 | (name (string-append name "-chromium"))
|
| 127 | (source #f)
|
| 128 | (native-inputs '())
|
| 129 | (inputs '())
|
| 130 | (propagated-inputs '())
|
| 131 | (outputs '("out"))
|
| 132 | (build-system trivial-build-system)
|
| 133 | (arguments
|
| 134 | (list #:modules '((guix build utils))
|
| 135 | #:builder
|
| 136 | (let*
|
| 137 | ((private-key (make-signing-key name))
|
| 138 | (public-key (signing-key->public-der private-key))
|
| 139 | (checksum (file-sha256sum public-key))
|
| 140 | (crx (make-crx private-key pkg pkg-output))
|
| 141 | (json (crx->chromium-json crx version)))
|
| 142 | #~(begin
|
| 143 | (use-modules (guix build utils))
|
| 144 | (define (base16-char->chromium-base16 char)
|
| 145 | |
| 146 | |
| 147 | (string-ref "abcdefghijklmnop"
|
| 148 | (string-index "0123456789abcdef" char)))
|
| 149 | (let ((file-name (string-map base16-char->chromium-base16
|
| 150 | (string-take #$checksum 32)))
|
| 151 | (extension-directory
|
| 152 | (string-append #$output
|
| 153 | "/share/chromium/extensions")))
|
| 154 | (mkdir-p extension-directory)
|
| 155 | (symlink #$json (string-append extension-directory "/"
|
| 156 | file-name ".json"))))))))))
|