1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import (
7	"crypto"
8
9	"github.com/keybase/go-crypto/openpgp/packet"
10)
11
12func combineSignatures(toSignatures []*packet.Signature, fromSignatures []*packet.Signature) (ret []*packet.Signature) {
13	ret = toSignatures
14	existingSignatures := make(map[crypto.Hash]bool)
15	for _, signature := range toSignatures {
16		existingSignatures[signature.Hash] = true
17	}
18	for _, signature := range fromSignatures {
19		if _, haveSignature := existingSignatures[signature.Hash]; haveSignature {
20			continue
21		}
22		ret = append(ret, signature)
23	}
24	return
25}
26
27// MergeKey adds the identities, revocations, and subkeys of another PGPKeyBundle to this key
28func (to *PGPKeyBundle) MergeKey(from *PGPKeyBundle) {
29
30	// First, merge identities, adding any signatures found in matching identities
31	for name, fromIdentity := range from.Identities {
32		if toIdentity, ok := to.Identities[name]; ok {
33			to.Identities[name].Signatures = combineSignatures(toIdentity.Signatures, fromIdentity.Signatures)
34
35			// There's a primary self-signature that we use. Always take the later
36			// of the two.
37			ssTo := to.Identities[name].SelfSignature
38			ssFrom := fromIdentity.SelfSignature
39			if ssFrom.CreationTime.After(ssTo.CreationTime) {
40				to.Identities[name].SelfSignature = ssFrom
41			}
42
43		} else {
44			to.Identities[fromIdentity.Name] = fromIdentity
45		}
46	}
47
48	// Then, merge revocations
49	to.Revocations = combineSignatures(to.Revocations, from.Revocations)
50
51	// Finally, merge subkeys
52	existingSubkeys := make(map[[20]byte]int)
53	for i, subkey := range to.Subkeys {
54		existingSubkeys[subkey.PublicKey.Fingerprint] = i
55	}
56	for _, subkey := range from.Subkeys {
57		if i, ok := existingSubkeys[subkey.PublicKey.Fingerprint]; ok {
58			if subkey.Sig.CreationTime.After(to.Subkeys[i].Sig.CreationTime) {
59				to.Subkeys[i].Sig = subkey.Sig
60				if subkey.Revocation != nil {
61					to.Subkeys[i].Revocation = subkey.Revocation
62				}
63			}
64		} else {
65			to.Subkeys = append(to.Subkeys, subkey)
66		}
67	}
68}
69