forked from alokmenghrajani/gpgeez
/
gpgeez.go
166 lines (146 loc) · 4.42 KB
/
gpgeez.go
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Package gpgeez is a wrapper around golang.org/x/crypto/openpgp
package gpgeez
import (
"bytes"
"time"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
)
// Config for generating keys.
type Config struct {
packet.Config
// Expiry is the duration that the generated key will be valid for.
Expiry time.Duration
}
// Key represents an OpenPGP key.
type Key struct {
openpgp.Entity
}
// Values from https://tools.ietf.org/html/rfc4880#section-9
const (
md5 = 1
sha1 = 2
ripemd160 = 3
sha256 = 8
sha384 = 9
sha512 = 10
sha224 = 11
)
// MakeKey converts an OpenPGP Entity into a gpgeez Key
func MakeKey(key openpgp.Entity) *Key {
return &Key{key}
}
// CreateKey creates an OpenPGP key which is similar to running gpg --gen-key
// on the command line. In other words, this method returns a primary signing
// key and an encryption subkey with expected self-signatures.
//
// There are a few differences:
//
// • GnuPG sets key server preference to 0x80, no-modify (see https://tools.ietf.org/html/rfc4880#section-5.2.3.17).
//
// • GnuPG sets features to 0x01, modification detection (see https://tools.ietf.org/html/rfc4880#page-36).
//
// • GnuPG sets the digest algorithm to SHA1. Go defaults to SHA256.
//
// • GnuPG includes Bzip2 as a compression method. Go currently doesn't support Bzip2, so that option isn't set.
//
// • Issuer key ID is hashed subpkt instead of subpkt, and contains a primary user ID sub packet.
//
// You can see these differences for yourself by comparing the output of:
// go run example/create_key.go | gpg --homedir /tmp --list-packets
// with:
// gpg --homedir /tmp --gen-key
// gpg --homedir /tmp -a --export | gpg --homedir /tmp --list-packets
//
// Or just look at
// https://github.com/alokmenghrajani/gpgeez/blob/master/gpgeez_test.pl
//
// Some useful links:
// https://godoc.org/golang.org/x/crypto/openpgp,
// https://davesteele.github.io/gpg/2014/09/20/anatomy-of-a-gpg-key,
// https://github.com/golang/go/issues/12153
func CreateKey(name, comment, email string, config *Config) (*Key, error) {
// Create the key
key, err := openpgp.NewEntity(name, comment, email, &config.Config)
if err != nil {
return nil, err
}
// Set expiry and algorithms. Self-sign the identity.
dur := uint32(config.Expiry.Seconds())
for _, id := range key.Identities {
id.SelfSignature.KeyLifetimeSecs = &dur
id.SelfSignature.PreferredSymmetric = []uint8{
uint8(packet.CipherAES256),
uint8(packet.CipherAES192),
uint8(packet.CipherAES128),
uint8(packet.CipherCAST5),
uint8(packet.Cipher3DES),
}
id.SelfSignature.PreferredHash = []uint8{
sha256,
sha1,
sha384,
sha512,
sha224,
}
id.SelfSignature.PreferredCompression = []uint8{
uint8(packet.CompressionZLIB),
uint8(packet.CompressionZIP),
}
err := id.SelfSignature.SignUserId(id.UserId.Id, key.PrimaryKey, key.PrivateKey, &config.Config)
if err != nil {
return nil, err
}
}
// Self-sign the Subkeys
for _, subkey := range key.Subkeys {
subkey.Sig.KeyLifetimeSecs = &dur
err := subkey.Sig.SignKey(subkey.PublicKey, key.PrivateKey, &config.Config)
if err != nil {
return nil, err
}
}
r := Key{*key}
return &r, nil
}
// Armor returns the public part of a key in armored format.
func (key *Key) Armor() (string, error) {
buf := new(bytes.Buffer)
armor, err := armor.Encode(buf, openpgp.PublicKeyType, nil)
if err != nil {
return "", err
}
key.Serialize(armor)
armor.Close()
return buf.String(), nil
}
// ArmorPrivate returns the private part of a key in armored format.
//
// Note: if you want to protect the string against varous low-level attacks,
// you should look at https://github.com/stouset/go.secrets and
// https://github.com/worr/secstring and then re-implement this function.
func (key *Key) ArmorPrivate(config *Config) (string, error) {
buf := new(bytes.Buffer)
armor, err := armor.Encode(buf, openpgp.PrivateKeyType, nil)
if err != nil {
return "", err
}
c := config.Config
key.SerializePrivate(armor, &c)
armor.Close()
return buf.String(), nil
}
// A keyring is simply one (or more) keys in binary format.
func (key *Key) Keyring() []byte {
buf := new(bytes.Buffer)
key.Serialize(buf)
return buf.Bytes()
}
// A secring is simply one (or more) keys in binary format.
func (key *Key) Secring(config *Config) []byte {
buf := new(bytes.Buffer)
c := config.Config
key.SerializePrivate(buf, &c)
return buf.Bytes()
}