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 }
// hashForSignature returns a pair of hashes that can be used to verify a // signature. The signature may specify that the contents of the signed message // should be preprocessed (i.e. to normalize line endings). Thus this function // returns two hashes. The second should be used to hash the message itself and // performs any needed preprocessing. func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { if !hashId.Available() { return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) } h := hashId.New() switch sigType { case packet.SigTypeBinary: return h, h, nil case packet.SigTypeText: return h, NewCanonicalTextHash(h), nil } return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) }
// 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 } // RFC 4880 Section 12.2 requires the low 8 bytes of the // modulus to form the key id. if len(pk.n.bytes) < 8 { return errors.StructuralError("v3 public key modulus is too short") } 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 }
// parseRSA parses RSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKey) parseRSA(r io.Reader) (err error) { pk.n.bytes, pk.n.bitLength, err = readMPI(r) if err != nil { return } pk.e.bytes, pk.e.bitLength, err = readMPI(r) if 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), E: 0, } for i := 0; i < len(pk.e.bytes); i++ { rsa.E <<= 8 rsa.E |= int(pk.e.bytes[i]) } pk.PublicKey = rsa return }
// 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 }
// Sign signs a message with a private key. The hash, h, must contain // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. // If config is nil, sensible defaults will be used. func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { sig.outSubpackets = sig.buildSubpackets() digest, err := sig.signPrepareHash(h) if err != nil { return } switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) case PubKeyAlgoDSA: dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) // Need to truncate hashBytes to match FIPS 186-3 section 4.6. subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 if len(digest) > subgroupSize { digest = digest[:subgroupSize] } r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) if err == nil { sig.DSASigR.bytes = r.Bytes() sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) sig.DSASigS.bytes = s.Bytes() sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) } default: err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) } return }
// Decrypt attempts to decrypt an encrypted session key. If it returns nil, // ske.Key will contain the session key. func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) error { if !ske.Encrypted { return nil } key := make([]byte, ske.CipherFunc.KeySize()) ske.s2k(key, passphrase) if len(ske.encryptedKey) == 0 { ske.Key = key } else { // the IV is all zeros iv := make([]byte, ske.CipherFunc.blockSize()) c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) c.XORKeyStream(ske.encryptedKey, ske.encryptedKey) ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) if ske.CipherFunc.blockSize() == 0 { return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(ske.CipherFunc))) } ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) ske.Key = ske.encryptedKey[1:] if len(ske.Key)%ske.CipherFunc.blockSize() != 0 { ske.Key = nil return errors.StructuralError("length of decrypted key not a multiple of block size") } } ske.Encrypted = false return nil }
func (pk *PublicKey) parse(r io.Reader) (err error) { // RFC 4880, section 5.5.2 var buf [6]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != 4 { 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.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: err = pk.parseRSA(r) case PubKeyAlgoDSA: err = pk.parseDSA(r) case PubKeyAlgoElGamal: err = pk.parseElGamal(r) case PubKeyAlgoECDSA: pk.ec = new(ecdsaKey) if err = pk.ec.parse(r); err != nil { return err } pk.PublicKey, err = pk.ec.newECDSA() case PubKeyAlgoECDH: pk.ec = new(ecdsaKey) if err = pk.ec.parse(r); err != nil { return } pk.ecdh = new(ecdhKdf) if err = pk.ecdh.parse(r); err != nil { return } // The ECDH key is stored in an ecdsa.PublicKey for convenience. pk.PublicKey, err = pk.ec.newECDSA() default: err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } if err != nil { return } pk.setFingerPrintAndKeyId() return }
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 }
// Parse reads a binary specification for a string-to-key transformation from r // and returns a function which performs that transform. func Parse(r io.Reader) (f func(out, in []byte), err error) { var buf [9]byte _, err = io.ReadFull(r, buf[:2]) if err != nil { return } hash, ok := HashIdToHash(buf[1]) if !ok { return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) } if !hash.Available() { return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) } h := hash.New() switch buf[0] { case 0: f := func(out, in []byte) { Simple(out, h, in) } return f, nil case 1: _, err = io.ReadFull(r, buf[:8]) if err != nil { return } f := func(out, in []byte) { Salted(out, h, in, buf[:8]) } return f, nil case 3: _, err = io.ReadFull(r, buf[:9]) if err != nil { return } count := decodeCount(buf[8]) f := func(out, in []byte) { Iterated(out, h, in, buf[:8], count) } return f, nil } return nil, errors.UnsupportedError("S2K function") }
// readSignedMessage reads a possibly signed message if mdin is non-zero then // that structure is updated and returned. Otherwise a fresh MessageDetails is // used. func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err error) { if mdin == nil { mdin = new(MessageDetails) } md = mdin var p packet.Packet var h hash.Hash var wrappedHash hash.Hash FindLiteralData: for { p, err = packets.Next() if err != nil { return nil, err } switch p := p.(type) { case *packet.Compressed: if err := packets.Push(p.Body); err != nil { return nil, err } case *packet.OnePassSignature: if !p.IsLast { return nil, errors.UnsupportedError("nested signatures") } h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) if err != nil { md = nil return } md.IsSigned = true md.SignedByKeyId = p.KeyId keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign) if len(keys) > 0 { md.SignedBy = &keys[0] } case *packet.LiteralData: md.LiteralData = p break FindLiteralData } } if md.SignedBy != nil { md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} } else if md.decrypted != nil { md.UnverifiedBody = checkReader{md} } else { md.UnverifiedBody = md.LiteralData.Body } return md, nil }
// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. 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 // SerializeSymmetricallyEncrypted. // If config is nil, sensible defaults will be used. func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { cipherFunc := config.Cipher() keySize := cipherFunc.KeySize() if keySize == 0 { return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) } s2kBuf := new(bytes.Buffer) keyEncryptingKey := make([]byte, keySize) // s2k.Serialize salts and stretches the passphrase, and writes the // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()}) if err != nil { return } s2kBytes := s2kBuf.Bytes() packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) if err != nil { return } var buf [2]byte buf[0] = symmetricKeyEncryptedVersion buf[1] = byte(cipherFunc) _, err = w.Write(buf[:]) if err != nil { return } _, err = w.Write(s2kBytes) if err != nil { return } sessionKey := make([]byte, keySize) _, err = io.ReadFull(config.Random(), sessionKey) if err != nil { return } iv := make([]byte, cipherFunc.blockSize()) c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) encryptedCipherAndKey := make([]byte, keySize+1) c.XORKeyStream(encryptedCipherAndKey, buf[1:]) c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) _, err = w.Write(encryptedCipherAndKey) if err != nil { return } key = sessionKey return }
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 keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { if !hashFunc.Available() { return nil, errors.UnsupportedError("hash function") } h = hashFunc.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) pk.serializeWithoutHeaders(h) return }
func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err error) { // RFC 4880, section 5.3. var buf [2]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != symmetricKeyEncryptedVersion { return errors.UnsupportedError("SymmetricKeyEncrypted version") } ske.CipherFunc = CipherFunction(buf[1]) if ske.CipherFunc.KeySize() == 0 { return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) } ske.s2k, err = s2k.Parse(r) if err != nil { return } encryptedKey := make([]byte, maxSessionKeySizeInBytes) // The session key may follow. We just have to try and read to find // out. If it exists then we limit it to maxSessionKeySizeInBytes. n, err := readFull(r, encryptedKey) if err != nil && err != io.ErrUnexpectedEOF { return } err = nil if n != 0 { if n == maxSessionKeySizeInBytes { return errors.UnsupportedError("oversized encrypted session key") } ske.encryptedKey = encryptedKey[:n] } ske.Encrypted = true return }
// 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 (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 }
// 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 !hfn.Available() { return nil, errors.UnsupportedError("hash function") } h = hfn.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) pk.serializeWithoutHeaders(h) h.Write([]byte(id)) return }
func (se *SymmetricallyEncrypted) parse(r io.Reader) error { if se.MDC { // See RFC 4880, section 5.13. var buf [1]byte _, err := readFull(r, buf[:]) if err != nil { return err } if buf[0] != symmetricallyEncryptedVersion { return errors.UnsupportedError("unknown SymmetricallyEncrypted version") } } se.contents = r return nil }
// 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 }
// Decrypt returns a ReadCloser, from which the decrypted contents of the // packet can be read. An incorrect key can, with high probability, be detected // immediately and this will result in a KeyIncorrect error being returned. func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { keySize := c.KeySize() if keySize == 0 { return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) } if len(key) != keySize { return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") } if se.prefix == nil { se.prefix = make([]byte, c.blockSize()+2) _, err := readFull(se.contents, se.prefix) if err != nil { return nil, err } } else if len(se.prefix) != c.blockSize()+2 { return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths") } ocfbResync := OCFBResync if se.MDC { // MDC packets use a different form of OCFB mode. ocfbResync = OCFBNoResync } s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) if s == nil { return nil, errors.ErrKeyIncorrect } plaintext := cipher.StreamReader{S: s, R: se.contents} if se.MDC { // MDC packets have an embedded hash that we need to check. h := sha1.New() h.Write(se.prefix) return &seMDCReader{in: plaintext, h: h}, nil } // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. return seReader{plaintext}, nil }
func (c *Compressed) parse(r io.Reader) error { var buf [1]byte _, err := readFull(r, buf[:]) if err != nil { return err } switch buf[0] { case 1: c.Body = flate.NewReader(r) case 2: c.Body, err = zlib.NewReader(r) case 3: c.Body = bzip2.NewReader(r) default: err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) } return err }
// Serialize marshals the given OnePassSignature to w. func (ops *OnePassSignature) Serialize(w io.Writer) error { var buf [13]byte buf[0] = onePassSignatureVersion buf[1] = uint8(ops.SigType) var ok bool buf[2], ok = s2k.HashToHashId(ops.Hash) if !ok { return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) } buf[3] = uint8(ops.PubKeyAlgo) binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) if ops.IsLast { buf[12] = 1 } if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { return err } _, err := w.Write(buf[:]) return err }
// userIdSignatureHash returns a Hash of the message that needs to be signed // to assert that pk is a valid key for id. func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { if !hashFunc.Available() { return nil, errors.UnsupportedError("hash function") } h = hashFunc.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) pk.serializeWithoutHeaders(h) var buf [5]byte buf[0] = 0xb4 buf[1] = byte(len(id) >> 24) buf[2] = byte(len(id) >> 16) buf[3] = byte(len(id) >> 8) buf[4] = byte(len(id)) h.Write(buf[:]) h.Write([]byte(id)) return }
func (e *EncryptedKey) parse(r io.Reader) (err error) { var buf [10]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != encryptedKeyVersion { return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) } e.KeyId = binary.BigEndian.Uint64(buf[1:9]) e.Algo = PublicKeyAlgorithm(buf[9]) switch e.Algo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) case PubKeyAlgoElGamal: e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) if err != nil { return } e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r) } _, err = consumeAll(r) return }
// SerializeEncryptedKey serializes an encrypted key packet to w that contains // key, encrypted to pub. // If config is nil, sensible defaults will be used. func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { var buf [10]byte buf[0] = encryptedKeyVersion binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) buf[9] = byte(pub.PubKeyAlgo) keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) keyBlock[0] = byte(cipherFunc) copy(keyBlock[1:], key) checksum := checksumKeyMaterial(key) keyBlock[1+len(key)] = byte(checksum >> 8) keyBlock[1+len(key)+1] = byte(checksum) switch pub.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) case PubKeyAlgoElGamal: return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) }
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 }
// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { // RFC 4880, section 5.2.3.1 var ( length uint32 packetType signatureSubpacketType isCritical bool ) switch { case subpacket[0] < 192: length = uint32(subpacket[0]) subpacket = subpacket[1:] case subpacket[0] < 255: if len(subpacket) < 2 { goto Truncated } length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 subpacket = subpacket[2:] default: if len(subpacket) < 5 { goto Truncated } length = uint32(subpacket[1])<<24 | uint32(subpacket[2])<<16 | uint32(subpacket[3])<<8 | uint32(subpacket[4]) subpacket = subpacket[5:] } if length > uint32(len(subpacket)) { goto Truncated } rest = subpacket[length:] subpacket = subpacket[:length] if len(subpacket) == 0 { err = errors.StructuralError("zero length signature subpacket") return } packetType = signatureSubpacketType(subpacket[0] & 0x7f) isCritical = subpacket[0]&0x80 == 0x80 subpacket = subpacket[1:] sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) switch packetType { case creationTimeSubpacket: if !isHashed { err = errors.StructuralError("signature creation time in non-hashed area") return } if len(subpacket) != 4 { err = errors.StructuralError("signature creation time not four bytes") return } t := binary.BigEndian.Uint32(subpacket) sig.CreationTime = time.Unix(int64(t), 0) case signatureExpirationSubpacket: // Signature expiration time, section 5.2.3.10 if !isHashed { return } if len(subpacket) != 4 { err = errors.StructuralError("expiration subpacket with bad length") return } sig.SigLifetimeSecs = new(uint32) *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) case keyExpirationSubpacket: // Key expiration time, section 5.2.3.6 if !isHashed { return } if len(subpacket) != 4 { err = errors.StructuralError("key expiration subpacket with bad length") return } sig.KeyLifetimeSecs = new(uint32) *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) case prefSymmetricAlgosSubpacket: // Preferred symmetric algorithms, section 5.2.3.7 if !isHashed { return } sig.PreferredSymmetric = make([]byte, len(subpacket)) copy(sig.PreferredSymmetric, subpacket) case issuerSubpacket: // Issuer, section 5.2.3.5 if len(subpacket) != 8 { err = errors.StructuralError("issuer subpacket with bad length") return } sig.IssuerKeyId = new(uint64) *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) case prefHashAlgosSubpacket: // Preferred hash algorithms, section 5.2.3.8 if !isHashed { return } sig.PreferredHash = make([]byte, len(subpacket)) copy(sig.PreferredHash, subpacket) case prefCompressionSubpacket: // Preferred compression algorithms, section 5.2.3.9 if !isHashed { return } sig.PreferredCompression = make([]byte, len(subpacket)) copy(sig.PreferredCompression, subpacket) case primaryUserIdSubpacket: // Primary User ID, section 5.2.3.19 if !isHashed { return } if len(subpacket) != 1 { err = errors.StructuralError("primary user id subpacket with bad length") return } sig.IsPrimaryId = new(bool) if subpacket[0] > 0 { *sig.IsPrimaryId = true } case keyFlagsSubpacket: // Key flags, section 5.2.3.21 if !isHashed { return } if len(subpacket) == 0 { err = errors.StructuralError("empty key flags subpacket") return } sig.FlagsValid = true if subpacket[0]&KeyFlagCertify != 0 { sig.FlagCertify = true } if subpacket[0]&KeyFlagSign != 0 { sig.FlagSign = true } if subpacket[0]&KeyFlagEncryptCommunications != 0 { sig.FlagEncryptCommunications = true } if subpacket[0]&KeyFlagEncryptStorage != 0 { sig.FlagEncryptStorage = true } case reasonForRevocationSubpacket: // Reason For Revocation, section 5.2.3.23 if !isHashed { return } if len(subpacket) == 0 { err = errors.StructuralError("empty revocation reason subpacket") return } sig.RevocationReason = new(uint8) *sig.RevocationReason = subpacket[0] sig.RevocationReasonText = string(subpacket[1:]) case featuresSubpacket: // Features subpacket, section 5.2.3.24 specifies a very general // mechanism for OpenPGP implementations to signal support for new // features. In practice, the subpacket is used exclusively to // indicate support for MDC-protected encryption. sig.MDC = len(subpacket) >= 1 && subpacket[0]&1 == 1 case embeddedSignatureSubpacket: // Only usage is in signatures that cross-certify // signing subkeys. section 5.2.3.26 describes the // format, with its usage described in section 11.1 if sig.EmbeddedSignature != nil { err = errors.StructuralError("Cannot have multiple embedded signatures") return } sig.EmbeddedSignature = new(Signature) // Embedded signatures are required to be v4 signatures see // section 12.1. However, we only parse v4 signatures in this // file anyway. if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { return nil, err } if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) } default: if isCritical { err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) return } } return Truncated: err = errors.StructuralError("signature subpacket truncated") 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 }