2021-11-24 10:29:15 +00:00
// Copyright 2019 The age Authors. All rights reserved.
2019-10-07 03:16:20 +00:00
// Use of this source code is governed by a BSD-style
2021-11-24 10:29:15 +00:00
// license that can be found in the LICENSE file.
2019-10-07 03:16:20 +00:00
2019-11-25 00:15:53 +00:00
// Package format implements the age file format.
2019-08-03 22:42:59 +00:00
package format
import (
"bufio"
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"strings"
)
type Header struct {
2020-06-27 23:44:26 +00:00
Recipients [ ] * Stanza
2019-08-03 22:42:59 +00:00
MAC [ ] byte
}
2020-06-27 23:44:26 +00:00
// Stanza is assignable to age.Stanza, and if this package is made public,
// age.Stanza can be made a type alias of this type.
type Stanza struct {
2019-08-03 22:42:59 +00:00
Type string
Args [ ] string
Body [ ] byte
}
2019-12-26 14:37:35 +00:00
var b64 = base64 . RawStdEncoding . Strict ( )
2019-08-03 22:42:59 +00:00
2019-10-07 01:19:04 +00:00
func DecodeString ( s string ) ( [ ] byte , error ) {
2019-10-13 21:24:21 +00:00
// CR and LF are ignored by DecodeString, but we don't want any malleability.
if strings . ContainsAny ( s , "\n\r" ) {
return nil , errors . New ( ` unexpected newline character ` )
2019-08-03 22:42:59 +00:00
}
return b64 . DecodeString ( s )
}
2019-10-13 21:24:21 +00:00
var EncodeToString = b64 . EncodeToString
2020-05-18 03:50:13 +00:00
const ColumnsPerLine = 64
2021-05-26 11:35:30 +00:00
2020-05-18 03:50:13 +00:00
const BytesPerLine = ColumnsPerLine / 4 * 3
2021-05-26 11:35:30 +00:00
// NewWrappedBase64Encoder returns a WrappedBase64Encoder that writes to dst.
func NewWrappedBase64Encoder ( enc * base64 . Encoding , dst io . Writer ) * WrappedBase64Encoder {
w := & WrappedBase64Encoder { dst : dst }
w . enc = base64 . NewEncoder ( enc , WriterFunc ( w . writeWrapped ) )
return w
2020-05-18 03:50:13 +00:00
}
2021-05-26 11:35:30 +00:00
type WriterFunc func ( p [ ] byte ) ( int , error )
func ( f WriterFunc ) Write ( p [ ] byte ) ( int , error ) { return f ( p ) }
// WrappedBase64Encoder is a standard base64 encoder that inserts an LF
// character every ColumnsPerLine bytes. It does not insert a newline neither at
// the beginning nor at the end of the stream, but it ensures the last line is
// shorter than ColumnsPerLine, which means it might be empty.
type WrappedBase64Encoder struct {
enc io . WriteCloser
2020-05-18 03:50:13 +00:00
dst io . Writer
written int
2021-01-02 16:19:25 +00:00
buf bytes . Buffer
2020-05-18 03:50:13 +00:00
}
2021-05-26 11:35:30 +00:00
func ( w * WrappedBase64Encoder ) Write ( p [ ] byte ) ( int , error ) { return w . enc . Write ( p ) }
func ( w * WrappedBase64Encoder ) Close ( ) error {
return w . enc . Close ( )
}
func ( w * WrappedBase64Encoder ) writeWrapped ( p [ ] byte ) ( int , error ) {
2021-01-02 16:19:25 +00:00
if w . buf . Len ( ) != 0 {
2021-05-26 11:35:30 +00:00
panic ( "age: internal error: non-empty WrappedBase64Encoder.buf" )
2021-01-02 16:19:25 +00:00
}
2020-05-18 03:50:13 +00:00
for len ( p ) > 0 {
2021-01-03 19:57:09 +00:00
toWrite := ColumnsPerLine - ( w . written % ColumnsPerLine )
2020-05-18 03:50:13 +00:00
if toWrite > len ( p ) {
toWrite = len ( p )
}
2021-01-02 16:19:25 +00:00
n , _ := w . buf . Write ( p [ : toWrite ] )
w . written += n
p = p [ n : ]
2021-01-03 19:57:09 +00:00
if w . written % ColumnsPerLine == 0 {
w . buf . Write ( [ ] byte ( "\n" ) )
}
2021-01-02 16:19:25 +00:00
}
if _ , err := w . buf . WriteTo ( w . dst ) ; err != nil {
// We always return n = 0 on error because it's hard to work back to the
// input length that ended up written out. Not ideal, but Write errors
// are not recoverable anyway.
return 0 , err
2020-05-18 03:50:13 +00:00
}
2021-01-02 16:19:25 +00:00
return len ( p ) , nil
2020-05-18 03:50:13 +00:00
}
2019-10-07 01:19:04 +00:00
2021-05-26 11:35:30 +00:00
// LastLineIsEmpty returns whether the last output line was empty, either
// because no input was written, or because a multiple of BytesPerLine was.
//
// Calling LastLineIsEmpty before Close is meaningless.
func ( w * WrappedBase64Encoder ) LastLineIsEmpty ( ) bool {
return w . written % ColumnsPerLine == 0
}
2019-12-26 16:59:20 +00:00
const intro = "age-encryption.org/v1\n"
2019-08-03 22:42:59 +00:00
var recipientPrefix = [ ] byte ( "->" )
2021-05-26 11:35:30 +00:00
2019-08-03 22:42:59 +00:00
var footerPrefix = [ ] byte ( "---" )
2020-06-27 23:44:26 +00:00
func ( r * Stanza ) Marshal ( w io . Writer ) error {
2019-10-13 21:24:21 +00:00
if _ , err := w . Write ( recipientPrefix ) ; err != nil {
2019-08-03 22:42:59 +00:00
return err
}
2019-10-13 21:24:21 +00:00
for _ , a := range append ( [ ] string { r . Type } , r . Args ... ) {
if _ , err := io . WriteString ( w , " " + a ) ; err != nil {
2019-08-03 22:42:59 +00:00
return err
}
2019-10-13 21:24:21 +00:00
}
if _ , err := io . WriteString ( w , "\n" ) ; err != nil {
return err
}
2021-05-26 11:35:30 +00:00
ww := NewWrappedBase64Encoder ( b64 , w )
2019-11-25 02:10:18 +00:00
if _ , err := ww . Write ( r . Body ) ; err != nil {
return err
}
if err := ww . Close ( ) ; err != nil {
return err
2019-10-13 21:24:21 +00:00
}
2019-11-25 02:10:18 +00:00
_ , err := io . WriteString ( w , "\n" )
return err
2019-10-13 21:24:21 +00:00
}
func ( h * Header ) MarshalWithoutMAC ( w io . Writer ) error {
2019-12-26 16:53:15 +00:00
if _ , err := io . WriteString ( w , intro ) ; err != nil {
return err
2019-10-13 21:24:21 +00:00
}
for _ , r := range h . Recipients {
if err := r . Marshal ( w ) ; err != nil {
2019-08-03 22:42:59 +00:00
return err
}
}
2019-10-07 01:57:26 +00:00
_ , err := fmt . Fprintf ( w , "%s" , footerPrefix )
2019-10-07 01:19:04 +00:00
return err
}
func ( h * Header ) Marshal ( w io . Writer ) error {
if err := h . MarshalWithoutMAC ( w ) ; err != nil {
return err
}
2019-08-03 22:42:59 +00:00
mac := b64 . EncodeToString ( h . MAC )
2019-10-07 01:19:04 +00:00
_ , err := fmt . Fprintf ( w , " %s\n" , mac )
2019-08-03 22:42:59 +00:00
return err
}
type ParseError string
func ( e ParseError ) Error ( ) string {
return "parsing age header: " + string ( e )
}
func errorf ( format string , a ... interface { } ) error {
return ParseError ( fmt . Sprintf ( format , a ... ) )
}
// Parse returns the header and a Reader that begins at the start of the
// payload.
func Parse ( input io . Reader ) ( * Header , io . Reader , error ) {
h := & Header { }
rr := bufio . NewReader ( input )
line , err := rr . ReadString ( '\n' )
if err != nil {
return nil , nil , errorf ( "failed to read intro: %v" , err )
}
2019-12-26 16:53:15 +00:00
if line != intro {
2019-08-03 22:42:59 +00:00
return nil , nil , errorf ( "unexpected intro: %q" , line )
}
2020-06-27 23:44:26 +00:00
var r * Stanza
2019-08-03 22:42:59 +00:00
for {
line , err := rr . ReadBytes ( '\n' )
if err != nil {
return nil , nil , errorf ( "failed to read header: %v" , err )
}
if bytes . HasPrefix ( line , footerPrefix ) {
2021-01-03 19:57:09 +00:00
if r != nil {
return nil , nil , errorf ( "malformed body line %q: reached footer without previous stanza being closed\nNote: this might be a file encrypted with an old beta version of rage. Use rage to decrypt it." , line )
}
2019-08-03 22:42:59 +00:00
prefix , args := splitArgs ( line )
2019-10-07 01:57:26 +00:00
if prefix != string ( footerPrefix ) || len ( args ) != 1 {
2019-08-03 22:42:59 +00:00
return nil , nil , errorf ( "malformed closing line: %q" , line )
}
2019-10-07 01:57:26 +00:00
h . MAC , err = DecodeString ( args [ 0 ] )
2019-08-03 22:42:59 +00:00
if err != nil {
return nil , nil , errorf ( "malformed closing line %q: %v" , line , err )
}
break
} else if bytes . HasPrefix ( line , recipientPrefix ) {
2021-01-03 19:57:09 +00:00
if r != nil {
return nil , nil , errorf ( "malformed body line %q: new stanza started without previous stanza being closed\nNote: this might be a file encrypted with an old beta version of rage. Use rage to decrypt it." , line )
}
2020-06-27 23:44:26 +00:00
r = & Stanza { }
2019-08-03 22:42:59 +00:00
prefix , args := splitArgs ( line )
if prefix != string ( recipientPrefix ) || len ( args ) < 1 {
return nil , nil , errorf ( "malformed recipient: %q" , line )
}
2020-03-25 05:29:30 +00:00
for _ , a := range args {
if ! isValidString ( a ) {
return nil , nil , errorf ( "malformed recipient: %q" , line )
}
}
2019-08-03 22:42:59 +00:00
r . Type = args [ 0 ]
r . Args = args [ 1 : ]
h . Recipients = append ( h . Recipients , r )
} else if r != nil {
2019-10-13 21:24:21 +00:00
b , err := DecodeString ( strings . TrimSuffix ( string ( line ) , "\n" ) )
if err != nil {
return nil , nil , errorf ( "malformed body line %q: %v" , line , err )
}
2020-05-18 03:50:13 +00:00
if len ( b ) > BytesPerLine {
2019-10-13 21:24:21 +00:00
return nil , nil , errorf ( "malformed body line %q: too long" , line )
}
r . Body = append ( r . Body , b ... )
2020-05-18 03:50:13 +00:00
if len ( b ) < BytesPerLine {
2019-10-13 21:24:21 +00:00
// Only the last line of a body can be short.
r = nil
}
2019-08-03 22:42:59 +00:00
} else {
return nil , nil , errorf ( "unexpected line: %q" , line )
}
}
2020-05-18 06:28:31 +00:00
// If input is a bufio.Reader, rr might be equal to input because
// bufio.NewReader short-circuits. In this case we can just return it (and
// we would end up reading the buffer twice if we prepended the peek below).
if rr == input {
return h , rr , nil
}
// Otherwise, unwind the bufio overread and return the unbuffered input.
2019-08-03 22:42:59 +00:00
buf , err := rr . Peek ( rr . Buffered ( ) )
if err != nil {
return nil , nil , errorf ( "internal error: %v" , err )
}
payload := io . MultiReader ( bytes . NewReader ( buf ) , input )
return h , payload , nil
}
func splitArgs ( line [ ] byte ) ( string , [ ] string ) {
l := strings . TrimSuffix ( string ( line ) , "\n" )
parts := strings . Split ( l , " " )
return parts [ 0 ] , parts [ 1 : ]
}
2020-03-25 05:29:30 +00:00
func isValidString ( s string ) bool {
if len ( s ) == 0 {
return false
}
for _ , c := range s {
if c < 33 || c > 126 {
return false
}
}
return true
}