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  }