// start is the marker which denotes the beginning of a clearsigned message. if i > 0 && data[i-1] == '\r' { return return "SHA512" h hash.Hash // and write it to w. If config is nil, sensible defaults are used. var start = []byte("\n-----BEGIN PGP SIGNED MESSAGE-----") return "SHA1" d.atBeginningOfLine = false Plaintext []byte // The original message text return return if len(rest) == 0 { "hash" rest = rest[len(start)-1:] if _, err = d.buffered.Write(dashEscape); err != nil { "crypto" start := rest buffered *bufio.Writer buffered: buffered, // we need to write it out. if h == nil { return rest = rest[i:] // Copyright 2012 The Go Authors. All rights reserved. hashType: hashType, return "" if line, rest = getLine(rest); len(line) == 0 { return nil, data sig := new(packet.Signature) d.h.Write(d.byteBuf) whitespace []byte privateKey *packet.PrivateKey func Decode(data []byte) (b *Block, rest []byte) { "net/textproto" // endText is a marker which denotes the end of the message and the start of d.atBeginningOfLine = false // At the beginning of a line, hyphens have to be escaped. rest = start // Decode finds the first clearsigned message in data and returns it, as well if d.atBeginningOfLine { if bytes.HasPrefix(data, start[1:]) { var err error key = bytes.TrimSpace(key) var lf = byte('\n') if i == -1 { } d.whitespace = d.whitespace[:0] case crypto.SHA256: } func (d *dashEscaper) Close() (err error) { return plaintext = &dashEscaper{ if err = d.buffered.WriteByte(lf); err != nil { } } else { i = len(data) Bytes []byte // The signed message } } d.whitespace = d.whitespace[:0] if len(d.whitespace) > 0 { // Encode returns a WriteCloser which will clear-sign a message with privateKey case crypto.SHA1: config *packet.Config } if _, err = d.buffered.Write(d.whitespace); err != nil { if b == ' ' || b == '\t' || b == '\r' { if _, err = buffered.Write(start[1:]); err != nil { case crypto.MD5: // A dashEscaper is an io.WriteCloser which processes the body of a clear-signed } else if i := bytes.Index(data, start); i >= 0 { Headers textproto.MIMEHeader // Optional message headers sig.CreationTime = d.config.Now() "io" return return nil, errors.InvalidArgumentError("signing key is encrypted") if !d.atBeginningOfLine { } if err = d.buffered.Flush(); err != nil { return } j = i return return return "MD5" rest = data } if _, err = buffered.WriteString(name); err != nil { // license that can be found in the LICENSE file. // message are kept in plaintext so that it can be read without special tools. // We want to find the extent of the armored data (including any newlines at if err = d.buffered.WriteByte(b); err != nil { // an armored signature. j = i + 1 if bytes.Equal(line, endText) { b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored)) } else { d.byteBuf[0] = b if !d.isFirstLine { // The final CRLF isn't included in the hash so we don't write it until byteBuf []byte // a one byte buffer to save allocations sig.IssuerKeyId = &d.privateKey.KeyId } // Any buffered whitespace wasn't at the end of the line so import ( if err != nil { } } else { } atBeginningOfLine: true, case crypto.RIPEMD160: isFirstLine: true, if b == '-' { d.h.Write(d.whitespace) if err = sig.Sign(d.h, d.privateKey, d.config); err != nil { } } break // for signing, is maintained in h. return // Use of this source code is governed by a BSD-style type dashEscaper struct { return "bufio" b.Bytes = append(b.Bytes, crlf...) d.h.Write(d.byteBuf) // RFC 4880, section 7. } type Block struct { hashType crypto.Hash case crypto.SHA512: func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) { // always be smaller than the original argument. d.h.Write(d.byteBuf) return func getLine(data []byte) (line, rest []byte) { hashType := config.Hash() Headers: make(textproto.MIMEHeader), var crlf = []byte("\r\n") if err = sig.Serialize(out); err != nil { i-- rest = rest[i+len(start):] } sig.Hash = d.hashType // start begins with a newline. However, at the very beginning of return // An empty line marks the end of the headers. return h := hashType.New() } b.Bytes = append(b.Bytes, line...) // CRLF. // end is a marker which denotes the end of the armored signature. if i < 0 { i := bytes.Index(data, []byte{'\n'}) } name := nameOfHash(hashType) } _, rest = getLine(rest) // The signature isn't calculated over the dash-escaped text so } else { } sig.PubKeyAlgo = d.privateKey.PubKeyAlgo return "RIPEMD160" func (d *dashEscaper) Write(data []byte) (n int, err error) { return for { // array. The line does not include the \r\n or \n. The remainder of the byte for { } if err = d.buffered.WriteByte(b); err != nil { if err = out.Close(); err != nil { var end = []byte("\n-----END PGP SIGNATURE-----") } if err = buffered.WriteByte(lf); err != nil { } d.h.Write(crlf) return func nameOfHash(h crypto.Hash) string { // nameOfHash returns the OpenPGP name for the given hash, or the empty string } // Back up to the start of the line because armor expects to see the if privateKey.Encrypted { // the escape is only written to buffered. // as the suffix of data which remains after the message. } config: config, // Package clearsign generates and processes OpenPGP, clear-signed data. See } // Any whitespace at the end of the line has to be removed so we } i++ } // the end). val = bytes.TrimSpace(val) case crypto.SHA384: if _, err = buffered.WriteString("Hash: "); err != nil { package clearsign b.Plaintext = append(b.Plaintext, lf) i := bytes.Index(rest, end) if len(name) == 0 { out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil) return return nil, data // We dely writing CRLF to the hash until the start of the // next line. } } // A Block represents a clearsigned message. A signature on a Block can } // can't be confused with endText. // be checked by passing Bytes into openpgp.CheckDetachedSignature. b.Headers.Add(string(key), string(val)) // Next come a series of header lines. if err = buffered.WriteByte(lf); err != nil { var j int return var line []byte } ArmoredSignature *armor.Block // The signature block return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType))) return nil, data if err != nil { // if the name isn't known. See RFC 4880, section 9.4. d.isFirstLine = false "bytes" // start has a \n at the beginning that we don't want here. if i == -1 { // Consume the start line. // When closed, an armored signature is created and written to complete the d.whitespace = append(d.whitespace, b) } // the byte array, we'll accept the start string without it. } line, rest = getLine(rest) } } // always smaller than its argument. "strconv" // This loop terminates because getLine's second result is } } } // we've seen the next line. // Clearsigned messages are cryptographically signed, but the contents of the } i := bytes.Index(line, []byte{':'}) if err = d.buffered.WriteByte(b); err != nil { atBeginningOfLine bool // message. isFirstLine bool "code.google.com/p/go.crypto/openpgp/errors" byteBuf: make([]byte, 1), // We got a raw \n. Drop any trailing whitespace and write a // switch h { return "SHA256" return } else if b == '\n' { // header line. sig.SigType = packet.SigTypeText "code.google.com/p/go.crypto/openpgp/packet" if bytes.HasPrefix(line, dashEscape) { buffered := bufio.NewWriter(w) // Nothing to do because we dely writing CRLF to the hash. } i += len(end) h: h, } line = line[2:] b.Plaintext = append(b.Plaintext, line...) } privateKey: privateKey, } b = &Block{ if err = buffered.WriteByte(lf); err != nil { } case crypto.SHA224: } return data[0:i], data[j:] // getLine returns the first \r\n or \n delineated line from the given byte return // until this point (the start of the next line) before writing it. } else if b == '\n' { return nil, data ) for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') { n = len(data) return nil, data } // The final CRLF isn't included in the hash so we have to wait key, val := line[0:i], line[i+1:] "code.google.com/p/go.crypto/openpgp/armor" break if len(b.Bytes) > 0 { } return b, rest for _, b := range data { // } var dashEscape = []byte("- ") // message. The clear-signed message is written to buffered and a hash, suitable return "SHA384" } d.atBeginningOfLine = true return "SHA224" // array (also not including the new line bytes) is also returned and this will // buffer it until we find out whether there's more on this line. // dashEscape is prefixed to any lines that begin with a hypen so that they armored := rest[:i] var endText = []byte("-----BEGIN PGP SIGNATURE-----") } line = bytes.TrimRight(line, " \t") return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType))) } } else {