From c4f3a5b215b5b2d36a58e5a502a3e9e8f660fdcc Mon Sep 17 00:00:00 2001 From: mpl Date: Sun, 9 Feb 2014 01:04:14 +0100 Subject: [PATCH] third_party,jsonsign: bump openpgp to 188:61c59dda830a http://camlistore.org/issue/370 Change-Id: I0066b1ad1455ff7960bda1f64ee953daa1195e1c --- pkg/jsonsign/jsonsign_test.go | 2 +- pkg/jsonsign/keys.go | 6 +- pkg/jsonsign/sign.go | 8 +- .../p/go.crypto/openpgp/armor/armor.go | 2 +- .../go.crypto/openpgp/clearsign/clearsign.go | 15 +- .../p/go.crypto/openpgp/errors/errors.go | 14 +- .../p/go.crypto/openpgp/keys.go | 109 +++---- .../p/go.crypto/openpgp/keys_test.go | 41 +++ .../p/go.crypto/openpgp/packet/compressed.go | 85 ++++++ .../p/go.crypto/openpgp/packet/config.go | 16 +- .../go.crypto/openpgp/packet/encrypted_key.go | 15 +- .../openpgp/packet/encrypted_key_test.go | 7 +- .../p/go.crypto/openpgp/packet/ocfb.go | 6 +- .../p/go.crypto/openpgp/packet/opaque.go | 161 ++++++++++ .../p/go.crypto/openpgp/packet/opaque_test.go | 67 +++++ .../p/go.crypto/openpgp/packet/packet.go | 63 +++- .../p/go.crypto/openpgp/packet/private_key.go | 2 +- .../p/go.crypto/openpgp/packet/public_key.go | 270 ++++++++++++++++- .../openpgp/packet/public_key_test.go | 103 +++++++ .../go.crypto/openpgp/packet/public_key_v3.go | 274 ++++++++++++++++++ .../openpgp/packet/public_key_v3_test.go | 82 ++++++ .../p/go.crypto/openpgp/packet/signature.go | 58 +++- .../go.crypto/openpgp/packet/signature_v3.go | 146 ++++++++++ .../openpgp/packet/signature_v3_test.go | 92 ++++++ .../openpgp/packet/symmetric_key_encrypted.go | 8 +- .../packet/symmetric_key_encrypted_test.go | 11 +- .../openpgp/packet/symmetrically_encrypted.go | 5 +- .../packet/symmetrically_encrypted_test.go | 3 +- .../go.crypto/openpgp/packet/userattribute.go | 91 ++++++ .../openpgp/packet/userattribute_test.go | 109 +++++++ .../p/go.crypto/openpgp/read.go | 39 ++- .../p/go.crypto/openpgp/read_test.go | 11 +- .../p/go.crypto/openpgp/s2k/s2k.go | 31 +- .../p/go.crypto/openpgp/s2k/s2k_test.go | 4 +- .../p/go.crypto/openpgp/write.go | 130 ++++++--- .../p/go.crypto/openpgp/write_test.go | 28 +- 36 files changed, 1901 insertions(+), 213 deletions(-) create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/keys_test.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/opaque.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/opaque_test.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3_test.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3_test.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute.go create mode 100644 third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute_test.go diff --git a/pkg/jsonsign/jsonsign_test.go b/pkg/jsonsign/jsonsign_test.go index a05836b48..b78eb9867 100644 --- a/pkg/jsonsign/jsonsign_test.go +++ b/pkg/jsonsign/jsonsign_test.go @@ -162,7 +162,7 @@ func TestSigning(t *testing.T) { if vr.Verify() { t.Fatalf("unexpected verification of faked signature") } - AssertErrorContains(t, vr.Err, "OpenPGP signature invalid: hash tag doesn't match", + AssertErrorContains(t, vr.Err, "openpgp: invalid signature: hash tag doesn't match", "expected signature verification error") t.Logf("TODO: verify GPG-vs-Go sign & verify interop both ways, once implemented.") diff --git a/pkg/jsonsign/keys.go b/pkg/jsonsign/keys.go index 9a54efc78..d676b6c0f 100644 --- a/pkg/jsonsign/keys.go +++ b/pkg/jsonsign/keys.go @@ -18,7 +18,6 @@ package jsonsign import ( "bytes" - "crypto/rand" "errors" "fmt" "io" @@ -26,7 +25,6 @@ import ( "os" "path/filepath" "strings" - "time" "camlistore.org/pkg/osutil" @@ -159,12 +157,12 @@ func NewEntity() (*openpgp.Entity, error) { name := "" // intentionally empty comment := "camlistore" email := "" // intentionally empty - return openpgp.NewEntity(rand.Reader, time.Now(), name, comment, email) + return openpgp.NewEntity(name, comment, email, nil) } func WriteKeyRing(w io.Writer, el openpgp.EntityList) error { for _, ent := range el { - if err := ent.SerializePrivate(w); err != nil { + if err := ent.SerializePrivate(w, nil); err != nil { return err } } diff --git a/pkg/jsonsign/sign.go b/pkg/jsonsign/sign.go index a10e63837..4bfaa368f 100644 --- a/pkg/jsonsign/sign.go +++ b/pkg/jsonsign/sign.go @@ -31,6 +31,7 @@ import ( "camlistore.org/pkg/blob" "camlistore.org/pkg/osutil" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp" + "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet" ) type EntityFetcher interface { @@ -204,7 +205,12 @@ func (sr *SignRequest) Sign() (signedJSON string, err error) { } var buf bytes.Buffer - err = openpgp.ArmoredDetachSignAt(&buf, signer, sr.SignatureTime, strings.NewReader(trimmedJSON)) + err = openpgp.ArmoredDetachSign( + &buf, + signer, + strings.NewReader(trimmedJSON), + &packet.Config{Time: func() time.Time { return sr.SignatureTime }}, + ) if err != nil { return "", err } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/armor/armor.go b/third_party/code.google.com/p/go.crypto/openpgp/armor/armor.go index 0850ab035..43c4f967a 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/armor/armor.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/armor/armor.go @@ -111,7 +111,7 @@ func (l *lineReader) Read(p []byte) (n int, err error) { return 0, io.EOF } - if len(line) > 64 { + if len(line) > 96 { return 0, ArmorCorrupt } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/clearsign/clearsign.go b/third_party/code.google.com/p/go.crypto/openpgp/clearsign/clearsign.go index 92e736a04..1f6df681c 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/clearsign/clearsign.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/clearsign/clearsign.go @@ -17,7 +17,6 @@ import ( "io" "net/textproto" "strconv" - "time" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" @@ -179,9 +178,8 @@ type dashEscaper struct { whitespace []byte byteBuf []byte // a one byte buffer to save allocations - privateKey *packet.PrivateKey - signingTime time.Time - rand io.Reader + privateKey *packet.PrivateKey + config *packet.Config } func (d *dashEscaper) Write(data []byte) (n int, err error) { @@ -261,10 +259,10 @@ func (d *dashEscaper) Close() (err error) { sig.SigType = packet.SigTypeText sig.PubKeyAlgo = d.privateKey.PubKeyAlgo sig.Hash = d.hashType - sig.CreationTime = d.signingTime + sig.CreationTime = d.config.Now() sig.IssuerKeyId = &d.privateKey.KeyId - if err = sig.Sign(d.rand, d.h, d.privateKey); err != nil { + if err = sig.Sign(d.h, d.privateKey, d.config); err != nil { return } @@ -334,9 +332,8 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) ( byteBuf: make([]byte, 1), - privateKey: privateKey, - signingTime: config.Now(), - rand: config.Random(), + privateKey: privateKey, + config: config, } return diff --git a/third_party/code.google.com/p/go.crypto/openpgp/errors/errors.go b/third_party/code.google.com/p/go.crypto/openpgp/errors/errors.go index b9016bd42..cb3f31962 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/errors/errors.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/errors/errors.go @@ -14,7 +14,7 @@ import ( type StructuralError string func (s StructuralError) Error() string { - return "OpenPGP data invalid: " + string(s) + return "openpgp: invalid data: " + string(s) } // UnsupportedError indicates that, although the OpenPGP data is valid, it @@ -22,7 +22,7 @@ func (s StructuralError) Error() string { type UnsupportedError string func (s UnsupportedError) Error() string { - return "OpenPGP feature unsupported: " + string(s) + return "openpgp: unsupported feature: " + string(s) } // InvalidArgumentError indicates that the caller is in error and passed an @@ -30,7 +30,7 @@ func (s UnsupportedError) Error() string { type InvalidArgumentError string func (i InvalidArgumentError) Error() string { - return "OpenPGP argument invalid: " + string(i) + return "openpgp: invalid argument: " + string(i) } // SignatureError indicates that a syntactically valid signature failed to @@ -38,13 +38,13 @@ func (i InvalidArgumentError) Error() string { type SignatureError string func (b SignatureError) Error() string { - return "OpenPGP signature invalid: " + string(b) + return "openpgp: invalid signature: " + string(b) } type keyIncorrectError int func (ki keyIncorrectError) Error() string { - return "the given key was incorrect" + return "openpgp: incorrect key" } var ErrKeyIncorrect error = keyIncorrectError(0) @@ -52,7 +52,7 @@ var ErrKeyIncorrect error = keyIncorrectError(0) type unknownIssuerError int func (unknownIssuerError) Error() string { - return "signature make by unknown entity" + return "openpgp: signature made by unknown entity" } var ErrUnknownIssuer error = unknownIssuerError(0) @@ -60,5 +60,5 @@ var ErrUnknownIssuer error = unknownIssuerError(0) type UnknownPacketTypeError uint8 func (upte UnknownPacketTypeError) Error() string { - return "unknown OpenPGP packet type: " + strconv.Itoa(int(upte)) + return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/keys.go b/third_party/code.google.com/p/go.crypto/openpgp/keys.go index 69ce8e3c5..68327f0ac 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/keys.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/keys.go @@ -8,8 +8,6 @@ import ( "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet" - "crypto" - "crypto/rand" "crypto/rsa" "io" "time" @@ -29,18 +27,6 @@ type Entity struct { PrivateKey *packet.PrivateKey Identities map[string]*Identity // indexed by Identity.Name Subkeys []Subkey - - // Clock optionally specifies an alternate clock function to - // use when signing or encrypting using this Entity, instead - // of time.Now(). - Clock func() time.Time -} - -func (e *Entity) timeNow() time.Time { - if e.Clock != nil { - return e.Clock() - } - return time.Now() } // An Identity represents an identity claimed by an Entity and zero or more @@ -95,59 +81,68 @@ func (e *Entity) primaryIdentity() *Identity { // encryptionKey returns the best candidate Key for encrypting a message to the // given Entity. -func (e *Entity) encryptionKey() Key { +func (e *Entity) encryptionKey(now time.Time) (Key, bool) { candidateSubkey := -1 for i, subkey := range e.Subkeys { - if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagEncryptCommunications && + subkey.PublicKey.PubKeyAlgo.CanEncrypt() && + !subkey.Sig.KeyExpired(now) { candidateSubkey = i break } } - i := e.primaryIdentity() - - if e.PrimaryKey.PubKeyAlgo.CanEncrypt() { - // If we don't have any candidate subkeys for encryption and - // the primary key doesn't have any usage metadata then we - // assume that the primary key is ok. Or, if the primary key is - // marked as ok to encrypt to, then we can obviously use it. - if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} - } - } - if candidateSubkey != -1 { subkey := e.Subkeys[candidateSubkey] - return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt to, then we can obviously use it. + i := e.primaryIdentity() + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && + e.PrimaryKey.PubKeyAlgo.CanEncrypt() && + !i.SelfSignature.KeyExpired(now) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true } // This Entity appears to be signing only. - return Key{} + return Key{}, false } // signingKey return the best candidate Key for signing a message with this // Entity. -func (e *Entity) signingKey() Key { +func (e *Entity) signingKey(now time.Time) (Key, bool) { candidateSubkey := -1 for i, subkey := range e.Subkeys { - if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagSign && + subkey.PublicKey.PubKeyAlgo.CanSign() && + !subkey.Sig.KeyExpired(now) { candidateSubkey = i break } } - i := e.primaryIdentity() + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } // If we have no candidate subkey then we assume that it's ok to sign // with the primary key. - if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} + i := e.primaryIdentity() + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && + !i.SelfSignature.KeyExpired(now) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true } - subkey := e.Subkeys[candidateSubkey] - return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} + return Key{}, false } // An EntityList contains one or more Entities. @@ -214,11 +209,16 @@ func ReadKeyRing(r io.Reader) (el EntityList, err error) { for { var e *Entity - e, err = readEntity(packets) + e, err = ReadEntity(packets) if err != nil { + // TODO: warn about skipped unsupported/unreadable keys if _, ok := err.(errors.UnsupportedError); ok { lastUnsupportedError = err err = readToNextPublicKey(packets) + } else if _, ok := err.(errors.StructuralError); ok { + // Skip unreadable, badly-formatted keys + lastUnsupportedError = err + err = readToNextPublicKey(packets) } if err == io.EOF { err = nil @@ -264,9 +264,9 @@ func readToNextPublicKey(packets *packet.Reader) (err error) { panic("unreachable") } -// readEntity reads an entity (public key, identities, subkeys etc) from the +// ReadEntity reads an entity (public key, identities, subkeys etc) from the // given Reader. -func readEntity(packets *packet.Reader) (*Entity, error) { +func ReadEntity(packets *packet.Reader) (*Entity, error) { e := new(Entity) e.Identities = make(map[string]*Identity) @@ -395,16 +395,19 @@ const defaultRSAKeyBits = 2048 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair 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(rand io.Reader, currentTime time.Time, name, comment, email string) (*Entity, error) { +// If config is nil, sensible defaults will be used. +func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { + currentTime := config.Now() + uid := packet.NewUserId(name, comment, email) if uid == nil { return nil, errors.InvalidArgumentError("user id field contained invalid characters") } - signingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) + signingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits) if err != nil { return nil, err } - encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) + encryptingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits) if err != nil { return nil, err } @@ -422,7 +425,7 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, + Hash: config.Hash(), IsPrimaryId: &isPrimaryId, FlagsValid: true, FlagSign: true, @@ -439,7 +442,7 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, + Hash: config.Hash(), FlagsValid: true, FlagEncryptStorage: true, FlagEncryptCommunications: true, @@ -455,7 +458,8 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin // SerializePrivate serializes an Entity, including private key material, to // the given Writer. For now, it must only be used on an Entity returned from // NewEntity. -func (e *Entity) SerializePrivate(w io.Writer) (err error) { +// If config is nil, sensible defaults will be used. +func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { err = e.PrivateKey.Serialize(w) if err != nil { return @@ -465,7 +469,7 @@ func (e *Entity) SerializePrivate(w io.Writer) (err error) { if err != nil { return } - err = ident.SelfSignature.SignUserId(rand.Reader, ident.UserId.Id, e.PrimaryKey, e.PrivateKey) + err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) if err != nil { return } @@ -479,7 +483,7 @@ func (e *Entity) SerializePrivate(w io.Writer) (err error) { if err != nil { return } - err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey) + err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) if err != nil { return } @@ -531,7 +535,8 @@ func (e *Entity) Serialize(w io.Writer) error { // associated with e. The provided identity must already be an element of // e.Identities and the private key of signer must have been decrypted if // necessary. -func (e *Entity) SignIdentity(identity string, signer *Entity) error { +// If config is nil, sensible defaults will be used. +func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing Entity must have a private key") } @@ -546,11 +551,11 @@ func (e *Entity) SignIdentity(identity string, signer *Entity) error { sig := &packet.Signature{ SigType: packet.SigTypeGenericCert, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, - Hash: crypto.SHA256, - CreationTime: time.Now(), + Hash: config.Hash(), + CreationTime: config.Now(), IssuerKeyId: &signer.PrivateKey.KeyId, } - if err := sig.SignKey(rand.Reader, e.PrimaryKey, signer.PrivateKey); err != nil { + if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey, config); err != nil { return err } ident.Signatures = append(ident.Signatures, sig) diff --git a/third_party/code.google.com/p/go.crypto/openpgp/keys_test.go b/third_party/code.google.com/p/go.crypto/openpgp/keys_test.go new file mode 100644 index 000000000..d0d9621e6 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/keys_test.go @@ -0,0 +1,41 @@ +package openpgp + +import ( + "testing" + "time" +) + +func TestKeyExpiry(t *testing.T) { + kring, _ := ReadKeyRing(readerFromHex(expiringKeyHex)) + entity := kring[0] + + const timeFormat = "2006-01-02" + time1, _ := time.Parse(timeFormat, "2013-07-01") + // The expiringKeyHex key is structured as: + // + // pub 1024R/5E237D8C created: 2013-07-01 expires: 2013-07-31 usage: SC + // sub 1024R/1ABB25A0 created: 2013-07-01 expires: 2013-07-08 usage: E + // sub 1024R/96A672F5 created: 2013-07-01 expires: 2013-07-31 usage: E + // + // So this should select the first, non-expired encryption key. + key, _ := entity.encryptionKey(time1) + if id := key.PublicKey.KeyIdShortString(); id != "1ABB25A0" { + t.Errorf("Expected key 1ABB25A0 at time %s, but got key %s", time1.Format(timeFormat), id) + } + + // Once the first encryption subkey has expired, the second should be + // selected. + time2, _ := time.Parse(timeFormat, "2013-07-09") + key, _ = entity.encryptionKey(time2) + if id := key.PublicKey.KeyIdShortString(); id != "96A672F5" { + t.Errorf("Expected key 96A672F5 at time %s, but got key %s", time2.Format(timeFormat), id) + } + + // Once all the keys have expired, nothing should be returned. + time3, _ := time.Parse(timeFormat, "2013-08-01") + if key, ok := entity.encryptionKey(time3); ok { + t.Errorf("Expected no key at time %s, but got key %s", time3.Format(timeFormat), key.PublicKey.KeyIdShortString()) + } +} + +const expiringKeyHex = "988d0451d1ec5d010400ba3385721f2dc3f4ab096b2ee867ab77213f0a27a8538441c35d2fa225b08798a1439a66a5150e6bdc3f40f5d28d588c712394c632b6299f77db8c0d48d37903fb72ebd794d61be6aa774688839e5fdecfe06b2684cc115d240c98c66cb1ef22ae84e3aa0c2b0c28665c1e7d4d044e7f270706193f5223c8d44e0d70b7b8da830011010001b40f4578706972792074657374206b657988be041301020028050251d1ec5d021b03050900278d00060b090807030206150802090a0b0416020301021e01021780000a091072589ad75e237d8c033503fd10506d72837834eb7f994117740723adc39227104b0d326a1161871c0b415d25b4aedef946ca77ea4c05af9c22b32cf98be86ab890111fced1ee3f75e87b7cc3c00dc63bbc85dfab91c0dc2ad9de2c4d13a34659333a85c6acc1a669c5e1d6cecb0cf1e56c10e72d855ae177ddc9e766f9b2dda57ccbb75f57156438bbdb4e42b88d0451d1ec5d0104009c64906559866c5cb61578f5846a94fcee142a489c9b41e67b12bb54cfe86eb9bc8566460f9a720cb00d6526fbccfd4f552071a8e3f7744b1882d01036d811ee5a3fb91a1c568055758f43ba5d2c6a9676b012f3a1a89e47bbf624f1ad571b208f3cc6224eb378f1645dd3d47584463f9eadeacfd1ce6f813064fbfdcc4b5a53001101000188a504180102000f021b0c050251d1f06b050900093e89000a091072589ad75e237d8c20e00400ab8310a41461425b37889c4da28129b5fae6084fafbc0a47dd1adc74a264c6e9c9cc125f40462ee1433072a58384daef88c961c390ed06426a81b464a53194c4e291ddd7e2e2ba3efced01537d713bd111f48437bde2363446200995e8e0d4e528dda377fd1e8f8ede9c8e2198b393bd86852ce7457a7e3daf74d510461a5b77b88d0451d1ece8010400b3a519f83ab0010307e83bca895170acce8964a044190a2b368892f7a244758d9fc193482648acb1fb9780d28cc22d171931f38bb40279389fc9bf2110876d4f3db4fcfb13f22f7083877fe56592b3b65251312c36f83ffcb6d313c6a17f197dd471f0712aad15a8537b435a92471ba2e5b0c72a6c72536c3b567c558d7b6051001101000188a504180102000f021b0c050251d1f07b050900279091000a091072589ad75e237d8ce69e03fe286026afacf7c97ee20673864d4459a2240b5655219950643c7dba0ac384b1d4359c67805b21d98211f7b09c2a0ccf6410c8c04d4ff4a51293725d8d6570d9d8bb0e10c07d22357caeb49626df99c180be02d77d1fe8ed25e7a54481237646083a9f89a11566cd20b9e995b1487c5f9e02aeb434f3a1897cd416dd0a87861838da3e9e" diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/compressed.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/compressed.go index 6b4d8f2ad..62330ed14 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/compressed.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/compressed.go @@ -6,6 +6,7 @@ package packet import ( "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" + "compress/bzip2" "compress/flate" "compress/zlib" "io" @@ -18,6 +19,26 @@ type Compressed struct { Body io.Reader } +const ( + NoCompression = flate.NoCompression + BestSpeed = flate.BestSpeed + BestCompression = flate.BestCompression + DefaultCompression = flate.DefaultCompression +) + +// CompressionConfig contains compressor configuration settings. +type CompressionConfig struct { + // Level is the compression level to use. It must be set to + // between -1 and 9, with -1 causing the compressor to use the + // default compression level, 0 causing the compressor to use + // no compression and 1 to 9 representing increasing (better, + // slower) compression levels. If Level is less than -1 or + // more then 9, a non-nil error will be returned during + // encryption. See the constants above for convenient common + // settings for Level. + Level int +} + func (c *Compressed) parse(r io.Reader) error { var buf [1]byte _, err := readFull(r, buf[:]) @@ -30,9 +51,73 @@ func (c *Compressed) parse(r io.Reader) error { c.Body = flate.NewReader(r) case 2: c.Body, err = zlib.NewReader(r) + case 3: + c.Body = bzip2.NewReader(r) default: err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) } return err } + +// compressedWriterCloser represents the serialized compression stream +// header and the compressor. Its Close() method ensures that both the +// compressor and serialized stream header are closed. Its Write() +// method writes to the compressor. +type compressedWriteCloser struct { + sh io.Closer // Stream Header + c io.WriteCloser // Compressor +} + +func (cwc compressedWriteCloser) Write(p []byte) (int, error) { + return cwc.c.Write(p) +} + +func (cwc compressedWriteCloser) Close() (err error) { + err = cwc.c.Close() + if err != nil { + return err + } + + return cwc.sh.Close() +} + +// SerializeCompressed serializes a compressed data packet to w and +// returns a WriteCloser to which the literal data packets themselves +// can be written and which MUST be closed on completion. If cc is +// nil, sensible defaults will be used to configure the compression +// algorithm. +func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) { + compressed, err := serializeStreamHeader(w, packetTypeCompressed) + if err != nil { + return + } + + _, err = compressed.Write([]byte{uint8(algo)}) + if err != nil { + return + } + + level := DefaultCompression + if cc != nil { + level = cc.Level + } + + var compressor io.WriteCloser + switch algo { + case CompressionZIP: + compressor, err = flate.NewWriter(compressed, level) + case CompressionZLIB: + compressor, err = zlib.NewWriterLevel(compressed, level) + default: + s := strconv.Itoa(int(algo)) + err = errors.UnsupportedError("Unsupported compression algorithm: " + s) + } + if err != nil { + return + } + + literaldata = compressedWriteCloser{compressed, compressor} + + return +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/config.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/config.go index 8fec7be7f..8c4f213c9 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/config.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/config.go @@ -12,8 +12,7 @@ import ( ) // Config collects a number of parameters along with sensible defaults. - -// A nil *Config is valid and produces all default values. +// A nil *Config is valid and results in all default values. type Config struct { // Rand provides the source of entropy. // If nil, the crypto/rand Reader is used. @@ -27,6 +26,12 @@ type Config struct { // Time returns the current time as the number of seconds since the // epoch. If Time is nil, time.Now is used. Time func() time.Time + // DefaultCompressionAlgo is the compression algorithm to be + // applied to the plaintext before encryption. If zero, no + // compression is done. + DefaultCompressionAlgo CompressionAlgo + // CompressionConfig configures the compression settings. + CompressionConfig *CompressionConfig } func (c *Config) Random() io.Reader { @@ -56,3 +61,10 @@ func (c *Config) Now() time.Time { } return c.Time() } + +func (c *Config) Compression() CompressionAlgo { + if c == nil { + return CompressionNone + } + return c.DefaultCompressionAlgo +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key.go index 28f7f0837..957360b06 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key.go @@ -7,7 +7,6 @@ package packet import ( "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/elgamal" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" - "crypto/rand" "crypto/rsa" "encoding/binary" "io" @@ -63,7 +62,8 @@ func checksumKeyMaterial(key []byte) uint16 { // Decrypt decrypts an encrypted session key with the given private key. The // private key must have been decrypted first. -func (e *EncryptedKey) Decrypt(priv *PrivateKey) error { +// If config is nil, sensible defaults will be used. +func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { var err error var b []byte @@ -71,7 +71,7 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey) error { // padding oracle attacks. switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) + b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) case PubKeyAlgoElGamal: c1 := new(big.Int).SetBytes(e.encryptedMPI1) c2 := new(big.Int).SetBytes(e.encryptedMPI2) @@ -97,13 +97,14 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey) error { // SerializeEncryptedKey serializes an encrypted key packet to w that contains // key, encrypted to pub. -func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) error { +// If config is nil, sensible defaults will be used. +func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { var buf [10]byte buf[0] = encryptedKeyVersion binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) buf[9] = byte(pub.PubKeyAlgo) - keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */ ) + keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) keyBlock[0] = byte(cipherFunc) copy(keyBlock[1:], key) checksum := checksumKeyMaterial(key) @@ -112,9 +113,9 @@ func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFu switch pub.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) case PubKeyAlgoElGamal: - return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key_test.go index 2b8eff7b3..0a8dcc6d8 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/encrypted_key_test.go @@ -6,7 +6,6 @@ package packet import ( "bytes" - "crypto/rand" "crypto/rsa" "fmt" "math/big" @@ -58,7 +57,7 @@ func TestDecryptingEncryptedKey(t *testing.T) { return } - err = ek.Decrypt(encryptedKeyPriv) + err = ek.Decrypt(encryptedKeyPriv, nil) if err != nil { t.Errorf("error from Decrypt: %s", err) return @@ -87,7 +86,7 @@ func TestEncryptingEncryptedKey(t *testing.T) { } buf := new(bytes.Buffer) - err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) + err := SerializeEncryptedKey(buf, pub, CipherAES128, key, nil) if err != nil { t.Errorf("error writing encrypted key packet: %s", err) } @@ -108,7 +107,7 @@ func TestEncryptingEncryptedKey(t *testing.T) { return } - err = ek.Decrypt(encryptedKeyPriv) + err = ek.Decrypt(encryptedKeyPriv, nil) if err != nil { t.Errorf("error from Decrypt: %s", err) return diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/ocfb.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/ocfb.go index 7e3262023..ce2a33a54 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/ocfb.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/ocfb.go @@ -20,9 +20,9 @@ type ocfbEncrypter struct { // performed. type OCFBResyncOption bool -var ( - OCFBResync = OCFBResyncOption(true) - OCFBNoResync = OCFBResyncOption(false) +const ( + OCFBResync OCFBResyncOption = true + OCFBNoResync OCFBResyncOption = false ) // NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/opaque.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/opaque.go new file mode 100644 index 000000000..205ea5547 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/opaque.go @@ -0,0 +1,161 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" + "io" + "io/ioutil" +) + +// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is +// useful for splitting and storing the original packet contents separately, +// handling unsupported packet types or accessing parts of the packet not yet +// implemented by this package. +type OpaquePacket struct { + // Packet type + Tag uint8 + // Reason why the packet was parsed opaquely + Reason error + // Binary contents of the packet data + Contents []byte +} + +func (op *OpaquePacket) parse(r io.Reader) (err error) { + op.Contents, err = ioutil.ReadAll(r) + return +} + +// Serialize marshals the packet to a writer in its original form, including +// the packet header. +func (op *OpaquePacket) Serialize(w io.Writer) (err error) { + err = serializeHeader(w, packetType(op.Tag), len(op.Contents)) + if err == nil { + _, err = w.Write(op.Contents) + } + return +} + +// Parse attempts to parse the opaque contents into a structure supported by +// this package. If the packet is not known then the result will be another +// OpaquePacket. +func (op *OpaquePacket) Parse() (p Packet, err error) { + hdr := bytes.NewBuffer(nil) + err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents)) + if err != nil { + op.Reason = err + return op, err + } + p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents))) + if err != nil { + op.Reason = err + p = op + } + return +} + +// OpaqueReader reads OpaquePackets from an io.Reader. +type OpaqueReader struct { + r io.Reader +} + +func NewOpaqueReader(r io.Reader) *OpaqueReader { + return &OpaqueReader{r: r} +} + +// Read the next OpaquePacket. +func (or *OpaqueReader) Next() (op *OpaquePacket, err error) { + tag, _, contents, err := readHeader(or.r) + if err != nil { + return + } + op = &OpaquePacket{Tag: uint8(tag), Reason: err} + err = op.parse(contents) + if err != nil { + consumeAll(contents) + } + return +} + +// OpaqueSubpacket represents an unparsed OpenPGP subpacket, +// as found in signature and user attribute packets. +type OpaqueSubpacket struct { + SubType uint8 + Contents []byte +} + +// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from +// their byte representation. +func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) { + var ( + subHeaderLen int + subPacket *OpaqueSubpacket + ) + for len(contents) > 0 { + subHeaderLen, subPacket, err = nextSubpacket(contents) + if err != nil { + break + } + result = append(result, subPacket) + contents = contents[subHeaderLen+len(subPacket.Contents):] + } + return +} + +func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) { + // RFC 4880, section 5.2.3.1 + var subLen uint32 + if len(contents) < 1 { + goto Truncated + } + subPacket = &OpaqueSubpacket{} + switch { + case contents[0] < 192: + subHeaderLen = 2 // 1 length byte, 1 subtype byte + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]) + contents = contents[1:] + case contents[0] < 255: + subHeaderLen = 3 // 2 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192 + contents = contents[2:] + default: + subHeaderLen = 6 // 5 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[1])<<24 | + uint32(contents[2])<<16 | + uint32(contents[3])<<8 | + uint32(contents[4]) + contents = contents[5:] + } + if subLen > uint32(len(contents)) { + goto Truncated + } + subPacket.SubType = contents[0] + subPacket.Contents = contents[1:subLen] + return +Truncated: + err = errors.StructuralError("subpacket truncated") + return +} + +func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) { + buf := make([]byte, 6) + n := serializeSubpacketLength(buf, len(osp.Contents)+1) + buf[n] = osp.SubType + if _, err = w.Write(buf[:n+1]); err != nil { + return + } + _, err = w.Write(osp.Contents) + return +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/opaque_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/opaque_test.go new file mode 100644 index 000000000..f27bbfe09 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/opaque_test.go @@ -0,0 +1,67 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "encoding/hex" + "io" + "testing" +) + +// Test packet.Read error handling in OpaquePacket.Parse, +// which attempts to re-read an OpaquePacket as a supported +// Packet type. +func TestOpaqueParseReason(t *testing.T) { + buf, err := hex.DecodeString(UnsupportedKeyHex) + if err != nil { + t.Fatal(err) + } + or := NewOpaqueReader(bytes.NewBuffer(buf)) + count := 0 + badPackets := 0 + var uid *UserId + for { + op, err := or.Next() + if err == io.EOF { + break + } else if err != nil { + t.Errorf("#%d: opaque read error: %v", count, err) + break + } + // try to parse opaque packet + p, err := op.Parse() + switch pkt := p.(type) { + case *UserId: + uid = pkt + case *OpaquePacket: + // If an OpaquePacket can't re-parse, packet.Read + // certainly had its reasons. + if pkt.Reason == nil { + t.Errorf("#%d: opaque packet, no reason", count) + } else { + badPackets++ + } + } + count++ + } + + const expectedBad = 3 + // Test post-conditions, make sure we actually parsed packets as expected. + if badPackets != expectedBad { + t.Errorf("unexpected # unparseable packets: %d (want %d)", badPackets, expectedBad) + } + if uid == nil { + t.Errorf("failed to find expected UID in unsupported keyring") + } else if uid.Id != "Armin M. Warda " { + t.Errorf("unexpected UID: %v", uid.Id) + } +} + +// This key material has public key and signature packet versions modified to +// an unsupported value (1), so that trying to parse the OpaquePacket to +// a typed packet will get an error. It also contains a GnuPG trust packet. +// (Created with: od -An -t x1 pubring.gpg | xargs | sed 's/ //g') +const UnsupportedKeyHex = `988d012e7a18a20000010400d6ac00d92b89c1f4396c243abb9b76d2e9673ad63483291fed88e22b82e255e441c078c6abbbf7d2d195e50b62eeaa915b85b0ec20c225ce2c64c167cacb6e711daf2e45da4a8356a059b8160e3b3628ac0dd8437b31f06d53d6e8ea4214d4a26406a6b63e1001406ef23e0bb3069fac9a99a91f77dfafd5de0f188a5da5e3c9000511b42741726d696e204d2e205761726461203c7761726461406e657068696c696d2e727568722e64653e8900950105102e8936c705d1eb399e58489901013f0e03ff5a0c4f421e34fcfa388129166420c08cd76987bcdec6f01bd0271459a85cc22048820dd4e44ac2c7d23908d540f54facf1b36b0d9c20488781ce9dca856531e76e2e846826e9951338020a03a09b57aa5faa82e9267458bd76105399885ac35af7dc1cbb6aaed7c39e1039f3b5beda2c0e916bd38560509bab81235d1a0ead83b0020000` diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/packet.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/packet.go index 05ffe0278..d8b139765 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/packet.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/packet.go @@ -7,10 +7,12 @@ package packet import ( + "bufio" "camlistore.org/third_party/code.google.com/p/go.crypto/cast5" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "crypto/aes" "crypto/cipher" + "crypto/des" "io" "math/big" ) @@ -292,9 +294,23 @@ const ( packetTypeLiteralData packetType = 11 packetTypeUserId packetType = 13 packetTypePublicSubkey packetType = 14 + packetTypeUserAttribute packetType = 17 packetTypeSymmetricallyEncryptedMDC packetType = 18 ) +// peekVersion detects the version of a public key packet about to +// be read. A bufio.Reader at the original position of the io.Reader +// is returned. +func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) { + bufr = bufio.NewReader(r) + var verBuf []byte + if verBuf, err = bufr.Peek(1); err != nil { + return + } + ver = verBuf[0] + return +} + // Read reads a single OpenPGP packet from the given io.Reader. If there is an // error parsing a packet, the whole packet is consumed from the input. func Read(r io.Reader) (p Packet, err error) { @@ -307,7 +323,16 @@ func Read(r io.Reader) (p Packet, err error) { case packetTypeEncryptedKey: p = new(EncryptedKey) case packetTypeSignature: - p = new(Signature) + var version byte + // Detect signature version + if contents, version, err = peekVersion(contents); err != nil { + return + } + if version < 4 { + p = new(SignatureV3) + } else { + p = new(Signature) + } case packetTypeSymmetricKeyEncrypted: p = new(SymmetricKeyEncrypted) case packetTypeOnePassSignature: @@ -319,11 +344,16 @@ func Read(r io.Reader) (p Packet, err error) { } p = pk case packetTypePublicKey, packetTypePublicSubkey: - pk := new(PublicKey) - if tag == packetTypePublicSubkey { - pk.IsSubkey = true + var version byte + if contents, version, err = peekVersion(contents); err != nil { + return + } + isSubkey := tag == packetTypePublicSubkey + if version < 4 { + p = &PublicKeyV3{IsSubkey: isSubkey} + } else { + p = &PublicKey{IsSubkey: isSubkey} } - p = pk case packetTypeCompressed: p = new(Compressed) case packetTypeSymmetricallyEncrypted: @@ -332,6 +362,8 @@ func Read(r io.Reader) (p Packet, err error) { p = new(LiteralData) case packetTypeUserId: p = new(UserId) + case packetTypeUserAttribute: + p = new(UserAttribute) case packetTypeSymmetricallyEncryptedMDC: se := new(SymmetricallyEncrypted) se.MDC = true @@ -373,6 +405,9 @@ const ( PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 PubKeyAlgoElGamal PublicKeyAlgorithm = 16 PubKeyAlgoDSA PublicKeyAlgorithm = 17 + // RFC 6637, Section 5. + PubKeyAlgoECDH PublicKeyAlgorithm = 18 + PubKeyAlgoECDSA PublicKeyAlgorithm = 19 ) // CanEncrypt returns true if it's possible to encrypt a message to a public @@ -400,6 +435,7 @@ func (pka PublicKeyAlgorithm) CanSign() bool { type CipherFunction uint8 const ( + Cipher3DES CipherFunction = 2 CipherCAST5 CipherFunction = 3 CipherAES128 CipherFunction = 7 CipherAES192 CipherFunction = 8 @@ -409,6 +445,8 @@ const ( // KeySize returns the key size, in bytes, of cipher. func (cipher CipherFunction) KeySize() int { switch cipher { + case Cipher3DES: + return 24 case CipherCAST5: return cast5.KeySize case CipherAES128: @@ -424,6 +462,8 @@ func (cipher CipherFunction) KeySize() int { // blockSize returns the block size, in bytes, of cipher. func (cipher CipherFunction) blockSize() int { switch cipher { + case Cipher3DES: + return des.BlockSize case CipherCAST5: return 8 case CipherAES128, CipherAES192, CipherAES256: @@ -435,6 +475,8 @@ func (cipher CipherFunction) blockSize() int { // new returns a fresh instance of the given cipher. func (cipher CipherFunction) new(key []byte) (block cipher.Block) { switch cipher { + case Cipher3DES: + block, _ = des.NewTripleDESCipher(key) case CipherCAST5: block, _ = cast5.NewCipher(key) case CipherAES128, CipherAES192, CipherAES256: @@ -480,3 +522,14 @@ func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) { func writeBig(w io.Writer, i *big.Int) error { return writeMPI(w, uint16(i.BitLen()), i.Bytes()) } + +// CompressionAlgo Represents the different compression algorithms +// supported by OpenPGP (except for BZIP2, which is not currently +// supported). See Section 9.3 of RFC 4880. +type CompressionAlgo uint8 + +const ( + CompressionNone CompressionAlgo = 0 + CompressionZIP CompressionAlgo = 1 + CompressionZLIB CompressionAlgo = 2 +) diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/private_key.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/private_key.go index 1725149fb..481da94e4 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/private_key.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/private_key.go @@ -121,7 +121,7 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) { if err != nil { return } - buf.WriteByte(0 /* no encryption */ ) + buf.WriteByte(0 /* no encryption */) privateKeyBuf := bytes.NewBuffer(nil) diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key.go index 3e8bd9ccd..e8c9c887f 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key.go @@ -5,11 +5,17 @@ package packet import ( + "bytes" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/elgamal" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" + "crypto" "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rsa" "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" "encoding/binary" "fmt" "hash" @@ -19,16 +25,150 @@ import ( "time" ) +var ( + // NIST curve P-256 + oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07} + // NIST curve P-384 + oidCurveP384 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x22} + // NIST curve P-521 + oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23} +) + +const maxOIDLength = 8 + +// ecdsaKey stores the algorithm-specific fields for ECDSA keys. +// as defined in RFC 6637, Section 9. +type ecdsaKey struct { + // oid contains the OID byte sequence identifying the elliptic curve used + oid []byte + // p contains the elliptic curve point that represents the public key + p parsedMPI +} + +// parseOID reads the OID for the curve as defined in RFC 6637, Section 9. +func parseOID(r io.Reader) (oid []byte, err error) { + buf := make([]byte, maxOIDLength) + if _, err = readFull(r, buf[:1]); err != nil { + return + } + oidLen := buf[0] + if int(oidLen) > len(buf) { + err = errors.UnsupportedError("invalid oid length: " + strconv.Itoa(int(oidLen))) + return + } + oid = buf[:oidLen] + _, err = readFull(r, oid) + return +} + +func (f *ecdsaKey) parse(r io.Reader) (err error) { + if f.oid, err = parseOID(r); err != nil { + return err + } + f.p.bytes, f.p.bitLength, err = readMPI(r) + return +} + +func (f *ecdsaKey) serialize(w io.Writer) (err error) { + buf := make([]byte, maxOIDLength+1) + buf[0] = byte(len(f.oid)) + copy(buf[1:], f.oid) + if _, err = w.Write(buf[:len(f.oid)+1]); err != nil { + return + } + return writeMPIs(w, f.p) +} + +func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) { + var c elliptic.Curve + if bytes.Equal(f.oid, oidCurveP256) { + c = elliptic.P256() + } else if bytes.Equal(f.oid, oidCurveP384) { + c = elliptic.P384() + } else if bytes.Equal(f.oid, oidCurveP521) { + c = elliptic.P521() + } else { + return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid)) + } + x, y := elliptic.Unmarshal(c, f.p.bytes) + if x == nil { + return nil, errors.UnsupportedError("failed to parse EC point") + } + return &ecdsa.PublicKey{Curve: c, X: x, Y: y}, nil +} + +func (f *ecdsaKey) byteLen() int { + return 1 + len(f.oid) + 2 + len(f.p.bytes) +} + +type kdfHashFunction byte +type kdfAlgorithm byte + +// ecdhKdf stores key derivation function parameters +// used for ECDH encryption. See RFC 6637, Section 9. +type ecdhKdf struct { + KdfHash kdfHashFunction + KdfAlgo kdfAlgorithm +} + +func (f *ecdhKdf) parse(r io.Reader) (err error) { + buf := make([]byte, 1) + if _, err = readFull(r, buf); err != nil { + return + } + kdfLen := int(buf[0]) + if kdfLen < 3 { + return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen)) + } + buf = make([]byte, kdfLen) + if _, err = readFull(r, buf); err != nil { + return + } + reserved := int(buf[0]) + f.KdfHash = kdfHashFunction(buf[1]) + f.KdfAlgo = kdfAlgorithm(buf[2]) + if reserved != 0x01 { + return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(reserved)) + } + return +} + +func (f *ecdhKdf) serialize(w io.Writer) (err error) { + buf := make([]byte, 4) + // See RFC 6637, Section 9, Algorithm-Specific Fields for ECDH keys. + buf[0] = byte(0x03) // Length of the following fields + buf[1] = byte(0x01) // Reserved for future extensions, must be 1 for now + buf[2] = byte(f.KdfHash) + buf[3] = byte(f.KdfAlgo) + _, err = w.Write(buf[:]) + return +} + +func (f *ecdhKdf) byteLen() int { + return 4 +} + // PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. type PublicKey struct { CreationTime time.Time PubKeyAlgo PublicKeyAlgorithm - PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey + PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey or *ecdsa.PublicKey Fingerprint [20]byte KeyId uint64 IsSubkey bool n, e, p, q, g, y parsedMPI + + // RFC 6637 fields + ec *ecdsaKey + ecdh *ecdhKdf +} + +// signingKey provides a convenient abstraction over signature verification +// for v3 and v4 public keys. +type signingKey interface { + SerializeSignaturePrefix(io.Writer) + serializeWithoutHeaders(io.Writer) error } func fromBig(n *big.Int) parsedMPI { @@ -87,6 +227,23 @@ func (pk *PublicKey) parse(r io.Reader) (err error) { err = pk.parseDSA(r) case PubKeyAlgoElGamal: err = pk.parseElGamal(r) + case PubKeyAlgoECDSA: + pk.ec = new(ecdsaKey) + if err = pk.ec.parse(r); err != nil { + return err + } + pk.PublicKey, err = pk.ec.newECDSA() + case PubKeyAlgoECDH: + pk.ec = new(ecdsaKey) + if err = pk.ec.parse(r); err != nil { + return + } + pk.ecdh = new(ecdhKdf) + if err = pk.ecdh.parse(r); err != nil { + return + } + // The ECDH key is stored in an ecdsa.PublicKey for convenience. + pk.PublicKey, err = pk.ec.newECDSA() default: err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } @@ -191,7 +348,7 @@ func (pk *PublicKey) parseElGamal(r io.Reader) (err error) { // SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // The prefix is used when calculating a signature over this public key. See // RFC 4880, section 5.2.4. -func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { +func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) { var pLength uint16 switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: @@ -206,6 +363,11 @@ func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { pLength += 2 + uint16(len(pk.p.bytes)) pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoECDSA: + pLength += uint16(pk.ec.byteLen()) + case PubKeyAlgoECDH: + pLength += uint16(pk.ec.byteLen()) + pLength += uint16(pk.ecdh.byteLen()) default: panic("unknown public key algorithm") } @@ -230,6 +392,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err error) { length += 2 + len(pk.p.bytes) length += 2 + len(pk.g.bytes) length += 2 + len(pk.y.bytes) + case PubKeyAlgoECDSA: + length += pk.ec.byteLen() + case PubKeyAlgoECDH: + length += pk.ec.byteLen() + length += pk.ecdh.byteLen() default: panic("unknown public key algorithm") } @@ -269,6 +436,13 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) case PubKeyAlgoElGamal: return writeMPIs(w, pk.p, pk.g, pk.y) + case PubKeyAlgoECDSA: + return pk.ec.serialize(w) + case PubKeyAlgoECDH: + if err = pk.ec.serialize(w); err != nil { + return + } + return pk.ecdh.serialize(w) } return errors.InvalidArgumentError("bad public-key algorithm") } @@ -315,6 +489,57 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro return errors.SignatureError("DSA verification failure") } return nil + case PubKeyAlgoECDSA: + ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey) + if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.bytes), new(big.Int).SetBytes(sig.ECDSASigS.bytes)) { + return errors.SignatureError("ECDSA verification failure") + } + return nil + default: + return errors.SignatureError("Unsupported public key algorithm used in signature") + } + panic("unreachable") +} + +// VerifySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + suffix := make([]byte, 5) + suffix[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) + signed.Write(suffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + rsaPublicKey := pk.PublicKey.(*rsa.PublicKey) + if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { + return errors.SignatureError("RSA verification failure") + } + return + case PubKeyAlgoDSA: + dsaPublicKey := pk.PublicKey.(*dsa.PublicKey) + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 + if len(hashBytes) > subgroupSize { + hashBytes = hashBytes[:subgroupSize] + } + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { + return errors.SignatureError("DSA verification failure") + } + return nil default: panic("shouldn't happen") } @@ -323,11 +548,11 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro // keySignatureHash returns a Hash of the message that needs to be signed for // pk to assert a subkey relationship to signed. -func keySignatureHash(pk, signed *PublicKey, sig *Signature) (h hash.Hash, err error) { - h = sig.Hash.New() - if h == nil { +func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { return nil, errors.UnsupportedError("hash function") } + h = hashFunc.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) @@ -340,7 +565,7 @@ func keySignatureHash(pk, signed *PublicKey, sig *Signature) (h hash.Hash, err e // VerifyKeySignature returns nil iff sig is a valid signature, made by this // public key, of signed. func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err error) { - h, err := keySignatureHash(pk, signed, sig) + h, err := keySignatureHash(pk, signed, sig.Hash) if err != nil { return err } @@ -349,11 +574,11 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err // userIdSignatureHash returns a Hash of the message that needs to be signed // to assert that pk is a valid key for id. -func userIdSignatureHash(id string, pk *PublicKey, sig *Signature) (h hash.Hash, err error) { - h = sig.Hash.New() - if h == nil { +func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { return nil, errors.UnsupportedError("hash function") } + h = hashFunc.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) @@ -374,13 +599,23 @@ func userIdSignatureHash(id string, pk *PublicKey, sig *Signature) (h hash.Hash, // VerifyUserIdSignature returns nil iff sig is a valid signature, made by this // public key, of id. func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err error) { - h, err := userIdSignatureHash(id, pk, sig) + h, err := userIdSignatureHash(id, pk, sig.Hash) if err != nil { return err } return pk.VerifySignature(h, sig) } +// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of id. +func (pk *PublicKey) VerifyUserIdSignatureV3(id string, sig *SignatureV3) (err error) { + h, err := userIdSignatureV3Hash(id, pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + // KeyIdString returns the public key's fingerprint in capital hex // (e.g. "6C7EE1B8621CC013"). func (pk *PublicKey) KeyIdString() string { @@ -412,3 +647,18 @@ func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) { } return } + +// BitLength returns the bit length for the given public key. +func (pk *PublicKey) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.bitLength + case PubKeyAlgoDSA: + bitLength = pk.p.bitLength + case PubKeyAlgoElGamal: + bitLength = pk.p.bitLength + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_test.go index 72f459f47..47168a411 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_test.go @@ -22,6 +22,7 @@ var pubKeyTests = []struct { }{ {rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, {dsaPkDataHex, dsaFingerprintHex, time.Unix(0x4d432f89, 0), PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, + {ecdsaPkDataHex, ecdsaFingerprintHex, time.Unix(0x5071c294, 0), PubKeyAlgoECDSA, 0x43fe956c542ca00b, "43FE956C542CA00B", "542CA00B"}, } func TestPublicKeyRead(t *testing.T) { @@ -90,6 +91,101 @@ func TestPublicKeySerialize(t *testing.T) { } } +func TestEcc384Serialize(t *testing.T) { + r := readerFromHex(ecc384PubHex) + var w bytes.Buffer + for i := 0; i < 2; i++ { + // Public key + p, err := Read(r) + if err != nil { + t.Error(err) + } + pubkey := p.(*PublicKey) + if !bytes.Equal(pubkey.ec.oid, []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) { + t.Errorf("Unexpected pubkey OID: %x", pubkey.ec.oid) + } + if !bytes.Equal(pubkey.ec.p.bytes[:5], []byte{0x04, 0xf6, 0xb8, 0xc5, 0xac}) { + t.Errorf("Unexpected pubkey P[:5]: %x", pubkey.ec.p.bytes) + } + if pubkey.KeyId != 0x098033880F54719F { + t.Errorf("Unexpected pubkey ID: %x", pubkey.KeyId) + } + err = pubkey.Serialize(&w) + if err != nil { + t.Error(err) + } + // User ID + p, err = Read(r) + if err != nil { + t.Error(err) + } + uid := p.(*UserId) + if uid.Id != "ec_dsa_dh_384 " { + t.Error("Unexpected UID:", uid.Id) + } + err = uid.Serialize(&w) + if err != nil { + t.Error(err) + } + // User ID Sig + p, err = Read(r) + if err != nil { + t.Error(err) + } + uidSig := p.(*Signature) + err = pubkey.VerifyUserIdSignature(uid.Id, uidSig) + if err != nil { + t.Error(err, ": UID") + } + err = uidSig.Serialize(&w) + if err != nil { + t.Error(err) + } + // Subkey + p, err = Read(r) + if err != nil { + t.Error(err) + } + subkey := p.(*PublicKey) + if !bytes.Equal(subkey.ec.oid, []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) { + t.Errorf("Unexpected subkey OID: %x", subkey.ec.oid) + } + if !bytes.Equal(subkey.ec.p.bytes[:5], []byte{0x04, 0x2f, 0xaa, 0x84, 0x02}) { + t.Errorf("Unexpected subkey P[:5]: %x", subkey.ec.p.bytes) + } + if subkey.ecdh.KdfHash != 0x09 { + t.Error("Expected KDF hash function SHA384 (0x09), got", subkey.ecdh.KdfHash) + } + if subkey.ecdh.KdfAlgo != 0x09 { + t.Error("Expected KDF symmetric alg AES256 (0x09), got", subkey.ecdh.KdfAlgo) + } + if subkey.KeyId != 0xAA8B938F9A201946 { + t.Errorf("Unexpected subkey ID: %x", subkey.KeyId) + } + err = subkey.Serialize(&w) + if err != nil { + t.Error(err) + } + // Subkey Sig + p, err = Read(r) + if err != nil { + t.Error(err) + } + subkeySig := p.(*Signature) + err = pubkey.VerifyKeySignature(subkey, subkeySig) + if err != nil { + t.Error(err) + } + err = subkeySig.Serialize(&w) + if err != nil { + t.Error(err) + } + // Now read back what we've written again + r = bytes.NewBuffer(w.Bytes()) + w.Reset() + } +} + const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" @@ -97,3 +193,10 @@ const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f94 const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" + +const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b" + +const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4" + +// Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key +const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267` diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3.go new file mode 100644 index 000000000..c41d5d5d5 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3.go @@ -0,0 +1,274 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "crypto/md5" + "crypto/rsa" + "encoding/binary" + "fmt" + "hash" + "io" + "math/big" + "strconv" + "time" + + "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" +) + +// PublicKeyV3 represents older, version 3 public keys. These keys are less secure and +// should not be used for signing or encrypting. They are supported here only for +// parsing version 3 key material and validating signatures. +// See RFC 4880, section 5.5.2. +type PublicKeyV3 struct { + CreationTime time.Time + DaysToExpire uint16 + PubKeyAlgo PublicKeyAlgorithm + PublicKey *rsa.PublicKey + Fingerprint [16]byte + KeyId uint64 + IsSubkey bool + + n, e parsedMPI +} + +// newRSAPublicKeyV3 returns a PublicKey that wraps the given rsa.PublicKey. +// Included here for testing purposes only. RFC 4880, section 5.5.2: +// "an implementation MUST NOT generate a V3 key, but MAY accept it." +func newRSAPublicKeyV3(creationTime time.Time, pub *rsa.PublicKey) *PublicKeyV3 { + pk := &PublicKeyV3{ + CreationTime: creationTime, + PublicKey: pub, + n: fromBig(pub.N), + e: fromBig(big.NewInt(int64(pub.E))), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +func (pk *PublicKeyV3) parse(r io.Reader) (err error) { + // RFC 4880, section 5.5.2 + var buf [8]byte + if _, err = readFull(r, buf[:]); err != nil { + return + } + if buf[0] < 2 || buf[0] > 3 { + return errors.UnsupportedError("public key version") + } + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) + pk.DaysToExpire = binary.BigEndian.Uint16(buf[5:7]) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[7]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + default: + err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) + } + if err != nil { + return + } + + pk.setFingerPrintAndKeyId() + return +} + +func (pk *PublicKeyV3) setFingerPrintAndKeyId() { + // RFC 4880, section 12.2 + fingerPrint := md5.New() + fingerPrint.Write(pk.n.bytes) + fingerPrint.Write(pk.e.bytes) + fingerPrint.Sum(pk.Fingerprint[:0]) + pk.KeyId = binary.BigEndian.Uint64(pk.n.bytes[len(pk.n.bytes)-8:]) +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) { + if pk.n.bytes, pk.n.bitLength, err = readMPI(r); err != nil { + return + } + if pk.e.bytes, pk.e.bitLength, err = readMPI(r); err != nil { + return + } + + if len(pk.e.bytes) > 3 { + err = errors.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)} + for i := 0; i < len(pk.e.bytes); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.bytes[i]) + } + pk.PublicKey = rsa + return +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKeyV3) SerializeSignaturePrefix(w io.Writer) { + var pLength uint16 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + pLength += 2 + uint16(len(pk.n.bytes)) + pLength += 2 + uint16(len(pk.e.bytes)) + default: + panic("unknown public key algorithm") + } + pLength += 6 + w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + return +} + +func (pk *PublicKeyV3) Serialize(w io.Writer) (err error) { + length := 8 // 8 byte header + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + length += 2 + len(pk.n.bytes) + length += 2 + len(pk.e.bytes) + default: + panic("unknown public key algorithm") + } + + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + if err = serializeHeader(w, packetType, length); err != nil { + return + } + return pk.serializeWithoutHeaders(w) +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKeyV3) serializeWithoutHeaders(w io.Writer) (err error) { + var buf [8]byte + // Version 3 + buf[0] = 3 + // Creation time + t := uint32(pk.CreationTime.Unix()) + buf[1] = byte(t >> 24) + buf[2] = byte(t >> 16) + buf[3] = byte(t >> 8) + buf[4] = byte(t) + // Days to expire + buf[5] = byte(pk.DaysToExpire >> 8) + buf[6] = byte(pk.DaysToExpire) + // Public key algorithm + buf[7] = byte(pk.PubKeyAlgo) + + if _, err = w.Write(buf[:]); err != nil { + return + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + return writeMPIs(w, pk.n, pk.e) + } + return errors.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKeyV3) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly +} + +// VerifySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKeyV3) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + suffix := make([]byte, 5) + suffix[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) + signed.Write(suffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { + return errors.SignatureError("RSA verification failure") + } + return + default: + // V3 public keys only support RSA. + panic("shouldn't happen") + } + panic("unreachable") +} + +// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of id. +func (pk *PublicKeyV3) VerifyUserIdSignatureV3(id string, sig *SignatureV3) (err error) { + h, err := userIdSignatureV3Hash(id, pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// VerifyKeySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKeyV3) VerifyKeySignatureV3(signed *PublicKeyV3, sig *SignatureV3) (err error) { + h, err := keySignatureHash(pk, signed, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// userIdSignatureV3Hash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureV3Hash(id string, pk signingKey, hfn crypto.Hash) (h hash.Hash, err error) { + if h = hfn.New(); h == nil { + return nil, errors.UnsupportedError("hash function") + } + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + h.Write([]byte(id)) + + return +} + +// KeyIdString returns the public key's fingerprint in capital hex +// (e.g. "6C7EE1B8621CC013"). +func (pk *PublicKeyV3) KeyIdString() string { + return fmt.Sprintf("%X", pk.KeyId) +} + +// KeyIdShortString returns the short form of public key's fingerprint +// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). +func (pk *PublicKeyV3) KeyIdShortString() string { + return fmt.Sprintf("%X", pk.KeyId&0xFFFFFFFF) +} + +// BitLength returns the bit length for the given public key. +func (pk *PublicKeyV3) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.bitLength + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3_test.go new file mode 100644 index 000000000..e06405904 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/public_key_v3_test.go @@ -0,0 +1,82 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "encoding/hex" + "testing" + "time" +) + +var pubKeyV3Test = struct { + hexFingerprint string + creationTime time.Time + pubKeyAlgo PublicKeyAlgorithm + keyId uint64 + keyIdString string + keyIdShort string +}{ + "103BECF5BD1E837C89D19E98487767F7", + time.Unix(779753634, 0), + PubKeyAlgoRSA, + 0xDE0F188A5DA5E3C9, + "DE0F188A5DA5E3C9", + "5DA5E3C9"} + +func TestPublicKeyV3Read(t *testing.T) { + i, test := 0, pubKeyV3Test + packet, err := Read(v3KeyReader(t)) + if err != nil { + t.Fatalf("#%d: Read error: %s", i, err) + } + pk, ok := packet.(*PublicKeyV3) + if !ok { + t.Fatalf("#%d: failed to parse, got: %#v", i, packet) + } + if pk.PubKeyAlgo != test.pubKeyAlgo { + t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) + } + if !pk.CreationTime.Equal(test.creationTime) { + t.Errorf("#%d: bad creation time got:%v want:%v", i, pk.CreationTime, test.creationTime) + } + expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) + if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { + t.Errorf("#%d: bad fingerprint got:%x want:%x", i, pk.Fingerprint[:], expectedFingerprint) + } + if pk.KeyId != test.keyId { + t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId) + } + if g, e := pk.KeyIdString(), test.keyIdString; g != e { + t.Errorf("#%d: bad KeyIdString got:%q want:%q", i, g, e) + } + if g, e := pk.KeyIdShortString(), test.keyIdShort; g != e { + t.Errorf("#%d: bad KeyIdShortString got:%q want:%q", i, g, e) + } +} + +func TestPublicKeyV3Serialize(t *testing.T) { + //for i, test := range pubKeyV3Tests { + i := 0 + packet, err := Read(v3KeyReader(t)) + if err != nil { + t.Fatalf("#%d: Read error: %s", i, err) + } + pk, ok := packet.(*PublicKeyV3) + if !ok { + t.Fatalf("#%d: failed to parse, got: %#v", i, packet) + } + var serializeBuf bytes.Buffer + if err = pk.Serialize(&serializeBuf); err != nil { + t.Fatalf("#%d: failed to serialize: %s", i, err) + } + + if packet, err = Read(bytes.NewBuffer(serializeBuf.Bytes())); err != nil { + t.Fatalf("#%d: Read error (from serialized data): %s", i, err) + } + if pk, ok = packet.(*PublicKeyV3); !ok { + t.Fatalf("#%d: failed to parse serialized data, got: %#v", i, packet) + } +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/signature.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/signature.go index 270b42812..bd69979a4 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/signature.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/signature.go @@ -30,8 +30,9 @@ type Signature struct { HashTag [2]byte CreationTime time.Time - RSASignature parsedMPI - DSASigR, DSASigS parsedMPI + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI + ECDSASigR, ECDSASigS parsedMPI // rawSubpackets contains the unparsed subpackets, in order. rawSubpackets []outputSubpacket @@ -71,7 +72,7 @@ func (sig *Signature) parse(r io.Reader) (err error) { sig.SigType = SignatureType(buf[0]) sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: default: err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) return @@ -135,6 +136,11 @@ func (sig *Signature) parse(r io.Reader) (err error) { if err == nil { sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) } + case PubKeyAlgoECDSA: + sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) + if err == nil { + sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) + } default: panic("unreachable") } @@ -338,13 +344,14 @@ func subpacketLengthLength(length int) int { // serializeSubpacketLength marshals the given length into to. func serializeSubpacketLength(to []byte, length int) int { + // RFC 4880, Section 4.2.2. if length < 192 { to[0] = byte(length) return 1 } if length < 16320 { length -= 192 - to[0] = byte(length >> 8) + to[0] = byte((length >> 8) + 192) to[1] = byte(length) return 2 } @@ -383,6 +390,16 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { return } +// KeyExpired returns whether sig is a self-signature of a key that has +// expired. +func (sig *Signature) KeyExpired(currentTime time.Time) bool { + if sig.KeyLifetimeSecs == nil { + return false + } + expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) + return currentTime.After(expiry) +} + // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. func (sig *Signature) buildHashSuffix() (err error) { hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) @@ -426,7 +443,8 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { // Sign signs a message with a private key. The hash, h, must contain // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err error) { +// If config is nil, sensible defaults will be used. +func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { sig.outSubpackets = sig.buildSubpackets() digest, err := sig.signPrepareHash(h) if err != nil { @@ -435,7 +453,7 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) + sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) case PubKeyAlgoDSA: dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) @@ -445,7 +463,7 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e if len(digest) > subgroupSize { digest = digest[:subgroupSize] } - r, s, err := dsa.Sign(rand, dsaPriv, digest) + r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) if err == nil { sig.DSASigR.bytes = r.Bytes() sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) @@ -462,31 +480,34 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e // SignUserId computes a signature from priv, asserting that pub is a valid // key for the identity id. On success, the signature is stored in sig. Call // Serialize to write it out. -func (sig *Signature) SignUserId(rand io.Reader, id string, pub *PublicKey, priv *PrivateKey) error { - h, err := userIdSignatureHash(id, pub, sig) +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := userIdSignatureHash(id, pub, sig.Hash) if err != nil { return nil } - return sig.Sign(rand, h, priv) + return sig.Sign(h, priv, config) } // SignKey computes a signature from priv, asserting that pub is a subkey. On // success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) SignKey(rand io.Reader, pub *PublicKey, priv *PrivateKey) error { - h, err := keySignatureHash(&priv.PublicKey, pub, sig) +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) if err != nil { return err } - return sig.Sign(rand, h, priv) + return sig.Sign(h, priv, config) } -// Serialize marshals sig to w. SignRSA or SignDSA must have been called first. +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. func (sig *Signature) Serialize(w io.Writer) (err error) { if len(sig.outSubpackets) == 0 { sig.outSubpackets = sig.rawSubpackets } - if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { - return errors.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize") + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") } sigLength := 0 @@ -496,6 +517,9 @@ func (sig *Signature) Serialize(w io.Writer) (err error) { case PubKeyAlgoDSA: sigLength = 2 + len(sig.DSASigR.bytes) sigLength += 2 + len(sig.DSASigS.bytes) + case PubKeyAlgoECDSA: + sigLength = 2 + len(sig.ECDSASigR.bytes) + sigLength += 2 + len(sig.ECDSASigS.bytes) default: panic("impossible") } @@ -533,6 +557,8 @@ func (sig *Signature) Serialize(w io.Writer) (err error) { err = writeMPIs(w, sig.RSASignature) case PubKeyAlgoDSA: err = writeMPIs(w, sig.DSASigR, sig.DSASigS) + case PubKeyAlgoECDSA: + err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS) default: panic("impossible") } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3.go new file mode 100644 index 000000000..c141692a7 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3.go @@ -0,0 +1,146 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "encoding/binary" + "fmt" + "io" + "strconv" + "time" + + "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" + "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/s2k" +) + +// SignatureV3 represents older version 3 signatures. These signatures are less secure +// than version 4 and should not be used to create new signatures. They are included +// here for backwards compatibility to read and validate with older key material. +// See RFC 4880, section 5.2.2. +type SignatureV3 struct { + SigType SignatureType + CreationTime time.Time + IssuerKeyId uint64 + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + HashTag [2]byte + + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI +} + +func (sig *SignatureV3) parse(r io.Reader) (err error) { + // RFC 4880, section 5.2.2 + var buf [8]byte + if _, err = readFull(r, buf[:1]); err != nil { + return + } + if buf[0] < 2 || buf[0] > 3 { + err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + if _, err = readFull(r, buf[:1]); err != nil { + return + } + if buf[0] != 5 { + err = errors.UnsupportedError( + "invalid hashed material length " + strconv.Itoa(int(buf[0]))) + return + } + + // Read hashed material: signature type + creation time + if _, err = readFull(r, buf[:5]); err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + t := binary.BigEndian.Uint32(buf[1:5]) + sig.CreationTime = time.Unix(int64(t), 0) + + // Eight-octet Key ID of signer. + if _, err = readFull(r, buf[:8]); err != nil { + return + } + sig.IssuerKeyId = binary.BigEndian.Uint64(buf[:]) + + // Public-key and hash algorithm + if _, err = readFull(r, buf[:2]); err != nil { + return + } + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[0]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: + default: + err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + var ok bool + if sig.Hash, ok = s2k.HashIdToHash(buf[1]); !ok { + return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + // Two-octet field holding left 16 bits of signed hash value. + if _, err = readFull(r, sig.HashTag[:2]); err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) + case PubKeyAlgoDSA: + if sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r); err != nil { + return + } + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) + default: + panic("unreachable") + } + return +} + +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. +func (sig *SignatureV3) Serialize(w io.Writer) (err error) { + buf := make([]byte, 8) + + // Write the sig type and creation time + buf[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(buf[1:5], uint32(sig.CreationTime.Unix())) + if _, err = w.Write(buf[:5]); err != nil { + return + } + + // Write the issuer long key ID + binary.BigEndian.PutUint64(buf[:8], sig.IssuerKeyId) + if _, err = w.Write(buf[:8]); err != nil { + return + } + + // Write public key algorithm, hash ID, and hash value + buf[0] = byte(sig.PubKeyAlgo) + hashId, ok := s2k.HashToHashId(sig.Hash) + if !ok { + return errors.UnsupportedError(fmt.Sprintf("hash function %v", sig.Hash)) + } + buf[1] = hashId + copy(buf[2:4], sig.HashTag[:]) + if _, err = w.Write(buf[:4]); err != nil { + return + } + + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + err = writeMPIs(w, sig.RSASignature) + case PubKeyAlgoDSA: + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) + default: + panic("impossible") + } + return +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3_test.go new file mode 100644 index 000000000..9d0856821 --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/signature_v3_test.go @@ -0,0 +1,92 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "encoding/hex" + "io" + "io/ioutil" + "testing" + + "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor" +) + +func TestSignatureV3Read(t *testing.T) { + r := v3KeyReader(t) + Read(r) // Skip public key + Read(r) // Skip uid + packet, err := Read(r) // Signature + if err != nil { + t.Error(err) + return + } + sig, ok := packet.(*SignatureV3) + if !ok || sig.SigType != SigTypeGenericCert || sig.PubKeyAlgo != PubKeyAlgoRSA || sig.Hash != crypto.MD5 { + t.Errorf("failed to parse, got: %#v", packet) + } +} + +func TestSignatureV3Reserialize(t *testing.T) { + r := v3KeyReader(t) + Read(r) // Skip public key + Read(r) // Skip uid + packet, err := Read(r) + if err != nil { + t.Error(err) + return + } + sig := packet.(*SignatureV3) + out := new(bytes.Buffer) + if err = sig.Serialize(out); err != nil { + t.Errorf("error reserializing: %s", err) + return + } + expected, err := ioutil.ReadAll(v3KeyReader(t)) + if err != nil { + t.Error(err) + return + } + expected = expected[4+141+4+39:] // See pgpdump offsets below, this is where the sig starts + if !bytes.Equal(expected, out.Bytes()) { + t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) + } +} + +func v3KeyReader(t *testing.T) io.Reader { + armorBlock, err := armor.Decode(bytes.NewBufferString(keySigV3Armor)) + if err != nil { + t.Fatalf("armor Decode failed: %v", err) + } + return armorBlock.Body +} + +// keySigV3Armor is some V3 public key I found in an SKS dump. +// Old: Public Key Packet(tag 6)(141 bytes) +// Ver 4 - new +// Public key creation time - Fri Sep 16 17:13:54 CDT 1994 +// Pub alg - unknown(pub 0) +// Unknown public key(pub 0) +// Old: User ID Packet(tag 13)(39 bytes) +// User ID - Armin M. Warda +// Old: Signature Packet(tag 2)(149 bytes) +// Ver 4 - new +// Sig type - unknown(05) +// Pub alg - ElGamal Encrypt-Only(pub 16) +// Hash alg - unknown(hash 46) +// Hashed Sub: unknown(sub 81, critical)(1988 bytes) +const keySigV3Armor = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.0.10 + +mI0CLnoYogAAAQQA1qwA2SuJwfQ5bCQ6u5t20ulnOtY0gykf7YjiK4LiVeRBwHjGq7v30tGV +5Qti7qqRW4Ww7CDCJc4sZMFnystucR2vLkXaSoNWoFm4Fg47NiisDdhDezHwbVPW6OpCFNSi +ZAamtj4QAUBu8j4LswafrJqZqR9336/V3g8Yil2l48kABRG0J0FybWluIE0uIFdhcmRhIDx3 +YXJkYUBuZXBoaWxpbS5ydWhyLmRlPoiVAgUQLok2xwXR6zmeWEiZAQE/DgP/WgxPQh40/Po4 +gSkWZCDAjNdph7zexvAb0CcUWahcwiBIgg3U5ErCx9I5CNVA9U+s8bNrDZwgSIeBzp3KhWUx +524uhGgm6ZUTOAIKA6CbV6pfqoLpJnRYvXYQU5mIWsNa99wcu2qu18OeEDnztb7aLA6Ra9OF +YFCbq4EjXRoOrYM= +=LPjs +-----END PGP PUBLIC KEY BLOCK-----` diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted.go index 7ae6a314d..78e72aadb 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted.go @@ -107,7 +107,9 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) error { // packet contains a random session key, encrypted by a key derived from the // given passphrase. The session key is returned and must be passed to // SerializeSymmetricallyEncrypted. -func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []byte, cipherFunc CipherFunction) (key []byte, err error) { +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { + cipherFunc := config.Cipher() keySize := cipherFunc.KeySize() if keySize == 0 { return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) @@ -117,7 +119,7 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []by keyEncryptingKey := make([]byte, keySize) // s2k.Serialize salts and stretches the passphrase, and writes the // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. - err = s2k.Serialize(s2kBuf, keyEncryptingKey, rand, passphrase) + err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase) if err != nil { return } @@ -142,7 +144,7 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []by } sessionKey := make([]byte, keySize) - _, err = io.ReadFull(rand, sessionKey) + _, err = io.ReadFull(config.Random(), sessionKey) if err != nil { return } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted_test.go index 87690f0b7..dd983cb38 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted_test.go @@ -6,7 +6,6 @@ package packet import ( "bytes" - "crypto/rand" "encoding/hex" "io" "io/ioutil" @@ -65,9 +64,11 @@ const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" func TestSerializeSymmetricKeyEncrypted(t *testing.T) { buf := bytes.NewBuffer(nil) passphrase := []byte("testing") - cipherFunc := CipherAES128 + config := &Config{ + DefaultCipher: CipherAES128, + } - key, err := SerializeSymmetricKeyEncrypted(buf, rand.Reader, passphrase, cipherFunc) + key, err := SerializeSymmetricKeyEncrypted(buf, passphrase, config) if err != nil { t.Errorf("failed to serialize: %s", err) return @@ -87,8 +88,8 @@ func TestSerializeSymmetricKeyEncrypted(t *testing.T) { if !ske.Encrypted { t.Errorf("SKE not encrypted but should be") } - if ske.CipherFunc != cipherFunc { - t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, cipherFunc) + if ske.CipherFunc != config.DefaultCipher { + t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, config.DefaultCipher) } err = ske.Decrypt(passphrase) if err != nil { diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted.go index 60de19a11..26165579f 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted.go @@ -252,7 +252,8 @@ func (c noOpCloser) Close() error { // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet // to w and returns a WriteCloser to which the to-be-encrypted packets can be // written. -func SerializeSymmetricallyEncrypted(w io.Writer, rand io.Reader, c CipherFunction, key []byte) (contents io.WriteCloser, err error) { +// If config is nil, sensible defaults will be used. +func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { if c.KeySize() != len(key) { return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") } @@ -270,7 +271,7 @@ func SerializeSymmetricallyEncrypted(w io.Writer, rand io.Reader, c CipherFuncti block := c.new(key) blockSize := block.BlockSize() iv := make([]byte, blockSize) - _, err = rand.Read(iv) + _, err = config.Random().Read(iv) if err != nil { return } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted_test.go index 961e09a21..d743fa694 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted_test.go @@ -7,7 +7,6 @@ package packet import ( "bytes" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" - "crypto/rand" "crypto/sha1" "encoding/hex" "io" @@ -83,7 +82,7 @@ func TestSerialize(t *testing.T) { c := CipherAES128 key := make([]byte, c.KeySize()) - w, err := SerializeSymmetricallyEncrypted(buf, rand.Reader, c, key) + w, err := SerializeSymmetricallyEncrypted(buf, c, key, nil) if err != nil { t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) return diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute.go new file mode 100644 index 000000000..96a2b382a --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute.go @@ -0,0 +1,91 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "image" + "image/jpeg" + "io" + "io/ioutil" +) + +const UserAttrImageSubpacket = 1 + +// UserAttribute is capable of storing other types of data about a user +// beyond name, email and a text comment. In practice, user attributes are typically used +// to store a signed thumbnail photo JPEG image of the user. +// See RFC 4880, section 5.12. +type UserAttribute struct { + Contents []*OpaqueSubpacket +} + +// NewUserAttributePhoto creates a user attribute packet +// containing the given images. +func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { + uat = new(UserAttribute) + for _, photo := range photos { + var buf bytes.Buffer + // RFC 4880, Section 5.12.1. + data := []byte{ + 0x10, 0x00, // Little-endian image header length (16 bytes) + 0x01, // Image header version 1 + 0x01, // JPEG + 0, 0, 0, 0, // 12 reserved octets, must be all zero. + 0, 0, 0, 0, + 0, 0, 0, 0} + if _, err = buf.Write(data); err != nil { + return + } + if err = jpeg.Encode(&buf, photo, nil); err != nil { + return + } + uat.Contents = append(uat.Contents, &OpaqueSubpacket{ + SubType: UserAttrImageSubpacket, + Contents: buf.Bytes()}) + } + return +} + +// NewUserAttribute creates a new user attribute packet containing the given subpackets. +func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { + return &UserAttribute{Contents: contents} +} + +func (uat *UserAttribute) parse(r io.Reader) (err error) { + // RFC 4880, section 5.13 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uat.Contents, err = OpaqueSubpackets(b) + return +} + +// Serialize marshals the user attribute to w in the form of an OpenPGP packet, including +// header. +func (uat *UserAttribute) Serialize(w io.Writer) (err error) { + var buf bytes.Buffer + for _, sp := range uat.Contents { + sp.Serialize(&buf) + } + if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { + return err + } + _, err = w.Write(buf.Bytes()) + return +} + +// ImageData returns zero or more byte slices, each containing +// JPEG File Interchange Format (JFIF), for each photo in the +// the user attribute packet. +func (uat *UserAttribute) ImageData() (imageData [][]byte) { + for _, sp := range uat.Contents { + if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { + imageData = append(imageData, sp.Contents[16:]) + } + } + return +} diff --git a/third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute_test.go b/third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute_test.go new file mode 100644 index 000000000..13ca5143c --- /dev/null +++ b/third_party/code.google.com/p/go.crypto/openpgp/packet/userattribute_test.go @@ -0,0 +1,109 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "encoding/base64" + "image/color" + "image/jpeg" + "testing" +) + +func TestParseUserAttribute(t *testing.T) { + r := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(userAttributePacket)) + for i := 0; i < 2; i++ { + p, err := Read(r) + if err != nil { + t.Fatal(err) + } + uat := p.(*UserAttribute) + imgs := uat.ImageData() + if len(imgs) != 1 { + t.Errorf("Unexpected number of images in user attribute packet: %d", len(imgs)) + } + if len(imgs[0]) != 3395 { + t.Errorf("Unexpected JPEG image size: %d", len(imgs[0])) + } + img, err := jpeg.Decode(bytes.NewBuffer(imgs[0])) + if err != nil { + t.Errorf("Error decoding JPEG image: %v", err) + } + // A pixel in my right eye. + pixel := color.NRGBAModel.Convert(img.At(56, 36)) + ref := color.NRGBA{R: 157, G: 128, B: 124, A: 255} + if pixel != ref { + t.Errorf("Unexpected pixel color: %v", pixel) + } + w := bytes.NewBuffer(nil) + err = uat.Serialize(w) + if err != nil { + t.Errorf("Error writing user attribute: %v", err) + } + r = bytes.NewBuffer(w.Bytes()) + } +} + +const userAttributePacket = ` +0cyWzJQBEAABAQAAAAAAAAAAAAAAAP/Y/+AAEEpGSUYAAQIAAAEAAQAA/9sAQwAFAwQEBAMFBAQE +BQUFBgcMCAcHBwcPCgsJDBEPEhIRDxEQExYcFxMUGhUQERghGBocHR8fHxMXIiQiHiQcHh8e/9sA +QwEFBQUHBgcOCAgOHhQRFB4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e +Hh4eHh4eHh4e/8AAEQgAZABkAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYH +CAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHw +JDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6 +g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk +5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIB +AgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEX +GBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKT +lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX2 +9/j5+v/aAAwDAQACEQMRAD8A5uGP06VehQ4pIox04q5EnHSvAep+hIIl4zVuMHGPWmRrUWtalaaN +pU2oXsgSGJSxPr6ClvoitErs0Itqjc7BQOpPAFYmrfEnwjojtHNqaXEynBjtx5hH4jj9a8B8d+Od +W8UXZjWR4LJT+7t0Jwfc+prnIdO1CWZEW2mZ3HyDactXXDB3V5s8evm1namj6r0H4weCLtxG+ova +ueP30RA/MV6not1bX0Ed1ZzxzwyDKvGwZSPqK+Ff+ES8R8t/ZV2oHUmM10Hgbxp4m8BatEfNnWBH +/eWshOxx9Kmpg4te49RUM1kn+8Wh9zQ4P1FaMC7l465rjPh14y0fxnoseoaXOpfaPOgJ+eI98j09 +67W19M15bi4uzPSqTU480WXkjZkAyAR61DPE6OCSOalWRRgZxjvTb598sfU4FBwx5uY4T4feIm8P +TeJbAgc65NIM+8cX+FFeLfF3Vr3SfiNrMFrMypJMJcDPUqP8KK+kpVFyLU+ar037SXqX4hxVpMY7 +1UhPpVlT2rybKx9smWYz3NeH/EDVLzxt40j8O6bITaQybPlbKkjq39K9O8fasdH8IahfKxWQRFIy +Ou9uB/OuE/Z/0y3j1d9TuyoZCMs5xjuea1pLli5nn46q240l13PcfhN8EvDNtpcEl/CklyVBLuMk +mvU/Dfwo0BL/AO13FjEDD/qyV7Vn+CvGPg8zRpJrVm8ikLtEg6+1ew2dxZ3EQaJgysuQPasH7eXW +1zzsbVhT92kk/PsYieEND+zlPs6c/wCyAPyryH4wfCPRtW0u6j+xRLOxLxSoADkDpXY+MPjJ4c0S +9k082d3O8ZKkxw5XI96ytK+IGk+IpFjRpod+Qq3C7QT6A1E6NenaXbqRg6rlLlqS0fRnxjpd1r/w +w8afa7GWRPKbZLGeBKmeVNfZngLxNaeKfDdprVjxHcLlkJ5Vh1H5185/tDad9h8XOsqAw3Cb0cjq +CfX61P8AsveKf7L8T3fhe5nxa3g324YniQdh9R/KuivTdSmp9TXB1/Z1nRlsfU249QBx1pWfcwI7 +Cq6u2Ovamb9rYz16V5x7Psz5q/aJhZfibcupIElvE3H+7j+lFbXx9szP45jlUfeso8/99OKK9elL +3EeNVopzZVharCtxVRGGMk02S5JyFOB69zWTieypnL/GksfB+0cr9oQt69awPhPpD69Y3Ky3DWth +CWluGU4LAdq3vibGs/g68BJygVxjrwRW5+ztoRv/AAs8EeCZnO/J/hzz/Kumi4wp3kePjlOdZKPY +ml8Mvo6WM9ppi7J0EkQYMzkb1X0wW+bJHGACa+ivg14huZPCkjXUO6SImIYOQAP6UQ2sGneHmiWF +CYoSAAuM8etXfhBpMr+EZ3SSNRcMx6ZxWdes6ytBGSwkMNFuo7pnP614Ut9Zn1C4uLySKcwObGFA +Qnm4+XcR71h+CfDHiKCQWuv2YWFtw+bBZQD8rcE8n2Ney+GbGGQSM6I7xvtI681rXdp8hKRRp6t3 +FYPE1VDlsY1nQjWdl+J8w/tOeDZZ/AMd/EGefTHyxxyYjwfyODXg3waRh8UtEcFh+8Jb8FNfZPxh +Ak8J6nbPIsiyW7LnseK+Ofh99ptPHFnf2lu0y2twGcKuSEPB/Q1WHk50miq1o14TXU+xop+On61H +NMC6Nis1LgsAcUTSt1APFcXJZn0EqmhyvxA037friTYziBV6f7Tf40Vr3k4aXLx5OMZIzRXZB2ik +efJXbPHJJcnaD9aN2R1qoGO8/WkuLlIV+YjdjpXSonQ5lTxfiTwzqCnkeQxx9BWx+zPrQsrBFYja +zEfrXL6lfie3khcjY6lSPUGud+G3iA6FrY0uQ/KJsA9gCa0jSvFpnBi6tpKSPu++nsIfDFxeXciR +qIicscY4rxTwB8RUkn1axsPEf2LTYx85kTGzqCUP8VcJ47+JOs+I0Hhq1njjt/ufIeSvq1VtE+Gs +eoaUbSHUrkHdu3WtuX5Ix81XRh7OL5jirVpV5Whdn0F8C/iX4auVn0i612T7bASoe8wjTAd89K9g +vtSt5NMa4t5lkRhgOh3Dn6V8aaz8KZrIR3OlQ6r56LySmSxxz06Vo/CHx34h0rxBP4XvJ5AjK2RP +nEbAEj6ZxjPrWM6fMmoswqJxqJ1VZnqHxn1NLPwveqWHmNC2BnnNcD8DfDkGi+CH1m+ijN1qMzNA +4GSIiAMf+hVxPxU8Tapc3c0F9MGCn5GU5BX0Pau3+HmrT3XgXSIJCBHDGdgAx1NYSpezha52Yauq +1dya2Wh2onAIwTj1p0lxxWWLkhRyCKWa5O3ORXOos9KVQluZm83j0oqi84JyWH50Vdmc7ep43d3I +t1Z2Iz2FYdxeSTsxyRnvTdVuDNcNluM9KrKcg817NOnZGNbEXdkNckjrXGeIIprPxFFdRHAlIwem +COtdmxrG8Q2cd/ZNExw45RvQ1bVjim+dWNzw7eaTD4mN3dndCQCo6hmI5zXpj/Ea/wBHjkh0kwRW +xXEfl4yTxXzXZalJDL9nuWKMmRnHcV2Hh3WreCyYXW2SWQhd5P3F6n+lS43d2cTm6d7Ox9EWPxH1 +ODQxPqWpCaSU/ukUc4z3/WvKW8UhviAdaMewYZG98gj9c1ymoa8LyWOJHwkTDaVPb0qpr+q2m6Nb +cfvNo349az9mou9iZVXNWbub3jm98/Vza2ReV7lsJg/e3dsV654UR9N0K0sZP9ZDGFbHr3rzL4P+ +H7rXfEEWr3I3W1qf3IYdW9fwqDxf4k8UeH/G95p08kscHmk25dPlZT0we9YTj7SXKjpw1aNG8mj3 +FLv5ccU959ycnmvKPDnxB82YQarGsZPAlTp+IrvIr1ZIgySKwIyCOhFYTpyg9T0qWIhVV4svzPvf +IdhgY4orPachj81FRdmtzxqdiZmJ9aQEgdqZcPtmbJ71DJcAZ5r20kkeXJtsfPIQDwPzrG1a+S3i +LyHAHvmp7y7HOD1rlNdm+1T7Acovf3o+J2RMpezjzMvrob67pX9o2ShZlYgg/wAWKxZLLWLZ/Ke3 +mVh14yK9M+BMC3dre2ko3LHKCB7EV7EngeGQJdQ7HyBkMKS0djgq1W3c+XtK03U522RwzsTwNiEk +ntXoHgf4calql9El/G8UZbLfLyfr7V9FeGvh+s+0Lbxxcglu2K1NW1nwN4Gk/wBLuI57tV5jjwzE +/QVNS+0dWYRqNvXRFv4eeCodKsY1ggVIY1G3K4z714h+1Jqul3GpwaXYeXJLbzgyyrg4b+6D+HNb +vjz436zq9m+naHF/ZdkeGfOZXH17V4Vqt2b29K+ZuOc5bnce5zWdPBShL2lTfojSeJhy+zp/NjVz +1Bwa6DSfFGq6fbJFDKrov8DjPFcu97ZxsUe4jVhwVJ5Bpp1mwQiLewJPXacVq6fNpYyjOUXdHoKf +EG8VQHsInbuVcgflRXnt5fIs2FYHgcgUVi8LG+xusdW/mN7U2KgEVkTzPt60UVfQ9eHxGHrV1MGi +iD4V25x1qvdgLAMd6KK0pbHm4x++dp8FtUubLxJ5EIjMc+A4Za+qfD8pe1JZVOBmiinW3RyRPMfi +R8QPE638+k2l6LK0Hylbddhb6nOa80mlkcmWR2kcnlnOSaKK7qCXKcNdu5narcSrAoBxvODWJIga +VckjDdqKKwq/EaQ0gUdbjQ6mr7QGBUcd6tPBC6gtGpOOuKKKie5qn7qIpEXd0HSiiimSf//Z` diff --git a/third_party/code.google.com/p/go.crypto/openpgp/read.go b/third_party/code.google.com/p/go.crypto/openpgp/read.go index dd475fd2e..ab47d7f50 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/read.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/read.go @@ -80,7 +80,8 @@ type keyEnvelopePair struct { // ReadMessage parses an OpenPGP message that may be signed and/or encrypted. // The given KeyRing should contain both public keys (for signature // verification) and, possibly encrypted, private keys for decrypting. -func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction) (md *MessageDetails, err error) { +// If config is nil, sensible defaults will be used. +func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) { var p packet.Packet var symKeys []*packet.SymmetricKeyEncrypted @@ -155,7 +156,7 @@ FindKey: } if !pk.key.PrivateKey.Encrypted { if len(pk.encryptedKey.Key) == 0 { - pk.encryptedKey.Decrypt(pk.key.PrivateKey) + pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) } if len(pk.encryptedKey.Key) == 0 { continue @@ -361,21 +362,32 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe return } - sig, ok := p.(*packet.Signature) - if !ok { + var issuerKeyId uint64 + var hashFunc crypto.Hash + var sigType packet.SignatureType + + switch sig := p.(type) { + case *packet.Signature: + if sig.IssuerKeyId == nil { + return nil, errors.StructuralError("signature doesn't have an issuer") + } + issuerKeyId = *sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + case *packet.SignatureV3: + issuerKeyId = sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + default: return nil, errors.StructuralError("non signature packet found") } - if sig.IssuerKeyId == nil { - return nil, errors.StructuralError("signature doesn't have an issuer") - } - - keys := keyring.KeysById(*sig.IssuerKeyId) + keys := keyring.KeysById(issuerKeyId) if len(keys) == 0 { return nil, errors.ErrUnknownIssuer } - h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) + h, wrappedHash, err := hashForSignature(hashFunc, sigType) if err != nil { return } @@ -389,7 +401,12 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { continue } - err = key.PublicKey.VerifySignature(h, sig) + switch sig := p.(type) { + case *packet.Signature: + err = key.PublicKey.VerifySignature(h, sig) + case *packet.SignatureV3: + err = key.PublicKey.VerifySignatureV3(h, sig) + } if err == nil { return key.Entity, nil } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/read_test.go b/third_party/code.google.com/p/go.crypto/openpgp/read_test.go index 51efb436c..d9fb6a3d2 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/read_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/read_test.go @@ -104,7 +104,7 @@ func TestGetKeyById(t *testing.T) { func checkSignedMessage(t *testing.T, signedHex, expected string) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - md, err := ReadMessage(readerFromHex(signedHex), kring, nil) + md, err := ReadMessage(readerFromHex(signedHex), kring, nil, nil) if err != nil { t.Error(err) return @@ -178,7 +178,7 @@ func TestSignedEncryptedMessage(t *testing.T) { return nil, nil } - md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) + md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt, nil) if err != nil { t.Errorf("#%d: error reading message: %s", i, err) return @@ -206,7 +206,7 @@ func TestUnspecifiedRecipient(t *testing.T) { expected := "Recipient unspecified\n" kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil) + md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil, nil) if err != nil { t.Errorf("error reading message: %s", err) return @@ -236,7 +236,7 @@ func TestSymmetricallyEncrypted(t *testing.T) { return []byte("password"), nil } - md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt) + md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt, nil) if err != nil { t.Errorf("ReadMessage: %s", err) return @@ -277,6 +277,7 @@ func TestDetachedSignature(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId) testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId) + testDetachedSignature(t, kring, readerFromHex(detachedSignatureV3TextHex), signedInput, "v3", testKey1KeyId) } func TestDetachedSignatureDSA(t *testing.T) { @@ -313,6 +314,8 @@ const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31 const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39" +const detachedSignatureV3TextHex = "8900950305005255c25ca34d7e18c20c31bb0102bb3f04009f6589ef8a028d6e54f6eaf25432e590d31c3a41f4710897585e10c31e5e332c7f9f409af8512adceaff24d0da1474ab07aa7bce4f674610b010fccc5b579ae5eb00a127f272fb799f988ab8e4574c141da6dbfecfef7e6b2c478d9a3d2551ba741f260ee22bec762812f0053e05380bfdd55ad0f22d8cdf71b233fe51ae8a24" + const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83" const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003" diff --git a/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k.go b/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k.go index ad7492d59..2ecfe4c41 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k.go @@ -97,12 +97,12 @@ func Parse(r io.Reader) (f func(out, in []byte), err error) { } switch buf[0] { - case 1: + case 0: f := func(out, in []byte) { Simple(out, h, in) } return f, nil - case 2: + case 1: _, err = io.ReadFull(r, buf[:8]) if err != nil { return @@ -151,14 +151,15 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error var hashToHashIdMapping = []struct { id byte hash crypto.Hash + name string }{ - {1, crypto.MD5}, - {2, crypto.SHA1}, - {3, crypto.RIPEMD160}, - {8, crypto.SHA256}, - {9, crypto.SHA384}, - {10, crypto.SHA512}, - {11, crypto.SHA224}, + {1, crypto.MD5, "MD5"}, + {2, crypto.SHA1, "SHA1"}, + {3, crypto.RIPEMD160, "RIPEMD160"}, + {8, crypto.SHA256, "SHA256"}, + {9, crypto.SHA384, "SHA384"}, + {10, crypto.SHA512, "SHA512"}, + {11, crypto.SHA224, "SHA224"}, } // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP @@ -172,6 +173,18 @@ func HashIdToHash(id byte) (h crypto.Hash, ok bool) { return 0, false } +// HashIdToString returns the name of the hash function corresponding to the +// given OpenPGP hash id, or panics if id is unknown. +func HashIdToString(id byte) (name string, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.name, true + } + } + + return "", false +} + // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. func HashToHashId(h crypto.Hash) (id byte, ok bool) { for _, m := range hashToHashIdMapping { diff --git a/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k_test.go b/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k_test.go index 3a094a10f..48988280c 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k_test.go @@ -66,9 +66,9 @@ var parseTests = []struct { spec, in, out string }{ /* Simple with SHA1 */ - {"0102", "hello", "aaf4c61d"}, + {"0002", "hello", "aaf4c61d"}, /* Salted with SHA1 */ - {"02020102030405060708", "hello", "f4f7d67e"}, + {"01020102030405060708", "hello", "f4f7d67e"}, /* Iterated with SHA1 */ {"03020102030405060708f1", "hello", "f2a57b7c"}, } diff --git a/third_party/code.google.com/p/go.crypto/openpgp/write.go b/third_party/code.google.com/p/go.crypto/openpgp/write.go index 5a62599cd..53374f8e2 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/write.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/write.go @@ -10,8 +10,6 @@ import ( "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/s2k" "crypto" - "crypto/rand" - _ "crypto/sha256" "hash" "io" "strconv" @@ -20,62 +18,59 @@ import ( // DetachSign signs message with the private key from signer (which must // already have been decrypted) and writes the signature to w. -func DetachSign(w io.Writer, signer *Entity, message io.Reader) error { - return detachSign(w, signer, message, time.Time{}, packet.SigTypeBinary) +// If config is nil, sensible defaults will be used. +func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeBinary, config) } // ArmoredDetachSign signs message with the private key from signer (which // must already have been decrypted) and writes an armored signature to w. -func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err error) { - return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeBinary) -} - -func ArmoredDetachSignAt(w io.Writer, signer *Entity, sigTime time.Time, message io.Reader) (err error) { - return armoredDetachSign(w, signer, message, sigTime, packet.SigTypeBinary) +// If config is nil, sensible defaults will be used. +func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { + return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) } // DetachSignText signs message (after canonicalising the line endings) with // the private key from signer (which must already have been decrypted) and // writes the signature to w. -func DetachSignText(w io.Writer, signer *Entity, message io.Reader) error { - return detachSign(w, signer, message, time.Time{}, packet.SigTypeText) +// If config is nil, sensible defaults will be used. +func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeText, config) } // ArmoredDetachSignText signs message (after canonicalising the line endings) // with the private key from signer (which must already have been decrypted) // and writes an armored signature to w. -func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) error { - return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeText) +// If config is nil, sensible defaults will be used. +func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return armoredDetachSign(w, signer, message, packet.SigTypeText, config) } -func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Time, sigType packet.SignatureType) (err error) { +func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { out, err := armor.Encode(w, SignatureType, nil) if err != nil { return } - err = detachSign(out, signer, message, sigTime, sigType) + err = detachSign(out, signer, message, sigType, config) if err != nil { return } return out.Close() } -func detachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Time, sigType packet.SignatureType) (err error) { +func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing key doesn't have a private key") } if signer.PrivateKey.Encrypted { return errors.InvalidArgumentError("signing key is encrypted") } - if sigTime.IsZero() { - sigTime = time.Now() - } sig := new(packet.Signature) sig.SigType = sigType sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo - sig.Hash = crypto.SHA256 - sig.CreationTime = sigTime + sig.Hash = config.Hash() + sig.CreationTime = config.Now() sig.IssuerKeyId = &signer.PrivateKey.KeyId h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) @@ -84,7 +79,7 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Tim } io.Copy(wrappedHash, message) - err = sig.Sign(rand.Reader, h, signer.PrivateKey) + err = sig.Sign(h, signer.PrivateKey, config) if err != nil { return } @@ -109,24 +104,38 @@ type FileHints struct { // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. // The resulting WriteCloser must be closed after the contents of the file have // been written. -func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err error) { +// If config is nil, sensible defaults will be used. +func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { if hints == nil { hints = &FileHints{} } - key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) + key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) if err != nil { return } - w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, packet.CipherAES128, key) + w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) if err != nil { return } + + literaldata := w + if algo := config.Compression(); algo != packet.CompressionNone { + var compConfig *packet.CompressionConfig + if config != nil { + compConfig = config.CompressionConfig + } + literaldata, err = packet.SerializeCompressed(w, algo, compConfig) + if err != nil { + return + } + } + var epochSeconds uint32 if !hints.ModTime.IsZero() { epochSeconds = uint32(hints.ModTime.Unix()) } - return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) + return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds) } // intersectPreferences mutates and returns a prefix of a that contains only @@ -158,11 +167,16 @@ func hashToHashId(h crypto.Hash) uint8 { // it. hints contains optional information, that is also encrypted, that aids // the recipients in processing the message. The resulting WriteCloser must // be closed after the contents of the file have been written. -func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err error) { +// If config is nil, sensible defaults will be used. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { var signer *packet.PrivateKey if signed != nil { - signer = signed.signingKey().PrivateKey - if signer == nil || signer.Encrypted { + signKey, ok := signed.signingKey(config.Now()) + if !ok { + return nil, errors.InvalidArgumentError("no valid signing keys") + } + signer = signKey.PrivateKey + if signer.Encrypted { return nil, errors.InvalidArgumentError("signing key must be decrypted") } } @@ -188,8 +202,9 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint encryptKeys := make([]Key, len(to)) for i := range to { - encryptKeys[i] = to[i].encryptionKey() - if encryptKeys[i].PublicKey == nil { + var ok bool + encryptKeys[i], ok = to[i].encryptionKey(config.Now()) + if !ok { return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") } @@ -212,19 +227,55 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint } cipher := packet.CipherFunction(candidateCiphers[0]) - hash, _ := s2k.HashIdToHash(candidateHashes[0]) + // If the cipher specifed by config is a candidate, we'll use that. + configuredCipher := config.Cipher() + for _, c := range candidateCiphers { + cipherFunc := packet.CipherFunction(c) + if cipherFunc == configuredCipher { + cipher = cipherFunc + break + } + } + + var hash crypto.Hash + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { + hash = h + break + } + } + + // If the hash specified by config is a candidate, we'll use that. + if configuredHash := config.Hash(); configuredHash.Available() { + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { + hash = h + break + } + } + } + + if hash == 0 { + hashId := candidateHashes[0] + name, ok := s2k.HashIdToString(hashId) + if !ok { + name = "#" + strconv.Itoa(int(hashId)) + } + return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") + } + symKey := make([]byte, cipher.KeySize()) - if _, err := io.ReadFull(rand.Reader, symKey); err != nil { + if _, err := io.ReadFull(config.Random(), symKey); err != nil { return nil, err } for _, key := range encryptKeys { - if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { + if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { return nil, err } } - encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, cipher, symKey) + encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) if err != nil { return } @@ -264,7 +315,7 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint } if signer != nil { - return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil + return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil } return literalData, nil } @@ -278,6 +329,7 @@ type signatureWriter struct { hashType crypto.Hash h hash.Hash signer *packet.PrivateKey + config *packet.Config } func (s signatureWriter) Write(data []byte) (int, error) { @@ -290,11 +342,11 @@ func (s signatureWriter) Close() error { SigType: packet.SigTypeBinary, PubKeyAlgo: s.signer.PubKeyAlgo, Hash: s.hashType, - CreationTime: time.Now(), + CreationTime: s.config.Now(), IssuerKeyId: &s.signer.KeyId, } - if err := sig.Sign(rand.Reader, s.h, s.signer); err != nil { + if err := sig.Sign(s.h, s.signer, s.config); err != nil { return err } if err := s.literalData.Close(); err != nil { diff --git a/third_party/code.google.com/p/go.crypto/openpgp/write_test.go b/third_party/code.google.com/p/go.crypto/openpgp/write_test.go index 7df02e7bd..9f8c358b0 100644 --- a/third_party/code.google.com/p/go.crypto/openpgp/write_test.go +++ b/third_party/code.google.com/p/go.crypto/openpgp/write_test.go @@ -6,7 +6,6 @@ package openpgp import ( "bytes" - "crypto/rand" "io" "io/ioutil" "testing" @@ -17,7 +16,7 @@ func TestSignDetached(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) + err := DetachSign(out, kring[0], message, nil) if err != nil { t.Error(err) } @@ -29,7 +28,7 @@ func TestSignTextDetached(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) - err := DetachSignText(out, kring[0], message) + err := DetachSignText(out, kring[0], message, nil) if err != nil { t.Error(err) } @@ -41,7 +40,7 @@ func TestSignDetachedDSA(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) + err := DetachSign(out, kring[0], message, nil) if err != nil { t.Error(err) } @@ -54,14 +53,14 @@ func TestNewEntity(t *testing.T) { return } - e, err := NewEntity(rand.Reader, time.Now(), "Test User", "test", "test@example.com") + e, err := NewEntity("Test User", "test", "test@example.com", nil) if err != nil { t.Errorf("failed to create entity: %s", err) return } w := bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { + if err := e.SerializePrivate(w, nil); err != nil { t.Errorf("failed to serialize entity: %s", err) return } @@ -78,7 +77,7 @@ func TestNewEntity(t *testing.T) { } w = bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { + if err := e.SerializePrivate(w, nil); err != nil { t.Errorf("failed to serialize entity second time: %s", err) return } @@ -90,7 +89,7 @@ func TestNewEntity(t *testing.T) { func TestSymmetricEncryption(t *testing.T) { buf := new(bytes.Buffer) - plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil) + plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil, nil) if err != nil { t.Errorf("error writing headers: %s", err) return @@ -107,7 +106,7 @@ func TestSymmetricEncryption(t *testing.T) { md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, error) { return []byte("testing"), nil - }) + }, nil) if err != nil { t.Errorf("error rereading message: %s", err) } @@ -171,7 +170,7 @@ func TestEncryption(t *testing.T) { } buf := new(bytes.Buffer) - w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) + w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */, nil) if err != nil { t.Errorf("#%d: error in Encrypt: %s", i, err) continue @@ -189,14 +188,16 @@ func TestEncryption(t *testing.T) { continue } - md, err := ReadMessage(buf, kring, nil /* no prompt */ ) + md, err := ReadMessage(buf, kring, nil /* no prompt */, nil) if err != nil { t.Errorf("#%d: error reading message: %s", i, err) continue } + testTime, _ := time.Parse("2006-01-02", "2013-07-01") if test.isSigned { - expectedKeyId := kring[0].signingKey().PublicKey.KeyId + signKey, _ := kring[0].signingKey(testTime) + expectedKeyId := signKey.PublicKey.KeyId if md.SignedByKeyId != expectedKeyId { t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId) } @@ -211,7 +212,8 @@ func TestEncryption(t *testing.T) { continue } - expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId + encryptKey, _ := kring[0].encryptionKey(testTime) + expectedKeyId := encryptKey.PublicKey.KeyId if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds) }