github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/crypto/openpgp/armor/encode.go (about) 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 }