Skip to content

Commit 9801aa3

Browse files
authored
[crypto] Add functions to convert ed25519 keys to curve25519 (#40)
[paserk] Add functions to convert ed25519 keys to curve25519 https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519 has a nice algorithm (with a proof!) that says that it is possible to use an ed25119 key (used for signature) and make it a curve25519 (used for asymmetric encryption). This change adds two new functions that achieve this.
1 parent d4e8729 commit 9801aa3

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

.semaphore/semaphore.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ blocks:
2929
commands:
3030
- checkout
3131
- ./scripts/install_codecov.sh
32-
- sem-version go 1.17
32+
- sem-version go 1.22
3333
- go mod download
3434
jobs:
3535
- name: run tests
3636
commands:
3737
- go test -cover -covermode=atomic -coverprofile coverage.out
38-
dependencies: []
38+
dependencies: []

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/replit/go-replidentity
33
go 1.17
44

55
require (
6+
filippo.io/edwards25519 v1.1.0
67
github.com/o1egl/paseto v1.0.0
78
github.com/stretchr/testify v1.8.0
89
golang.org/x/crypto v0.35.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
2+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
13
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
24
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
35
github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb h1:6Z/wqhPFZ7y5ksCEV/V5MXOazLaeu/EW97CU5rz8NWk=

identity_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func Example() {
6161
)
6262
}
6363

64-
func ExampleRenew() {
64+
func ExampleVerifyRenewIdentity() {
6565
identity := os.Getenv("REPL_RENEWAL")
6666
if identity == "" {
6767
fmt.Println("Sorry, this repl does not yet have an identity (anonymous run?).")

keys.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package replidentity
2+
3+
import (
4+
"crypto/ed25519"
5+
"crypto/sha512"
6+
7+
"filippo.io/edwards25519"
8+
"golang.org/x/crypto/curve25519"
9+
)
10+
11+
// ed25519PrivateKeyToCurve25519 converts a ed25519 private key in X25519 equivalent
12+
// source: https://github.com/FiloSottile/age/blob/980763a16e30ea5c285c271344d2202fcb18c33b/agessh/agessh.go#L287
13+
func Ed25519PrivateKeyToCurve25519(pk ed25519.PrivateKey) [32]byte {
14+
h := sha512.New()
15+
h.Write(pk.Seed())
16+
out := h.Sum(nil)
17+
var res [curve25519.ScalarSize]byte
18+
copy(res[:], out[:curve25519.ScalarSize])
19+
return res
20+
}
21+
22+
// ed25519PublicKeyToCurve25519 converts a ed25519 public key in X25519 equivalent
23+
// source: https://github.com/FiloSottile/age/blob/main/agessh/agessh.go#L190
24+
func Ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) ([32]byte, error) {
25+
// See https://blog.filippo.io/using-ed25519-keys-for-encryption and
26+
// https://pkg.go.dev/filippo.io/edwards25519#Point.BytesMontgomery.
27+
p, err := new(edwards25519.Point).SetBytes(pk)
28+
var res [curve25519.ScalarSize]byte
29+
if err != nil {
30+
return res, err
31+
}
32+
copy(res[:], p.BytesMontgomery())
33+
return res, nil
34+
}

keys_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package replidentity
2+
3+
import (
4+
"crypto/ed25519"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
var (
12+
// https://github.com/jedisct1/libsodium/blob/09e995c0c85a0026510704b8ce7f5867a09a3841/test/default/ed25519_convert.c#L5
13+
seed = []byte{
14+
0x42, 0x11, 0x51, 0xa4, 0x59, 0xfa, 0xea, 0xde, 0x3d, 0x24, 0x71, 0x15, 0xf9, 0x4a, 0xed, 0xae,
15+
0x42, 0x31, 0x81, 0x24, 0x09, 0x5a, 0xfa, 0xbe, 0x4d, 0x14, 0x51, 0xa5, 0x59, 0xfa, 0xed, 0xee,
16+
}
17+
)
18+
19+
func TestEd25519PrivateKeyToCurve25519(t *testing.T) {
20+
privateKey := ed25519.NewKeyFromSeed(seed)
21+
22+
privateCurve := Ed25519PrivateKeyToCurve25519(privateKey)
23+
// https://github.com/jedisct1/libsodium/blob/09e995c0c85a0026510704b8ce7f5867a09a3841/test/default/ed25519_convert.c#L36
24+
assert.Equal(t, privateCurve, [32]byte{
25+
0x82, 0x52, 0x03, 0x03, 0x76, 0xd4, 0x71, 0x12, 0xbe, 0x7f, 0x73, 0xed, 0x7a, 0x01, 0x92, 0x93,
26+
0xdd, 0x12, 0xad, 0x91, 0x0b, 0x65, 0x44, 0x55, 0x79, 0x8b, 0x46, 0x67, 0xd7, 0x3d, 0xe1, 0xe6,
27+
})
28+
publicCurve, err := Ed25519PublicKeyToCurve25519(privateKey.Public().(ed25519.PublicKey))
29+
require.NoError(t, err)
30+
// https://github.com/jedisct1/libsodium/blob/09e995c0c85a0026510704b8ce7f5867a09a3841/test/default/ed25519_convert.c#L35
31+
assert.Equal(t, publicCurve, [32]byte{
32+
0xf1, 0x81, 0x4f, 0x0e, 0x8f, 0xf1, 0x04, 0x3d, 0x8a, 0x44, 0xd2, 0x5b, 0xab, 0xff, 0x3c, 0xed,
33+
0xca, 0xe6, 0xc2, 0x2c, 0x3e, 0xda, 0xa4, 0x8f, 0x85, 0x7a, 0xe7, 0x0d, 0xe2, 0xba, 0xae, 0x50,
34+
})
35+
}

0 commit comments

Comments
 (0)