You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

armor.go 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. // Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
  5. // very similar to PEM except that it has an additional CRC checksum.
  6. package armor // import "github.com/keybase/go-crypto/openpgp/armor"
  7. import (
  8. "bufio"
  9. "bytes"
  10. "encoding/base64"
  11. "fmt"
  12. "io"
  13. "strings"
  14. "unicode"
  15. "github.com/keybase/go-crypto/openpgp/errors"
  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. var ArmorCorrupt error = errors.StructuralError("armor invalid")
  38. const crc24Init = 0xb704ce
  39. const crc24Poly = 0x1864cfb
  40. const crc24Mask = 0xffffff
  41. // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
  42. func crc24(crc uint32, d []byte) uint32 {
  43. for _, b := range d {
  44. crc ^= uint32(b) << 16
  45. for i := 0; i < 8; i++ {
  46. crc <<= 1
  47. if crc&0x1000000 != 0 {
  48. crc ^= crc24Poly
  49. }
  50. }
  51. }
  52. return crc
  53. }
  54. var armorStart = []byte("-----BEGIN ")
  55. var armorEnd = []byte("-----END ")
  56. var armorEndOfLine = []byte("-----")
  57. // lineReader wraps a line based reader. It watches for the end of an armor
  58. // block and records the expected CRC value.
  59. type lineReader struct {
  60. in *bufio.Reader
  61. buf []byte
  62. eof bool
  63. crc *uint32
  64. }
  65. // ourIsSpace checks if a rune is either space according to unicode
  66. // package, or ZeroWidthSpace (which is not a space according to
  67. // unicode module). Used to trim lines during header reading.
  68. func ourIsSpace(r rune) bool {
  69. return r == '\u200b' || unicode.IsSpace(r)
  70. }
  71. func (l *lineReader) Read(p []byte) (n int, err error) {
  72. if l.eof {
  73. return 0, io.EOF
  74. }
  75. if len(l.buf) > 0 {
  76. n = copy(p, l.buf)
  77. l.buf = l.buf[n:]
  78. return
  79. }
  80. line, isPrefix, err := l.in.ReadLine()
  81. if err != nil {
  82. return
  83. }
  84. // Entry-level cleanup, just trim spaces.
  85. line = bytes.TrimFunc(line, ourIsSpace)
  86. lineWithChecksum := false
  87. foldedChecksum := false
  88. if !isPrefix && len(line) >= 5 && line[len(line)-5] == '=' && line[len(line)-4] != '=' {
  89. // This is the checksum line. Checksum should appear on separate line,
  90. // but some bundles don't have a newline between main payload and the
  91. // checksum, and we try to support that.
  92. // `=` is not a base64 character with the exception of padding, and the
  93. // padding can only be 2 characters long at most ("=="), so we can
  94. // safely assume that 5 characters starting with `=` at the end of the
  95. // line can't be a valid ending of a base64 stream. In other words, `=`
  96. // at position len-5 in base64 stream can never be a valid part of that
  97. // stream.
  98. // Checksum can never appear if isPrefix is true - that is, when
  99. // ReadLine returned non-final part of some line because it was longer
  100. // than its buffer.
  101. if l.crc != nil {
  102. // Error out early if there are multiple checksums.
  103. return 0, ArmorCorrupt
  104. }
  105. var expectedBytes [3]byte
  106. var m int
  107. m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[len(line)-4:])
  108. if err != nil {
  109. return 0, fmt.Errorf("error decoding CRC: %s", err.Error())
  110. } else if m != 3 {
  111. return 0, fmt.Errorf("error decoding CRC: wrong size CRC")
  112. }
  113. crc := uint32(expectedBytes[0])<<16 |
  114. uint32(expectedBytes[1])<<8 |
  115. uint32(expectedBytes[2])
  116. l.crc = &crc
  117. line = line[:len(line)-5]
  118. lineWithChecksum = true
  119. // If we've found a checksum but there is still data left, we don't
  120. // want to enter the "looking for armor end" loop, we still need to
  121. // return the leftover data to the reader.
  122. foldedChecksum = len(line) > 0
  123. // At this point, `line` contains leftover data or "" (if checksum
  124. // was on separate line.)
  125. }
  126. expectArmorEnd := false
  127. if l.crc != nil && !foldedChecksum {
  128. // "looking for armor end" loop
  129. // We have a checksum, and we are now reading what comes afterwards.
  130. // Skip all empty lines until we see something and we except it to be
  131. // ArmorEnd at this point.
  132. // This loop is not entered if there is more data *before* the CRC
  133. // suffix (if the CRC is not on separate line).
  134. for {
  135. if len(strings.TrimSpace(string(line))) > 0 {
  136. break
  137. }
  138. lineWithChecksum = false
  139. line, _, err = l.in.ReadLine()
  140. if err == io.EOF {
  141. break
  142. }
  143. if err != nil {
  144. return
  145. }
  146. }
  147. expectArmorEnd = true
  148. }
  149. if bytes.HasPrefix(line, armorEnd) {
  150. if lineWithChecksum {
  151. // ArmorEnd and checksum at the same line?
  152. return 0, ArmorCorrupt
  153. }
  154. l.eof = true
  155. return 0, io.EOF
  156. } else if expectArmorEnd {
  157. // We wanted armorEnd but didn't see one.
  158. return 0, ArmorCorrupt
  159. }
  160. // Clean-up line from whitespace to pass it further (to base64
  161. // decoder). This is done after test for CRC and test for
  162. // armorEnd. Keys that have whitespace in CRC will have CRC
  163. // treated as part of the payload and probably fail in base64
  164. // reading.
  165. line = bytes.Map(func(r rune) rune {
  166. if ourIsSpace(r) {
  167. return -1
  168. }
  169. return r
  170. }, line)
  171. n = copy(p, line)
  172. bytesToSave := len(line) - n
  173. if bytesToSave > 0 {
  174. if cap(l.buf) < bytesToSave {
  175. l.buf = make([]byte, 0, bytesToSave)
  176. }
  177. l.buf = l.buf[0:bytesToSave]
  178. copy(l.buf, line[n:])
  179. }
  180. return
  181. }
  182. // openpgpReader passes Read calls to the underlying base64 decoder, but keeps
  183. // a running CRC of the resulting data and checks the CRC against the value
  184. // found by the lineReader at EOF.
  185. type openpgpReader struct {
  186. lReader *lineReader
  187. b64Reader io.Reader
  188. currentCRC uint32
  189. }
  190. func (r *openpgpReader) Read(p []byte) (n int, err error) {
  191. n, err = r.b64Reader.Read(p)
  192. r.currentCRC = crc24(r.currentCRC, p[:n])
  193. if err == io.EOF {
  194. if r.lReader.crc != nil && *r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
  195. return 0, ArmorCorrupt
  196. }
  197. }
  198. return
  199. }
  200. // Decode reads a PGP armored block from the given Reader. It will ignore
  201. // leading garbage. If it doesn't find a block, it will return nil, io.EOF. The
  202. // given Reader is not usable after calling this function: an arbitrary amount
  203. // of data may have been read past the end of the block.
  204. func Decode(in io.Reader) (p *Block, err error) {
  205. r := bufio.NewReaderSize(in, 100)
  206. var line []byte
  207. ignoreNext := false
  208. TryNextBlock:
  209. p = nil
  210. // Skip leading garbage
  211. for {
  212. ignoreThis := ignoreNext
  213. line, ignoreNext, err = r.ReadLine()
  214. if err != nil {
  215. return
  216. }
  217. if ignoreNext || ignoreThis {
  218. continue
  219. }
  220. line = bytes.TrimSpace(line)
  221. if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
  222. break
  223. }
  224. }
  225. p = new(Block)
  226. p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
  227. p.Header = make(map[string]string)
  228. nextIsContinuation := false
  229. var lastKey string
  230. // Read headers
  231. for {
  232. isContinuation := nextIsContinuation
  233. line, nextIsContinuation, err = r.ReadLine()
  234. if err != nil {
  235. p = nil
  236. return
  237. }
  238. if isContinuation {
  239. p.Header[lastKey] += string(line)
  240. continue
  241. }
  242. line = bytes.TrimFunc(line, ourIsSpace)
  243. if len(line) == 0 {
  244. break
  245. }
  246. i := bytes.Index(line, []byte(": "))
  247. if i == -1 {
  248. goto TryNextBlock
  249. }
  250. lastKey = string(line[:i])
  251. p.Header[lastKey] = string(line[i+2:])
  252. }
  253. p.lReader.in = r
  254. p.oReader.currentCRC = crc24Init
  255. p.oReader.lReader = &p.lReader
  256. p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
  257. p.Body = &p.oReader
  258. return
  259. }