...

Source file src/golang.org/x/crypto/openpgp/armor/encode.go

Documentation: golang.org/x/crypto/openpgp/armor

     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  //
   100  //	encoding -> base64 encoder -> lineBreaker -> out
   101  type encoding struct {
   102  	out       io.Writer
   103  	breaker   *lineBreaker
   104  	b64       io.WriteCloser
   105  	crc       uint32
   106  	blockType []byte
   107  }
   108  
   109  func (e *encoding) Write(data []byte) (n int, err error) {
   110  	e.crc = crc24(e.crc, data)
   111  	return e.b64.Write(data)
   112  }
   113  
   114  func (e *encoding) Close() (err error) {
   115  	err = e.b64.Close()
   116  	if err != nil {
   117  		return
   118  	}
   119  	e.breaker.Close()
   120  
   121  	var checksumBytes [3]byte
   122  	checksumBytes[0] = byte(e.crc >> 16)
   123  	checksumBytes[1] = byte(e.crc >> 8)
   124  	checksumBytes[2] = byte(e.crc)
   125  
   126  	var b64ChecksumBytes [4]byte
   127  	base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
   128  
   129  	return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
   130  }
   131  
   132  // Encode returns a WriteCloser which will encode the data written to it in
   133  // OpenPGP armor.
   134  func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
   135  	bType := []byte(blockType)
   136  	err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
   137  	if err != nil {
   138  		return
   139  	}
   140  
   141  	for k, v := range headers {
   142  		err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
   143  		if err != nil {
   144  			return
   145  		}
   146  	}
   147  
   148  	_, err = out.Write(newline)
   149  	if err != nil {
   150  		return
   151  	}
   152  
   153  	e := &encoding{
   154  		out:       out,
   155  		breaker:   newLineBreaker(out, 64),
   156  		crc:       crc24Init,
   157  		blockType: bType,
   158  	}
   159  	e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
   160  	return e, nil
   161  }
   162  

View as plain text