X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=pkg%2Fkey%2Fkey.go;h=37f2c63839c5b14d70eac3ffaa07c4a5d95a7592;hb=203da80978a9c2924d02c039298ee80bafda14ff;hp=195b7c587261e167e9ad213fd27d80f06ae5c65e;hpb=60f039199482cbeffc865a259f31809aa17d9698;p=epoint diff --git a/pkg/key/key.go b/pkg/key/key.go index 195b7c5..37f2c63 100644 --- a/pkg/key/key.go +++ b/pkg/key/key.go @@ -1,3 +1,18 @@ +// Package key implements epoint key pair generation and handling. +// +// An epoint key is an OpenPGP signing key that contains a self-signed +// user id packet which matches +// "Issuer ()" +// or +// "Holder of ()" +// +// The OpenPGP DSA key material is generated from a random seed using +// a deterministic algorithm. (The self-signature is not deterministic +// but the key material and thus the fingerprint is.) +// This makes it possible to represent an obligation issuer or holder key +// pair with a few bits of secret random seed. +// (The user id only needs to be set up correctly when the key is uploaded +// to the epoint server, it is not required for signing draft documents.) package key import ( @@ -12,34 +27,30 @@ import ( "math/big" ) -const P = "C1773C0DEF5C1D75BA556137CBCE0F6EE534034FCE503D7ED1FF7A27E8638EAC3BD627C734E08D1D828B52C39EB602DC63D9544D1734A981AE2408F8037305B548EFE457E2A79EB511CFF11A0C3DB05CF64971A6AF3EF191D3EBA0841AAAC3BECF4B6CF199EDD59C732BA642A0074BAE1DC3CF724F830930C898B1865F597EF7" -const Q = "DCA9E7C9FDC18CB0B8E9A80E487F96438147EF75" -const G = "502FF28CC4D7BC1100123C9227994341C29773BFBD8D7E8FFED6D87A9D82FE573744AC8E4CCAE93E3A017A6388921CA5B0C9349B249EF87AB30AE01B3C9FD723001CB25E560CA6C25EDFC97613B41346D0597C2ECA2BED7BC6C9A032049B3FFF9AED462D09651A5995DB8E5E111384AC7B62CBAD827009269FC79D3E4E6D8AA3" +// TODO: keep denomination only in issuer key? +// TODO: cleanup -func PrivKey(r []byte) *dsa.PrivateKey { +const P = "A4D2B9575C25F0E622B8694387128A793E1AD27D12FFF4B5BA11A37CEFD31C935BCBB0A944581A6E6DA12986FCBA9D666607D71D365C286B9BCB57F6D938BE74982B7D770CE438F03B0A20ABA02E5691458C39D96E6E86AE564176ED1A6DFBAFB6EE7674CC5EDCF9FEB6158471FB3FAB53BA1CE1BA64C5626B9E8585FCEF5D31" +const Q = "FFFFFFFFFFFFFFFFFFFF254EAF9E7916D607AAAF" +const G = "7EA5C898777BE4BB29DCDC47289E718F7274C9CD7E570D3D552F3B3EE43C3DEF7BA68E57786926520CCAC71DBA13F37C4064395D5AF3334A04ABD8CED5E7FF476C661953936E8ADDE96A39D8C4AC1080A2BE3FE863A24B08BD43827E54AFADA72433704EA3C12E50E5BD08C130C68A1402FC20DA79CFE0DE931C414348D32B10" + +// Calculate DSA private key from given random seed r +func DsaKey(r []byte) *dsa.PrivateKey { priv := new(dsa.PrivateKey) priv.Parameters.P, _ = new(big.Int).SetString(P, 16) priv.Parameters.Q, _ = new(big.Int).SetString(Q, 16) priv.Parameters.G, _ = new(big.Int).SetString(G, 16) - // q > 2^159 prime - // x = sha1(r) - // if x == 0 then x = q - 1 - // if x == q then x = q - 2 - // if x > q then x = x mod q - x := new(big.Int) +loop: h := sha1.New() h.Write(r) - x.SetBytes(h.Sum()) - if x.Sign() == 0 { - x.Sub(priv.Q, big.NewInt(1)) - } - switch x.Cmp(priv.Q) { - case 0: - x.Sub(priv.Q, big.NewInt(2)) - case 1: - x.Sub(x, priv.Q) + r = h.Sum() + x.SetBytes(r) + // TODO: zero out r and h ? + if x.Sign() == 0 || x.Cmp(priv.Q) >= 0 { + // very rare + goto loop } priv.X = x priv.Y = new(big.Int) @@ -47,17 +58,18 @@ func PrivKey(r []byte) *dsa.PrivateKey { return priv } -func GenKey() (priv *dsa.PrivateKey, err error) { - x := make([]byte, len(Q)/2) - _, err = io.ReadFull(rand.Reader, x) - priv = PrivKey(x) +// Generate a random DSA private key +func RandomDsaKey() (priv *dsa.PrivateKey, err error) { + r := make([]byte, sha1.Size) + _, err = io.ReadFull(rand.Reader, r) + priv = DsaKey(r) return } -// NewEntity returns an Entity that contains a fresh DSA private key with a +// New returns an openpgp.Entity that contains a fresh DSA private key with a // single identity composed of the given full name, comment and email, any of // which may be empty but must not contain any of "()<>\x00". -func NewEntity(priv *dsa.PrivateKey, currentTimeSecs int64, name, comment, email string) (e *openpgp.Entity, err error) { +func New(priv *dsa.PrivateKey, currentTimeSecs int64, name, comment, email string) (e *openpgp.Entity, err error) { uid := packet.NewUserId(name, comment, email) if uid == nil { return nil, fmt.Errorf("NewEntity: invalid argument: user id field contained invalid characters") @@ -84,56 +96,34 @@ func NewEntity(priv *dsa.PrivateKey, currentTimeSecs int64, name, comment, email IssuerKeyId: &e.PrimaryKey.KeyId, }, } - /* - e.Subkeys = make([]Subkey, 1) - e.Subkeys[0] = Subkey{ - PublicKey: packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true), - PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true), - Sig: &packet.Signature{ - CreationTime: t, - SigType: packet.SigTypeSubkeyBinding, - PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, - FlagsValid: true, - FlagEncryptStorage: true, - FlagEncryptCommunications: true, - IssuerKeyId: &e.PrimaryKey.KeyId, - }, - } - */ return } -// simple key generation for obligation issuer clients -func NewIssuerEntity(r []byte, denomination string) (e *openpgp.Entity, err error) { - return NewEntity(PrivKey(r), 0, "Issuer", denomination, "") +// Issuer generates a key for obligation issuer clients from random seed r +func Issuer(r []byte, denomination string) (e *openpgp.Entity, err error) { + return New(DsaKey(r), 0, "Issuer", denomination, "") } -// simple key generation for obligation holder clients -func NewHolderEntity(r []byte, issuer, denomination string) (e *openpgp.Entity, err error) { - return NewEntity(PrivKey(r), 0, "Holder of "+issuer, denomination, "") +// Holder generates a key for obligation holder clients from random seed r +func Holder(r []byte, issuer, denomination string) (e *openpgp.Entity, err error) { + return New(DsaKey(r), 0, "Holder of "+issuer, denomination, "") } -// check the issuer and denomination associated with the given pgp key -func CheckEntity(e *openpgp.Entity) (isIssuer bool, issuer, denomination string, err error) { - // TODO: allow non-epoint uids - if len(e.Identities) != 1 { - err = fmt.Errorf("CheckEntity: expected one identity") - return - } - for _, i := range e.Identities { - denomination = i.UserId.Comment - if i.UserId.Name == "Issuer" { +// Check the issuer and denomination associated with the given pgp key +func Check(e *openpgp.Entity) (isIssuer bool, issuer, denomination string, err error) { + // allow multiple identities, use the first one that looks like an epoint uid + for _, id := range e.Identities { + denomination = id.UserId.Comment + if id.UserId.Name == "Issuer" { isIssuer = true issuer = fmt.Sprintf("%X", e.PrimaryKey.Fingerprint) return } - prefix := "Holder of " - if i.UserId.Name[:len(prefix)] == prefix { - issuer = i.UserId.Name[len(prefix):] + const prefix = "Holder of " + if id.UserId.Name[:len(prefix)] == prefix { + issuer = id.UserId.Name[len(prefix):] return } - break } - err = fmt.Errorf("CheckENtity: invalid userid") + err = fmt.Errorf("Check: no valid userid was found") return }