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.
7package armor // import "golang.org/x/crypto/openpgp/armor"
8
9import (
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.
30type 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
38var ArmorCorrupt error = errors.StructuralError("armor invalid")
39
40const crc24Init = 0xb704ce
41const crc24Poly = 0x1864cfb
42const crc24Mask = 0xffffff
43
44// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
45func 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
58var armorStart = []byte("-----BEGIN ")
59var armorEnd = []byte("-----END ")
60var 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.
64type lineReader struct {
65	in  *bufio.Reader
66	buf []byte
67	eof bool
68	crc uint32
69}
70
71func (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.
134type openpgpReader struct {
135	lReader    *lineReader
136	b64Reader  io.Reader
137	currentCRC uint32
138}
139
140func (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.
157func Decode(in io.Reader) (p *Block, err error) {
158	r := bufio.NewReaderSize(in, 100)
159	var line []byte
160	ignoreNext := false
161
162TryNextBlock:
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}
220