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