2
Hockeypuck - OpenPGP key server
3
Copyright (C) 2012-2014 Casey Marshall
5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU Affero General Public License as published by
7
the Free Software Foundation, version 3.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU Affero General Public License for more details.
14
You should have received a copy of the GNU Affero General Public License
15
along with this program. If not, see <http://www.gnu.org/licenses/>.
18
// hockeypuck is an OpenPGP keyserver.
29
"github.com/cmars/conflux"
30
"github.com/cmars/conflux/recon"
31
"github.com/jmoiron/sqlx"
33
"launchpad.net/gnuflag"
35
. "github.com/hockeypuck/hockeypuck"
36
"github.com/hockeypuck/hockeypuck/openpgp"
48
ptree recon.PrefixTree
53
func (ec *loadCmd) Name() string { return "load" }
55
func (ec *loadCmd) Desc() string { return "Load OpenPGP keyring files into database" }
57
func newLoadCmd() *loadCmd {
59
flags := gnuflag.NewFlagSet(cmd.Name(), gnuflag.ExitOnError)
60
flags.StringVar(&cmd.configPath, "config", "", "Hockeypuck configuration file")
61
flags.StringVar(&cmd.path, "path", "", "OpenPGP keyring file path or glob pattern")
62
flags.IntVar(&cmd.txnSize, "txn-size", 5000, "Transaction size; public keys per commit")
63
flags.BoolVar(&cmd.ignoreDups, "ignore-dups", false, "Ignore duplicate entries")
64
flags.BoolVar(&cmd.verifyRoundTrip, "verify-round-trip", false, "Fetch key after insert and verify digest (slow)")
69
func (ec *loadCmd) Main() {
71
Usage(ec, "--path is required")
73
if ec.verifyRoundTrip {
77
Usage(ec, "Invalid --txn-size, must be >= 1")
79
ec.configuredCmd.Main()
82
if ec.db, err = openpgp.NewDB(); err != nil {
85
ec.w = &openpgp.Worker{Loader: openpgp.NewLoader(ec.db, true)}
86
// Ensure tables all exist
87
if err = ec.db.CreateTables(); err != nil {
90
reconSettings := recon.NewSettings(openpgp.Config().Settings.TomlTree)
91
if ec.ptree, err = openpgp.NewSksPTree(reconSettings); err != nil {
94
// Create the prefix tree (if not exists)
95
if err = ec.ptree.Create(); err != nil {
96
die(fmt.Errorf("Unable to create prefix tree: %v", err))
98
// Ensure tables all exist
99
if err = ec.db.CreateTables(); err != nil {
100
die(fmt.Errorf("Unable to create database tables: %v", err))
102
// Load all keys from input material
103
ec.loadAllKeys(ec.path)
104
// Close the prefix tree
105
if err = ec.ptree.Close(); err != nil {
106
log.Println("Close ptree:", err)
108
// Close the database connection
109
if err = ec.db.Close(); err != nil {
110
log.Println("Close database:", err)
114
func (ec *loadCmd) flushDb() {
116
if !ec.verifyRoundTrip {
117
log.Println("Loaded", ec.nkeys, "keys")
119
if err := ec.tx.Commit(); err != nil {
120
die(fmt.Errorf("Error committing transaction: %v", err))
127
func (ec *loadCmd) insertKey(keyRead *openpgp.ReadKeyResult) error {
130
if ec.tx, err = ec.w.Begin(); err != nil {
131
die(fmt.Errorf("Error starting new transaction: %v", err))
133
} else if ec.nkeys%ec.txnSize == 0 {
135
if ec.tx, err = ec.w.Begin(); err != nil {
136
die(fmt.Errorf("Error starting new transaction: %v", err))
139
// Load key into relational database
140
if err = ec.w.InsertKeyTx(ec.tx, keyRead.Pubkey); err != nil {
141
log.Println("Error inserting key:", keyRead.Pubkey.Fingerprint(), ":", err)
142
if _, is := err.(pq.Error); is {
143
die(fmt.Errorf("Unable to load due to database errors."))
148
if ec.verifyRoundTrip {
150
loadKey := keyRead.Pubkey
151
loadDigest := openpgp.SksDigest(loadKey, md5.New())
152
if loadKey.Md5 != loadDigest {
153
log.Println("RTC: loaded key", loadKey.Md5, "!=", "recalculated", loadDigest)
155
checkKey, err := ec.w.FetchKey(loadKey.RFingerprint)
157
log.Println("RTC: check failed for", loadKey.Fingerprint(), ":", err)
160
checkDigest := openpgp.SksDigest(checkKey, md5.New())
161
if checkKey.Md5 != checkDigest {
162
log.Println("RTC: check key", checkKey.Md5, "!=", "recalculated", checkDigest)
164
if loadKey.Md5 != checkKey.Md5 {
165
log.Println("RTC: load key", loadKey.Md5, "!=", "check key", checkKey.Md5)
171
func (ec *loadCmd) loadAllKeys(path string) {
172
keyfiles, err := filepath.Glob(path)
176
for _, keyfile := range keyfiles {
178
if f, err = os.Open(keyfile); err != nil {
179
log.Println("Failed to open", keyfile, ":", err)
183
log.Println("Loading keys from", keyfile)
185
for keyRead := range openpgp.ReadKeys(f) {
186
if keyRead.Error != nil {
187
log.Println("Error reading key:", keyRead.Error)
190
digest, err := hex.DecodeString(keyRead.Pubkey.Md5)
192
log.Println("bad digest:", keyRead.Pubkey.Md5)
195
digest = recon.PadSksElement(digest)
196
digestZp := conflux.Zb(conflux.P_SKS, digest)
197
err = ec.ptree.Insert(digestZp)
199
log.Println("Error inserting digest ", keyRead.Pubkey.Md5, ":", err)
202
err = ec.insertKey(keyRead)
204
log.Println("Error inserting key", keyRead.Pubkey.Md5, "into database:", err)
205
// Attempt to remove digest from ptree, since it was not successfully loaded
206
ec.ptree.Remove(digestZp)