 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			188 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2019 The age 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 provides a strict, streaming implementation of the ASCII
 | |
| // armoring format for age files.
 | |
| //
 | |
| // It's PEM with type "AGE ENCRYPTED FILE", 64 character columns, no headers,
 | |
| // and strict base64 decoding.
 | |
| package armor
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"bytes"
 | |
| 	"encoding/base64"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 
 | |
| 	"filippo.io/age/internal/format"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	Header = "-----BEGIN AGE ENCRYPTED FILE-----"
 | |
| 	Footer = "-----END AGE ENCRYPTED FILE-----"
 | |
| )
 | |
| 
 | |
| type armoredWriter struct {
 | |
| 	started, closed bool
 | |
| 	encoder         *format.WrappedBase64Encoder
 | |
| 	dst             io.Writer
 | |
| }
 | |
| 
 | |
| func (a *armoredWriter) Write(p []byte) (int, error) {
 | |
| 	if !a.started {
 | |
| 		if _, err := io.WriteString(a.dst, Header+"\n"); err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 	}
 | |
| 	a.started = true
 | |
| 	return a.encoder.Write(p)
 | |
| }
 | |
| 
 | |
| func (a *armoredWriter) Close() error {
 | |
| 	if a.closed {
 | |
| 		return errors.New("ArmoredWriter already closed")
 | |
| 	}
 | |
| 	a.closed = true
 | |
| 	if err := a.encoder.Close(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	footer := Footer + "\n"
 | |
| 	if !a.encoder.LastLineIsEmpty() {
 | |
| 		footer = "\n" + footer
 | |
| 	}
 | |
| 	_, err := io.WriteString(a.dst, footer)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func NewWriter(dst io.Writer) io.WriteCloser {
 | |
| 	// TODO: write a test with aligned and misaligned sizes, and 8 and 10 steps.
 | |
| 	return &armoredWriter{
 | |
| 		dst:     dst,
 | |
| 		encoder: format.NewWrappedBase64Encoder(base64.StdEncoding, dst),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type armoredReader struct {
 | |
| 	r       *bufio.Reader
 | |
| 	started bool
 | |
| 	unread  []byte // backed by buf
 | |
| 	buf     [format.BytesPerLine]byte
 | |
| 	err     error
 | |
| }
 | |
| 
 | |
| func NewReader(r io.Reader) io.Reader {
 | |
| 	return &armoredReader{r: bufio.NewReader(r)}
 | |
| }
 | |
| 
 | |
| func (r *armoredReader) Read(p []byte) (int, error) {
 | |
| 	if len(r.unread) > 0 {
 | |
| 		n := copy(p, r.unread)
 | |
| 		r.unread = r.unread[n:]
 | |
| 		return n, nil
 | |
| 	}
 | |
| 	if r.err != nil {
 | |
| 		return 0, r.err
 | |
| 	}
 | |
| 
 | |
| 	getLine := func() ([]byte, error) {
 | |
| 		line, err := r.r.ReadBytes('\n')
 | |
| 		if err == io.EOF && len(line) == 0 {
 | |
| 			return nil, io.ErrUnexpectedEOF
 | |
| 		} else if err != nil && err != io.EOF {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		line = bytes.TrimSuffix(line, []byte("\n"))
 | |
| 		line = bytes.TrimSuffix(line, []byte("\r"))
 | |
| 		return line, nil
 | |
| 	}
 | |
| 
 | |
| 	const maxWhitespace = 1024
 | |
| 	drainTrailing := func() error {
 | |
| 		buf, err := io.ReadAll(io.LimitReader(r.r, maxWhitespace))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if len(bytes.TrimSpace(buf)) != 0 {
 | |
| 			return errors.New("trailing data after armored file")
 | |
| 		}
 | |
| 		if len(buf) == maxWhitespace {
 | |
| 			return errors.New("too much trailing whitespace")
 | |
| 		}
 | |
| 		return io.EOF
 | |
| 	}
 | |
| 
 | |
| 	var removedWhitespace int
 | |
| 	for !r.started {
 | |
| 		line, err := getLine()
 | |
| 		if err != nil {
 | |
| 			return 0, r.setErr(err)
 | |
| 		}
 | |
| 		// Ignore leading whitespace.
 | |
| 		if len(bytes.TrimSpace(line)) == 0 {
 | |
| 			removedWhitespace += len(line) + 1
 | |
| 			if removedWhitespace > maxWhitespace {
 | |
| 				return 0, r.setErr(errors.New("too much leading whitespace"))
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 		if string(line) != Header {
 | |
| 			return 0, r.setErr(fmt.Errorf("invalid first line: %q", line))
 | |
| 		}
 | |
| 		r.started = true
 | |
| 	}
 | |
| 	line, err := getLine()
 | |
| 	if err != nil {
 | |
| 		return 0, r.setErr(err)
 | |
| 	}
 | |
| 	if string(line) == Footer {
 | |
| 		return 0, r.setErr(drainTrailing())
 | |
| 	}
 | |
| 	if len(line) > format.ColumnsPerLine {
 | |
| 		return 0, r.setErr(errors.New("column limit exceeded"))
 | |
| 	}
 | |
| 	r.unread = r.buf[:]
 | |
| 	n, err := base64.StdEncoding.Strict().Decode(r.unread, line)
 | |
| 	if err != nil {
 | |
| 		return 0, r.setErr(err)
 | |
| 	}
 | |
| 	r.unread = r.unread[:n]
 | |
| 
 | |
| 	if n < format.BytesPerLine {
 | |
| 		line, err := getLine()
 | |
| 		if err != nil {
 | |
| 			return 0, r.setErr(err)
 | |
| 		}
 | |
| 		if string(line) != Footer {
 | |
| 			return 0, r.setErr(fmt.Errorf("invalid closing line: %q", line))
 | |
| 		}
 | |
| 		r.setErr(drainTrailing())
 | |
| 	}
 | |
| 
 | |
| 	nn := copy(p, r.unread)
 | |
| 	r.unread = r.unread[nn:]
 | |
| 	return nn, nil
 | |
| }
 | |
| 
 | |
| type Error struct {
 | |
| 	err error
 | |
| }
 | |
| 
 | |
| func (e *Error) Error() string {
 | |
| 	return "invalid armor: " + e.err.Error()
 | |
| }
 | |
| 
 | |
| func (e *Error) Unwrap() error {
 | |
| 	return e.err
 | |
| }
 | |
| 
 | |
| func (r *armoredReader) setErr(err error) error {
 | |
| 	if err != io.EOF {
 | |
| 		err = &Error{err}
 | |
| 	}
 | |
| 	r.err = err
 | |
| 	return err
 | |
| }
 |