1// Copyright 2012 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package clearsign generates and processes OpenPGP, clear-signed data. See 6// RFC 4880, section 7. 7// 8// Clearsigned messages are cryptographically signed, but the contents of the 9// message are kept in plaintext so that it can be read without special tools. 10package clearsign // import "golang.org/x/crypto/openpgp/clearsign" 11 12import ( 13 "bufio" 14 "bytes" 15 "crypto" 16 "fmt" 17 "hash" 18 "io" 19 "net/textproto" 20 "strconv" 21 "strings" 22 23 "golang.org/x/crypto/openpgp/armor" 24 "golang.org/x/crypto/openpgp/errors" 25 "golang.org/x/crypto/openpgp/packet" 26) 27 28// A Block represents a clearsigned message. A signature on a Block can 29// be checked by passing Bytes into openpgp.CheckDetachedSignature. 30type Block struct { 31 Headers textproto.MIMEHeader // Optional unverified Hash headers 32 Plaintext []byte // The original message text 33 Bytes []byte // The signed message 34 ArmoredSignature *armor.Block // The signature block 35} 36 37// start is the marker which denotes the beginning of a clearsigned message. 38var start = []byte("\n-----BEGIN PGP SIGNED MESSAGE-----") 39 40// dashEscape is prefixed to any lines that begin with a hyphen so that they 41// can't be confused with endText. 42var dashEscape = []byte("- ") 43 44// endText is a marker which denotes the end of the message and the start of 45// an armored signature. 46var endText = []byte("-----BEGIN PGP SIGNATURE-----") 47 48// end is a marker which denotes the end of the armored signature. 49var end = []byte("\n-----END PGP SIGNATURE-----") 50 51var crlf = []byte("\r\n") 52var lf = byte('\n') 53 54// getLine returns the first \r\n or \n delineated line from the given byte 55// array. The line does not include the \r\n or \n. The remainder of the byte 56// array (also not including the new line bytes) is also returned and this will 57// always be smaller than the original argument. 58func getLine(data []byte) (line, rest []byte) { 59 i := bytes.Index(data, []byte{'\n'}) 60 var j int 61 if i < 0 { 62 i = len(data) 63 j = i 64 } else { 65 j = i + 1 66 if i > 0 && data[i-1] == '\r' { 67 i-- 68 } 69 } 70 return data[0:i], data[j:] 71} 72 73// Decode finds the first clearsigned message in data and returns it, as well as 74// the suffix of data which remains after the message. Any prefix data is 75// discarded. 76// 77// If no message is found, or if the message is invalid, Decode returns nil and 78// the whole data slice. The only allowed header type is Hash, and it is not 79// verified against the signature hash. 80func Decode(data []byte) (b *Block, rest []byte) { 81 // start begins with a newline. However, at the very beginning of 82 // the byte array, we'll accept the start string without it. 83 rest = data 84 if bytes.HasPrefix(data, start[1:]) { 85 rest = rest[len(start)-1:] 86 } else if i := bytes.Index(data, start); i >= 0 { 87 rest = rest[i+len(start):] 88 } else { 89 return nil, data 90 } 91 92 // Consume the start line and check it does not have a suffix. 93 suffix, rest := getLine(rest) 94 if len(suffix) != 0 { 95 return nil, data 96 } 97 98 var line []byte 99 b = &Block{ 100 Headers: make(textproto.MIMEHeader), 101 } 102 103 // Next come a series of header lines. 104 for { 105 // This loop terminates because getLine's second result is 106 // always smaller than its argument. 107 if len(rest) == 0 { 108 return nil, data 109 } 110 // An empty line marks the end of the headers. 111 if line, rest = getLine(rest); len(line) == 0 { 112 break 113 } 114 115 // Reject headers with control or Unicode characters. 116 if i := bytes.IndexFunc(line, func(r rune) bool { 117 return r < 0x20 || r > 0x7e 118 }); i != -1 { 119 return nil, data 120 } 121 122 i := bytes.Index(line, []byte{':'}) 123 if i == -1 { 124 return nil, data 125 } 126 127 key, val := string(line[0:i]), string(line[i+1:]) 128 key = strings.TrimSpace(key) 129 if key != "Hash" { 130 return nil, data 131 } 132 val = strings.TrimSpace(val) 133 b.Headers.Add(key, val) 134 } 135 136 firstLine := true 137 for { 138 start := rest 139 140 line, rest = getLine(rest) 141 if len(line) == 0 && len(rest) == 0 { 142 // No armored data was found, so this isn't a complete message. 143 return nil, data 144 } 145 if bytes.Equal(line, endText) { 146 // Back up to the start of the line because armor expects to see the 147 // header line. 148 rest = start 149 break 150 } 151 152 // The final CRLF isn't included in the hash so we don't write it until 153 // we've seen the next line. 154 if firstLine { 155 firstLine = false 156 } else { 157 b.Bytes = append(b.Bytes, crlf...) 158 } 159 160 if bytes.HasPrefix(line, dashEscape) { 161 line = line[2:] 162 } 163 line = bytes.TrimRight(line, " \t") 164 b.Bytes = append(b.Bytes, line...) 165 166 b.Plaintext = append(b.Plaintext, line...) 167 b.Plaintext = append(b.Plaintext, lf) 168 } 169 170 // We want to find the extent of the armored data (including any newlines at 171 // the end). 172 i := bytes.Index(rest, end) 173 if i == -1 { 174 return nil, data 175 } 176 i += len(end) 177 for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') { 178 i++ 179 } 180 armored := rest[:i] 181 rest = rest[i:] 182 183 var err error 184 b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored)) 185 if err != nil { 186 return nil, data 187 } 188 189 return b, rest 190} 191 192// A dashEscaper is an io.WriteCloser which processes the body of a clear-signed 193// message. The clear-signed message is written to buffered and a hash, suitable 194// for signing, is maintained in h. 195// 196// When closed, an armored signature is created and written to complete the 197// message. 198type dashEscaper struct { 199 buffered *bufio.Writer 200 hashers []hash.Hash // one per key in privateKeys 201 hashType crypto.Hash 202 toHash io.Writer // writes to all the hashes in hashers 203 204 atBeginningOfLine bool 205 isFirstLine bool 206 207 whitespace []byte 208 byteBuf []byte // a one byte buffer to save allocations 209 210 privateKeys []*packet.PrivateKey 211 config *packet.Config 212} 213 214func (d *dashEscaper) Write(data []byte) (n int, err error) { 215 for _, b := range data { 216 d.byteBuf[0] = b 217 218 if d.atBeginningOfLine { 219 // The final CRLF isn't included in the hash so we have to wait 220 // until this point (the start of the next line) before writing it. 221 if !d.isFirstLine { 222 d.toHash.Write(crlf) 223 } 224 d.isFirstLine = false 225 } 226 227 // Any whitespace at the end of the line has to be removed so we 228 // buffer it until we find out whether there's more on this line. 229 if b == ' ' || b == '\t' || b == '\r' { 230 d.whitespace = append(d.whitespace, b) 231 d.atBeginningOfLine = false 232 continue 233 } 234 235 if d.atBeginningOfLine { 236 // At the beginning of a line, hyphens have to be escaped. 237 if b == '-' { 238 // The signature isn't calculated over the dash-escaped text so 239 // the escape is only written to buffered. 240 if _, err = d.buffered.Write(dashEscape); err != nil { 241 return 242 } 243 d.toHash.Write(d.byteBuf) 244 d.atBeginningOfLine = false 245 } else if b == '\n' { 246 // Nothing to do because we delay writing CRLF to the hash. 247 } else { 248 d.toHash.Write(d.byteBuf) 249 d.atBeginningOfLine = false 250 } 251 if err = d.buffered.WriteByte(b); err != nil { 252 return 253 } 254 } else { 255 if b == '\n' { 256 // We got a raw \n. Drop any trailing whitespace and write a 257 // CRLF. 258 d.whitespace = d.whitespace[:0] 259 // We delay writing CRLF to the hash until the start of the 260 // next line. 261 if err = d.buffered.WriteByte(b); err != nil { 262 return 263 } 264 d.atBeginningOfLine = true 265 } else { 266 // Any buffered whitespace wasn't at the end of the line so 267 // we need to write it out. 268 if len(d.whitespace) > 0 { 269 d.toHash.Write(d.whitespace) 270 if _, err = d.buffered.Write(d.whitespace); err != nil { 271 return 272 } 273 d.whitespace = d.whitespace[:0] 274 } 275 d.toHash.Write(d.byteBuf) 276 if err = d.buffered.WriteByte(b); err != nil { 277 return 278 } 279 } 280 } 281 } 282 283 n = len(data) 284 return 285} 286 287func (d *dashEscaper) Close() (err error) { 288 if !d.atBeginningOfLine { 289 if err = d.buffered.WriteByte(lf); err != nil { 290 return 291 } 292 } 293 294 out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil) 295 if err != nil { 296 return 297 } 298 299 t := d.config.Now() 300 for i, k := range d.privateKeys { 301 sig := new(packet.Signature) 302 sig.SigType = packet.SigTypeText 303 sig.PubKeyAlgo = k.PubKeyAlgo 304 sig.Hash = d.hashType 305 sig.CreationTime = t 306 sig.IssuerKeyId = &k.KeyId 307 308 if err = sig.Sign(d.hashers[i], k, d.config); err != nil { 309 return 310 } 311 if err = sig.Serialize(out); err != nil { 312 return 313 } 314 } 315 316 if err = out.Close(); err != nil { 317 return 318 } 319 if err = d.buffered.Flush(); err != nil { 320 return 321 } 322 return 323} 324 325// Encode returns a WriteCloser which will clear-sign a message with privateKey 326// and write it to w. If config is nil, sensible defaults are used. 327func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) { 328 return EncodeMulti(w, []*packet.PrivateKey{privateKey}, config) 329} 330 331// EncodeMulti returns a WriteCloser which will clear-sign a message with all the 332// private keys indicated and write it to w. If config is nil, sensible defaults 333// are used. 334func EncodeMulti(w io.Writer, privateKeys []*packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) { 335 for _, k := range privateKeys { 336 if k.Encrypted { 337 return nil, errors.InvalidArgumentError(fmt.Sprintf("signing key %s is encrypted", k.KeyIdString())) 338 } 339 } 340 341 hashType := config.Hash() 342 name := nameOfHash(hashType) 343 if len(name) == 0 { 344 return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType))) 345 } 346 347 if !hashType.Available() { 348 return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType))) 349 } 350 var hashers []hash.Hash 351 var ws []io.Writer 352 for range privateKeys { 353 h := hashType.New() 354 hashers = append(hashers, h) 355 ws = append(ws, h) 356 } 357 toHash := io.MultiWriter(ws...) 358 359 buffered := bufio.NewWriter(w) 360 // start has a \n at the beginning that we don't want here. 361 if _, err = buffered.Write(start[1:]); err != nil { 362 return 363 } 364 if err = buffered.WriteByte(lf); err != nil { 365 return 366 } 367 if _, err = buffered.WriteString("Hash: "); err != nil { 368 return 369 } 370 if _, err = buffered.WriteString(name); err != nil { 371 return 372 } 373 if err = buffered.WriteByte(lf); err != nil { 374 return 375 } 376 if err = buffered.WriteByte(lf); err != nil { 377 return 378 } 379 380 plaintext = &dashEscaper{ 381 buffered: buffered, 382 hashers: hashers, 383 hashType: hashType, 384 toHash: toHash, 385 386 atBeginningOfLine: true, 387 isFirstLine: true, 388 389 byteBuf: make([]byte, 1), 390 391 privateKeys: privateKeys, 392 config: config, 393 } 394 395 return 396} 397 398// nameOfHash returns the OpenPGP name for the given hash, or the empty string 399// if the name isn't known. See RFC 4880, section 9.4. 400func nameOfHash(h crypto.Hash) string { 401 switch h { 402 case crypto.MD5: 403 return "MD5" 404 case crypto.SHA1: 405 return "SHA1" 406 case crypto.RIPEMD160: 407 return "RIPEMD160" 408 case crypto.SHA224: 409 return "SHA224" 410 case crypto.SHA256: 411 return "SHA256" 412 case crypto.SHA384: 413 return "SHA384" 414 case crypto.SHA512: 415 return "SHA512" 416 } 417 return "" 418} 419