func (ops *OnePassSignature) parse(r io.Reader) (err error) { var buf [13]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != onePassSignatureVersion { err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) } var ok bool ops.Hash, ok = s2k.HashIdToHash(buf[2]) if !ok { return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) } ops.SigType = SignatureType(buf[1]) ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) ops.IsLast = buf[12] != 0 return }
func (sig *Signature) parse(r io.Reader) (err error) { // RFC 4880, section 5.2.3 var buf [5]byte _, err = readFull(r, buf[:1]) if err != nil { return } if buf[0] != 4 { err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) return } _, err = readFull(r, buf[:5]) if err != nil { return } sig.SigType = SignatureType(buf[0]) sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: default: err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) return } var ok bool sig.Hash, ok = s2k.HashIdToHash(buf[2]) if !ok { return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) } hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) l := 6 + hashedSubpacketsLength sig.HashSuffix = make([]byte, l+6) sig.HashSuffix[0] = 4 copy(sig.HashSuffix[1:], buf[:5]) hashedSubpackets := sig.HashSuffix[6:l] _, err = readFull(r, hashedSubpackets) if err != nil { return } // See RFC 4880, section 5.2.4 trailer := sig.HashSuffix[l:] trailer[0] = 4 trailer[1] = 0xff trailer[2] = uint8(l>>24) trailer[3] = uint8(l>>16) trailer[4] = uint8(l>>8) trailer[5] = uint8(l) err = parseSignatureSubpackets(sig, hashedSubpackets, true) if err != nil { return } _, err = readFull(r, buf[:2]) if err != nil { return } unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) unhashedSubpackets := make([]byte, unhashedSubpacketsLength) _, err = readFull(r, unhashedSubpackets) if err != nil { return } err = parseSignatureSubpackets(sig, unhashedSubpackets, false) if err != nil { return } _, err = readFull(r, sig.HashTag[:2]) if err != nil { return } switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) case PubKeyAlgoDSA: sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) if err == nil { sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) } case PubKeyAlgoECDSA: sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) if err == nil { sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) } default: panic("unreachable") } return }
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 }
// 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.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), } // In the event that a recipient doesn't specify any supported ciphers // or hash functions, these are the ones that we assume that every // implementation supports. defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] defaultHashes := candidateHashes[len(candidateHashes)-1:] 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 || len(candidateHashes)==0 { return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") } 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 }