Skip to content
This repository has been archived by the owner on Feb 20, 2020. It is now read-only.

Commit

Permalink
Merge pull request #137 from escapewindow/ed25519
Browse files Browse the repository at this point in the history
bug 1518913 - add ed25519 support
  • Loading branch information
petemoore committed Jan 29, 2019
2 parents c19d2db + eae05ed commit eb88e78
Show file tree
Hide file tree
Showing 29 changed files with 384 additions and 137 deletions.
13 changes: 11 additions & 2 deletions README.md
Expand Up @@ -66,17 +66,19 @@ Once you have been granted the above scope:
# Set up your env

* Generate a GPG key pair with `generic-worker new-openpgp-keypair --file <file>` where `file` is where you want the generated GPG private key to be written to
* Generate a GPG key pair with `generic-worker new-ed25519-keypair --file <file>` where `file` is where you want the generated ed25519 private key to be written to
* Create a generic worker configuration file somewhere, with the following content:

```
{
"accessToken": "<access token of your permanent credentials>",
"certificate": "",
"clientId": "<client ID of your permanent credentials>",
"ed25519SigningKeyLocation": "<file location you wrote ed25519 private key to>",
"livelogSecret": "<anything you like>",
"openpgpSigningKeyLocation": "<file location you wrote gpg private key to>",
"provisionerId": "test-provisioner",
"publicIP": "<ideally an IP address of one of your network interfaces>",
"signingKeyLocation": "<file location you wrote gpg private key to>",
"workerGroup": "test-worker-group",
"workerId": "test-worker-id",
"workerType": "<a unique name that only you will use for your test worker(s)>"
Expand All @@ -101,6 +103,7 @@ and reports back results to the queue.
[--config CONFIG-FILE]
[--configure-for-aws]
generic-worker show-payload-schema
generic-worker new-ed25519-keypair --file PRIVATE-KEY-FILE
generic-worker new-openpgp-keypair --file PRIVATE-KEY-FILE
generic-worker grant-winsta-access --sid SID
generic-worker --help
Expand All @@ -125,6 +128,10 @@ and reports back results to the queue.
after you have installed the service, and
instead explicitly start the service when the
preconditions have been met.
new-ed25519-keypair This will generate a fresh, new ed25519
compliant private/public key pair. The public
key will be written to stdout and the private
key will be written to the specified file.
new-openpgp-keypair This will generate a fresh, new OpenPGP
compliant private/public key pair. The public
key will be written to stdout and the private
Expand Down Expand Up @@ -176,17 +183,18 @@ and reports back results to the queue.
to talk to taskcluster queue.
clientId Taskcluster client id used by generic worker to
talk to taskcluster queue.
ed25519SigningKeyLocation The ed25519 signing key for signing artifacts with.
livelogSecret This should match the secret used by the
stateless dns server; see
https://github.com/taskcluster/stateless-dns-server
openpgpSigningKeyLocation The PGP signing key for signing artifacts with.
publicIP The IP address for clients to be directed to
for serving live logs; see
https://github.com/taskcluster/livelog and
https://github.com/taskcluster/stateless-dns-server
rootURL The root URL of the Taskcluster deploment to which
clientId and accessToken grant access. For example,
'https://taskcluster.net'.
signingKeyLocation The PGP signing key for signing artifacts with.
workerId A name to uniquely identify your worker.
workerType This should match a worker_type managed by the
provisioner you have specified.
Expand Down Expand Up @@ -360,6 +368,7 @@ and reports back results to the queue.
73 The config provided to the worker is invalid.
74 Could not grant provided SID full control of interactive windows stations and
desktop.
75 Not able to create an ed25519 key pair.
```

# Start the generic worker
Expand Down
10 changes: 5 additions & 5 deletions all-unix-style.yml
Expand Up @@ -133,12 +133,12 @@ properties:
properties:
chainOfTrust:
type: boolean
title: Enable generation of a openpgp signed Chain of Trust artifact
title: Enable generation of signed Chain of Trust artifacts
description: |-
An artifact named `public/chainOfTrust.json.asc` should be generated
which will include information for downstream tasks to build a level
of trust for the artifacts produced by the task and the environment
it ran in.
Artifacts named `public/chain-of-trust.json`, `public/chain-of-trust.json.sig`,
`public/chainOfTrust.json.asc` should be generated which will include information
for downstream tasks to build a level of trust for the artifacts produced by the
task and the environment it ran in.
Since: generic-worker 5.3.0
taskclusterProxy:
Expand Down
84 changes: 70 additions & 14 deletions artifacts_test.go
Expand Up @@ -2,14 +2,17 @@ package main

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
"time"

