* Switch to keybase go-crypto (for some elliptic curve key) + test
* Use assert.NoError 
and add a little more context to failing test description
* Use assert.(No)Error everywhere 🌈
and assert.Error in place of .Nil/.NotNil
		
	
			
		
			
				
	
	
		
			161 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2010 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package armor
 | |
| 
 | |
| import (
 | |
| 	"encoding/base64"
 | |
| 	"io"
 | |
| )
 | |
| 
 | |
| var armorHeaderSep = []byte(": ")
 | |
| var blockEnd = []byte("\n=")
 | |
| var newline = []byte("\n")
 | |
| var armorEndOfLineOut = []byte("-----\n")
 | |
| 
 | |
| // writeSlices writes its arguments to the given Writer.
 | |
| func writeSlices(out io.Writer, slices ...[]byte) (err error) {
 | |
| 	for _, s := range slices {
 | |
| 		_, err = out.Write(s)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // lineBreaker breaks data across several lines, all of the same byte length
 | |
| // (except possibly the last). Lines are broken with a single '\n'.
 | |
| type lineBreaker struct {
 | |
| 	lineLength  int
 | |
| 	line        []byte
 | |
| 	used        int
 | |
| 	out         io.Writer
 | |
| 	haveWritten bool
 | |
| }
 | |
| 
 | |
| func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
 | |
| 	return &lineBreaker{
 | |
| 		lineLength: lineLength,
 | |
| 		line:       make([]byte, lineLength),
 | |
| 		used:       0,
 | |
| 		out:        out,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (l *lineBreaker) Write(b []byte) (n int, err error) {
 | |
| 	n = len(b)
 | |
| 
 | |
| 	if n == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if l.used == 0 && l.haveWritten {
 | |
| 		_, err = l.out.Write([]byte{'\n'})
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if l.used+len(b) < l.lineLength {
 | |
| 		l.used += copy(l.line[l.used:], b)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	l.haveWritten = true
 | |
| 	_, err = l.out.Write(l.line[0:l.used])
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	excess := l.lineLength - l.used
 | |
| 	l.used = 0
 | |
| 
 | |
| 	_, err = l.out.Write(b[0:excess])
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	_, err = l.Write(b[excess:])
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (l *lineBreaker) Close() (err error) {
 | |
| 	if l.used > 0 {
 | |
| 		_, err = l.out.Write(l.line[0:l.used])
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // encoding keeps track of a running CRC24 over the data which has been written
 | |
| // to it and outputs a OpenPGP checksum when closed, followed by an armor
 | |
| // trailer.
 | |
| //
 | |
| // It's built into a stack of io.Writers:
 | |
| //    encoding -> base64 encoder -> lineBreaker -> out
 | |
| type encoding struct {
 | |
| 	out       io.Writer
 | |
| 	breaker   *lineBreaker
 | |
| 	b64       io.WriteCloser
 | |
| 	crc       uint32
 | |
| 	blockType []byte
 | |
| }
 | |
| 
 | |
| func (e *encoding) Write(data []byte) (n int, err error) {
 | |
| 	e.crc = crc24(e.crc, data)
 | |
| 	return e.b64.Write(data)
 | |
| }
 | |
| 
 | |
| func (e *encoding) Close() (err error) {
 | |
| 	err = e.b64.Close()
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	e.breaker.Close()
 | |
| 
 | |
| 	var checksumBytes [3]byte
 | |
| 	checksumBytes[0] = byte(e.crc >> 16)
 | |
| 	checksumBytes[1] = byte(e.crc >> 8)
 | |
| 	checksumBytes[2] = byte(e.crc)
 | |
| 
 | |
| 	var b64ChecksumBytes [4]byte
 | |
| 	base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
 | |
| 
 | |
| 	return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine, []byte{'\n'})
 | |
| }
 | |
| 
 | |
| // Encode returns a WriteCloser which will encode the data written to it in
 | |
| // OpenPGP armor.
 | |
| func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
 | |
| 	bType := []byte(blockType)
 | |
| 	err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	for k, v := range headers {
 | |
| 		err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	_, err = out.Write(newline)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	e := &encoding{
 | |
| 		out:       out,
 | |
| 		breaker:   newLineBreaker(out, 64),
 | |
| 		crc:       crc24Init,
 | |
| 		blockType: bType,
 | |
| 	}
 | |
| 	e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
 | |
| 	return e, nil
 | |
| }
 |