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