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