mirror of
https://github.com/FiloSottile/age.git
synced 2025-12-23 05:25:14 +00:00
internal/format: wrap body at 56 columns
This commit is contained in:
@@ -19,13 +19,10 @@ import (
|
||||
)
|
||||
|
||||
func TestX25519RoundTrip(t *testing.T) {
|
||||
var secretKey, publicKey, fileKey [32]byte
|
||||
var secretKey, publicKey [32]byte
|
||||
if _, err := rand.Read(secretKey[:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := rand.Read(fileKey[:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
curve25519.ScalarBaseMult(&publicKey, &secretKey)
|
||||
|
||||
r, err := age.NewX25519Recipient(publicKey[:])
|
||||
@@ -41,11 +38,17 @@ func TestX25519RoundTrip(t *testing.T) {
|
||||
t.Errorf("invalid Type values: %v, %v", r.Type(), i.Type())
|
||||
}
|
||||
|
||||
fileKey := make([]byte, 16)
|
||||
if _, err := rand.Read(fileKey[:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block, err := r.Wrap(fileKey[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", block)
|
||||
b := &bytes.Buffer{}
|
||||
block.Marshal(b)
|
||||
t.Logf("%s", b.Bytes())
|
||||
|
||||
out, err := i.Unwrap(block)
|
||||
if err != nil {
|
||||
@@ -82,7 +85,9 @@ func TestScryptRoundTrip(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", block)
|
||||
b := &bytes.Buffer{}
|
||||
block.Marshal(b)
|
||||
t.Logf("%s", b.Bytes())
|
||||
|
||||
out, err := i.Unwrap(block)
|
||||
if err != nil {
|
||||
@@ -125,7 +130,9 @@ func TestSSHRSARoundTrip(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", block)
|
||||
b := &bytes.Buffer{}
|
||||
block.Marshal(b)
|
||||
t.Logf("%s", b.Bytes())
|
||||
|
||||
out, err := i.Unwrap(block)
|
||||
if err != nil {
|
||||
@@ -168,7 +175,9 @@ func TestSSHEd25519RoundTrip(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", block)
|
||||
b := &bytes.Buffer{}
|
||||
block.Marshal(b)
|
||||
t.Logf("%s", b.Bytes())
|
||||
|
||||
out, err := i.Unwrap(block)
|
||||
if err != nil {
|
||||
|
||||
@@ -68,7 +68,7 @@ func (r *ScryptRecipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Body = []byte(format.EncodeToString(wrappedKey) + "\n")
|
||||
l.Body = wrappedKey
|
||||
|
||||
return l, nil
|
||||
}
|
||||
@@ -126,17 +126,13 @@ func (i *ScryptIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
if logN <= 0 {
|
||||
return nil, fmt.Errorf("invalid scrypt work factor: %v", logN)
|
||||
}
|
||||
wrappedKey, err := format.DecodeString(string(block.Body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse scrypt recipient: %v", err)
|
||||
}
|
||||
|
||||
k, err := scrypt.Key(i.password, salt, 1<<logN, 8, 1, chacha20poly1305.KeySize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate scrypt hash: %v", err)
|
||||
}
|
||||
|
||||
fileKey, err := aeadDecrypt(k, wrappedKey)
|
||||
fileKey, err := aeadDecrypt(k, block.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt file key: %v", err)
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (r *SSHRSARecipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Body = []byte(format.EncodeToString(wrappedKey) + "\n")
|
||||
l.Body = wrappedKey
|
||||
|
||||
return l, nil
|
||||
}
|
||||
@@ -110,10 +110,6 @@ func (i *SSHRSAIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
if len(hash) != 4 {
|
||||
return nil, errors.New("invalid ssh-rsa recipient block")
|
||||
}
|
||||
wrappedKey, err := format.DecodeString(string(block.Body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ssh-rsa recipient: %v", err)
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
h.Write(i.sshKey.Marshal())
|
||||
@@ -123,7 +119,7 @@ func (i *SSHRSAIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
}
|
||||
|
||||
fileKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, i.k,
|
||||
wrappedKey, []byte(oaepLabel))
|
||||
block.Body, []byte(oaepLabel))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt file key: %v", err)
|
||||
}
|
||||
@@ -254,7 +250,7 @@ func (r *SSHEd25519Recipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Body = []byte(format.EncodeToString(wrappedKey) + "\n")
|
||||
l.Body = wrappedKey
|
||||
|
||||
return l, nil
|
||||
}
|
||||
@@ -327,10 +323,6 @@ func (i *SSHEd25519Identity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
if len(publicKey) != 32 {
|
||||
return nil, errors.New("invalid ssh-ed25519 recipient block")
|
||||
}
|
||||
wrappedKey, err := format.DecodeString(string(block.Body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ssh-ed25519 recipient: %v", err)
|
||||
}
|
||||
|
||||
sH := sha256.New()
|
||||
sH.Write(i.sshKey.Marshal())
|
||||
@@ -357,7 +349,7 @@ func (i *SSHEd25519Identity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileKey, err := aeadDecrypt(wrappingKey, wrappedKey)
|
||||
fileKey, err := aeadDecrypt(wrappingKey, block.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt file key: %v", err)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (r *X25519Recipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Body = []byte(format.EncodeToString(wrappedKey) + "\n")
|
||||
l.Body = wrappedKey
|
||||
|
||||
return l, nil
|
||||
}
|
||||
@@ -140,10 +140,6 @@ func (i *X25519Identity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
if len(publicKey) != 32 {
|
||||
return nil, errors.New("invalid X25519 recipient block")
|
||||
}
|
||||
wrappedKey, err := format.DecodeString(string(block.Body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse X25519 recipient: %v", err)
|
||||
}
|
||||
|
||||
var sharedSecret, theirPublicKey [32]byte
|
||||
copy(theirPublicKey[:], publicKey)
|
||||
@@ -158,7 +154,7 @@ func (i *X25519Identity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileKey, err := aeadDecrypt(wrappingKey, wrappedKey)
|
||||
fileKey, err := aeadDecrypt(wrappingKey, block.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt file key: %v", err)
|
||||
}
|
||||
|
||||
@@ -30,39 +30,53 @@ type Recipient struct {
|
||||
var b64 = base64.RawURLEncoding.Strict()
|
||||
|
||||
func DecodeString(s string) ([]byte, error) {
|
||||
// CR and LF are ignored by DecodeString. LF is handled by the parser,
|
||||
// but CR can introduce malleability.
|
||||
if strings.Contains(s, "\r") {
|
||||
return nil, errors.New(`invalid character: \r`)
|
||||
// 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`)
|
||||
}
|
||||
return b64.DecodeString(s)
|
||||
}
|
||||
|
||||
var EncodeToString = b64.EncodeToString // TODO: wrap lines
|
||||
var EncodeToString = b64.EncodeToString
|
||||
|
||||
const bytesPerLine = 56 / 4 * 3 // 56 columns of Base64
|
||||
|
||||
const intro = "This is a file encrypted with age-tool.com, version 1\n"
|
||||
|
||||
var recipientPrefix = []byte("->")
|
||||
var footerPrefix = []byte("---")
|
||||
|
||||
func (r *Recipient) Marshal(w io.Writer) error {
|
||||
if _, err := w.Write(recipientPrefix); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, a := range append([]string{r.Type}, r.Args...) {
|
||||
if _, err := io.WriteString(w, " "+a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Header) MarshalWithoutMAC(w io.Writer) error {
|
||||
if _, err := io.WriteString(w, intro); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range h.Recipients {
|
||||
if _, err := w.Write(recipientPrefix); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, a := range append([]string{r.Type}, r.Args...) {
|
||||
if _, err := io.WriteString(w, " "+a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := io.WriteString(w, "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: check that Body ends with a newline.
|
||||
if _, err := w.Write(r.Body); err != nil {
|
||||
if err := r.Marshal(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -132,7 +146,18 @@ func Parse(input io.Reader) (*Header, io.Reader, error) {
|
||||
h.Recipients = append(h.Recipients, r)
|
||||
|
||||
} else if r != nil {
|
||||
r.Body = append(r.Body, line...)
|
||||
b, err := DecodeString(strings.TrimSuffix(string(line), "\n"))
|
||||
if err != nil {
|
||||
return nil, nil, errorf("malformed body line %q: %v", line, err)
|
||||
}
|
||||
if len(b) > bytesPerLine {
|
||||
return nil, nil, errorf("malformed body line %q: too long", line)
|
||||
}
|
||||
r.Body = append(r.Body, b...)
|
||||
if len(b) < bytesPerLine {
|
||||
// Only the last line of a body can be short.
|
||||
r = nil
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, nil, errorf("unexpected line: %q", line)
|
||||
|
||||
Reference in New Issue
Block a user