schemesign.go 3.02 KB
Newer Older
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/asn1"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"os"
	"path/filepath"
	"strings"

	"github.com/credentials/irmago"
	"github.com/credentials/irmago/internal/fs"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Println("Usage: irmasig path_to_private_key path_to_irma_configuration")
		os.Exit(0)
	}
	// Validate arguments
	privatekey, err := readPrivateKey(os.Args[1])
	if err != nil {
		die("Failed to read private key:", err)
	}
	confpath, err := filepath.Abs(os.Args[2])
	if err != nil {
		die("Invalid path", err)
	}
	if err = fs.AssertPathExists(confpath); err != nil {
		die("Specified path does not exist", nil)
	}

	// Traverse dir and add file hashes to index
	var index irma.SchemeManagerIndex = make(map[string]irma.ConfigurationFileHash)
	err = filepath.Walk(confpath, func(path string, info os.FileInfo, err error) error {
		return calculateFileHash(path, info, err, confpath, index)

	})
	if err != nil {
		die("Failed to calculate file index:", err)
	}

	// Write index.xml
	bts := []byte(index.String())
	if err = ioutil.WriteFile(confpath+"/index", bts, 0644); err != nil {
		die("Failed to write index.xml", err)
	}

	// Create and write signature
	indexHash := sha256.Sum256(bts)
	r, s, err := ecdsa.Sign(rand.Reader, privatekey, indexHash[:])
	if err != nil {
		die("Failed to sign index:", err)
	}
	sigbytes, err := asn1.Marshal([]*big.Int{r, s})
	if err != nil {
		die("Failed to serialize signature:", err)
	}
	if err = ioutil.WriteFile(confpath+"/index.sig", sigbytes, 0644); err != nil {
		die("Failed to write index.xml.sig", err)
	}

	// Write public key
	bts, err = x509.MarshalPKIXPublicKey(&privatekey.PublicKey)
	if err != nil {
		die("Failed to serialize public key", err)
	}
	pemEncodedPub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: bts})
	ioutil.WriteFile(confpath+"/pk.pem", pemEncodedPub, 0644)
}

func readPrivateKey(path string) (*ecdsa.PrivateKey, error) {
	bts, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bts)
	return x509.ParseECPrivateKey(block.Bytes)
}

func calculateFileHash(path string, info os.FileInfo, err error, confpath string, index irma.SchemeManagerIndex) error {
	if err != nil {
		return err
	}
	if info.IsDir() || // Can only sign files
		strings.HasSuffix(path, "index") || // Skip the index file itself
		strings.Contains(path, "/.git/") || // No need to traverse .git dirs
		strings.Contains(path, "/PrivateKeys/") || // Don't sign private keys
		(!strings.HasSuffix(path, ".xml") && !strings.HasSuffix(path, ".png")) {
		return nil
	}

	bts, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	relativePath, err := filepath.Rel(confpath, path)
	if err != nil {
		return err
	}
	relativePath = filepath.Join(filepath.Base(confpath), relativePath)

	hash := sha256.Sum256(bts)
	index[relativePath] = hash[:]
	return nil
}

func die(message string, err error) {
	if err != nil {
		fmt.Println(message, err)
	} else {
		fmt.Println(message)
	}
	os.Exit(1)
}