github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/crypto/openpgp/armor/armor.go (about)

     1  // Copyright 2010 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 armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
     6  // very similar to PEM except that it has an additional CRC checksum.
     7  package armor // import "golang.org/x/crypto/openpgp/armor"
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"encoding/base64"
    13  	"golang.org/x/crypto/openpgp/errors"
    14  	"io"
    15  )
    16  
    17  // A Block represents an OpenPGP armored structure.
    18  //
    19  // The encoded form is:
    20  //    -----BEGIN Type-----
    21  //    Headers
    22  //
    23  //    base64-encoded Bytes
    24  //    '=' base64 encoded checksum
    25  //    -----END Type-----
    26  // where Headers is a possibly empty sequence of Key: Value lines.
    27  //
    28  // Since the armored data can be very large, this package presents a streaming
    29  // interface.
    30  type Block struct {
    31  	Type    string            // The type, taken from the preamble (i.e. "PGP SIGNATURE").
    32  	Header  map[string]string // Optional headers.
    33  	Body    io.Reader         // A Reader from which the contents can be read
    34  	lReader lineReader
    35  	oReader openpgpReader
    36  }
    37  
    38  var ArmorCorrupt error = errors.StructuralError("armor invalid")
    39  
    40  const crc24Init = 0xb704ce
    41  const crc24Poly = 0x1864cfb
    42  const crc24Mask = 0xffffff
    43  
    44  // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
    45  func crc24(crc uint32, d []byte) uint32 {
    46  	for _, b := range d {
    47  		crc ^= uint32(b) << 16
    48  		for i := 0; i < 8; i++ {
    49  			crc <<= 1
    50  			if crc&0x1000000 != 0 {
    51  				crc ^= crc24Poly
    52  			}
    53  		}
    54  	}
    55  	return crc
    56  }
    57  
    58  var armorStart = []byte("-----BEGIN ")
    59  var armorEnd = []byte("-----END ")
    60  var armorEndOfLine = []byte("-----")
    61  
    62  // lineReader wraps a line based reader. It watches for the end of an armor
    63  // block and records the expected CRC value.
    64  type lineReader struct {
    65  	in  *bufio.Reader
    66  	buf []byte
    67  	eof bool
    68  	crc uint32
    69  }
    70  
    71  func (l *lineReader) Read(p []byte) (n int, err error) {
    72  	if l.eof {
    73  		return 0, io.EOF
    74  	}
    75  
    76  	if len(l.buf) > 0 {
    77  		n = copy(p, l.buf)
    78  		l.buf = l.buf[n:]
    79  		return
    80  	}
    81  
    82  	line, isPrefix, err := l.in.ReadLine()
    83  	if err != nil {
    84  		return
    85  	}
    86  	if isPrefix {
    87  		return 0, ArmorCorrupt
    88  	}
    89  
    90  	if len(line) == 5 && line[0] == '=' {
    91  		// This is the checksum line
    92  		var expectedBytes [3]byte
    93  		var m int
    94  		m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
    95  		if m != 3 || err != nil {
    96  			return
    97  		}
    98  		l.crc = uint32(expectedBytes[0])<<16 |
    99  			uint32(expectedBytes[1])<<8 |
   100  			uint32(expectedBytes[2])
   101  
   102  		line, _, err = l.in.ReadLine()
   103  		if err != nil && err != io.EOF {
   104  			return
   105  		}
   106  		if !bytes.HasPrefix(line, armorEnd) {
   107  			return 0, ArmorCorrupt
   108  		}
   109  
   110  		l.eof = true
   111  		return 0, io.EOF
   112  	}
   113  
   114  	if len(line) > 96 {
   115  		return 0, ArmorCorrupt
   116  	}
   117  
   118  	n = copy(p, line)
   119  	bytesToSave := len(line) - n
   120  	if bytesToSave > 0 {
   121  		if cap(l.buf) < bytesToSave {
   122  			l.buf = make([]byte, 0, bytesToSave)
   123  		}
   124  		l.buf = l.buf[0:bytesToSave]
   125  		copy(l.buf, line[n:])
   126  	}
   127  
   128  	return
   129  }
   130  
   131  // openpgpReader passes Read calls to the underlying base64 decoder, but keeps
   132  // a running CRC of the resulting data and checks the CRC against the value
   133  // found by the lineReader at EOF.
   134  type openpgpReader struct {
   135  	lReader    *lineReader
   136  	b64Reader  io.Reader
   137  	currentCRC uint32
   138  }
   139  
   140  func (r *openpgpReader) Read(p []byte) (n int, err error) {
   141  	n, err = r.b64Reader.Read(p)
   142  	r.currentCRC = crc24(r.currentCRC, p[:n])
   143  
   144  	if err == io.EOF {
   145  		if r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
   146  			return 0, ArmorCorrupt
   147  		}
   148  	}
   149  
   150  	return
   151  }
   152  
   153  // Decode reads a PGP armored block from the given Reader. It will ignore
   154  // leading garbage. If it doesn't find a block, it will return nil, io.EOF. The
   155  // given Reader is not usable after calling this function: an arbitrary amount
   156  // of data may have been read past the end of the block.
   157  func Decode(in io.Reader) (p *Block, err error) {
   158  	r := bufio.NewReaderSize(in, 100)
   159  	var line []byte
   160  	ignoreNext := false
   161  
   162  TryNextBlock:
   163  	p = nil
   164  
   165  	// Skip leading garbage
   166  	for {
   167  		ignoreThis := ignoreNext
   168  		line, ignoreNext, err = r.ReadLine()
   169  		if err != nil {
   170  			return
   171  		}
   172  		if ignoreNext || ignoreThis {
   173  			continue
   174  		}
   175  		line = bytes.TrimSpace(line)
   176  		if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
   177  			break
   178  		}
   179  	}
   180  
   181  	p = new(Block)
   182  	p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
   183  	p.Header = make(map[string]string)
   184  	nextIsContinuation := false
   185  	var lastKey string
   186  
   187  	// Read headers
   188  	for {
   189  		isContinuation := nextIsContinuation
   190  		line, nextIsContinuation, err = r.ReadLine()
   191  		if err != nil {
   192  			p = nil
   193  			return
   194  		}
   195  		if isContinuation {
   196  			p.Header[lastKey] += string(line)
   197  			continue
   198  		}
   199  		line = bytes.TrimSpace(line)
   200  		if len(line) == 0 {
   201  			break
   202  		}
   203  
   204  		i := bytes.Index(line, []byte(": "))
   205  		if i == -1 {
   206  			goto TryNextBlock
   207  		}
   208  		lastKey = string(line[:i])
   209  		p.Header[lastKey] = string(line[i+2:])
   210  	}
   211  
   212  	p.lReader.in = r
   213  	p.oReader.currentCRC = crc24Init
   214  	p.oReader.lReader = &p.lReader
   215  	p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
   216  	p.Body = &p.oReader
   217  
   218  	return
   219  }