...

Source file src/golang.org/x/crypto/openpgp/armor/armor.go

Documentation: golang.org/x/crypto/openpgp/armor

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

View as plain text