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.
12 var armorHeaderSep = []byte(": ")
13 var blockEnd = []byte("\n=")
14 var newline = []byte("\n")
15 var armorEndOfLineOut = []byte("-----\n")
17 // writeSlices writes its arguments to the given Writer.
18 func writeSlices(out io.Writer, slices ...[]byte) (err error) {
19 for _, s := range slices {
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 {
38 func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
40 lineLength: lineLength,
41 line: make([]byte, lineLength),
47 func (l *lineBreaker) Write(b []byte) (n int, err error) {
54 if l.used == 0 && l.haveWritten {
55 _, err = l.out.Write([]byte{'\n'})
61 if l.used+len(b) < l.lineLength {
62 l.used += copy(l.line[l.used:], b)
67 _, err = l.out.Write(l.line[0:l.used])
71 excess := l.lineLength - l.used
74 _, err = l.out.Write(b[0:excess])
79 _, err = l.Write(b[excess:])
83 func (l *lineBreaker) Close() (err error) {
85 _, err = l.out.Write(l.line[0:l.used])
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
98 // It's built into a stack of io.Writers:
99 // encoding -> base64 encoder -> lineBreaker -> out
100 type encoding struct {
108 func (e *encoding) Write(data []byte) (n int, err error) {
109 e.crc = crc24(e.crc, data)
110 return e.b64.Write(data)
113 func (e *encoding) Close() (err error) {
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)
125 var b64ChecksumBytes [4]byte
126 base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
128 return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
131 // Encode returns a WriteCloser which will encode the data written to it in
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)
140 for k, v := range headers {
141 err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
147 _, err = out.Write(newline)
154 breaker: newLineBreaker(out, 64),
158 e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)