armor.go 4.9 KB

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