From 33c9a25665e6626ec17901ffbc5a504509bd394b Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 24 Mar 2023 13:01:53 +0100 Subject: [PATCH] Add sym key --- .../acl/aclrecordproto/aclreadkeyderive.go | 3 +- .../object/acl/list/aclrecordbuilder.go | 3 +- commonspace/object/acl/list/aclstate.go | 17 +++-- .../acl/testutils/acllistbuilder/keychain.go | 9 ++- .../acllistbuilder/liststoragebuilder.go | 4 +- .../object/tree/objecttree/changebuilder.go | 4 +- .../object/tree/objecttree/objecttree.go | 6 +- .../tree/objecttree/objecttreefactory.go | 6 +- .../symmetric/symmetric.go => crypto/aes.go} | 67 +++++++------------ util/crypto/cryptoproto/protos/crypto.proto | 16 ++--- util/crypto/curve25519.go | 52 -------------- util/crypto/key.go | 11 ++- util/crypto/x25519.go | 48 +++++++++++++ 13 files changed, 112 insertions(+), 134 deletions(-) rename util/{keys/symmetric/symmetric.go => crypto/aes.go} (58%) delete mode 100644 util/crypto/curve25519.go diff --git a/commonspace/object/acl/aclrecordproto/aclreadkeyderive.go b/commonspace/object/acl/aclrecordproto/aclreadkeyderive.go index fa7b074a..6d6bab90 100644 --- a/commonspace/object/acl/aclrecordproto/aclreadkeyderive.go +++ b/commonspace/object/acl/aclrecordproto/aclreadkeyderive.go @@ -1,10 +1,11 @@ package aclrecordproto import ( + "github.com/anytypeio/any-sync/util/crypto" "github.com/anytypeio/any-sync/util/keys/symmetric" ) -func AclReadKeyDerive(signKey []byte, encKey []byte) (*symmetric.Key, error) { +func AclReadKeyDerive(signKey []byte, encKey []byte) (*crypto.AESKey, error) { concBuf := make([]byte, 0, len(signKey)+len(encKey)) concBuf = append(concBuf, signKey...) concBuf = append(concBuf, encKey...) diff --git a/commonspace/object/acl/list/aclrecordbuilder.go b/commonspace/object/acl/list/aclrecordbuilder.go index 6b3f72e2..4a68ce77 100644 --- a/commonspace/object/acl/list/aclrecordbuilder.go +++ b/commonspace/object/acl/list/aclrecordbuilder.go @@ -5,7 +5,6 @@ import ( "github.com/anytypeio/any-sync/commonspace/object/keychain" "github.com/anytypeio/any-sync/util/cidutil" "github.com/anytypeio/any-sync/util/crypto" - "github.com/anytypeio/any-sync/util/keys/symmetric" "github.com/gogo/protobuf/proto" "time" ) @@ -37,7 +36,7 @@ func (a *aclRecordBuilder) BuildUserJoin(acceptPrivKeyBytes []byte, encSymKeyByt if err != nil { return } - encSymKey, err := symmetric.FromBytes(encSymKeyBytes) + encSymKey, err := crypto.UnmarshallAESKey(encSymKeyBytes) if err != nil { return } diff --git a/commonspace/object/acl/list/aclstate.go b/commonspace/object/acl/list/aclstate.go index 97300054..e5922a56 100644 --- a/commonspace/object/acl/list/aclstate.go +++ b/commonspace/object/acl/list/aclstate.go @@ -11,7 +11,6 @@ import ( "github.com/anytypeio/any-sync/util/keys" "github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey" - "github.com/anytypeio/any-sync/util/keys/symmetric" "github.com/gogo/protobuf/proto" "go.uber.org/zap" "hash/fnv" @@ -43,7 +42,7 @@ type UserPermissionPair struct { type AclState struct { id string currentReadKeyHash uint64 - userReadKeys map[uint64]*symmetric.Key + userReadKeys map[uint64]*crypto.AESKey userStates map[string]*aclrecordproto.AclUserState userInvites map[string]*aclrecordproto.AclUserInvite encryptionKey encryptionkey.PrivKey @@ -70,7 +69,7 @@ func newAclStateWithKeys( identity: string(identity), signingKey: signingKey, encryptionKey: encryptionKey, - userReadKeys: make(map[uint64]*symmetric.Key), + userReadKeys: make(map[uint64]*crypto.AESKey), userStates: make(map[string]*aclrecordproto.AclUserState), userInvites: make(map[string]*aclrecordproto.AclUserInvite), permissionsAtRecord: make(map[string][]UserPermissionPair), @@ -80,7 +79,7 @@ func newAclStateWithKeys( func newAclState(id string) *AclState { return &AclState{ id: id, - userReadKeys: make(map[uint64]*symmetric.Key), + userReadKeys: make(map[uint64]*crypto.AESKey), userStates: make(map[string]*aclrecordproto.AclUserState), userInvites: make(map[string]*aclrecordproto.AclUserInvite), permissionsAtRecord: make(map[string][]UserPermissionPair), @@ -91,7 +90,7 @@ func (st *AclState) CurrentReadKeyHash() uint64 { return st.currentReadKeyHash } -func (st *AclState) CurrentReadKey() (*symmetric.Key, error) { +func (st *AclState) CurrentReadKey() (*crypto.AESKey, error) { key, exists := st.userReadKeys[st.currentReadKeyHash] if !exists { return nil, ErrNoReadKey @@ -99,7 +98,7 @@ func (st *AclState) CurrentReadKey() (*symmetric.Key, error) { return key, nil } -func (st *AclState) UserReadKeys() map[uint64]*symmetric.Key { +func (st *AclState) UserReadKeys() map[uint64]*crypto.AESKey { return st.userReadKeys } @@ -194,7 +193,7 @@ func (st *AclState) applyRoot(root *aclrecordproto.AclRoot) (err error) { } func (st *AclState) saveReadKeyFromRoot(root *aclrecordproto.AclRoot) (err error) { - var readKey *symmetric.Key + var readKey *crypto.AESKey if len(root.GetDerivationScheme()) != 0 { var encPrivKey []byte encPrivKey, err = st.encryptionKey.Raw() @@ -400,13 +399,13 @@ func (st *AclState) applyUserRemove(ch *aclrecordproto.AclUserRemove) error { return nil } -func (st *AclState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) { +func (st *AclState) decryptReadKeyAndHash(msg []byte) (*crypto.AESKey, uint64, error) { decrypted, err := st.encryptionKey.Decrypt(msg) if err != nil { return nil, 0, ErrFailedToDecrypt } - key, err := symmetric.FromBytes(decrypted) + key, err := crypto.UnmarshallAESKey(decrypted) if err != nil { return nil, 0, ErrFailedToDecrypt } diff --git a/commonspace/object/acl/testutils/acllistbuilder/keychain.go b/commonspace/object/acl/testutils/acllistbuilder/keychain.go index 4e727cfa..00e207a6 100644 --- a/commonspace/object/acl/testutils/acllistbuilder/keychain.go +++ b/commonspace/object/acl/testutils/acllistbuilder/keychain.go @@ -6,14 +6,13 @@ import ( "github.com/anytypeio/any-sync/util/keys" "github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey" - "github.com/anytypeio/any-sync/util/keys/symmetric" "hash/fnv" "strings" ) type SymKey struct { Hash uint64 - Key *symmetric.Key + Key *crypto.AESKey } type YAMLKeychain struct { @@ -111,11 +110,11 @@ func (k *YAMLKeychain) AddReadKey(key *Key) { } var ( - rkey *symmetric.Key + rkey *crypto.AESKey err error ) if key.Value == "generated" { - rkey, err = symmetric.NewRandom() + rkey, err = crypto.NewRandomAES() if err != nil { panic("should be able to generate symmetric key") } @@ -127,7 +126,7 @@ func (k *YAMLKeychain) AddReadKey(key *Key) { panic("should be able to derive symmetric key") } } else { - rkey, err = symmetric.FromString(key.Value) + rkey, err = crypto.UnmarshallAESKeyString(key.Value) if err != nil { panic("should be able to parse symmetric key") } diff --git a/commonspace/object/acl/testutils/acllistbuilder/liststoragebuilder.go b/commonspace/object/acl/testutils/acllistbuilder/liststoragebuilder.go index 35a17bda..a73c2924 100644 --- a/commonspace/object/acl/testutils/acllistbuilder/liststoragebuilder.go +++ b/commonspace/object/acl/testutils/acllistbuilder/liststoragebuilder.go @@ -7,9 +7,9 @@ import ( "github.com/anytypeio/any-sync/commonspace/object/acl/liststorage" "github.com/anytypeio/any-sync/commonspace/object/acl/testutils/yamltests" "github.com/anytypeio/any-sync/util/cidutil" + "github.com/anytypeio/any-sync/util/crypto" "github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey" - "github.com/anytypeio/any-sync/util/keys/symmetric" "gopkg.in/yaml.v3" "io/ioutil" "path" @@ -249,7 +249,7 @@ func (t *AclListStorageBuilder) encryptReadKeysWithPubKey(keys []string, encKey return } -func (t *AclListStorageBuilder) encryptReadKeysWithSymKey(keys []string, key *symmetric.Key) (enc [][]byte) { +func (t *AclListStorageBuilder) encryptReadKeysWithSymKey(keys []string, key *crypto.AESKey) (enc [][]byte) { for _, k := range keys { realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes() res, err := key.Encrypt(realKey) diff --git a/commonspace/object/tree/objecttree/changebuilder.go b/commonspace/object/tree/objecttree/changebuilder.go index 8e312309..396f5e20 100644 --- a/commonspace/object/tree/objecttree/changebuilder.go +++ b/commonspace/object/tree/objecttree/changebuilder.go @@ -5,8 +5,8 @@ import ( "github.com/anytypeio/any-sync/commonspace/object/keychain" "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto" "github.com/anytypeio/any-sync/util/cidutil" + "github.com/anytypeio/any-sync/util/crypto" "github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey" - "github.com/anytypeio/any-sync/util/keys/symmetric" "github.com/gogo/protobuf/proto" "time" ) @@ -21,7 +21,7 @@ type BuilderContent struct { Identity []byte IsSnapshot bool SigningKey signingkey.PrivKey - ReadKey *symmetric.Key + ReadKey *crypto.AESKey Content []byte } diff --git a/commonspace/object/tree/objecttree/objecttree.go b/commonspace/object/tree/objecttree/objecttree.go index af169ad2..5930334e 100644 --- a/commonspace/object/tree/objecttree/objecttree.go +++ b/commonspace/object/tree/objecttree/objecttree.go @@ -4,6 +4,7 @@ package objecttree import ( "context" "errors" + "github.com/anytypeio/any-sync/util/crypto" "sync" "time" @@ -11,7 +12,6 @@ import ( "github.com/anytypeio/any-sync/commonspace/object/acl/list" "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto" "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage" - "github.com/anytypeio/any-sync/util/keys/symmetric" "github.com/anytypeio/any-sync/util/slice" ) @@ -99,7 +99,7 @@ type objectTree struct { root *Change tree *Tree - keys map[uint64]*symmetric.Key + keys map[uint64]*crypto.AESKey // buffers difSnapshotBuf []*treechangeproto.RawTreeChangeWithId @@ -225,7 +225,7 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt var ( state = ot.aclList.AclState() // special method for own keys - readKey *symmetric.Key + readKey *crypto.AESKey readKeyHash uint64 ) canWrite := state.HasPermission(content.Identity, aclrecordproto.AclUserPermissions_Writer) || diff --git a/commonspace/object/tree/objecttree/objecttreefactory.go b/commonspace/object/tree/objecttree/objecttreefactory.go index e3ec5c6f..af350ef4 100644 --- a/commonspace/object/tree/objecttree/objecttreefactory.go +++ b/commonspace/object/tree/objecttree/objecttreefactory.go @@ -5,8 +5,8 @@ import ( "github.com/anytypeio/any-sync/commonspace/object/keychain" "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto" "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage" + "github.com/anytypeio/any-sync/util/crypto" "github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey" - "github.com/anytypeio/any-sync/util/keys/symmetric" "math/rand" "time" ) @@ -189,7 +189,7 @@ func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) { aclList: deps.aclList, changeBuilder: deps.changeBuilder, rawChangeLoader: deps.rawChangeLoader, - keys: make(map[uint64]*symmetric.Key), + keys: make(map[uint64]*crypto.AESKey), newChangesBuf: make([]*Change, 0, 10), difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10), notSeenIdxBuf: make([]int, 0, 10), @@ -225,7 +225,7 @@ func buildHistoryTree(deps objectTreeDeps, params HistoryTreeParams) (ht History aclList: deps.aclList, changeBuilder: deps.changeBuilder, rawChangeLoader: deps.rawChangeLoader, - keys: make(map[uint64]*symmetric.Key), + keys: make(map[uint64]*crypto.AESKey), newChangesBuf: make([]*Change, 0, 10), difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10), notSeenIdxBuf: make([]int, 0, 10), diff --git a/util/keys/symmetric/symmetric.go b/util/crypto/aes.go similarity index 58% rename from util/keys/symmetric/symmetric.go rename to util/crypto/aes.go index 711f1fdd..e6ac4976 100644 --- a/util/keys/symmetric/symmetric.go +++ b/util/crypto/aes.go @@ -1,12 +1,11 @@ -package symmetric +package crypto import ( "crypto/aes" "crypto/cipher" "crypto/rand" + "crypto/subtle" "fmt" - "github.com/minio/sha256-simd" - mbase "github.com/multiformats/go-multibase" ) @@ -18,83 +17,65 @@ const ( KeyBytes = 32 ) -type Key struct { +type AESKey struct { raw []byte } -func DeriveFromBytes(bytes []byte) (*Key, error) { - bArray := sha256.Sum256(bytes) - bSlice := bArray[:] - return FromBytes(bSlice) -} - -func (k *Key) Equals(otherKey *Key) bool { - otherRaw := otherKey.raw - keyRaw := k.raw - - if len(keyRaw) != len(otherRaw) { +func (k *AESKey) Equals(key Key) bool { + aesKey, ok := key.(*AESKey) + if !ok { return false } - for i := 0; i < len(keyRaw); i++ { - if keyRaw[i] != otherRaw[i] { - return false - } - } - return true + return subtle.ConstantTimeCompare(k.raw, aesKey.raw) == 1 } -func (k *Key) Raw() ([]byte, error) { +func (k *AESKey) Raw() ([]byte, error) { return k.raw, nil } -// NewRandom returns a random key. -func NewRandom() (*Key, error) { +// NewRandomAES returns a random key. +func NewRandomAES() (*AESKey, error) { raw := make([]byte, KeyBytes) if _, err := rand.Read(raw); err != nil { return nil, err } - return &Key{raw: raw}, nil + return &AESKey{raw: raw}, nil } -// New returns Key if err is nil and panics otherwise. -func New() *Key { - k, err := NewRandom() +// NewAES returns AESKey if err is nil and panics otherwise. +func NewAES() *AESKey { + k, err := NewRandomAES() if err != nil { panic(err) } return k } -// FromBytes returns a key by decoding bytes. -func FromBytes(k []byte) (*Key, error) { +// UnmarshallAESKey returns a key by decoding bytes. +func UnmarshallAESKey(k []byte) (*AESKey, error) { if len(k) != KeyBytes { return nil, fmt.Errorf("invalid key") } - return &Key{raw: k}, nil + return &AESKey{raw: k}, nil } -// FromString returns a key by decoding a base32-encoded string. -func FromString(k string) (*Key, error) { +// UnmarshallAESKeyString returns a key by decoding a base32-encoded string. +func UnmarshallAESKeyString(k string) (*AESKey, error) { _, b, err := mbase.Decode(k) if err != nil { return nil, err } - return FromBytes(b) + return UnmarshallAESKey(b) } // Bytes returns raw key bytes. -func (k *Key) Bytes() []byte { +func (k *AESKey) Bytes() []byte { return k.raw } -// MarshalBinary implements BinaryMarshaler. -func (k *Key) MarshalBinary() ([]byte, error) { - return k.raw, nil -} - // String returns the base32-encoded string representation of raw key bytes. -func (k *Key) String() string { +func (k *AESKey) String() string { str, err := mbase.Encode(mbase.Base32, k.raw) if err != nil { panic("should not error with hardcoded mbase: " + err.Error()) @@ -103,7 +84,7 @@ func (k *Key) String() string { } // Encrypt performs AES-256 GCM encryption on plaintext. -func (k *Key) Encrypt(plaintext []byte) ([]byte, error) { +func (k *AESKey) Encrypt(plaintext []byte) ([]byte, error) { block, err := aes.NewCipher(k.raw[:KeyBytes]) if err != nil { return nil, err @@ -122,7 +103,7 @@ func (k *Key) Encrypt(plaintext []byte) ([]byte, error) { } // Decrypt uses key to perform AES-256 GCM decryption on ciphertext. -func (k *Key) Decrypt(ciphertext []byte) ([]byte, error) { +func (k *AESKey) Decrypt(ciphertext []byte) ([]byte, error) { block, err := aes.NewCipher(k.raw[:KeyBytes]) if err != nil { return nil, err diff --git a/util/crypto/cryptoproto/protos/crypto.proto b/util/crypto/cryptoproto/protos/crypto.proto index 02352719..dea6164b 100644 --- a/util/crypto/cryptoproto/protos/crypto.proto +++ b/util/crypto/cryptoproto/protos/crypto.proto @@ -1,20 +1,14 @@ syntax = "proto3"; -package utilcrypto; +package crypto; option go_package = "util/crypto/cryptoproto"; enum KeyType { - RSA = 0; - Ed25519 = 1; - Secp256k1 = 2; - ECDSA = 3; + Ed25519Public = 0; + Ed25519Private = 1; + AES = 2; } -message PublicKey { +message Key { KeyType Type = 1; bytes Data = 2; } - -message PrivateKey { - KeyType Type = 1; - bytes Data = 2; -} \ No newline at end of file diff --git a/util/crypto/curve25519.go b/util/crypto/curve25519.go deleted file mode 100644 index f60050a5..00000000 --- a/util/crypto/curve25519.go +++ /dev/null @@ -1,52 +0,0 @@ -package crypto - -import ( - "crypto/ed25519" - "crypto/sha512" - "filippo.io/edwards25519" - "golang.org/x/crypto/curve25519" -) - -// Ed25519PublicKeyToCurve25519 converts an Ed25519 public key to a Curve25519 public key -func Ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte { - // Unmarshalling public key into edwards curve point - epk, err := (&edwards25519.Point{}).SetBytes(pk) - if err != nil { - panic(err) - } - // converting to curve25519 (see here for more details https://github.com/golang/go/issues/20504) - return epk.BytesMontgomery() -} - -// ISC License -// -// Copyright (c) 2013-2020 -// Frank Denis -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c#L69-L83 - -// Ed25519PrivateKeyToCurve25519 converts an Ed25519 private key to a Curve25519 private key -// This code is originally taken from here https://github.com/jorrizza/ed2curve25519/blob/master/ed2curve25519.go -func Ed25519PrivateKeyToCurve25519(pk ed25519.PrivateKey) []byte { - h := sha512.New() - h.Write(pk.Seed()) - out := h.Sum(nil) - - // used in libsodium - out[0] &= 248 - out[31] &= 127 - out[31] |= 64 - - return out[:curve25519.ScalarSize] -} diff --git a/util/crypto/key.go b/util/crypto/key.go index 50b01c97..ad408ab9 100644 --- a/util/crypto/key.go +++ b/util/crypto/key.go @@ -25,7 +25,7 @@ type PrivKey interface { GetPublic() PubKey } -// PubKey is the public key used to verify the signatures made by SignPrivKey +// PubKey is the public key used to verify the signatures and decrypt messages type PubKey interface { Key @@ -35,6 +35,15 @@ type PubKey interface { Verify(data []byte, sig []byte) (bool, error) } +type SymKey interface { + Key + + // Decrypt decrypts the message and returns the result + Decrypt(message []byte) ([]byte, error) + // Encrypt encrypts the message and returns the result + Encrypt(message []byte) ([]byte, error) +} + func KeyEquals(k1, k2 Key) bool { a, err := k1.Raw() if err != nil { diff --git a/util/crypto/x25519.go b/util/crypto/x25519.go index ef2ab05d..96864ebc 100644 --- a/util/crypto/x25519.go +++ b/util/crypto/x25519.go @@ -1,14 +1,62 @@ package crypto import ( + "crypto/ed25519" "crypto/rand" + "crypto/sha512" "errors" + "filippo.io/edwards25519" "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/curve25519" "golang.org/x/crypto/nacl/box" ) var ErrX25519DecryptionFailed = errors.New("failed decryption with x25519 key") +// Ed25519PublicKeyToCurve25519 converts an Ed25519 public key to a Curve25519 public key +func Ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte { + // Unmarshalling public key into edwards curve point + epk, err := (&edwards25519.Point{}).SetBytes(pk) + if err != nil { + panic(err) + } + // converting to curve25519 (see here for more details https://github.com/golang/go/issues/20504) + return epk.BytesMontgomery() +} + +// ISC License +// +// Copyright (c) 2013-2020 +// Frank Denis +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c#L69-L83 + +// Ed25519PrivateKeyToCurve25519 converts an Ed25519 private key to a Curve25519 private key +// This code is originally taken from here https://github.com/jorrizza/ed2curve25519/blob/master/ed2curve25519.go +func Ed25519PrivateKeyToCurve25519(pk ed25519.PrivateKey) []byte { + h := sha512.New() + h.Write(pk.Seed()) + out := h.Sum(nil) + + // used in libsodium + out[0] &= 248 + out[31] &= 127 + out[31] |= 64 + + return out[:curve25519.ScalarSize] +} + // EncryptX25519 takes a x25519 public key and encrypts the message func EncryptX25519(pubKey *[32]byte, msg []byte) []byte { // see discussion here https://github.com/golang/go/issues/29128