1 // Package key implements epoint key pair generation and handling.
3 // An epoint key is an OpenPGP signing key that contains a self-signed
4 // user id packet which matches
5 // "Issuer (<denomination>)"
7 // "Holder of <issuer fpr> (<denomination>)"
9 // The OpenPGP DSA key material is generated from a random seed using
10 // a deterministic algorithm. (The self-signature is not deterministic
11 // but the key material and thus the fingerprint is.)
12 // This makes it possible to represent an obligation issuer or holder key
13 // pair with a few bits of secret random seed.
14 // (The user id only needs to be set up correctly when the key is uploaded
15 // to the epoint server, it is not required for signing draft documents.)
23 "crypto/openpgp/packet"
32 // TODO: keep denomination only in issuer key?
36 const P = "A4D2B9575C25F0E622B8694387128A793E1AD27D12FFF4B5BA11A37CEFD31C935BCBB0A944581A6E6DA12986FCBA9D666607D71D365C286B9BCB57F6D938BE74982B7D770CE438F03B0A20ABA02E5691458C39D96E6E86AE564176ED1A6DFBAFB6EE7674CC5EDCF9FEB6158471FB3FAB53BA1CE1BA64C5626B9E8585FCEF5D31"
37 const Q = "FFFFFFFFFFFFFFFFFFFF254EAF9E7916D607AAAF"
38 const G = "7EA5C898777BE4BB29DCDC47289E718F7274C9CD7E570D3D552F3B3EE43C3DEF7BA68E57786926520CCAC71DBA13F37C4064395D5AF3334A04ABD8CED5E7FF476C661953936E8ADDE96A39D8C4AC1080A2BE3FE863A24B08BD43827E54AFADA72433704EA3C12E50E5BD08C130C68A1402FC20DA79CFE0DE931C414348D32B10"
40 // Calculate DSA private key from given random seed r
41 func DsaKey(r []byte) *dsa.PrivateKey {
42 priv := new(dsa.PrivateKey)
43 priv.Parameters.P, _ = new(big.Int).SetString(P, 16)
44 priv.Parameters.Q, _ = new(big.Int).SetString(Q, 16)
45 priv.Parameters.G, _ = new(big.Int).SetString(G, 16)
53 // TODO: zero out r and h ?
54 if x.Sign() == 0 || x.Cmp(priv.Q) >= 0 {
60 priv.Y.Exp(priv.G, x, priv.P)
64 // Generate a random DSA private key
65 func RandomDsaKey() (priv *dsa.PrivateKey, err error) {
66 r := make([]byte, sha1.Size)
67 _, err = io.ReadFull(rand.Reader, r)
72 // New returns an openpgp.Entity that contains a fresh DSA private key with a
73 // single identity composed of the given full name, comment and email, any of
74 // which may be empty but must not contain any of "()<>\x00".
75 func New(priv *dsa.PrivateKey, t time.Time, name, comment, email string) (e *openpgp.Entity, err error) {
76 uid := packet.NewUserId(name, comment, email)
78 return nil, fmt.Errorf("NewEntity: invalid argument: user id field contained invalid characters")
81 PrimaryKey: packet.NewDSAPublicKey(t, &priv.PublicKey),
82 PrivateKey: packet.NewDSAPrivateKey(t, priv),
83 Identities: make(map[string]*openpgp.Identity),
86 e.Identities[uid.Id] = &openpgp.Identity{
89 SelfSignature: &packet.Signature{
91 SigType: packet.SigTypePositiveCert,
92 PubKeyAlgo: packet.PubKeyAlgoDSA,
94 IsPrimaryId: &isPrimaryId,
98 IssuerKeyId: &e.PrimaryKey.KeyId,
104 // Parse armored or binary openpgp public or private key
105 func Parse(d []byte) (e *openpgp.Entity, err error) {
106 elist, err := openpgp.ReadArmoredKeyRing(bytes.NewBuffer(d))
108 elist1, err1 := openpgp.ReadKeyRing(bytes.NewBuffer(d))
116 err = fmt.Errorf("Parse: expected exactly one key")
123 // Issuer generates a key for obligation issuer clients from random seed r
124 func Issuer(r []byte, denomination string) (e *openpgp.Entity, err error) {
125 return New(DsaKey(r), time.Unix(0,0), "Issuer", denomination, "")
127 // Holder generates a key for obligation holder clients from random seed r
128 func Holder(r []byte, issuer, denomination string) (e *openpgp.Entity, err error) {
129 return New(DsaKey(r), time.Unix(0,0), "Holder of "+issuer, denomination, "")
131 // Server generates a key for the server from random seed r
132 func Server(r []byte) (e *openpgp.Entity, err error) {
133 return New(DsaKey(r), time.Now(), "Server", "", "")
136 // Key id (fingerprint)
137 func Id(e *openpgp.Entity) string {
138 return fmt.Sprintf("%X", e.PrimaryKey.Fingerprint)
141 // Check the issuer and denomination associated with the given pgp key
142 func Check(e *openpgp.Entity) (isIssuer bool, issuer, denomination string, err error) {
143 // allow multiple identities, use the first one that looks like an epoint uid
144 for _, id := range e.Identities {
145 denomination = id.UserId.Comment
146 if id.UserId.Name == "Issuer" {
151 const prefix = "Holder of "
152 if id.UserId.Name[:len(prefix)] == prefix {
153 issuer = id.UserId.Name[len(prefix):]
157 err = fmt.Errorf("Check: no valid userid was found")