x25519.go (4778B)
1 // Copyright 2019 Google LLC 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file or at 5 // https://developers.google.com/open-source/licenses/bsd 6 7 package age 8 9 import ( 10 "crypto/rand" 11 "crypto/sha256" 12 "errors" 13 "fmt" 14 "io" 15 "strings" 16 17 "github.com/FiloSottile/age/internal/format" 18 "golang.org/x/crypto/chacha20poly1305" 19 "golang.org/x/crypto/curve25519" 20 "golang.org/x/crypto/hkdf" 21 ) 22 23 const x25519Label = "age-tool.com X25519" 24 25 type X25519Recipient struct { 26 theirPublicKey [32]byte 27 } 28 29 var _ Recipient = &X25519Recipient{} 30 31 func (*X25519Recipient) Type() string { return "X25519" } 32 33 func NewX25519Recipient(publicKey []byte) (*X25519Recipient, error) { 34 if len(publicKey) != 32 { 35 return nil, errors.New("invalid X25519 public key") 36 } 37 r := &X25519Recipient{} 38 copy(r.theirPublicKey[:], publicKey) 39 return r, nil 40 } 41 42 func ParseX25519Recipient(s string) (*X25519Recipient, error) { 43 if !strings.HasPrefix(s, "pubkey:") { 44 return nil, fmt.Errorf("malformed recipient: %s", s) 45 } 46 pubKey := strings.TrimPrefix(s, "pubkey:") 47 k, err := format.DecodeString(pubKey) 48 if err != nil { 49 return nil, fmt.Errorf("malformed recipient: %s", s) 50 } 51 r, err := NewX25519Recipient(k) 52 if err != nil { 53 return nil, fmt.Errorf("malformed recipient: %s", s) 54 } 55 return r, nil 56 } 57 58 func (r *X25519Recipient) Wrap(fileKey []byte) (*format.Recipient, error) { 59 var ephemeral, ourPublicKey [32]byte 60 if _, err := rand.Read(ephemeral[:]); err != nil { 61 return nil, err 62 } 63 curve25519.ScalarBaseMult(&ourPublicKey, &ephemeral) 64 65 var sharedSecret [32]byte 66 curve25519.ScalarMult(&sharedSecret, &ephemeral, &r.theirPublicKey) 67 68 l := &format.Recipient{ 69 Type: "X25519", 70 Args: []string{format.EncodeToString(ourPublicKey[:])}, 71 } 72 73 salt := make([]byte, 0, 32*2) 74 salt = append(salt, ourPublicKey[:]...) 75 salt = append(salt, r.theirPublicKey[:]...) 76 h := hkdf.New(sha256.New, sharedSecret[:], salt, []byte(x25519Label)) 77 wrappingKey := make([]byte, chacha20poly1305.KeySize) 78 if _, err := io.ReadFull(h, wrappingKey); err != nil { 79 return nil, err 80 } 81 82 wrappedKey, err := aeadEncrypt(wrappingKey, fileKey) 83 if err != nil { 84 return nil, err 85 } 86 l.Body = []byte(format.EncodeToString(wrappedKey) + "\n") 87 88 return l, nil 89 } 90 91 func (r *X25519Recipient) String() string { 92 return "pubkey:" + format.EncodeToString(r.theirPublicKey[:]) 93 } 94 95 type X25519Identity struct { 96 secretKey, ourPublicKey [32]byte 97 } 98 99 var _ Identity = &X25519Identity{} 100 101 func (*X25519Identity) Type() string { return "X25519" } 102 103 func NewX25519Identity(secretKey []byte) (*X25519Identity, error) { 104 if len(secretKey) != 32 { 105 return nil, errors.New("invalid X25519 secret key") 106 } 107 i := &X25519Identity{} 108 copy(i.secretKey[:], secretKey) 109 curve25519.ScalarBaseMult(&i.ourPublicKey, &i.secretKey) 110 return i, nil 111 } 112 113 func ParseX25519Identity(s string) (*X25519Identity, error) { 114 if !strings.HasPrefix(s, "AGE_SECRET_KEY_") { 115 return nil, fmt.Errorf("malformed secret key: %s", s) 116 } 117 privKey := strings.TrimPrefix(s, "AGE_SECRET_KEY_") 118 k, err := format.DecodeString(privKey) 119 if err != nil { 120 return nil, fmt.Errorf("malformed secret key: %s", s) 121 } 122 r, err := NewX25519Identity(k) 123 if err != nil { 124 return nil, fmt.Errorf("malformed secret key: %s", s) 125 } 126 return r, nil 127 } 128 129 func (i *X25519Identity) Unwrap(block *format.Recipient) ([]byte, error) { 130 if block.Type != "X25519" { 131 return nil, errors.New("wrong recipient block type") 132 } 133 if len(block.Args) != 1 { 134 return nil, errors.New("invalid X25519 recipient block") 135 } 136 publicKey, err := format.DecodeString(block.Args[0]) 137 if err != nil { 138 return nil, fmt.Errorf("failed to parse X25519 recipient: %v", err) 139 } 140 if len(publicKey) != 32 { 141 return nil, errors.New("invalid X25519 recipient block") 142 } 143 wrappedKey, err := format.DecodeString(string(block.Body)) 144 if err != nil { 145 return nil, fmt.Errorf("failed to parse X25519 recipient: %v", err) 146 } 147 148 var sharedSecret, theirPublicKey [32]byte 149 copy(theirPublicKey[:], publicKey) 150 curve25519.ScalarMult(&sharedSecret, &i.secretKey, &theirPublicKey) 151 152 salt := make([]byte, 0, 32*2) 153 salt = append(salt, theirPublicKey[:]...) 154 salt = append(salt, i.ourPublicKey[:]...) 155 h := hkdf.New(sha256.New, sharedSecret[:], salt, []byte(x25519Label)) 156 wrappingKey := make([]byte, chacha20poly1305.KeySize) 157 if _, err := io.ReadFull(h, wrappingKey); err != nil { 158 return nil, err 159 } 160 161 fileKey, err := aeadDecrypt(wrappingKey, wrappedKey) 162 if err != nil { 163 return nil, fmt.Errorf("failed to decrypt file key: %v", err) 164 } 165 return fileKey, nil 166 } 167 168 func (i *X25519Identity) Recipient() *X25519Recipient { 169 r := &X25519Recipient{} 170 r.theirPublicKey = i.ourPublicKey 171 return r 172 } 173 174 func (i *X25519Identity) String() string { 175 return "AGE_SECRET_KEY_" + format.EncodeToString(i.secretKey[:]) 176 }