github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/encoding/pem/pem.go (about) 1 // Copyright 2009 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 pem implements the PEM data encoding, which originated in Privacy 6 // Enhanced Mail. The most common use of PEM encoding today is in TLS keys and 7 // certificates. See RFC 1421. 8 package pem 9 10 import ( 11 "bytes" 12 "encoding/base64" 13 "errors" 14 "io" 15 "sort" 16 "strings" 17 ) 18 19 // A Block represents a PEM encoded structure. 20 // 21 // The encoded form is: 22 // -----BEGIN Type----- 23 // Headers 24 // base64-encoded Bytes 25 // -----END Type----- 26 // where Headers is a possibly empty sequence of Key: Value lines. 27 type Block struct { 28 Type string // The type, taken from the preamble (i.e. "RSA PRIVATE KEY"). 29 Headers map[string]string // Optional headers. 30 Bytes []byte // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure. 31 } 32 33 // getLine results the first \r\n or \n delineated line from the given byte 34 // array. The line does not include trailing whitespace or the trailing new 35 // line bytes. The remainder of the byte array (also not including the new line 36 // bytes) is also returned and this will always be smaller than the original 37 // argument. 38 func getLine(data []byte) (line, rest []byte) { 39 i := bytes.IndexByte(data, '\n') 40 var j int 41 if i < 0 { 42 i = len(data) 43 j = i 44 } else { 45 j = i + 1 46 if i > 0 && data[i-1] == '\r' { 47 i-- 48 } 49 } 50 return bytes.TrimRight(data[0:i], " \t"), data[j:] 51 } 52 53 // removeSpacesAndTabs returns a copy of its input with all spaces and tabs 54 // removed, if there were any. Otherwise, the input is returned unchanged. 55 // 56 // The base64 decoder already skips newline characters, so we don't need to 57 // filter them out here. 58 func removeSpacesAndTabs(data []byte) []byte { 59 if !bytes.ContainsAny(data, " \t") { 60 // Fast path; most base64 data within PEM contains newlines, but 61 // no spaces nor tabs. Skip the extra alloc and work. 62 return data 63 } 64 result := make([]byte, len(data)) 65 n := 0 66 67 for _, b := range data { 68 if b == ' ' || b == '\t' { 69 continue 70 } 71 result[n] = b 72 n++ 73 } 74 75 return result[0:n] 76 } 77 78 var pemStart = []byte("\n-----BEGIN ") 79 var pemEnd = []byte("\n-----END ") 80 var pemEndOfLine = []byte("-----") 81 82 // Decode will find the next PEM formatted block (certificate, private key 83 // etc) in the input. It returns that block and the remainder of the input. If 84 // no PEM data is found, p is nil and the whole of the input is returned in 85 // rest. 86 func Decode(data []byte) (p *Block, rest []byte) { 87 // pemStart begins with a newline. However, at the very beginning of 88 // the byte array, we'll accept the start string without it. 89 rest = data 90 if bytes.HasPrefix(data, pemStart[1:]) { 91 rest = rest[len(pemStart)-1 : len(data)] 92 } else if i := bytes.Index(data, pemStart); i >= 0 { 93 rest = rest[i+len(pemStart) : len(data)] 94 } else { 95 return nil, data 96 } 97 98 typeLine, rest := getLine(rest) 99 if !bytes.HasSuffix(typeLine, pemEndOfLine) { 100 return decodeError(data, rest) 101 } 102 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] 103 104 p = &Block{ 105 Headers: make(map[string]string), 106 Type: string(typeLine), 107 } 108 109 for { 110 // This loop terminates because getLine's second result is 111 // always smaller than its argument. 112 if len(rest) == 0 { 113 return nil, data 114 } 115 line, next := getLine(rest) 116 117 i := bytes.IndexByte(line, ':') 118 if i == -1 { 119 break 120 } 121 122 // TODO(agl): need to cope with values that spread across lines. 123 key, val := line[:i], line[i+1:] 124 key = bytes.TrimSpace(key) 125 val = bytes.TrimSpace(val) 126 p.Headers[string(key)] = string(val) 127 rest = next 128 } 129 130 var endIndex, endTrailerIndex int 131 132 // If there were no headers, the END line might occur 133 // immediately, without a leading newline. 134 if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) { 135 endIndex = 0 136 endTrailerIndex = len(pemEnd) - 1 137 } else { 138 endIndex = bytes.Index(rest, pemEnd) 139 endTrailerIndex = endIndex + len(pemEnd) 140 } 141 142 if endIndex < 0 { 143 return decodeError(data, rest) 144 } 145 146 // After the "-----" of the ending line, there should be the same type 147 // and then a final five dashes. 148 endTrailer := rest[endTrailerIndex:] 149 endTrailerLen := len(typeLine) + len(pemEndOfLine) 150 if len(endTrailer) < endTrailerLen { 151 return decodeError(data, rest) 152 } 153 154 restOfEndLine := endTrailer[endTrailerLen:] 155 endTrailer = endTrailer[:endTrailerLen] 156 if !bytes.HasPrefix(endTrailer, typeLine) || 157 !bytes.HasSuffix(endTrailer, pemEndOfLine) { 158 return decodeError(data, rest) 159 } 160 161 // The line must end with only whitespace. 162 if s, _ := getLine(restOfEndLine); len(s) != 0 { 163 return decodeError(data, rest) 164 } 165 166 base64Data := removeSpacesAndTabs(rest[:endIndex]) 167 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) 168 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) 169 if err != nil { 170 return decodeError(data, rest) 171 } 172 p.Bytes = p.Bytes[:n] 173 174 // the -1 is because we might have only matched pemEnd without the 175 // leading newline if the PEM block was empty. 176 _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) 177 178 return 179 } 180 181 func decodeError(data, rest []byte) (*Block, []byte) { 182 // If we get here then we have rejected a likely looking, but 183 // ultimately invalid PEM block. We need to start over from a new 184 // position. We have consumed the preamble line and will have consumed 185 // any lines which could be header lines. However, a valid preamble 186 // line is not a valid header line, therefore we cannot have consumed 187 // the preamble line for the any subsequent block. Thus, we will always 188 // find any valid block, no matter what bytes precede it. 189 // 190 // For example, if the input is 191 // 192 // -----BEGIN MALFORMED BLOCK----- 193 // junk that may look like header lines 194 // or data lines, but no END line 195 // 196 // -----BEGIN ACTUAL BLOCK----- 197 // realdata 198 // -----END ACTUAL BLOCK----- 199 // 200 // we've failed to parse using the first BEGIN line 201 // and now will try again, using the second BEGIN line. 202 p, rest := Decode(rest) 203 if p == nil { 204 rest = data 205 } 206 return p, rest 207 } 208 209 const pemLineLength = 64 210 211 type lineBreaker struct { 212 line [pemLineLength]byte 213 used int 214 out io.Writer 215 } 216 217 var nl = []byte{'\n'} 218 219 func (l *lineBreaker) Write(b []byte) (n int, err error) { 220 if l.used+len(b) < pemLineLength { 221 copy(l.line[l.used:], b) 222 l.used += len(b) 223 return len(b), nil 224 } 225 226 n, err = l.out.Write(l.line[0:l.used]) 227 if err != nil { 228 return 229 } 230 excess := pemLineLength - l.used 231 l.used = 0 232 233 n, err = l.out.Write(b[0:excess]) 234 if err != nil { 235 return 236 } 237 238 n, err = l.out.Write(nl) 239 if err != nil { 240 return 241 } 242 243 return l.Write(b[excess:]) 244 } 245 246 func (l *lineBreaker) Close() (err error) { 247 if l.used > 0 { 248 _, err = l.out.Write(l.line[0:l.used]) 249 if err != nil { 250 return 251 } 252 _, err = l.out.Write(nl) 253 } 254 255 return 256 } 257 258 func writeHeader(out io.Writer, k, v string) error { 259 _, err := out.Write([]byte(k + ": " + v + "\n")) 260 return err 261 } 262 263 // Encode writes the PEM encoding of b to out. 264 func Encode(out io.Writer, b *Block) error { 265 // Check for invalid block before writing any output. 266 for k := range b.Headers { 267 if strings.Contains(k, ":") { 268 return errors.New("pem: cannot encode a header key that contains a colon") 269 } 270 } 271 272 // All errors below are relayed from underlying io.Writer, 273 // so it is now safe to write data. 274 275 if _, err := out.Write(pemStart[1:]); err != nil { 276 return err 277 } 278 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { 279 return err 280 } 281 282 if len(b.Headers) > 0 { 283 const procType = "Proc-Type" 284 h := make([]string, 0, len(b.Headers)) 285 hasProcType := false 286 for k := range b.Headers { 287 if k == procType { 288 hasProcType = true 289 continue 290 } 291 h = append(h, k) 292 } 293 // The Proc-Type header must be written first. 294 // See RFC 1421, section 4.6.1.1 295 if hasProcType { 296 if err := writeHeader(out, procType, b.Headers[procType]); err != nil { 297 return err 298 } 299 } 300 // For consistency of output, write other headers sorted by key. 301 sort.Strings(h) 302 for _, k := range h { 303 if err := writeHeader(out, k, b.Headers[k]); err != nil { 304 return err 305 } 306 } 307 if _, err := out.Write(nl); err != nil { 308 return err 309 } 310 } 311 312 var breaker lineBreaker 313 breaker.out = out 314 315 b64 := base64.NewEncoder(base64.StdEncoding, &breaker) 316 if _, err := b64.Write(b.Bytes); err != nil { 317 return err 318 } 319 b64.Close() 320 breaker.Close() 321 322 if _, err := out.Write(pemEnd[1:]); err != nil { 323 return err 324 } 325 _, err := out.Write([]byte(b.Type + "-----\n")) 326 return err 327 } 328 329 // EncodeToMemory returns the PEM encoding of b. 330 // 331 // If b has invalid headers and cannot be encoded, 332 // EncodeToMemory returns nil. If it is important to 333 // report details about this error case, use Encode instead. 334 func EncodeToMemory(b *Block) []byte { 335 var buf bytes.Buffer 336 if err := Encode(&buf, b); err != nil { 337 return nil 338 } 339 return buf.Bytes() 340 }