mirror of
https://github.com/FiloSottile/age.git
synced 2026-01-10 13:47:20 +00:00
internal/age,internal/format: implement armored file generation
This commit is contained in:
69
internal/format/armor.go
Normal file
69
internal/format/armor.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
package format
|
||||
|
||||
import "io"
|
||||
|
||||
import "encoding/base64"
|
||||
|
||||
type newlineWriter struct {
|
||||
dst io.Writer
|
||||
written int
|
||||
}
|
||||
|
||||
func (w *newlineWriter) Write(p []byte) (n int, err error) {
|
||||
for len(p) > 0 {
|
||||
remainingInLine := columnsPerLine - (w.written % columnsPerLine)
|
||||
if remainingInLine == columnsPerLine && w.written != 0 {
|
||||
if _, err := w.dst.Write([]byte("\n")); err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
toWrite := remainingInLine
|
||||
if toWrite > len(p) {
|
||||
toWrite = len(p)
|
||||
}
|
||||
nn, err := w.dst.Write(p[:toWrite])
|
||||
n += nn
|
||||
w.written += nn
|
||||
p = p[nn:]
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type CloserFunc func() error
|
||||
|
||||
func (f CloserFunc) Close() error { return f() }
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func NopCloser(w io.Writer) io.WriteCloser { return nopCloser{w} }
|
||||
|
||||
func ArmoredWriter(dst io.Writer) io.WriteCloser {
|
||||
// TODO: write a test with aligned and misaligned sizes, and 8 and 10 steps.
|
||||
w := base64.NewEncoder(b64, &newlineWriter{dst: dst})
|
||||
return struct {
|
||||
io.Writer
|
||||
io.Closer
|
||||
}{
|
||||
Writer: w,
|
||||
Closer: CloserFunc(func() error {
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := dst.Write([]byte("\n--- end of file ---\n"))
|
||||
return err
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
Armor bool
|
||||
Recipients []*Recipient
|
||||
MAC []byte
|
||||
}
|
||||
@@ -40,9 +41,11 @@ func DecodeString(s string) ([]byte, error) {
|
||||
|
||||
var EncodeToString = b64.EncodeToString
|
||||
|
||||
const bytesPerLine = 56 / 4 * 3 // 56 columns of Base64
|
||||
const columnsPerLine = 56
|
||||
const bytesPerLine = columnsPerLine / 4 * 3
|
||||
|
||||
const intro = "This is a file encrypted with age-tool.com, version 1\n"
|
||||
const introWithArmor = "This is an armored file encrypted with age-tool.com, version 1\n"
|
||||
|
||||
var recipientPrefix = []byte("->")
|
||||
var footerPrefix = []byte("---")
|
||||
@@ -59,22 +62,26 @@ func (r *Recipient) Marshal(w io.Writer) error {
|
||||
if _, err := io.WriteString(w, "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < len(r.Body); i += bytesPerLine {
|
||||
n := bytesPerLine
|
||||
if n > len(r.Body)-i {
|
||||
n = len(r.Body) - i
|
||||
}
|
||||
s := EncodeToString(r.Body[i : i+n])
|
||||
if _, err := io.WriteString(w, s+"\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
ww := base64.NewEncoder(b64, &newlineWriter{dst: w})
|
||||
if _, err := ww.Write(r.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if err := ww.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := io.WriteString(w, "\n")
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Header) MarshalWithoutMAC(w io.Writer) error {
|
||||
if _, err := io.WriteString(w, intro); err != nil {
|
||||
return err
|
||||
if h.Armor {
|
||||
if _, err := io.WriteString(w, introWithArmor); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := io.WriteString(w, intro); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, r := range h.Recipients {
|
||||
if err := r.Marshal(w); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user