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 5package armor 6 7import ( 8 "encoding/base64" 9 "io" 10) 11 12var armorHeaderSep = []byte(": ") 13var blockEnd = []byte("\n=") 14var newline = []byte("\n") 15var armorEndOfLineOut = []byte("-----\n") 16 17// writeSlices writes its arguments to the given Writer. 18func 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'. 30type lineBreaker struct { 31 lineLength int 32 line []byte 33 used int 34 out io.Writer 35 haveWritten bool 36} 37 38func 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 47func (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 83func (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 100type encoding struct { 101 out io.Writer 102 breaker *lineBreaker 103 b64 io.WriteCloser 104 crc uint32 105 blockType []byte 106} 107 108func (e *encoding) Write(data []byte) (n int, err error) { 109 e.crc = crc24(e.crc, data) 110 return e.b64.Write(data) 111} 112 113func (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. 133func 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} 161