mirror of
https://github.com/FiloSottile/age.git
synced 2025-12-23 13:35:14 +00:00
agessh: use filippo.io/edwards25519 for Ed25519 to Curve25519 conversion
This commit is contained in:
@@ -24,10 +24,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"filippo.io/age"
|
"filippo.io/age"
|
||||||
"filippo.io/age/internal/format"
|
"filippo.io/age/internal/format"
|
||||||
|
"filippo.io/edwards25519"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
@@ -139,20 +139,24 @@ func NewEd25519Recipient(pk ssh.PublicKey) (*Ed25519Recipient, error) {
|
|||||||
if pk.Type() != "ssh-ed25519" {
|
if pk.Type() != "ssh-ed25519" {
|
||||||
return nil, errors.New("SSH public key is not an Ed25519 key")
|
return nil, errors.New("SSH public key is not an Ed25519 key")
|
||||||
}
|
}
|
||||||
r := &Ed25519Recipient{
|
|
||||||
sshKey: pk,
|
|
||||||
}
|
|
||||||
|
|
||||||
if pk, ok := pk.(ssh.CryptoPublicKey); ok {
|
cpk, ok := pk.(ssh.CryptoPublicKey)
|
||||||
if pk, ok := pk.CryptoPublicKey().(ed25519.PublicKey); ok {
|
if !ok {
|
||||||
r.theirPublicKey = ed25519PublicKeyToCurve25519(pk)
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("unexpected public key type")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("pk does not implement ssh.CryptoPublicKey")
|
return nil, errors.New("pk does not implement ssh.CryptoPublicKey")
|
||||||
}
|
}
|
||||||
return r, nil
|
epk, ok := cpk.CryptoPublicKey().(ed25519.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("unexpected public key type")
|
||||||
|
}
|
||||||
|
mpk, err := ed25519PublicKeyToCurve25519(epk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid Ed25519 public key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed25519Recipient{
|
||||||
|
sshKey: pk,
|
||||||
|
theirPublicKey: mpk,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseRecipient(s string) (age.Recipient, error) {
|
func ParseRecipient(s string) (age.Recipient, error) {
|
||||||
@@ -177,35 +181,14 @@ func ParseRecipient(s string) (age.Recipient, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var curve25519P, _ = new(big.Int).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819949", 10)
|
func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) ([]byte, error) {
|
||||||
|
// See https://blog.filippo.io/using-ed25519-keys-for-encryption and
|
||||||
func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte {
|
// https://pkg.go.dev/filippo.io/edwards25519#Point.BytesMontgomery.
|
||||||
// ed25519.PublicKey is a little endian representation of the y-coordinate,
|
p, err := (&edwards25519.Point{}).SetBytes(pk)
|
||||||
// with the most significant bit set based on the sign of the x-coordinate.
|
if err != nil {
|
||||||
bigEndianY := make([]byte, ed25519.PublicKeySize)
|
return nil, err
|
||||||
for i, b := range pk {
|
|
||||||
bigEndianY[ed25519.PublicKeySize-i-1] = b
|
|
||||||
}
|
}
|
||||||
bigEndianY[0] &= 0b0111_1111
|
return p.BytesMontgomery(), nil
|
||||||
|
|
||||||
// The Montgomery u-coordinate is derived through the bilinear map
|
|
||||||
//
|
|
||||||
// u = (1 + y) / (1 - y)
|
|
||||||
//
|
|
||||||
// See https://blog.filippo.io/using-ed25519-keys-for-encryption.
|
|
||||||
y := new(big.Int).SetBytes(bigEndianY)
|
|
||||||
denom := big.NewInt(1)
|
|
||||||
denom.ModInverse(denom.Sub(denom, y), curve25519P) // 1 / (1 - y)
|
|
||||||
u := y.Mul(y.Add(y, big.NewInt(1)), denom)
|
|
||||||
u.Mod(u, curve25519P)
|
|
||||||
|
|
||||||
out := make([]byte, curve25519.PointSize)
|
|
||||||
uBytes := u.Bytes()
|
|
||||||
for i, b := range uBytes {
|
|
||||||
out[len(uBytes)-i-1] = b
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ed25519Label = "age-encryption.org/v1/ssh-ed25519"
|
const ed25519Label = "age-encryption.org/v1/ssh-ed25519"
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -3,6 +3,7 @@ module filippo.io/age
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
filippo.io/edwards25519 v1.0.0-alpha.2
|
||||||
github.com/sergi/go-diff v1.1.0
|
github.com/sergi/go-diff v1.1.0
|
||||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
filippo.io/edwards25519 v1.0.0-alpha.2 h1:EWbZLqGEPSIj2W69gx04KtNVkyPIfe3uj0DhDQJonbQ=
|
||||||
|
filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
|||||||
Reference in New Issue
Block a user