third_party,jsonsign: bump openpgp to 188:61c59dda830a

http://camlistore.org/issue/370

Change-Id: I0066b1ad1455ff7960bda1f64ee953daa1195e1c
This commit is contained in:
mpl 2014-02-09 01:04:14 +01:00
parent c5c6fe1adc
commit c4f3a5b215
36 changed files with 1901 additions and 213 deletions

View File

@ -162,7 +162,7 @@ func TestSigning(t *testing.T) {
if vr.Verify() { if vr.Verify() {
t.Fatalf("unexpected verification of faked signature") 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") "expected signature verification error")
t.Logf("TODO: verify GPG-vs-Go sign & verify interop both ways, once implemented.") t.Logf("TODO: verify GPG-vs-Go sign & verify interop both ways, once implemented.")

View File

@ -18,7 +18,6 @@ package jsonsign
import ( import (
"bytes" "bytes"
"crypto/rand"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -26,7 +25,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"camlistore.org/pkg/osutil" "camlistore.org/pkg/osutil"
@ -159,12 +157,12 @@ func NewEntity() (*openpgp.Entity, error) {
name := "" // intentionally empty name := "" // intentionally empty
comment := "camlistore" comment := "camlistore"
email := "" // intentionally empty 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 { func WriteKeyRing(w io.Writer, el openpgp.EntityList) error {
for _, ent := range el { for _, ent := range el {
if err := ent.SerializePrivate(w); err != nil { if err := ent.SerializePrivate(w, nil); err != nil {
return err return err
} }
} }

View File

@ -31,6 +31,7 @@ import (
"camlistore.org/pkg/blob" "camlistore.org/pkg/blob"
"camlistore.org/pkg/osutil" "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"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet"
) )
type EntityFetcher interface { type EntityFetcher interface {
@ -204,7 +205,12 @@ func (sr *SignRequest) Sign() (signedJSON string, err error) {
} }
var buf bytes.Buffer 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 { if err != nil {
return "", err return "", err
} }

View File

@ -111,7 +111,7 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
return 0, io.EOF return 0, io.EOF
} }
if len(line) > 64 { if len(line) > 96 {
return 0, ArmorCorrupt return 0, ArmorCorrupt
} }

View File

@ -17,7 +17,6 @@ import (
"io" "io"
"net/textproto" "net/textproto"
"strconv" "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/armor"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
@ -179,9 +178,8 @@ type dashEscaper struct {
whitespace []byte whitespace []byte
byteBuf []byte // a one byte buffer to save allocations byteBuf []byte // a one byte buffer to save allocations
privateKey *packet.PrivateKey privateKey *packet.PrivateKey
signingTime time.Time config *packet.Config
rand io.Reader
} }
func (d *dashEscaper) Write(data []byte) (n int, err error) { 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.SigType = packet.SigTypeText
sig.PubKeyAlgo = d.privateKey.PubKeyAlgo sig.PubKeyAlgo = d.privateKey.PubKeyAlgo
sig.Hash = d.hashType sig.Hash = d.hashType
sig.CreationTime = d.signingTime sig.CreationTime = d.config.Now()
sig.IssuerKeyId = &d.privateKey.KeyId 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 return
} }
@ -334,9 +332,8 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (
byteBuf: make([]byte, 1), byteBuf: make([]byte, 1),
privateKey: privateKey, privateKey: privateKey,
signingTime: config.Now(), config: config,
rand: config.Random(),
} }
return return

View File

@ -14,7 +14,7 @@ import (
type StructuralError string type StructuralError string
func (s StructuralError) Error() 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 // UnsupportedError indicates that, although the OpenPGP data is valid, it
@ -22,7 +22,7 @@ func (s StructuralError) Error() string {
type UnsupportedError string type UnsupportedError string
func (s UnsupportedError) Error() 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 // InvalidArgumentError indicates that the caller is in error and passed an
@ -30,7 +30,7 @@ func (s UnsupportedError) Error() string {
type InvalidArgumentError string type InvalidArgumentError string
func (i InvalidArgumentError) Error() 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 // SignatureError indicates that a syntactically valid signature failed to
@ -38,13 +38,13 @@ func (i InvalidArgumentError) Error() string {
type SignatureError string type SignatureError string
func (b SignatureError) Error() string { func (b SignatureError) Error() string {
return "OpenPGP signature invalid: " + string(b) return "openpgp: invalid signature: " + string(b)
} }
type keyIncorrectError int type keyIncorrectError int
func (ki keyIncorrectError) Error() string { func (ki keyIncorrectError) Error() string {
return "the given key was incorrect" return "openpgp: incorrect key"
} }
var ErrKeyIncorrect error = keyIncorrectError(0) var ErrKeyIncorrect error = keyIncorrectError(0)
@ -52,7 +52,7 @@ var ErrKeyIncorrect error = keyIncorrectError(0)
type unknownIssuerError int type unknownIssuerError int
func (unknownIssuerError) Error() string { func (unknownIssuerError) Error() string {
return "signature make by unknown entity" return "openpgp: signature made by unknown entity"
} }
var ErrUnknownIssuer error = unknownIssuerError(0) var ErrUnknownIssuer error = unknownIssuerError(0)
@ -60,5 +60,5 @@ var ErrUnknownIssuer error = unknownIssuerError(0)
type UnknownPacketTypeError uint8 type UnknownPacketTypeError uint8
func (upte UnknownPacketTypeError) Error() string { func (upte UnknownPacketTypeError) Error() string {
return "unknown OpenPGP packet type: " + strconv.Itoa(int(upte)) return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
} }

View File

@ -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/armor"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet"
"crypto"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"io" "io"
"time" "time"
@ -29,18 +27,6 @@ type Entity struct {
PrivateKey *packet.PrivateKey PrivateKey *packet.PrivateKey
Identities map[string]*Identity // indexed by Identity.Name Identities map[string]*Identity // indexed by Identity.Name
Subkeys []Subkey 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 // 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 // encryptionKey returns the best candidate Key for encrypting a message to the
// given Entity. // given Entity.
func (e *Entity) encryptionKey() Key { func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
candidateSubkey := -1 candidateSubkey := -1
for i, subkey := range e.Subkeys { 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 candidateSubkey = i
break 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 { if candidateSubkey != -1 {
subkey := e.Subkeys[candidateSubkey] 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. // This Entity appears to be signing only.
return Key{} return Key{}, false
} }
// signingKey return the best candidate Key for signing a message with this // signingKey return the best candidate Key for signing a message with this
// Entity. // Entity.
func (e *Entity) signingKey() Key { func (e *Entity) signingKey(now time.Time) (Key, bool) {
candidateSubkey := -1 candidateSubkey := -1
for i, subkey := range e.Subkeys { 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 candidateSubkey = i
break 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 // If we have no candidate subkey then we assume that it's ok to sign
// with the primary key. // with the primary key.
if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign { i := e.primaryIdentity()
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} 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{}, false
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}
} }
// An EntityList contains one or more Entities. // An EntityList contains one or more Entities.
@ -214,11 +209,16 @@ func ReadKeyRing(r io.Reader) (el EntityList, err error) {
for { for {
var e *Entity var e *Entity
e, err = readEntity(packets) e, err = ReadEntity(packets)
if err != nil { if err != nil {
// TODO: warn about skipped unsupported/unreadable keys
if _, ok := err.(errors.UnsupportedError); ok { if _, ok := err.(errors.UnsupportedError); ok {
lastUnsupportedError = err lastUnsupportedError = err
err = readToNextPublicKey(packets) err = readToNextPublicKey(packets)
} else if _, ok := err.(errors.StructuralError); ok {
// Skip unreadable, badly-formatted keys
lastUnsupportedError = err
err = readToNextPublicKey(packets)
} }
if err == io.EOF { if err == io.EOF {
err = nil err = nil
@ -264,9 +264,9 @@ func readToNextPublicKey(packets *packet.Reader) (err error) {
panic("unreachable") 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. // given Reader.
func readEntity(packets *packet.Reader) (*Entity, error) { func ReadEntity(packets *packet.Reader) (*Entity, error) {
e := new(Entity) e := new(Entity)
e.Identities = make(map[string]*Identity) 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 // 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 // single identity composed of the given full name, comment and email, any of
// which may be empty but must not contain any of "()<>\x00". // 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) uid := packet.NewUserId(name, comment, email)
if uid == nil { if uid == nil {
return nil, errors.InvalidArgumentError("user id field contained invalid characters") 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 { if err != nil {
return nil, err return nil, err
} }
encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) encryptingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -422,7 +425,7 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin
CreationTime: currentTime, CreationTime: currentTime,
SigType: packet.SigTypePositiveCert, SigType: packet.SigTypePositiveCert,
PubKeyAlgo: packet.PubKeyAlgoRSA, PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: crypto.SHA256, Hash: config.Hash(),
IsPrimaryId: &isPrimaryId, IsPrimaryId: &isPrimaryId,
FlagsValid: true, FlagsValid: true,
FlagSign: true, FlagSign: true,
@ -439,7 +442,7 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin
CreationTime: currentTime, CreationTime: currentTime,
SigType: packet.SigTypeSubkeyBinding, SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: packet.PubKeyAlgoRSA, PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: crypto.SHA256, Hash: config.Hash(),
FlagsValid: true, FlagsValid: true,
FlagEncryptStorage: true, FlagEncryptStorage: true,
FlagEncryptCommunications: 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 // SerializePrivate serializes an Entity, including private key material, to
// the given Writer. For now, it must only be used on an Entity returned from // the given Writer. For now, it must only be used on an Entity returned from
// NewEntity. // 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) err = e.PrivateKey.Serialize(w)
if err != nil { if err != nil {
return return
@ -465,7 +469,7 @@ func (e *Entity) SerializePrivate(w io.Writer) (err error) {
if err != nil { if err != nil {
return 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 { if err != nil {
return return
} }
@ -479,7 +483,7 @@ func (e *Entity) SerializePrivate(w io.Writer) (err error) {
if err != nil { if err != nil {
return return
} }
err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey) err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
if err != nil { if err != nil {
return 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 // 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 // e.Identities and the private key of signer must have been decrypted if
// necessary. // 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 { if signer.PrivateKey == nil {
return errors.InvalidArgumentError("signing Entity must have a private key") 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{ sig := &packet.Signature{
SigType: packet.SigTypeGenericCert, SigType: packet.SigTypeGenericCert,
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
Hash: crypto.SHA256, Hash: config.Hash(),
CreationTime: time.Now(), CreationTime: config.Now(),
IssuerKeyId: &signer.PrivateKey.KeyId, 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 return err
} }
ident.Signatures = append(ident.Signatures, sig) ident.Signatures = append(ident.Signatures, sig)

View File

@ -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"

View File

@ -6,6 +6,7 @@ package packet
import ( import (
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
"compress/bzip2"
"compress/flate" "compress/flate"
"compress/zlib" "compress/zlib"
"io" "io"
@ -18,6 +19,26 @@ type Compressed struct {
Body io.Reader 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 { func (c *Compressed) parse(r io.Reader) error {
var buf [1]byte var buf [1]byte
_, err := readFull(r, buf[:]) _, err := readFull(r, buf[:])
@ -30,9 +51,73 @@ func (c *Compressed) parse(r io.Reader) error {
c.Body = flate.NewReader(r) c.Body = flate.NewReader(r)
case 2: case 2:
c.Body, err = zlib.NewReader(r) c.Body, err = zlib.NewReader(r)
case 3:
c.Body = bzip2.NewReader(r)
default: default:
err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0])))
} }
return err 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
}

View File

@ -12,8 +12,7 @@ import (
) )
// Config collects a number of parameters along with sensible defaults. // Config collects a number of parameters along with sensible defaults.
// A nil *Config is valid and results in all default values.
// A nil *Config is valid and produces all default values.
type Config struct { type Config struct {
// Rand provides the source of entropy. // Rand provides the source of entropy.
// If nil, the crypto/rand Reader is used. // 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 // Time returns the current time as the number of seconds since the
// epoch. If Time is nil, time.Now is used. // epoch. If Time is nil, time.Now is used.
Time func() time.Time 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 { func (c *Config) Random() io.Reader {
@ -56,3 +61,10 @@ func (c *Config) Now() time.Time {
} }
return c.Time() return c.Time()
} }
func (c *Config) Compression() CompressionAlgo {
if c == nil {
return CompressionNone
}
return c.DefaultCompressionAlgo
}

View File

@ -7,7 +7,6 @@ package packet
import ( import (
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/elgamal" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/elgamal"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"encoding/binary" "encoding/binary"
"io" "io"
@ -63,7 +62,8 @@ func checksumKeyMaterial(key []byte) uint16 {
// Decrypt decrypts an encrypted session key with the given private key. The // Decrypt decrypts an encrypted session key with the given private key. The
// private key must have been decrypted first. // 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 err error
var b []byte var b []byte
@ -71,7 +71,7 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey) error {
// padding oracle attacks. // padding oracle attacks.
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: 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: case PubKeyAlgoElGamal:
c1 := new(big.Int).SetBytes(e.encryptedMPI1) c1 := new(big.Int).SetBytes(e.encryptedMPI1)
c2 := new(big.Int).SetBytes(e.encryptedMPI2) 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 // SerializeEncryptedKey serializes an encrypted key packet to w that contains
// key, encrypted to pub. // 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 var buf [10]byte
buf[0] = encryptedKeyVersion buf[0] = encryptedKeyVersion
binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) binary.BigEndian.PutUint64(buf[1:9], pub.KeyId)
buf[9] = byte(pub.PubKeyAlgo) 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) keyBlock[0] = byte(cipherFunc)
copy(keyBlock[1:], key) copy(keyBlock[1:], key)
checksum := checksumKeyMaterial(key) checksum := checksumKeyMaterial(key)
@ -112,9 +113,9 @@ func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFu
switch pub.PubKeyAlgo { switch pub.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: 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: 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: case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
} }

View File

@ -6,7 +6,6 @@ package packet
import ( import (
"bytes" "bytes"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"fmt" "fmt"
"math/big" "math/big"
@ -58,7 +57,7 @@ func TestDecryptingEncryptedKey(t *testing.T) {
return return
} }
err = ek.Decrypt(encryptedKeyPriv) err = ek.Decrypt(encryptedKeyPriv, nil)
if err != nil { if err != nil {
t.Errorf("error from Decrypt: %s", err) t.Errorf("error from Decrypt: %s", err)
return return
@ -87,7 +86,7 @@ func TestEncryptingEncryptedKey(t *testing.T) {
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) err := SerializeEncryptedKey(buf, pub, CipherAES128, key, nil)
if err != nil { if err != nil {
t.Errorf("error writing encrypted key packet: %s", err) t.Errorf("error writing encrypted key packet: %s", err)
} }
@ -108,7 +107,7 @@ func TestEncryptingEncryptedKey(t *testing.T) {
return return
} }
err = ek.Decrypt(encryptedKeyPriv) err = ek.Decrypt(encryptedKeyPriv, nil)
if err != nil { if err != nil {
t.Errorf("error from Decrypt: %s", err) t.Errorf("error from Decrypt: %s", err)
return return

View File

@ -20,9 +20,9 @@ type ocfbEncrypter struct {
// performed. // performed.
type OCFBResyncOption bool type OCFBResyncOption bool
var ( const (
OCFBResync = OCFBResyncOption(true) OCFBResync OCFBResyncOption = true
OCFBNoResync = OCFBResyncOption(false) OCFBNoResync OCFBResyncOption = false
) )
// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's // NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's

View File

@ -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
}

View File

@ -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 <warda@nephilim.ruhr.de>" {
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`

View File

@ -7,10 +7,12 @@
package packet package packet
import ( import (
"bufio"
"camlistore.org/third_party/code.google.com/p/go.crypto/cast5" "camlistore.org/third_party/code.google.com/p/go.crypto/cast5"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/des"
"io" "io"
"math/big" "math/big"
) )
@ -292,9 +294,23 @@ const (
packetTypeLiteralData packetType = 11 packetTypeLiteralData packetType = 11
packetTypeUserId packetType = 13 packetTypeUserId packetType = 13
packetTypePublicSubkey packetType = 14 packetTypePublicSubkey packetType = 14
packetTypeUserAttribute packetType = 17
packetTypeSymmetricallyEncryptedMDC packetType = 18 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 // 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. // error parsing a packet, the whole packet is consumed from the input.
func Read(r io.Reader) (p Packet, err error) { func Read(r io.Reader) (p Packet, err error) {
@ -307,7 +323,16 @@ func Read(r io.Reader) (p Packet, err error) {
case packetTypeEncryptedKey: case packetTypeEncryptedKey:
p = new(EncryptedKey) p = new(EncryptedKey)
case packetTypeSignature: 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: case packetTypeSymmetricKeyEncrypted:
p = new(SymmetricKeyEncrypted) p = new(SymmetricKeyEncrypted)
case packetTypeOnePassSignature: case packetTypeOnePassSignature:
@ -319,11 +344,16 @@ func Read(r io.Reader) (p Packet, err error) {
} }
p = pk p = pk
case packetTypePublicKey, packetTypePublicSubkey: case packetTypePublicKey, packetTypePublicSubkey:
pk := new(PublicKey) var version byte
if tag == packetTypePublicSubkey { if contents, version, err = peekVersion(contents); err != nil {
pk.IsSubkey = true return
}
isSubkey := tag == packetTypePublicSubkey
if version < 4 {
p = &PublicKeyV3{IsSubkey: isSubkey}
} else {
p = &PublicKey{IsSubkey: isSubkey}
} }
p = pk
case packetTypeCompressed: case packetTypeCompressed:
p = new(Compressed) p = new(Compressed)
case packetTypeSymmetricallyEncrypted: case packetTypeSymmetricallyEncrypted:
@ -332,6 +362,8 @@ func Read(r io.Reader) (p Packet, err error) {
p = new(LiteralData) p = new(LiteralData)
case packetTypeUserId: case packetTypeUserId:
p = new(UserId) p = new(UserId)
case packetTypeUserAttribute:
p = new(UserAttribute)
case packetTypeSymmetricallyEncryptedMDC: case packetTypeSymmetricallyEncryptedMDC:
se := new(SymmetricallyEncrypted) se := new(SymmetricallyEncrypted)
se.MDC = true se.MDC = true
@ -373,6 +405,9 @@ const (
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
PubKeyAlgoElGamal PublicKeyAlgorithm = 16 PubKeyAlgoElGamal PublicKeyAlgorithm = 16
PubKeyAlgoDSA PublicKeyAlgorithm = 17 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 // 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 type CipherFunction uint8
const ( const (
Cipher3DES CipherFunction = 2
CipherCAST5 CipherFunction = 3 CipherCAST5 CipherFunction = 3
CipherAES128 CipherFunction = 7 CipherAES128 CipherFunction = 7
CipherAES192 CipherFunction = 8 CipherAES192 CipherFunction = 8
@ -409,6 +445,8 @@ const (
// KeySize returns the key size, in bytes, of cipher. // KeySize returns the key size, in bytes, of cipher.
func (cipher CipherFunction) KeySize() int { func (cipher CipherFunction) KeySize() int {
switch cipher { switch cipher {
case Cipher3DES:
return 24
case CipherCAST5: case CipherCAST5:
return cast5.KeySize return cast5.KeySize
case CipherAES128: case CipherAES128:
@ -424,6 +462,8 @@ func (cipher CipherFunction) KeySize() int {
// blockSize returns the block size, in bytes, of cipher. // blockSize returns the block size, in bytes, of cipher.
func (cipher CipherFunction) blockSize() int { func (cipher CipherFunction) blockSize() int {
switch cipher { switch cipher {
case Cipher3DES:
return des.BlockSize
case CipherCAST5: case CipherCAST5:
return 8 return 8
case CipherAES128, CipherAES192, CipherAES256: case CipherAES128, CipherAES192, CipherAES256:
@ -435,6 +475,8 @@ func (cipher CipherFunction) blockSize() int {
// new returns a fresh instance of the given cipher. // new returns a fresh instance of the given cipher.
func (cipher CipherFunction) new(key []byte) (block cipher.Block) { func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
switch cipher { switch cipher {
case Cipher3DES:
block, _ = des.NewTripleDESCipher(key)
case CipherCAST5: case CipherCAST5:
block, _ = cast5.NewCipher(key) block, _ = cast5.NewCipher(key)
case CipherAES128, CipherAES192, CipherAES256: 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 { func writeBig(w io.Writer, i *big.Int) error {
return writeMPI(w, uint16(i.BitLen()), i.Bytes()) 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
)

View File

@ -121,7 +121,7 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
if err != nil { if err != nil {
return return
} }
buf.WriteByte(0 /* no encryption */ ) buf.WriteByte(0 /* no encryption */)
privateKeyBuf := bytes.NewBuffer(nil) privateKeyBuf := bytes.NewBuffer(nil)

View File

@ -5,11 +5,17 @@
package packet package packet
import ( 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/elgamal"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
"crypto"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"hash" "hash"
@ -19,16 +25,150 @@ import (
"time" "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. // PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
type PublicKey struct { type PublicKey struct {
CreationTime time.Time CreationTime time.Time
PubKeyAlgo PublicKeyAlgorithm PubKeyAlgo PublicKeyAlgorithm
PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey or *ecdsa.PublicKey
Fingerprint [20]byte Fingerprint [20]byte
KeyId uint64 KeyId uint64
IsSubkey bool IsSubkey bool
n, e, p, q, g, y parsedMPI 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 { func fromBig(n *big.Int) parsedMPI {
@ -87,6 +227,23 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
err = pk.parseDSA(r) err = pk.parseDSA(r)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
err = pk.parseElGamal(r) 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: default:
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) 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. // 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 // The prefix is used when calculating a signature over this public key. See
// RFC 4880, section 5.2.4. // RFC 4880, section 5.2.4.
func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) {
var pLength uint16 var pLength uint16
switch pk.PubKeyAlgo { switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: 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.p.bytes))
pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.g.bytes))
pLength += 2 + uint16(len(pk.y.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: default:
panic("unknown public key algorithm") 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.p.bytes)
length += 2 + len(pk.g.bytes) length += 2 + len(pk.g.bytes)
length += 2 + len(pk.y.bytes) length += 2 + len(pk.y.bytes)
case PubKeyAlgoECDSA:
length += pk.ec.byteLen()
case PubKeyAlgoECDH:
length += pk.ec.byteLen()
length += pk.ecdh.byteLen()
default: default:
panic("unknown public key algorithm") 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) return writeMPIs(w, pk.p, pk.q, pk.g, pk.y)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
return writeMPIs(w, pk.p, pk.g, pk.y) 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") 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 errors.SignatureError("DSA verification failure")
} }
return nil 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: default:
panic("shouldn't happen") 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 // keySignatureHash returns a Hash of the message that needs to be signed for
// pk to assert a subkey relationship to signed. // pk to assert a subkey relationship to signed.
func keySignatureHash(pk, signed *PublicKey, sig *Signature) (h hash.Hash, err error) { func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
h = sig.Hash.New() if !hashFunc.Available() {
if h == nil {
return nil, errors.UnsupportedError("hash function") return nil, errors.UnsupportedError("hash function")
} }
h = hashFunc.New()
// RFC 4880, section 5.2.4 // RFC 4880, section 5.2.4
pk.SerializeSignaturePrefix(h) 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 // VerifyKeySignature returns nil iff sig is a valid signature, made by this
// public key, of signed. // public key, of signed.
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err error) { 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 { if err != nil {
return err 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 // userIdSignatureHash returns a Hash of the message that needs to be signed
// to assert that pk is a valid key for id. // to assert that pk is a valid key for id.
func userIdSignatureHash(id string, pk *PublicKey, sig *Signature) (h hash.Hash, err error) { func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
h = sig.Hash.New() if !hashFunc.Available() {
if h == nil {
return nil, errors.UnsupportedError("hash function") return nil, errors.UnsupportedError("hash function")
} }
h = hashFunc.New()
// RFC 4880, section 5.2.4 // RFC 4880, section 5.2.4
pk.SerializeSignaturePrefix(h) 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 // VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
// public key, of id. // public key, of id.
func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err error) { 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 { if err != nil {
return err return err
} }
return pk.VerifySignature(h, sig) 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 // KeyIdString returns the public key's fingerprint in capital hex
// (e.g. "6C7EE1B8621CC013"). // (e.g. "6C7EE1B8621CC013").
func (pk *PublicKey) KeyIdString() string { func (pk *PublicKey) KeyIdString() string {
@ -412,3 +647,18 @@ func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) {
} }
return 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
}

View File

@ -22,6 +22,7 @@ var pubKeyTests = []struct {
}{ }{
{rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, {rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"},
{dsaPkDataHex, dsaFingerprintHex, time.Unix(0x4d432f89, 0), PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, {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) { 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 <openpgp@brainhub.org>" {
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 rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"
@ -97,3 +193,10 @@ const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f94
const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed"
const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" 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`

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -30,8 +30,9 @@ type Signature struct {
HashTag [2]byte HashTag [2]byte
CreationTime time.Time CreationTime time.Time
RSASignature parsedMPI RSASignature parsedMPI
DSASigR, DSASigS parsedMPI DSASigR, DSASigS parsedMPI
ECDSASigR, ECDSASigS parsedMPI
// rawSubpackets contains the unparsed subpackets, in order. // rawSubpackets contains the unparsed subpackets, in order.
rawSubpackets []outputSubpacket rawSubpackets []outputSubpacket
@ -71,7 +72,7 @@ func (sig *Signature) parse(r io.Reader) (err error) {
sig.SigType = SignatureType(buf[0]) sig.SigType = SignatureType(buf[0])
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA:
default: default:
err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
return return
@ -135,6 +136,11 @@ func (sig *Signature) parse(r io.Reader) (err error) {
if err == nil { if err == nil {
sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) 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: default:
panic("unreachable") panic("unreachable")
} }
@ -338,13 +344,14 @@ func subpacketLengthLength(length int) int {
// serializeSubpacketLength marshals the given length into to. // serializeSubpacketLength marshals the given length into to.
func serializeSubpacketLength(to []byte, length int) int { func serializeSubpacketLength(to []byte, length int) int {
// RFC 4880, Section 4.2.2.
if length < 192 { if length < 192 {
to[0] = byte(length) to[0] = byte(length)
return 1 return 1
} }
if length < 16320 { if length < 16320 {
length -= 192 length -= 192
to[0] = byte(length >> 8) to[0] = byte((length >> 8) + 192)
to[1] = byte(length) to[1] = byte(length)
return 2 return 2
} }
@ -383,6 +390,16 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
return 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. // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
func (sig *Signature) buildHashSuffix() (err error) { func (sig *Signature) buildHashSuffix() (err error) {
hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) 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 // 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. // 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. // 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() sig.outSubpackets = sig.buildSubpackets()
digest, err := sig.signPrepareHash(h) digest, err := sig.signPrepareHash(h)
if err != nil { if err != nil {
@ -435,7 +453,7 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: 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)) sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes))
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) 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 { if len(digest) > subgroupSize {
digest = 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 { if err == nil {
sig.DSASigR.bytes = r.Bytes() sig.DSASigR.bytes = r.Bytes()
sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.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 // 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 // key for the identity id. On success, the signature is stored in sig. Call
// Serialize to write it out. // Serialize to write it out.
func (sig *Signature) SignUserId(rand io.Reader, id string, pub *PublicKey, priv *PrivateKey) error { // If config is nil, sensible defaults will be used.
h, err := userIdSignatureHash(id, pub, sig) func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error {
h, err := userIdSignatureHash(id, pub, sig.Hash)
if err != nil { if err != nil {
return 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 // 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. // 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 { // If config is nil, sensible defaults will be used.
h, err := keySignatureHash(&priv.PublicKey, pub, sig) func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error {
h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash)
if err != nil { if err != nil {
return err 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) { func (sig *Signature) Serialize(w io.Writer) (err error) {
if len(sig.outSubpackets) == 0 { if len(sig.outSubpackets) == 0 {
sig.outSubpackets = sig.rawSubpackets sig.outSubpackets = sig.rawSubpackets
} }
if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil {
return errors.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize") return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
} }
sigLength := 0 sigLength := 0
@ -496,6 +517,9 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
sigLength = 2 + len(sig.DSASigR.bytes) sigLength = 2 + len(sig.DSASigR.bytes)
sigLength += 2 + len(sig.DSASigS.bytes) sigLength += 2 + len(sig.DSASigS.bytes)
case PubKeyAlgoECDSA:
sigLength = 2 + len(sig.ECDSASigR.bytes)
sigLength += 2 + len(sig.ECDSASigS.bytes)
default: default:
panic("impossible") panic("impossible")
} }
@ -533,6 +557,8 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
err = writeMPIs(w, sig.RSASignature) err = writeMPIs(w, sig.RSASignature)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
err = writeMPIs(w, sig.DSASigR, sig.DSASigS) err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
case PubKeyAlgoECDSA:
err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS)
default: default:
panic("impossible") panic("impossible")
} }

View File

@ -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
}

View File

@ -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 <warda@nephilim.ruhr.de>
// 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-----`

View File

@ -107,7 +107,9 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) error {
// packet contains a random session key, encrypted by a key derived from the // 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 // given passphrase. The session key is returned and must be passed to
// SerializeSymmetricallyEncrypted. // 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() keySize := cipherFunc.KeySize()
if keySize == 0 { if keySize == 0 {
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) 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) keyEncryptingKey := make([]byte, keySize)
// s2k.Serialize salts and stretches the passphrase, and writes the // s2k.Serialize salts and stretches the passphrase, and writes the
// resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. // 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 { if err != nil {
return return
} }
@ -142,7 +144,7 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []by
} }
sessionKey := make([]byte, keySize) sessionKey := make([]byte, keySize)
_, err = io.ReadFull(rand, sessionKey) _, err = io.ReadFull(config.Random(), sessionKey)
if err != nil { if err != nil {
return return
} }

View File

@ -6,7 +6,6 @@ package packet
import ( import (
"bytes" "bytes"
"crypto/rand"
"encoding/hex" "encoding/hex"
"io" "io"
"io/ioutil" "io/ioutil"
@ -65,9 +64,11 @@ const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a"
func TestSerializeSymmetricKeyEncrypted(t *testing.T) { func TestSerializeSymmetricKeyEncrypted(t *testing.T) {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
passphrase := []byte("testing") 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 { if err != nil {
t.Errorf("failed to serialize: %s", err) t.Errorf("failed to serialize: %s", err)
return return
@ -87,8 +88,8 @@ func TestSerializeSymmetricKeyEncrypted(t *testing.T) {
if !ske.Encrypted { if !ske.Encrypted {
t.Errorf("SKE not encrypted but should be") t.Errorf("SKE not encrypted but should be")
} }
if ske.CipherFunc != cipherFunc { if ske.CipherFunc != config.DefaultCipher {
t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, cipherFunc) t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, config.DefaultCipher)
} }
err = ske.Decrypt(passphrase) err = ske.Decrypt(passphrase)
if err != nil { if err != nil {

View File

@ -252,7 +252,8 @@ func (c noOpCloser) Close() error {
// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
// to w and returns a WriteCloser to which the to-be-encrypted packets can be // to w and returns a WriteCloser to which the to-be-encrypted packets can be
// written. // 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) { if c.KeySize() != len(key) {
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") 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) block := c.new(key)
blockSize := block.BlockSize() blockSize := block.BlockSize()
iv := make([]byte, blockSize) iv := make([]byte, blockSize)
_, err = rand.Read(iv) _, err = config.Random().Read(iv)
if err != nil { if err != nil {
return return
} }

View File

@ -7,7 +7,6 @@ package packet
import ( import (
"bytes" "bytes"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
"crypto/rand"
"crypto/sha1" "crypto/sha1"
"encoding/hex" "encoding/hex"
"io" "io"
@ -83,7 +82,7 @@ func TestSerialize(t *testing.T) {
c := CipherAES128 c := CipherAES128
key := make([]byte, c.KeySize()) key := make([]byte, c.KeySize())
w, err := SerializeSymmetricallyEncrypted(buf, rand.Reader, c, key) w, err := SerializeSymmetricallyEncrypted(buf, c, key, nil)
if err != nil { if err != nil {
t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err)
return return

View File

@ -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
}

View File

@ -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`

View File

@ -80,7 +80,8 @@ type keyEnvelopePair struct {
// ReadMessage parses an OpenPGP message that may be signed and/or encrypted. // ReadMessage parses an OpenPGP message that may be signed and/or encrypted.
// The given KeyRing should contain both public keys (for signature // The given KeyRing should contain both public keys (for signature
// verification) and, possibly encrypted, private keys for decrypting. // 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 p packet.Packet
var symKeys []*packet.SymmetricKeyEncrypted var symKeys []*packet.SymmetricKeyEncrypted
@ -155,7 +156,7 @@ FindKey:
} }
if !pk.key.PrivateKey.Encrypted { if !pk.key.PrivateKey.Encrypted {
if len(pk.encryptedKey.Key) == 0 { 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 { if len(pk.encryptedKey.Key) == 0 {
continue continue
@ -361,21 +362,32 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
return return
} }
sig, ok := p.(*packet.Signature) var issuerKeyId uint64
if !ok { 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") return nil, errors.StructuralError("non signature packet found")
} }
if sig.IssuerKeyId == nil { keys := keyring.KeysById(issuerKeyId)
return nil, errors.StructuralError("signature doesn't have an issuer")
}
keys := keyring.KeysById(*sig.IssuerKeyId)
if len(keys) == 0 { if len(keys) == 0 {
return nil, errors.ErrUnknownIssuer return nil, errors.ErrUnknownIssuer
} }
h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) h, wrappedHash, err := hashForSignature(hashFunc, sigType)
if err != nil { if err != nil {
return return
} }
@ -389,7 +401,12 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign {
continue 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 { if err == nil {
return key.Entity, nil return key.Entity, nil
} }

View File

@ -104,7 +104,7 @@ func TestGetKeyById(t *testing.T) {
func checkSignedMessage(t *testing.T, signedHex, expected string) { func checkSignedMessage(t *testing.T, signedHex, expected string) {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex))
md, err := ReadMessage(readerFromHex(signedHex), kring, nil) md, err := ReadMessage(readerFromHex(signedHex), kring, nil, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -178,7 +178,7 @@ func TestSignedEncryptedMessage(t *testing.T) {
return nil, nil return nil, nil
} }
md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt, nil)
if err != nil { if err != nil {
t.Errorf("#%d: error reading message: %s", i, err) t.Errorf("#%d: error reading message: %s", i, err)
return return
@ -206,7 +206,7 @@ func TestUnspecifiedRecipient(t *testing.T) {
expected := "Recipient unspecified\n" expected := "Recipient unspecified\n"
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex))
md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil) md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil, nil)
if err != nil { if err != nil {
t.Errorf("error reading message: %s", err) t.Errorf("error reading message: %s", err)
return return
@ -236,7 +236,7 @@ func TestSymmetricallyEncrypted(t *testing.T) {
return []byte("password"), nil return []byte("password"), nil
} }
md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt) md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt, nil)
if err != nil { if err != nil {
t.Errorf("ReadMessage: %s", err) t.Errorf("ReadMessage: %s", err)
return return
@ -277,6 +277,7 @@ func TestDetachedSignature(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex))
testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId) testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId)
testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId) testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId)
testDetachedSignature(t, kring, readerFromHex(detachedSignatureV3TextHex), signedInput, "v3", testKey1KeyId)
} }
func TestDetachedSignatureDSA(t *testing.T) { func TestDetachedSignatureDSA(t *testing.T) {
@ -313,6 +314,8 @@ const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31
const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39" const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39"
const detachedSignatureV3TextHex = "8900950305005255c25ca34d7e18c20c31bb0102bb3f04009f6589ef8a028d6e54f6eaf25432e590d31c3a41f4710897585e10c31e5e332c7f9f409af8512adceaff24d0da1474ab07aa7bce4f674610b010fccc5b579ae5eb00a127f272fb799f988ab8e4574c141da6dbfecfef7e6b2c478d9a3d2551ba741f260ee22bec762812f0053e05380bfdd55ad0f22d8cdf71b233fe51ae8a24"
const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83" const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83"
const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003" const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003"

View File

@ -97,12 +97,12 @@ func Parse(r io.Reader) (f func(out, in []byte), err error) {
} }
switch buf[0] { switch buf[0] {
case 1: case 0:
f := func(out, in []byte) { f := func(out, in []byte) {
Simple(out, h, in) Simple(out, h, in)
} }
return f, nil return f, nil
case 2: case 1:
_, err = io.ReadFull(r, buf[:8]) _, err = io.ReadFull(r, buf[:8])
if err != nil { if err != nil {
return return
@ -151,14 +151,15 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error
var hashToHashIdMapping = []struct { var hashToHashIdMapping = []struct {
id byte id byte
hash crypto.Hash hash crypto.Hash
name string
}{ }{
{1, crypto.MD5}, {1, crypto.MD5, "MD5"},
{2, crypto.SHA1}, {2, crypto.SHA1, "SHA1"},
{3, crypto.RIPEMD160}, {3, crypto.RIPEMD160, "RIPEMD160"},
{8, crypto.SHA256}, {8, crypto.SHA256, "SHA256"},
{9, crypto.SHA384}, {9, crypto.SHA384, "SHA384"},
{10, crypto.SHA512}, {10, crypto.SHA512, "SHA512"},
{11, crypto.SHA224}, {11, crypto.SHA224, "SHA224"},
} }
// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP // 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 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. // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash.
func HashToHashId(h crypto.Hash) (id byte, ok bool) { func HashToHashId(h crypto.Hash) (id byte, ok bool) {
for _, m := range hashToHashIdMapping { for _, m := range hashToHashIdMapping {

View File

@ -66,9 +66,9 @@ var parseTests = []struct {
spec, in, out string spec, in, out string
}{ }{
/* Simple with SHA1 */ /* Simple with SHA1 */
{"0102", "hello", "aaf4c61d"}, {"0002", "hello", "aaf4c61d"},
/* Salted with SHA1 */ /* Salted with SHA1 */
{"02020102030405060708", "hello", "f4f7d67e"}, {"01020102030405060708", "hello", "f4f7d67e"},
/* Iterated with SHA1 */ /* Iterated with SHA1 */
{"03020102030405060708f1", "hello", "f2a57b7c"}, {"03020102030405060708f1", "hello", "f2a57b7c"},
} }

View File

@ -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/packet"
"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/s2k" "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/s2k"
"crypto" "crypto"
"crypto/rand"
_ "crypto/sha256"
"hash" "hash"
"io" "io"
"strconv" "strconv"
@ -20,62 +18,59 @@ import (
// DetachSign signs message with the private key from signer (which must // DetachSign signs message with the private key from signer (which must
// already have been decrypted) and writes the signature to w. // already have been decrypted) and writes the signature to w.
func DetachSign(w io.Writer, signer *Entity, message io.Reader) error { // If config is nil, sensible defaults will be used.
return detachSign(w, signer, message, time.Time{}, packet.SigTypeBinary) 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 // ArmoredDetachSign signs message with the private key from signer (which
// must already have been decrypted) and writes an armored signature to w. // must already have been decrypted) and writes an armored signature to w.
func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err error) { // If config is nil, sensible defaults will be used.
return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeBinary) func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) {
} return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config)
func ArmoredDetachSignAt(w io.Writer, signer *Entity, sigTime time.Time, message io.Reader) (err error) {
return armoredDetachSign(w, signer, message, sigTime, packet.SigTypeBinary)
} }
// DetachSignText signs message (after canonicalising the line endings) with // DetachSignText signs message (after canonicalising the line endings) with
// the private key from signer (which must already have been decrypted) and // the private key from signer (which must already have been decrypted) and
// writes the signature to w. // writes the signature to w.
func DetachSignText(w io.Writer, signer *Entity, message io.Reader) error { // If config is nil, sensible defaults will be used.
return detachSign(w, signer, message, time.Time{}, packet.SigTypeText) 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) // ArmoredDetachSignText signs message (after canonicalising the line endings)
// with the private key from signer (which must already have been decrypted) // with the private key from signer (which must already have been decrypted)
// and writes an armored signature to w. // and writes an armored signature to w.
func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) error { // If config is nil, sensible defaults will be used.
return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeText) 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) out, err := armor.Encode(w, SignatureType, nil)
if err != nil { if err != nil {
return return
} }
err = detachSign(out, signer, message, sigTime, sigType) err = detachSign(out, signer, message, sigType, config)
if err != nil { if err != nil {
return return
} }
return out.Close() 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 { if signer.PrivateKey == nil {
return errors.InvalidArgumentError("signing key doesn't have a private key") return errors.InvalidArgumentError("signing key doesn't have a private key")
} }
if signer.PrivateKey.Encrypted { if signer.PrivateKey.Encrypted {
return errors.InvalidArgumentError("signing key is encrypted") return errors.InvalidArgumentError("signing key is encrypted")
} }
if sigTime.IsZero() {
sigTime = time.Now()
}
sig := new(packet.Signature) sig := new(packet.Signature)
sig.SigType = sigType sig.SigType = sigType
sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
sig.Hash = crypto.SHA256 sig.Hash = config.Hash()
sig.CreationTime = sigTime sig.CreationTime = config.Now()
sig.IssuerKeyId = &signer.PrivateKey.KeyId sig.IssuerKeyId = &signer.PrivateKey.KeyId
h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) 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) io.Copy(wrappedHash, message)
err = sig.Sign(rand.Reader, h, signer.PrivateKey) err = sig.Sign(h, signer.PrivateKey, config)
if err != nil { if err != nil {
return return
} }
@ -109,24 +104,38 @@ type FileHints struct {
// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. // 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 // The resulting WriteCloser must be closed after the contents of the file have
// been written. // 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 { if hints == nil {
hints = &FileHints{} hints = &FileHints{}
} }
key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
if err != nil { if err != nil {
return return
} }
w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, packet.CipherAES128, key) w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
if err != nil { if err != nil {
return 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 var epochSeconds uint32
if !hints.ModTime.IsZero() { if !hints.ModTime.IsZero() {
epochSeconds = uint32(hints.ModTime.Unix()) 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 // 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 // it. hints contains optional information, that is also encrypted, that aids
// the recipients in processing the message. The resulting WriteCloser must // the recipients in processing the message. The resulting WriteCloser must
// be closed after the contents of the file have been written. // 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 var signer *packet.PrivateKey
if signed != nil { if signed != nil {
signer = signed.signingKey().PrivateKey signKey, ok := signed.signingKey(config.Now())
if signer == nil || signer.Encrypted { 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") 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)) encryptKeys := make([]Key, len(to))
for i := range to { for i := range to {
encryptKeys[i] = to[i].encryptionKey() var ok bool
if encryptKeys[i].PublicKey == nil { 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") 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]) 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()) 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 return nil, err
} }
for _, key := range encryptKeys { 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 return nil, err
} }
} }
encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, cipher, symKey) encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
if err != nil { if err != nil {
return return
} }
@ -264,7 +315,7 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
} }
if signer != nil { 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 return literalData, nil
} }
@ -278,6 +329,7 @@ type signatureWriter struct {
hashType crypto.Hash hashType crypto.Hash
h hash.Hash h hash.Hash
signer *packet.PrivateKey signer *packet.PrivateKey
config *packet.Config
} }
func (s signatureWriter) Write(data []byte) (int, error) { func (s signatureWriter) Write(data []byte) (int, error) {
@ -290,11 +342,11 @@ func (s signatureWriter) Close() error {
SigType: packet.SigTypeBinary, SigType: packet.SigTypeBinary,
PubKeyAlgo: s.signer.PubKeyAlgo, PubKeyAlgo: s.signer.PubKeyAlgo,
Hash: s.hashType, Hash: s.hashType,
CreationTime: time.Now(), CreationTime: s.config.Now(),
IssuerKeyId: &s.signer.KeyId, 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 return err
} }
if err := s.literalData.Close(); err != nil { if err := s.literalData.Close(); err != nil {

View File

@ -6,7 +6,6 @@ package openpgp
import ( import (
"bytes" "bytes"
"crypto/rand"
"io" "io"
"io/ioutil" "io/ioutil"
"testing" "testing"
@ -17,7 +16,7 @@ func TestSignDetached(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex))
out := bytes.NewBuffer(nil) out := bytes.NewBuffer(nil)
message := bytes.NewBufferString(signedInput) message := bytes.NewBufferString(signedInput)
err := DetachSign(out, kring[0], message) err := DetachSign(out, kring[0], message, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -29,7 +28,7 @@ func TestSignTextDetached(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex))
out := bytes.NewBuffer(nil) out := bytes.NewBuffer(nil)
message := bytes.NewBufferString(signedInput) message := bytes.NewBufferString(signedInput)
err := DetachSignText(out, kring[0], message) err := DetachSignText(out, kring[0], message, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -41,7 +40,7 @@ func TestSignDetachedDSA(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex)) kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex))
out := bytes.NewBuffer(nil) out := bytes.NewBuffer(nil)
message := bytes.NewBufferString(signedInput) message := bytes.NewBufferString(signedInput)
err := DetachSign(out, kring[0], message) err := DetachSign(out, kring[0], message, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -54,14 +53,14 @@ func TestNewEntity(t *testing.T) {
return 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 { if err != nil {
t.Errorf("failed to create entity: %s", err) t.Errorf("failed to create entity: %s", err)
return return
} }
w := bytes.NewBuffer(nil) 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) t.Errorf("failed to serialize entity: %s", err)
return return
} }
@ -78,7 +77,7 @@ func TestNewEntity(t *testing.T) {
} }
w = bytes.NewBuffer(nil) 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) t.Errorf("failed to serialize entity second time: %s", err)
return return
} }
@ -90,7 +89,7 @@ func TestNewEntity(t *testing.T) {
func TestSymmetricEncryption(t *testing.T) { func TestSymmetricEncryption(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil) plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil, nil)
if err != nil { if err != nil {
t.Errorf("error writing headers: %s", err) t.Errorf("error writing headers: %s", err)
return return
@ -107,7 +106,7 @@ func TestSymmetricEncryption(t *testing.T) {
md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, error) { md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, error) {
return []byte("testing"), nil return []byte("testing"), nil
}) }, nil)
if err != nil { if err != nil {
t.Errorf("error rereading message: %s", err) t.Errorf("error rereading message: %s", err)
} }
@ -171,7 +170,7 @@ func TestEncryption(t *testing.T) {
} }
buf := new(bytes.Buffer) 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 { if err != nil {
t.Errorf("#%d: error in Encrypt: %s", i, err) t.Errorf("#%d: error in Encrypt: %s", i, err)
continue continue
@ -189,14 +188,16 @@ func TestEncryption(t *testing.T) {
continue continue
} }
md, err := ReadMessage(buf, kring, nil /* no prompt */ ) md, err := ReadMessage(buf, kring, nil /* no prompt */, nil)
if err != nil { if err != nil {
t.Errorf("#%d: error reading message: %s", i, err) t.Errorf("#%d: error reading message: %s", i, err)
continue continue
} }
testTime, _ := time.Parse("2006-01-02", "2013-07-01")
if test.isSigned { if test.isSigned {
expectedKeyId := kring[0].signingKey().PublicKey.KeyId signKey, _ := kring[0].signingKey(testTime)
expectedKeyId := signKey.PublicKey.KeyId
if md.SignedByKeyId != expectedKeyId { if md.SignedByKeyId != expectedKeyId {
t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, 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 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 { 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) t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds)
} }