update key to new p,q,g
[epoint] / pkg / key / key.go
1 // Package key implements epoint key pair generation and handling.
2 //
3 // An epoint key is an OpenPGP signing key that contains a self-signed
4 // user id packet which matches
5 //     "Issuer (<denomination>)"
6 // or
7 //     "Holder of <issuer fpr> (<denomination>)"
8 //
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.)
16 package key
17
18 import (
19         "crypto"
20         "crypto/dsa"
21         "crypto/openpgp"
22         "crypto/openpgp/packet"
23         "crypto/rand"
24         "crypto/sha1"
25         "fmt"
26         "io"
27         "math/big"
28 )
29
30 // TODO: keep denomination only in issuer key?
31 // TODO: cleanup
32
33 const P = "A4D2B9575C25F0E622B8694387128A793E1AD27D12FFF4B5BA11A37CEFD31C935BCBB0A944581A6E6DA12986FCBA9D666607D71D365C286B9BCB57F6D938BE74982B7D770CE438F03B0A20ABA02E5691458C39D96E6E86AE564176ED1A6DFBAFB6EE7674CC5EDCF9FEB6158471FB3FAB53BA1CE1BA64C5626B9E8585FCEF5D31"
34 const Q = "FFFFFFFFFFFFFFFFFFFF254EAF9E7916D607AAAF"
35 const G = "7EA5C898777BE4BB29DCDC47289E718F7274C9CD7E570D3D552F3B3EE43C3DEF7BA68E57786926520CCAC71DBA13F37C4064395D5AF3334A04ABD8CED5E7FF476C661953936E8ADDE96A39D8C4AC1080A2BE3FE863A24B08BD43827E54AFADA72433704EA3C12E50E5BD08C130C68A1402FC20DA79CFE0DE931C414348D32B10"
36
37 func PrivKey(r []byte) *dsa.PrivateKey {
38         priv := new(dsa.PrivateKey)
39         priv.Parameters.P, _ = new(big.Int).SetString(P, 16)
40         priv.Parameters.Q, _ = new(big.Int).SetString(Q, 16)
41         priv.Parameters.G, _ = new(big.Int).SetString(G, 16)
42
43         x := new(big.Int)
44         for {
45                 h := sha1.New()
46                 h.Write(r)
47                 r = h.Sum()
48                 x.SetBytes(r)
49                 if x.Sign() == 1 && x.Cmp(priv.Q) < 0 {
50                         break
51                 }
52                 // rarely reachable
53         }
54         priv.X = x
55         priv.Y = new(big.Int)
56         priv.Y.Exp(priv.G, x, priv.P)
57         return priv
58 }
59
60 func GenKey() (priv *dsa.PrivateKey, err error) {
61         x := make([]byte, len(Q)/2)
62         _, err = io.ReadFull(rand.Reader, x)
63         priv = PrivKey(x)
64         return
65 }
66
67 // NewEntity returns an Entity that contains a fresh DSA private key with a
68 // single identity composed of the given full name, comment and email, any of
69 // which may be empty but must not contain any of "()<>\x00".
70 func NewEntity(priv *dsa.PrivateKey, currentTimeSecs int64, name, comment, email string) (e *openpgp.Entity, err error) {
71         uid := packet.NewUserId(name, comment, email)
72         if uid == nil {
73                 return nil, fmt.Errorf("NewEntity: invalid argument: user id field contained invalid characters")
74         }
75         t := uint32(currentTimeSecs)
76         e = &openpgp.Entity{
77                 PrimaryKey: packet.NewDSAPublicKey(t, &priv.PublicKey, false /* not a subkey */ ),
78                 PrivateKey: packet.NewDSAPrivateKey(t, priv, false /* not a subkey */ ),
79                 Identities: make(map[string]*openpgp.Identity),
80         }
81         isPrimaryId := true
82         e.Identities[uid.Id] = &openpgp.Identity{
83                 Name:   uid.Name,
84                 UserId: uid,
85                 SelfSignature: &packet.Signature{
86                         CreationTime: t,
87                         SigType:      packet.SigTypePositiveCert,
88                         PubKeyAlgo:   packet.PubKeyAlgoDSA,
89                         Hash:         crypto.SHA256,
90                         IsPrimaryId:  &isPrimaryId,
91                         FlagsValid:   true,
92                         FlagSign:     true,
93                         FlagCertify:  true,
94                         IssuerKeyId:  &e.PrimaryKey.KeyId,
95                 },
96         }
97         /*
98                 e.Subkeys = make([]Subkey, 1)
99                 e.Subkeys[0] = Subkey{
100                         PublicKey:  packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true),
101                         PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true),
102                         Sig: &packet.Signature{
103                                 CreationTime:              t,
104                                 SigType:                   packet.SigTypeSubkeyBinding,
105                                 PubKeyAlgo:                packet.PubKeyAlgoRSA,
106                                 Hash:                      crypto.SHA256,
107                                 FlagsValid:                true,
108                                 FlagEncryptStorage:        true,
109                                 FlagEncryptCommunications: true,
110                                 IssuerKeyId:               &e.PrimaryKey.KeyId,
111                         },
112                 }
113         */
114         return
115 }
116
117 // simple key generation for obligation issuer clients
118 func NewIssuerEntity(r []byte, denomination string) (e *openpgp.Entity, err error) {
119         return NewEntity(PrivKey(r), 0, "Issuer", denomination, "")
120 }
121 // simple key generation for obligation holder clients
122 func NewHolderEntity(r []byte, issuer, denomination string) (e *openpgp.Entity, err error) {
123         return NewEntity(PrivKey(r), 0, "Holder of "+issuer, denomination, "")
124 }
125
126 // check the issuer and denomination associated with the given pgp key
127 func CheckEntity(e *openpgp.Entity) (isIssuer bool, issuer, denomination string, err error) {
128         // TODO: allow non-epoint uids
129         if len(e.Identities) != 1 {
130                 err = fmt.Errorf("CheckEntity: expected one identity")
131                 return
132         }
133         for _, i := range e.Identities {
134                 denomination = i.UserId.Comment
135                 if i.UserId.Name == "Issuer" {
136                         isIssuer = true
137                         issuer = fmt.Sprintf("%X", e.PrimaryKey.Fingerprint)
138                         return
139                 }
140                 prefix := "Holder of "
141                 if i.UserId.Name[:len(prefix)] == prefix {
142                         issuer = i.UserId.Name[len(prefix):]
143                         return
144                 }
145                 break
146         }
147         err = fmt.Errorf("CheckENtity: invalid userid")
148         return
149 }