"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/clearsign"

Expand Down Expand Up @@ -410,6 +413,8 @@ func TestProtectedArtifactsReplaced(t *testing.T) {
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/logs/live_backing.log")...)
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/logs/certified.log")...)
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/chainOfTrust.json.asc")...)
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/chain-of-trust.json")...)
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/chain-of-trust.json.sig")...)
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/X.txt")...)
command = append(command, copyTestdataFileTo("SampleArtifacts/_/X.txt", "public/Y.txt")...)

Expand Down Expand Up @@ -437,6 +442,16 @@ func TestProtectedArtifactsReplaced(t *testing.T) {
Expires: expires,
Type: "file",
},
{
Path: "public/chain-of-trust.json",
Expires: expires,
Type: "file",
},
{
Path: "public/chain-of-trust.json.sig",
Expires: expires,
Type: "file",
},
{
Path: "public/X.txt",
Expires: expires,
Expand Down Expand Up @@ -469,8 +484,8 @@ func TestProtectedArtifactsReplaced(t *testing.T) {
t.Fatalf("Error listing artifacts: %v", err)
}

if l := len(artifacts.Artifacts); l != 6 {
t.Fatalf("Was expecting 5 artifacts, but got %v", l)
if l := len(artifacts.Artifacts); l != 8 {
t.Fatalf("Was expecting 8 artifacts, but got %v", l)
}

// use the artifact names as keys in a map, so we can look up that each key exists
Expand All @@ -491,6 +506,8 @@ func TestProtectedArtifactsReplaced(t *testing.T) {
"public/logs/live_backing.log",
"public/logs/certified.log",
"public/chainOfTrust.json.asc",
"public/chain-of-trust.json",
"public/chain-of-trust.json.sig",
} {
if !a[artifactName] {
t.Fatalf("Artifact %v missing in task %v", artifactName, taskID)
Expand Down Expand Up @@ -780,6 +797,22 @@ func TestUpload(t *testing.T) {
ContentEncoding: "gzip",
Expires: td.Expires,
},
"public/chain-of-trust.json": {
// e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./%%%/v/X
// 8308d593eb56527137532595a60255a3fcfbe4b6b068e29b22d99742bad80f6f ./_/X.txt
// a0ed21ab50992121f08da55365da0336062205fd6e7953dbff781a7de0d625b7 ./b/c/d.jpg
Extracts: []string{
"8308d593eb56527137532595a60255a3fcfbe4b6b068e29b22d99742bad80f6f",
},
ContentType: "text/plain; charset=utf-8",
ContentEncoding: "gzip",
Expires: td.Expires,
},
"public/chain-of-trust.json.sig": {
ContentType: "application/octet-stream",
ContentEncoding: "gzip",
Expires: td.Expires,
},
"public/chainOfTrust.json.asc": {
// e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./%%%/v/X
// 8308d593eb56527137532595a60255a3fcfbe4b6b068e29b22d99742bad80f6f ./_/X.txt
Expand Down Expand Up @@ -814,31 +847,52 @@ func TestUpload(t *testing.T) {
if len(b) == 0 {
t.Fatalf("Could not retrieve content of public/chainOfTrust.json.asc")
}
pubKey, err := os.Open(filepath.Join("testdata", "public-openpgp-key"))
openpgpPubKey, err := os.Open(filepath.Join("testdata", "public-openpgp-key"))
if err != nil {
t.Fatalf("Error opening public key file")
t.Fatalf("Error opening openpgp public key file")
}
defer pubKey.Close()
entityList, err := openpgp.ReadArmoredKeyRing(pubKey)
defer openpgpPubKey.Close()
entityList, err := openpgp.ReadArmoredKeyRing(openpgpPubKey)
if err != nil {
t.Fatalf("Error decoding public key file")
t.Fatalf("Error decoding openpgp public key file")
}
block, _ := clearsign.Decode(b)
openpgpBlock, _ := clearsign.Decode(b)

// signer of public/chainOfTrust.json.asc
signer, err := openpgp.CheckDetachedSignature(entityList, bytes.NewBuffer(block.Bytes), block.ArmoredSignature.Body)
signer, err := openpgp.CheckDetachedSignature(entityList, bytes.NewBuffer(openpgpBlock.Bytes), openpgpBlock.ArmoredSignature.Body)
if err != nil {
t.Fatalf("Not able to validate openpgp signature of public/chainOfTrust.json.asc")
}
var cotCert ChainOfTrustData
err = json.Unmarshal(block.Plaintext, &cotCert)
var openpgpCotCert ChainOfTrustData
err = json.Unmarshal(openpgpBlock.Plaintext, &openpgpCotCert)
if err != nil {
t.Fatalf("Could not interpret public/chainOfTrust.json as json")
}
if signer.Identities["Generic-Worker <taskcluster-accounts+gpgsigning@mozilla.com>"] == nil {
t.Fatalf("Did not get correct signer identity in public/chainOfTrust.json.asc - %#v", signer.Identities)
}

cotUnsignedBytes, _, _, _ := getArtifactContent(t, taskID, "public/chain-of-trust.json")
var cotCert ChainOfTrustData
err = json.Unmarshal(cotUnsignedBytes, &cotCert)
if err != nil {
t.Fatalf("Could not interpret public/chain-of-trust.json as json")
}
cotSignature, _, _, _ := getArtifactContent(t, taskID, "public/chain-of-trust.json.sig")
var ed25519Pubkey ed25519.PublicKey
base64Ed25519Pubkey, err := ioutil.ReadFile(filepath.Join("testdata", "ed25519_public_key"))
if err != nil {
t.Fatalf("Error opening ed25519 public key file")
}
ed25519Pubkey, err = base64.StdEncoding.DecodeString(string(base64Ed25519Pubkey))
if err != nil {
t.Fatalf("Error converting ed25519 public key to a valid pubkey")
}
ed25519Verified := ed25519.Verify(ed25519Pubkey, cotUnsignedBytes, cotSignature)
if ed25519Verified != true {
t.Fatalf("Could not verify public/chain-of-trust.json.sig signature against public/chain-of-trust.json")
}

// This trickery is to convert a TaskDefinitionResponse into a
// TaskDefinitionRequest in order that we can compare. We cannot cast, so
// need to transform to json as an intermediary step.
Expand Down Expand Up @@ -901,9 +955,11 @@ func TestUpload(t *testing.T) {
// blacklist is for artifacts that by design should not be included in
// chain of trust artifact list
blacklist := map[string]bool{
"public/logs/live.log": true,
"public/logs/live_backing.log": true,
"public/chainOfTrust.json.asc": true,
"public/logs/live.log": true,
"public/logs/live_backing.log": true,
"public/chain-of-trust.json": true,
"public/chain-of-trust.json.sig": true,
"public/chainOfTrust.json.asc": true,
}
for artifactName := range expectedArtifacts {
if _, inBlacklist := blacklist[artifactName]; !inBlacklist {
Expand Down
3 changes: 2 additions & 1 deletion aws_helper_test.go
Expand Up @@ -156,7 +156,8 @@ func (m *MockAWSProvisionedEnvironment) Secrets(t *testing.T) interface{} {
"sentryProject": "generic-worker-tests",
"shutdownMachineOnIdle": false,
"shutdownMachineOnInternalError": false,
"signingKeyLocation": filepath.Join(testdataDir, "private-opengpg-key"),
"openpgpSigningKeyLocation": filepath.Join(testdataDir, "private-opengpg-key"),
"ed25519SigningKeyLocation": filepath.Join(testdataDir, "ed25519_private_key"),
"subdomain": "taskcluster-worker.net",
"tasksDir": filepath.Join(testdataDir, t.Name()),
"workerTypeMetadata": map[string]interface{}{
Expand Down

0 comments on commit eb88e78

Please sign in to comment.