func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { signerSubkey, ok := signer.signingKey(config.Now()) if !ok { err = errors.InvalidArgumentError("no valid signing keys") return } if signerSubkey.PrivateKey == nil { return errors.InvalidArgumentError("signing key doesn't have a private key") } if signerSubkey.PrivateKey.Encrypted { return errors.InvalidArgumentError("signing key is encrypted") } sig := new(packet.Signature) sig.SigType = sigType sig.PubKeyAlgo = signerSubkey.PrivateKey.PubKeyAlgo sig.Hash = config.Hash() sig.CreationTime = config.Now() sig.IssuerKeyId = &signerSubkey.PrivateKey.KeyId h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) if err != nil { return } io.Copy(wrappedHash, message) err = sig.Sign(h, signerSubkey.PrivateKey, config) if err != nil { return } return sig.Serialize(w) }
// SignWithSigner signs the message of type sigType with s and writes the // signature to w. // If config is nil, sensible defaults will be used. func SignWithSigner(s packet.Signer, w io.Writer, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { keyId := s.KeyId() sig := new(packet.Signature) sig.SigType = sigType sig.PubKeyAlgo = s.PublicKeyAlgo() sig.Hash = config.Hash() sig.CreationTime = config.Now() sig.IssuerKeyId = &keyId s.Reset() wrapped := s.(hash.Hash) if sigType == packet.SigTypeText { wrapped = NewCanonicalTextHash(s) } io.Copy(wrapped, message) err = sig.Sign(s, nil, config) if err != nil { return } err = sig.Serialize(w) return }
// SignIdentity adds a signature to e, from signer, attesting that identity is // associated with e. The provided identity must already be an element of // e.Identities and the private key of signer must have been decrypted if // necessary. // If config is nil, sensible defaults will be used. func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing Entity must have a private key") } if signer.PrivateKey.Encrypted { return errors.InvalidArgumentError("signing Entity's private key must be decrypted") } ident, ok := e.Identities[identity] if !ok { return errors.InvalidArgumentError("given identity string not found in Entity") } sig := &packet.Signature{ SigType: packet.SigTypeGenericCert, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, Hash: config.Hash(), CreationTime: config.Now(), IssuerKeyId: &signer.PrivateKey.KeyId, } if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil { return err } ident.Signatures = append(ident.Signatures, sig) return nil }
// AttachedSign is like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but // don't encrypt at all, just sign the literal unencrypted data. // Unfortunately we need to duplicate some code here that's already // in write.go func AttachedSign(out io.WriteCloser, signed Entity, hints *FileHints, config *packet.Config) (in io.WriteCloser, err error) { if hints == nil { hints = &FileHints{} } if config == nil { config = &packet.Config{} } var signer *packet.PrivateKey signKey, ok := signed.signingKey(config.Now()) if !ok { err = errors.InvalidArgumentError("no valid signing keys") return } signer = signKey.PrivateKey if signer == nil { err = errors.InvalidArgumentError("no valid signing keys") return } if signer.Encrypted { err = errors.InvalidArgumentError("signing key must be decrypted") return } hasher := crypto.SHA512 ops := &packet.OnePassSignature{ SigType: packet.SigTypeBinary, Hash: hasher, PubKeyAlgo: signer.PubKeyAlgo, KeyId: signer.KeyId, IsLast: true, } if err = ops.Serialize(out); err != nil { return } var epochSeconds uint32 if !hints.ModTime.IsZero() { epochSeconds = uint32(hints.ModTime.Unix()) } // We don't want the literal serializer to closer the output stream // since we're going to need to write to it when we finish up the // signature stuff. in, err = packet.SerializeLiteral(noOpCloser{out}, hints.IsBinary, hints.FileName, epochSeconds) if err != nil { return } // If we need to write a signature packet after the literal // data then we need to stop literalData from closing // encryptedData. in = signatureWriter{out, in, hasher, hasher.New(), signer, config} return }
// Encrypt encrypts a message to a number of recipients and, optionally, signs // it. hints contains optional information, that is also encrypted, that aids // the recipients in processing the message. The resulting WriteCloser must // be closed after the contents of the file have been written. // If config is nil, sensible defaults will be used. func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { var signer *packet.PrivateKey if signed != nil { signKey, ok := signed.signingKey(config.Now()) if !ok { return nil, errors.InvalidArgumentError("no valid signing keys") } signer = signKey.PrivateKey if signer == nil { return nil, errors.InvalidArgumentError("no private key in signing key") } if signer.Encrypted { return nil, errors.InvalidArgumentError("signing key must be decrypted") } } // These are the possible ciphers that we'll use for the message. candidateCiphers := []uint8{ uint8(packet.CipherAES128), uint8(packet.CipherAES256), uint8(packet.CipherCAST5), } // These are the possible hash functions that we'll use for the signature. candidateHashes := []uint8{ hashToHashId(crypto.SHA256), hashToHashId(crypto.SHA512), hashToHashId(crypto.SHA1), hashToHashId(crypto.RIPEMD160), } // If no preferences were specified, assume something safe and reasonable. defaultCiphers := []uint8{ uint8(packet.CipherAES128), uint8(packet.CipherAES192), uint8(packet.CipherAES256), uint8(packet.CipherCAST5), } defaultHashes := []uint8{ hashToHashId(crypto.SHA256), hashToHashId(crypto.SHA512), hashToHashId(crypto.RIPEMD160), } encryptKeys := make([]Key, len(to)) for i := range to { var ok bool encryptKeys[i], ok = to[i].encryptionKey(config.Now()) if !ok { return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") } sig := to[i].primaryIdentity().SelfSignature preferredSymmetric := sig.PreferredSymmetric if len(preferredSymmetric) == 0 { preferredSymmetric = defaultCiphers } preferredHashes := sig.PreferredHash if len(preferredHashes) == 0 { preferredHashes = defaultHashes } candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) candidateHashes = intersectPreferences(candidateHashes, preferredHashes) } if len(candidateCiphers) == 0 { return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common ciphers") } if len(candidateHashes) == 0 { return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common hashes") } cipher := packet.CipherFunction(candidateCiphers[0]) // If the cipher specifed by config is a candidate, we'll use that. configuredCipher := config.Cipher() for _, c := range candidateCiphers { cipherFunc := packet.CipherFunction(c) if cipherFunc == configuredCipher { cipher = cipherFunc break } } var hash crypto.Hash for _, hashId := range candidateHashes { if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { hash = h break } } // If the hash specified by config is a candidate, we'll use that. if configuredHash := config.Hash(); configuredHash.Available() { for _, hashId := range candidateHashes { if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { hash = h break } } } if hash == 0 { hashId := candidateHashes[0] name, ok := s2k.HashIdToString(hashId) if !ok { name = "#" + strconv.Itoa(int(hashId)) } return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") } symKey := make([]byte, cipher.KeySize()) if _, err := io.ReadFull(config.Random(), symKey); err != nil { return nil, err } for _, key := range encryptKeys { if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { return nil, err } } encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) if err != nil { return } if signer != nil { ops := &packet.OnePassSignature{ SigType: packet.SigTypeBinary, Hash: hash, PubKeyAlgo: signer.PubKeyAlgo, KeyId: signer.KeyId, IsLast: true, } if err := ops.Serialize(encryptedData); err != nil { return nil, err } } if hints == nil { hints = &FileHints{} } w := encryptedData if signer != nil { // If we need to write a signature packet after the literal // data then we need to stop literalData from closing // encryptedData. w = noOpCloser{encryptedData} } var epochSeconds uint32 if !hints.ModTime.IsZero() { epochSeconds = uint32(hints.ModTime.Unix()) } literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) if err != nil { return nil, err } if signer != nil { return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil } return literalData, nil }
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a // single identity composed of the given full name, comment and email, any of // which may be empty but must not contain any of "()<>\x00". // If config is nil, sensible defaults will be used. func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { currentTime := config.Now() bits := defaultRSAKeyBits if config != nil && config.RSABits != 0 { bits = config.RSABits } uid := packet.NewUserId(name, comment, email) if uid == nil { return nil, errors.InvalidArgumentError("user id field contained invalid characters") } signingPriv, err := rsa.GenerateKey(config.Random(), bits) if err != nil { return nil, err } encryptingPriv, err := rsa.GenerateKey(config.Random(), bits) if err != nil { return nil, err } e := &Entity{ PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey), PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv), Identities: make(map[string]*Identity), } isPrimaryId := true e.Identities[uid.Id] = &Identity{ Name: uid.Name, UserId: uid, SelfSignature: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), IsPrimaryId: &isPrimaryId, FlagsValid: true, FlagSign: true, FlagCertify: true, IssuerKeyId: &e.PrimaryKey.KeyId, }, } e.Subkeys = make([]Subkey, 1) e.Subkeys[0] = Subkey{ PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey), PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv), Sig: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), FlagsValid: true, FlagEncryptStorage: true, FlagEncryptCommunications: true, IssuerKeyId: &e.PrimaryKey.KeyId, }, } e.Subkeys[0].PublicKey.IsSubkey = true e.Subkeys[0].PrivateKey.IsSubkey = true return e, nil }