OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / crypto / openpgp / armor / encode.go
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
6
7 import (
8         "encoding/base64"
9         "io"
10 )
11
12 var armorHeaderSep = []byte(": ")
13 var blockEnd = []byte("\n=")
14 var newline = []byte("\n")
15 var armorEndOfLineOut = []byte("-----\n")
16
17 // writeSlices writes its arguments to the given Writer.
18 func writeSlices(out io.Writer, slices ...[]byte) (err error) {
19         for _, s := range slices {
20                 _, err = out.Write(s)
21                 if err != nil {
22                         return err
23                 }
24         }
25         return
26 }
27
28 // lineBreaker breaks data across several lines, all of the same byte length
29 // (except possibly the last). Lines are broken with a single '\n'.
30 type lineBreaker struct {
31         lineLength  int
32         line        []byte
33         used        int
34         out         io.Writer
35         haveWritten bool
36 }
37
38 func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
39         return &lineBreaker{
40                 lineLength: lineLength,
41                 line:       make([]byte, lineLength),
42                 used:       0,
43                 out:        out,
44         }
45 }
46
47 func (l *lineBreaker) Write(b []byte) (n int, err error) {
48         n = len(b)
49
50         if n == 0 {
51                 return
52         }
53
54         if l.used == 0 && l.haveWritten {
55                 _, err = l.out.Write([]byte{'\n'})
56                 if err != nil {
57                         return
58                 }
59         }
60
61         if l.used+len(b) < l.lineLength {
62                 l.used += copy(l.line[l.used:], b)
63                 return
64         }
65
66         l.haveWritten = true
67         _, err = l.out.Write(l.line[0:l.used])
68         if err != nil {
69                 return
70         }
71         excess := l.lineLength - l.used
72         l.used = 0
73
74         _, err = l.out.Write(b[0:excess])
75         if err != nil {
76                 return
77         }
78
79         _, err = l.Write(b[excess:])
80         return
81 }
82
83 func (l *lineBreaker) Close() (err error) {
84         if l.used > 0 {
85                 _, err = l.out.Write(l.line[0:l.used])
86                 if err != nil {
87                         return
88                 }
89         }
90
91         return
92 }
93
94 // encoding keeps track of a running CRC24 over the data which has been written
95 // to it and outputs a OpenPGP checksum when closed, followed by an armor
96 // trailer.
97 //
98 // It's built into a stack of io.Writers:
99 //    encoding -> base64 encoder -> lineBreaker -> out
100 type encoding struct {
101         out       io.Writer
102         breaker   *lineBreaker
103         b64       io.WriteCloser
104         crc       uint32
105         blockType []byte
106 }
107
108 func (e *encoding) Write(data []byte) (n int, err error) {
109         e.crc = crc24(e.crc, data)
110         return e.b64.Write(data)
111 }
112
113 func (e *encoding) Close() (err error) {
114         err = e.b64.Close()
115         if err != nil {
116                 return
117         }
118         e.breaker.Close()
119
120         var checksumBytes [3]byte
121         checksumBytes[0] = byte(e.crc >> 16)
122         checksumBytes[1] = byte(e.crc >> 8)
123         checksumBytes[2] = byte(e.crc)
124
125         var b64ChecksumBytes [4]byte
126         base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
127
128         return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
129 }
130
131 // Encode returns a WriteCloser which will encode the data written to it in
132 // OpenPGP armor.
133 func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
134         bType := []byte(blockType)
135         err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
136         if err != nil {
137                 return
138         }
139
140         for k, v := range headers {
141                 err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
142                 if err != nil {
143                         return
144                 }
145         }
146
147         _, err = out.Write(newline)
148         if err != nil {
149                 return
150         }
151
152         e := &encoding{
153                 out:       out,
154                 breaker:   newLineBreaker(out, 64),
155                 crc:       crc24Init,
156                 blockType: bType,
157         }
158         e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
159         return e, nil
160 }