From 3b65d98c0cb80cfc2c4f1b41c33e8a3598d8de63 Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Fri, 8 Mar 2024 15:01:44 -0700 Subject: [PATCH] olm/pk: make an interface Signed-off-by: Sumner Evans --- crypto/cross_sign_key.go | 54 +++++++++---------- crypto/cross_sign_signing.go | 10 ++-- crypto/cross_sign_ssss.go | 12 ++--- crypto/cross_sign_test.go | 56 ++++++++++---------- crypto/goolm/pk/decryption.go | 4 +- crypto/goolm/pk/pk_test.go | 13 +++-- crypto/goolm/pk/signing.go | 43 +++++++++++---- crypto/olm/pk_goolm.go | 80 +++++++--------------------- crypto/olm/pk_interface.go | 41 +++++++++++++++ crypto/olm/{pk.go => pk_libolm.go} | 84 ++++++++++++++++++------------ 10 files changed, 222 insertions(+), 175 deletions(-) create mode 100644 crypto/olm/pk_interface.go rename crypto/olm/{pk.go => pk_libolm.go} (67%) diff --git a/crypto/cross_sign_key.go b/crypto/cross_sign_key.go index 45e56b4b..f7dc08cb 100644 --- a/crypto/cross_sign_key.go +++ b/crypto/cross_sign_key.go @@ -19,16 +19,16 @@ import ( // CrossSigningKeysCache holds the three cross-signing keys for the current user. type CrossSigningKeysCache struct { - MasterKey *olm.PkSigning - SelfSigningKey *olm.PkSigning - UserSigningKey *olm.PkSigning + MasterKey olm.PKSigning + SelfSigningKey olm.PKSigning + UserSigningKey olm.PKSigning } func (cskc *CrossSigningKeysCache) PublicKeys() *CrossSigningPublicKeysCache { return &CrossSigningPublicKeysCache{ - MasterKey: cskc.MasterKey.PublicKey, - SelfSigningKey: cskc.SelfSigningKey.PublicKey, - UserSigningKey: cskc.UserSigningKey.PublicKey, + MasterKey: cskc.MasterKey.PublicKey(), + SelfSigningKey: cskc.SelfSigningKey.PublicKey(), + UserSigningKey: cskc.UserSigningKey.PublicKey(), } } @@ -40,28 +40,28 @@ type CrossSigningSeeds struct { func (mach *OlmMachine) ExportCrossSigningKeys() CrossSigningSeeds { return CrossSigningSeeds{ - MasterKey: mach.CrossSigningKeys.MasterKey.Seed, - SelfSigningKey: mach.CrossSigningKeys.SelfSigningKey.Seed, - UserSigningKey: mach.CrossSigningKeys.UserSigningKey.Seed, + MasterKey: mach.CrossSigningKeys.MasterKey.Seed(), + SelfSigningKey: mach.CrossSigningKeys.SelfSigningKey.Seed(), + UserSigningKey: mach.CrossSigningKeys.UserSigningKey.Seed(), } } func (mach *OlmMachine) ImportCrossSigningKeys(keys CrossSigningSeeds) (err error) { var keysCache CrossSigningKeysCache - if keysCache.MasterKey, err = olm.NewPkSigningFromSeed(keys.MasterKey); err != nil { + if keysCache.MasterKey, err = olm.NewPKSigningFromSeed(keys.MasterKey); err != nil { return } - if keysCache.SelfSigningKey, err = olm.NewPkSigningFromSeed(keys.SelfSigningKey); err != nil { + if keysCache.SelfSigningKey, err = olm.NewPKSigningFromSeed(keys.SelfSigningKey); err != nil { return } - if keysCache.UserSigningKey, err = olm.NewPkSigningFromSeed(keys.UserSigningKey); err != nil { + if keysCache.UserSigningKey, err = olm.NewPKSigningFromSeed(keys.UserSigningKey); err != nil { return } mach.Log.Debug(). - Str("master", keysCache.MasterKey.PublicKey.String()). - Str("self_signing", keysCache.SelfSigningKey.PublicKey.String()). - Str("user_signing", keysCache.UserSigningKey.PublicKey.String()). + Str("master", keysCache.MasterKey.PublicKey().String()). + Str("self_signing", keysCache.SelfSigningKey.PublicKey().String()). + Str("user_signing", keysCache.UserSigningKey.PublicKey().String()). Msg("Imported own cross-signing keys") mach.CrossSigningKeys = &keysCache @@ -73,19 +73,19 @@ func (mach *OlmMachine) ImportCrossSigningKeys(keys CrossSigningSeeds) (err erro func (mach *OlmMachine) GenerateCrossSigningKeys() (*CrossSigningKeysCache, error) { var keysCache CrossSigningKeysCache var err error - if keysCache.MasterKey, err = olm.NewPkSigning(); err != nil { + if keysCache.MasterKey, err = olm.NewPKSigning(); err != nil { return nil, fmt.Errorf("failed to generate master key: %w", err) } - if keysCache.SelfSigningKey, err = olm.NewPkSigning(); err != nil { + if keysCache.SelfSigningKey, err = olm.NewPKSigning(); err != nil { return nil, fmt.Errorf("failed to generate self-signing key: %w", err) } - if keysCache.UserSigningKey, err = olm.NewPkSigning(); err != nil { + if keysCache.UserSigningKey, err = olm.NewPKSigning(); err != nil { return nil, fmt.Errorf("failed to generate user-signing key: %w", err) } mach.Log.Debug(). - Str("master", keysCache.MasterKey.PublicKey.String()). - Str("self_signing", keysCache.SelfSigningKey.PublicKey.String()). - Str("user_signing", keysCache.UserSigningKey.PublicKey.String()). + Str("master", keysCache.MasterKey.PublicKey().String()). + Str("self_signing", keysCache.SelfSigningKey.PublicKey().String()). + Str("user_signing", keysCache.UserSigningKey.PublicKey().String()). Msg("Generated cross-signing keys") return &keysCache, nil } @@ -93,12 +93,12 @@ func (mach *OlmMachine) GenerateCrossSigningKeys() (*CrossSigningKeysCache, erro // PublishCrossSigningKeys signs and uploads the public keys of the given cross-signing keys to the server. func (mach *OlmMachine) PublishCrossSigningKeys(ctx context.Context, keys *CrossSigningKeysCache, uiaCallback mautrix.UIACallback) error { userID := mach.Client.UserID - masterKeyID := id.NewKeyID(id.KeyAlgorithmEd25519, keys.MasterKey.PublicKey.String()) + masterKeyID := id.NewKeyID(id.KeyAlgorithmEd25519, keys.MasterKey.PublicKey().String()) masterKey := mautrix.CrossSigningKeys{ UserID: userID, Usage: []id.CrossSigningUsage{id.XSUsageMaster}, Keys: map[id.KeyID]id.Ed25519{ - masterKeyID: keys.MasterKey.PublicKey, + masterKeyID: keys.MasterKey.PublicKey(), }, } masterSig, err := mach.account.Internal.SignJSON(masterKey) @@ -111,27 +111,27 @@ func (mach *OlmMachine) PublishCrossSigningKeys(ctx context.Context, keys *Cross UserID: userID, Usage: []id.CrossSigningUsage{id.XSUsageSelfSigning}, Keys: map[id.KeyID]id.Ed25519{ - id.NewKeyID(id.KeyAlgorithmEd25519, keys.SelfSigningKey.PublicKey.String()): keys.SelfSigningKey.PublicKey, + id.NewKeyID(id.KeyAlgorithmEd25519, keys.SelfSigningKey.PublicKey().String()): keys.SelfSigningKey.PublicKey(), }, } selfSig, err := keys.MasterKey.SignJSON(selfKey) if err != nil { return fmt.Errorf("failed to sign self-signing key: %w", err) } - selfKey.Signatures = signatures.NewSingleSignature(userID, id.KeyAlgorithmEd25519, keys.MasterKey.PublicKey.String(), selfSig) + selfKey.Signatures = signatures.NewSingleSignature(userID, id.KeyAlgorithmEd25519, keys.MasterKey.PublicKey().String(), selfSig) userKey := mautrix.CrossSigningKeys{ UserID: userID, Usage: []id.CrossSigningUsage{id.XSUsageUserSigning}, Keys: map[id.KeyID]id.Ed25519{ - id.NewKeyID(id.KeyAlgorithmEd25519, keys.UserSigningKey.PublicKey.String()): keys.UserSigningKey.PublicKey, + id.NewKeyID(id.KeyAlgorithmEd25519, keys.UserSigningKey.PublicKey().String()): keys.UserSigningKey.PublicKey(), }, } userSig, err := keys.MasterKey.SignJSON(userKey) if err != nil { return fmt.Errorf("failed to sign user-signing key: %w", err) } - userKey.Signatures = signatures.NewSingleSignature(userID, id.KeyAlgorithmEd25519, keys.MasterKey.PublicKey.String(), userSig) + userKey.Signatures = signatures.NewSingleSignature(userID, id.KeyAlgorithmEd25519, keys.MasterKey.PublicKey().String(), userSig) err = mach.Client.UploadCrossSigningKeys(ctx, &mautrix.UploadCrossSigningKeysReq{ Master: masterKey, diff --git a/crypto/cross_sign_signing.go b/crypto/cross_sign_signing.go index c0efd54e..86920728 100644 --- a/crypto/cross_sign_signing.go +++ b/crypto/cross_sign_signing.go @@ -60,7 +60,7 @@ func (mach *OlmMachine) SignUser(ctx context.Context, userID id.UserID, masterKe Str("signature", signature). Msg("Signed master key of user with our user-signing key") - if err := mach.CryptoStore.PutSignature(ctx, userID, masterKey, mach.Client.UserID, mach.CrossSigningKeys.UserSigningKey.PublicKey, signature); err != nil { + if err := mach.CryptoStore.PutSignature(ctx, userID, masterKey, mach.Client.UserID, mach.CrossSigningKeys.UserSigningKey.PublicKey(), signature); err != nil { return fmt.Errorf("error storing signature in crypto store: %w", err) } @@ -77,7 +77,7 @@ func (mach *OlmMachine) SignOwnMasterKey(ctx context.Context) error { userID := mach.Client.UserID deviceID := mach.Client.DeviceID - masterKey := mach.CrossSigningKeys.MasterKey.PublicKey + masterKey := mach.CrossSigningKeys.MasterKey.PublicKey() masterKeyObj := mautrix.ReqKeysSignatures{ UserID: userID, @@ -149,7 +149,7 @@ func (mach *OlmMachine) SignOwnDevice(ctx context.Context, device *id.Device) er Str("signature", signature). Msg("Signed own device key with self-signing key") - if err := mach.CryptoStore.PutSignature(ctx, device.UserID, device.SigningKey, mach.Client.UserID, mach.CrossSigningKeys.SelfSigningKey.PublicKey, signature); err != nil { + if err := mach.CryptoStore.PutSignature(ctx, device.UserID, device.SigningKey, mach.Client.UserID, mach.CrossSigningKeys.SelfSigningKey.PublicKey(), signature); err != nil { return fmt.Errorf("error storing signature in crypto store: %w", err) } @@ -180,12 +180,12 @@ func (mach *OlmMachine) getFullDeviceKeys(ctx context.Context, device *id.Device } // signAndUpload signs the given key signatures object and uploads it to the server. -func (mach *OlmMachine) signAndUpload(ctx context.Context, req mautrix.ReqKeysSignatures, userID id.UserID, signedThing string, key *olm.PkSigning) (string, error) { +func (mach *OlmMachine) signAndUpload(ctx context.Context, req mautrix.ReqKeysSignatures, userID id.UserID, signedThing string, key olm.PKSigning) (string, error) { signature, err := key.SignJSON(req) if err != nil { return "", fmt.Errorf("failed to sign JSON: %w", err) } - req.Signatures = signatures.NewSingleSignature(mach.Client.UserID, id.KeyAlgorithmEd25519, key.PublicKey.String(), signature) + req.Signatures = signatures.NewSingleSignature(mach.Client.UserID, id.KeyAlgorithmEd25519, key.PublicKey().String(), signature) resp, err := mach.Client.UploadSignatures(ctx, &mautrix.ReqUploadSignatures{ userID: map[string]mautrix.ReqKeysSignatures{ diff --git a/crypto/cross_sign_ssss.go b/crypto/cross_sign_ssss.go index a87f87de..389a9fd2 100644 --- a/crypto/cross_sign_ssss.go +++ b/crypto/cross_sign_ssss.go @@ -110,24 +110,24 @@ func (mach *OlmMachine) GenerateAndUploadCrossSigningKeys(ctx context.Context, u // UploadCrossSigningKeysToSSSS stores the given cross-signing keys on the server encrypted with the given key. func (mach *OlmMachine) UploadCrossSigningKeysToSSSS(ctx context.Context, key *ssss.Key, keys *CrossSigningKeysCache) error { - if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningMaster, keys.MasterKey.Seed, key); err != nil { + if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningMaster, keys.MasterKey.Seed(), key); err != nil { return err } - if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningSelf, keys.SelfSigningKey.Seed, key); err != nil { + if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningSelf, keys.SelfSigningKey.Seed(), key); err != nil { return err } - if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningUser, keys.UserSigningKey.Seed, key); err != nil { + if err := mach.SSSS.SetEncryptedAccountData(ctx, event.AccountDataCrossSigningUser, keys.UserSigningKey.Seed(), key); err != nil { return err } // Also store these locally - if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageMaster, keys.MasterKey.PublicKey); err != nil { + if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageMaster, keys.MasterKey.PublicKey()); err != nil { return err } - if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageSelfSigning, keys.SelfSigningKey.PublicKey); err != nil { + if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageSelfSigning, keys.SelfSigningKey.PublicKey()); err != nil { return err } - if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageUserSigning, keys.UserSigningKey.PublicKey); err != nil { + if err := mach.CryptoStore.PutCrossSigningKey(ctx, mach.Client.UserID, id.XSUsageUserSigning, keys.UserSigningKey.PublicKey()); err != nil { return err } diff --git a/crypto/cross_sign_test.go b/crypto/cross_sign_test.go index b53da102..e11fb018 100644 --- a/crypto/cross_sign_test.go +++ b/crypto/cross_sign_test.go @@ -37,13 +37,13 @@ func getOlmMachine(t *testing.T) *OlmMachine { } userID := id.UserID("@mautrix") - mk, _ := olm.NewPkSigning() - ssk, _ := olm.NewPkSigning() - usk, _ := olm.NewPkSigning() + mk, _ := olm.NewPKSigning() + ssk, _ := olm.NewPKSigning() + usk, _ := olm.NewPKSigning() - sqlStore.PutCrossSigningKey(context.TODO(), userID, id.XSUsageMaster, mk.PublicKey) - sqlStore.PutCrossSigningKey(context.TODO(), userID, id.XSUsageSelfSigning, ssk.PublicKey) - sqlStore.PutCrossSigningKey(context.TODO(), userID, id.XSUsageUserSigning, usk.PublicKey) + sqlStore.PutCrossSigningKey(context.TODO(), userID, id.XSUsageMaster, mk.PublicKey()) + sqlStore.PutCrossSigningKey(context.TODO(), userID, id.XSUsageSelfSigning, ssk.PublicKey()) + sqlStore.PutCrossSigningKey(context.TODO(), userID, id.XSUsageUserSigning, usk.PublicKey()) return &OlmMachine{ CryptoStore: sqlStore, @@ -70,10 +70,10 @@ func TestTrustOwnDevice(t *testing.T) { t.Error("Own device trusted while it shouldn't be") } - m.CryptoStore.PutSignature(context.TODO(), ownDevice.UserID, m.CrossSigningKeys.SelfSigningKey.PublicKey, - ownDevice.UserID, m.CrossSigningKeys.MasterKey.PublicKey, "sig1") + m.CryptoStore.PutSignature(context.TODO(), ownDevice.UserID, m.CrossSigningKeys.SelfSigningKey.PublicKey(), + ownDevice.UserID, m.CrossSigningKeys.MasterKey.PublicKey(), "sig1") m.CryptoStore.PutSignature(context.TODO(), ownDevice.UserID, ownDevice.SigningKey, - ownDevice.UserID, m.CrossSigningKeys.SelfSigningKey.PublicKey, "sig2") + ownDevice.UserID, m.CrossSigningKeys.SelfSigningKey.PublicKey(), "sig2") if trusted, _ := m.IsUserTrusted(context.TODO(), ownDevice.UserID); !trusted { t.Error("Own user not trusted while they should be") @@ -90,22 +90,22 @@ func TestTrustOtherUser(t *testing.T) { t.Error("Other user trusted while they shouldn't be") } - theirMasterKey, _ := olm.NewPkSigning() - m.CryptoStore.PutCrossSigningKey(context.TODO(), otherUser, id.XSUsageMaster, theirMasterKey.PublicKey) + theirMasterKey, _ := olm.NewPKSigning() + m.CryptoStore.PutCrossSigningKey(context.TODO(), otherUser, id.XSUsageMaster, theirMasterKey.PublicKey()) - m.CryptoStore.PutSignature(context.TODO(), m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey, - m.Client.UserID, m.CrossSigningKeys.MasterKey.PublicKey, "sig1") + m.CryptoStore.PutSignature(context.TODO(), m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey(), + m.Client.UserID, m.CrossSigningKeys.MasterKey.PublicKey(), "sig1") // sign them with self-signing instead of user-signing key - m.CryptoStore.PutSignature(context.TODO(), otherUser, theirMasterKey.PublicKey, - m.Client.UserID, m.CrossSigningKeys.SelfSigningKey.PublicKey, "invalid_sig") + m.CryptoStore.PutSignature(context.TODO(), otherUser, theirMasterKey.PublicKey(), + m.Client.UserID, m.CrossSigningKeys.SelfSigningKey.PublicKey(), "invalid_sig") if trusted, _ := m.IsUserTrusted(context.TODO(), otherUser); trusted { t.Error("Other user trusted before their master key has been signed with our user-signing key") } - m.CryptoStore.PutSignature(context.TODO(), otherUser, theirMasterKey.PublicKey, - m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey, "sig2") + m.CryptoStore.PutSignature(context.TODO(), otherUser, theirMasterKey.PublicKey(), + m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey(), "sig2") if trusted, _ := m.IsUserTrusted(context.TODO(), otherUser); !trusted { t.Error("Other user not trusted while they should be") @@ -127,29 +127,29 @@ func TestTrustOtherDevice(t *testing.T) { t.Error("Other device trusted while it shouldn't be") } - theirMasterKey, _ := olm.NewPkSigning() - m.CryptoStore.PutCrossSigningKey(context.TODO(), otherUser, id.XSUsageMaster, theirMasterKey.PublicKey) - theirSSK, _ := olm.NewPkSigning() - m.CryptoStore.PutCrossSigningKey(context.TODO(), otherUser, id.XSUsageSelfSigning, theirSSK.PublicKey) + theirMasterKey, _ := olm.NewPKSigning() + m.CryptoStore.PutCrossSigningKey(context.TODO(), otherUser, id.XSUsageMaster, theirMasterKey.PublicKey()) + theirSSK, _ := olm.NewPKSigning() + m.CryptoStore.PutCrossSigningKey(context.TODO(), otherUser, id.XSUsageSelfSigning, theirSSK.PublicKey()) - m.CryptoStore.PutSignature(context.TODO(), m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey, - m.Client.UserID, m.CrossSigningKeys.MasterKey.PublicKey, "sig1") - m.CryptoStore.PutSignature(context.TODO(), otherUser, theirMasterKey.PublicKey, - m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey, "sig2") + m.CryptoStore.PutSignature(context.TODO(), m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey(), + m.Client.UserID, m.CrossSigningKeys.MasterKey.PublicKey(), "sig1") + m.CryptoStore.PutSignature(context.TODO(), otherUser, theirMasterKey.PublicKey(), + m.Client.UserID, m.CrossSigningKeys.UserSigningKey.PublicKey(), "sig2") if trusted, _ := m.IsUserTrusted(context.TODO(), otherUser); !trusted { t.Error("Other user not trusted while they should be") } - m.CryptoStore.PutSignature(context.TODO(), otherUser, theirSSK.PublicKey, - otherUser, theirMasterKey.PublicKey, "sig3") + m.CryptoStore.PutSignature(context.TODO(), otherUser, theirSSK.PublicKey(), + otherUser, theirMasterKey.PublicKey(), "sig3") if m.IsDeviceTrusted(theirDevice) { t.Error("Other device trusted before it has been signed with user's SSK") } m.CryptoStore.PutSignature(context.TODO(), otherUser, theirDevice.SigningKey, - otherUser, theirSSK.PublicKey, "sig4") + otherUser, theirSSK.PublicKey(), "sig4") if !m.IsDeviceTrusted(theirDevice) { t.Error("Other device not trusted while it should be") diff --git a/crypto/goolm/pk/decryption.go b/crypto/goolm/pk/decryption.go index 3fb3c2a5..d08e09f4 100644 --- a/crypto/goolm/pk/decryption.go +++ b/crypto/goolm/pk/decryption.go @@ -45,8 +45,8 @@ func NewDecryptionFromPrivate(privateKey crypto.Curve25519PrivateKey) (*Decrypti return s, nil } -// PubKey returns the public key base 64 encoded. -func (s Decryption) PubKey() id.Curve25519 { +// PublicKey returns the public key base 64 encoded. +func (s Decryption) PublicKey() id.Curve25519 { return s.KeyPair.B64Encoded() } diff --git a/crypto/goolm/pk/pk_test.go b/crypto/goolm/pk/pk_test.go index 91bab5b9..7ac524be 100644 --- a/crypto/goolm/pk/pk_test.go +++ b/crypto/goolm/pk/pk_test.go @@ -30,14 +30,14 @@ func TestEncryptionDecryption(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal([]byte(decryption.PubKey()), alicePublic) { + if !bytes.Equal([]byte(decryption.PublicKey()), alicePublic) { t.Fatal("public key not correct") } if !bytes.Equal(decryption.PrivateKey(), alicePrivate) { t.Fatal("private key not correct") } - encryption, err := pk.NewEncryption(decryption.PubKey()) + encryption, err := pk.NewEncryption(decryption.PublicKey()) if err != nil { t.Fatal(err) } @@ -66,7 +66,10 @@ func TestSigning(t *testing.T) { } message := []byte("We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness.") signing, _ := pk.NewSigningFromSeed(seed) - signature := signing.Sign(message) + signature, err := signing.Sign(message) + if err != nil { + t.Fatal(err) + } signatureDecoded, err := goolm.Base64Decode(signature) if err != nil { t.Fatal(err) @@ -101,7 +104,7 @@ func TestDecryptionPickling(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal([]byte(decryption.PubKey()), alicePublic) { + if !bytes.Equal([]byte(decryption.PublicKey()), alicePublic) { t.Fatal("public key not correct") } if !bytes.Equal(decryption.PrivateKey(), alicePrivate) { @@ -125,7 +128,7 @@ func TestDecryptionPickling(t *testing.T) { if err != nil { t.Fatal(err) } - if !bytes.Equal([]byte(newDecription.PubKey()), alicePublic) { + if !bytes.Equal([]byte(newDecription.PublicKey()), alicePublic) { t.Fatal("public key not correct") } if !bytes.Equal(newDecription.PrivateKey(), alicePrivate) { diff --git a/crypto/goolm/pk/signing.go b/crypto/goolm/pk/signing.go index 046838ff..a98330d5 100644 --- a/crypto/goolm/pk/signing.go +++ b/crypto/goolm/pk/signing.go @@ -2,7 +2,11 @@ package pk import ( "crypto/rand" + "encoding/json" + "github.com/tidwall/sjson" + + "maunium.net/go/mautrix/crypto/canonicaljson" "maunium.net/go/mautrix/crypto/goolm" "maunium.net/go/mautrix/crypto/goolm/crypto" "maunium.net/go/mautrix/id" @@ -10,15 +14,15 @@ import ( // Signing is used for signing a pk type Signing struct { - KeyPair crypto.Ed25519KeyPair `json:"key_pair"` - Seed []byte `json:"seed"` + keyPair crypto.Ed25519KeyPair + seed []byte } // NewSigningFromSeed constructs a new Signing based on a seed. func NewSigningFromSeed(seed []byte) (*Signing, error) { s := &Signing{} - s.Seed = seed - s.KeyPair = crypto.Ed25519GenerateFromSeed(seed) + s.seed = seed + s.keyPair = crypto.Ed25519GenerateFromSeed(seed) return s, nil } @@ -32,13 +36,34 @@ func NewSigning() (*Signing, error) { return NewSigningFromSeed(seed) } -// Sign returns the signature of the message base64 encoded. -func (s Signing) Sign(message []byte) []byte { - signature := s.KeyPair.Sign(message) - return goolm.Base64Encode(signature) +// Seed returns the seed of the key pair. +func (s Signing) Seed() []byte { + return s.seed } // PublicKey returns the public key of the key pair base 64 encoded. func (s Signing) PublicKey() id.Ed25519 { - return s.KeyPair.B64Encoded() + return s.keyPair.B64Encoded() +} + +// Sign returns the signature of the message base64 encoded. +func (s Signing) Sign(message []byte) ([]byte, error) { + signature := s.keyPair.Sign(message) + return goolm.Base64Encode(signature), nil +} + +// SignJSON creates a signature for the given object after encoding it to +// canonical JSON. +func (s Signing) SignJSON(obj any) (string, error) { + objJSON, err := json.Marshal(obj) + if err != nil { + return "", err + } + objJSON, _ = sjson.DeleteBytes(objJSON, "unsigned") + objJSON, _ = sjson.DeleteBytes(objJSON, "signatures") + signature, err := s.Sign(canonicaljson.CanonicalJSONAssumeValid(objJSON)) + if err != nil { + return "", err + } + return string(signature), nil } diff --git a/crypto/olm/pk_goolm.go b/crypto/olm/pk_goolm.go index 9659e918..372c94fa 100644 --- a/crypto/olm/pk_goolm.go +++ b/crypto/olm/pk_goolm.go @@ -1,71 +1,29 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// When the goolm build flag is enabled, this file will make [PKSigning] +// constructors use the goolm constuctors. + //go:build goolm package olm -import ( - "encoding/json" +import "maunium.net/go/mautrix/crypto/goolm/pk" - "github.com/tidwall/sjson" - - "maunium.net/go/mautrix/crypto/canonicaljson" - "maunium.net/go/mautrix/crypto/goolm/pk" - "maunium.net/go/mautrix/id" -) - -// PkSigning stores a key pair for signing messages. -type PkSigning struct { - pk.Signing - PublicKey id.Ed25519 - Seed []byte +// NewPKSigningFromSeed creates a new PKSigning object using the given seed. +func NewPKSigningFromSeed(seed []byte) (PKSigning, error) { + return pk.NewSigningFromSeed(seed) } -// Clear clears the underlying memory of a PkSigning object. -func (p *PkSigning) Clear() { - p.Signing = pk.Signing{} +// NewPKSigning creates a new [PKSigning] object, containing a key pair for +// signing messages. +func NewPKSigning() (PKSigning, error) { + return pk.NewSigning() } -// NewPkSigningFromSeed creates a new PkSigning object using the given seed. -func NewPkSigningFromSeed(seed []byte) (*PkSigning, error) { - p := &PkSigning{} - signing, err := pk.NewSigningFromSeed(seed) - if err != nil { - return nil, err - } - p.Signing = *signing - p.Seed = seed - p.PublicKey = p.Signing.PublicKey() - return p, nil -} - -// NewPkSigning creates a new PkSigning object, containing a key pair for signing messages. -func NewPkSigning() (*PkSigning, error) { - p := &PkSigning{} - signing, err := pk.NewSigning() - if err != nil { - return nil, err - } - p.Signing = *signing - p.Seed = signing.Seed - p.PublicKey = p.Signing.PublicKey() - return p, err -} - -// Sign creates a signature for the given message using this key. -func (p *PkSigning) Sign(message []byte) ([]byte, error) { - return p.Signing.Sign(message), nil -} - -// SignJSON creates a signature for the given object after encoding it to canonical JSON. -func (p *PkSigning) SignJSON(obj interface{}) (string, error) { - objJSON, err := json.Marshal(obj) - if err != nil { - return "", err - } - objJSON, _ = sjson.DeleteBytes(objJSON, "unsigned") - objJSON, _ = sjson.DeleteBytes(objJSON, "signatures") - signature, err := p.Sign(canonicaljson.CanonicalJSONAssumeValid(objJSON)) - if err != nil { - return "", err - } - return string(signature), nil +func NewPKDecryption(privateKey []byte) (PKDecryption, error) { + return pk.NewDecryption() } diff --git a/crypto/olm/pk_interface.go b/crypto/olm/pk_interface.go new file mode 100644 index 00000000..11c41431 --- /dev/null +++ b/crypto/olm/pk_interface.go @@ -0,0 +1,41 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package olm + +import ( + "maunium.net/go/mautrix/crypto/goolm/pk" + "maunium.net/go/mautrix/id" +) + +// PKSigning is an interface for signing messages. +type PKSigning interface { + // Seed returns the seed of the key. + Seed() []byte + + // PublicKey returns the public key. + PublicKey() id.Ed25519 + + // Sign creates a signature for the given message using this key. + Sign(message []byte) ([]byte, error) + + // SignJSON creates a signature for the given object after encoding it to + // canonical JSON. + SignJSON(obj any) (string, error) +} + +var _ PKSigning = (*pk.Signing)(nil) + +// PKDecryption is an interface for decrypting messages. +type PKDecryption interface { + // PublicKey returns the public key. + PublicKey() id.Curve25519 + + // Decrypt verifies and decrypts the given message. + Decrypt(ciphertext, mac []byte, key id.Curve25519) ([]byte, error) +} + +var _ PKDecryption = (*pk.Decryption)(nil) diff --git a/crypto/olm/pk.go b/crypto/olm/pk_libolm.go similarity index 67% rename from crypto/olm/pk.go rename to crypto/olm/pk_libolm.go index ba390afe..0854b4d1 100644 --- a/crypto/olm/pk.go +++ b/crypto/olm/pk_libolm.go @@ -1,3 +1,9 @@ +// Copyright (c) 2024 Sumner Evans +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + //go:build !goolm package olm @@ -18,14 +24,17 @@ import ( "maunium.net/go/mautrix/id" ) -// PkSigning stores a key pair for signing messages. -type PkSigning struct { +// LibOlmPKSigning stores a key pair for signing messages. +type LibOlmPKSigning struct { int *C.OlmPkSigning mem []byte - PublicKey id.Ed25519 - Seed []byte + publicKey id.Ed25519 + seed []byte } +// Ensure that LibOlmPKSigning implements PKSigning. +var _ PKSigning = (*LibOlmPKSigning)(nil) + func pkSigningSize() uint { return uint(C.olm_pk_signing_size()) } @@ -42,48 +51,57 @@ func pkSigningSignatureLength() uint { return uint(C.olm_pk_signature_length()) } -func NewBlankPkSigning() *PkSigning { +func newBlankPKSigning() *LibOlmPKSigning { memory := make([]byte, pkSigningSize()) - return &PkSigning{ + return &LibOlmPKSigning{ int: C.olm_pk_signing(unsafe.Pointer(&memory[0])), mem: memory, } } -// Clear clears the underlying memory of a PkSigning object. -func (p *PkSigning) Clear() { - C.olm_clear_pk_signing((*C.OlmPkSigning)(p.int)) -} - -// NewPkSigningFromSeed creates a new PkSigning object using the given seed. -func NewPkSigningFromSeed(seed []byte) (*PkSigning, error) { - p := NewBlankPkSigning() - p.Clear() +// NewPKSigningFromSeed creates a new [PKSigning] object using the given seed. +func NewPKSigningFromSeed(seed []byte) (PKSigning, error) { + p := newBlankPKSigning() + p.clear() pubKey := make([]byte, pkSigningPublicKeyLength()) if C.olm_pk_signing_key_from_seed((*C.OlmPkSigning)(p.int), unsafe.Pointer(&pubKey[0]), C.size_t(len(pubKey)), unsafe.Pointer(&seed[0]), C.size_t(len(seed))) == errorVal() { return nil, p.lastError() } - p.PublicKey = id.Ed25519(pubKey) - p.Seed = seed + p.publicKey = id.Ed25519(pubKey) + p.seed = seed return p, nil } -// NewPkSigning creates a new PkSigning object, containing a key pair for signing messages. -func NewPkSigning() (*PkSigning, error) { +// NewPKSigning creates a new LibOlmPKSigning object, containing a key pair for +// signing messages. +func NewPKSigning() (PKSigning, error) { // Generate the seed seed := make([]byte, pkSigningSeedLength()) _, err := rand.Read(seed) if err != nil { panic(NotEnoughGoRandom) } - pk, err := NewPkSigningFromSeed(seed) + pk, err := NewPKSigningFromSeed(seed) return pk, err } +func (p *LibOlmPKSigning) PublicKey() id.Ed25519 { + return p.publicKey +} + +func (p *LibOlmPKSigning) Seed() []byte { + return p.seed +} + +// clear clears the underlying memory of a LibOlmPKSigning object. +func (p *LibOlmPKSigning) clear() { + C.olm_clear_pk_signing((*C.OlmPkSigning)(p.int)) +} + // Sign creates a signature for the given message using this key. -func (p *PkSigning) Sign(message []byte) ([]byte, error) { +func (p *LibOlmPKSigning) Sign(message []byte) ([]byte, error) { signature := make([]byte, pkSigningSignatureLength()) if C.olm_pk_sign((*C.OlmPkSigning)(p.int), (*C.uint8_t)(unsafe.Pointer(&message[0])), C.size_t(len(message)), (*C.uint8_t)(unsafe.Pointer(&signature[0])), C.size_t(len(signature))) == errorVal() { @@ -93,7 +111,7 @@ func (p *PkSigning) Sign(message []byte) ([]byte, error) { } // SignJSON creates a signature for the given object after encoding it to canonical JSON. -func (p *PkSigning) SignJSON(obj interface{}) (string, error) { +func (p *LibOlmPKSigning) SignJSON(obj interface{}) (string, error) { objJSON, err := json.Marshal(obj) if err != nil { return "", err @@ -107,12 +125,13 @@ func (p *PkSigning) SignJSON(obj interface{}) (string, error) { return string(signature), nil } -// lastError returns the last error that happened in relation to this PkSigning object. -func (p *PkSigning) lastError() error { +// lastError returns the last error that happened in relation to this +// LibOlmPKSigning object. +func (p *LibOlmPKSigning) lastError() error { return convertError(C.GoString(C.olm_pk_signing_last_error((*C.OlmPkSigning)(p.int)))) } -type PkDecryption struct { +type LibOlmPKDecryption struct { int *C.OlmPkDecryption mem []byte PublicKey []byte @@ -126,13 +145,13 @@ func pkDecryptionPublicKeySize() uint { return uint(C.olm_pk_key_length()) } -func NewPkDecryption(privateKey []byte) (*PkDecryption, error) { +func NewPkDecryption(privateKey []byte) (*LibOlmPKDecryption, error) { memory := make([]byte, pkDecryptionSize()) - p := &PkDecryption{ + p := &LibOlmPKDecryption{ int: C.olm_pk_decryption(unsafe.Pointer(&memory[0])), mem: memory, } - p.Clear() + p.clear() pubKey := make([]byte, pkDecryptionPublicKeySize()) if C.olm_pk_key_from_private((*C.OlmPkDecryption)(p.int), @@ -145,7 +164,7 @@ func NewPkDecryption(privateKey []byte) (*PkDecryption, error) { return p, nil } -func (p *PkDecryption) Decrypt(ephemeralKey []byte, mac []byte, ciphertext []byte) ([]byte, error) { +func (p *LibOlmPKDecryption) Decrypt(ephemeralKey []byte, mac []byte, ciphertext []byte) ([]byte, error) { maxPlaintextLength := uint(C.olm_pk_max_plaintext_length((*C.OlmPkDecryption)(p.int), C.size_t(len(ciphertext)))) plaintext := make([]byte, maxPlaintextLength) @@ -162,11 +181,12 @@ func (p *PkDecryption) Decrypt(ephemeralKey []byte, mac []byte, ciphertext []byt } // Clear clears the underlying memory of a PkDecryption object. -func (p *PkDecryption) Clear() { +func (p *LibOlmPKDecryption) clear() { C.olm_clear_pk_decryption((*C.OlmPkDecryption)(p.int)) } -// lastError returns the last error that happened in relation to this PkDecryption object. -func (p *PkDecryption) lastError() error { +// lastError returns the last error that happened in relation to this +// LibOlmPKDecryption object. +func (p *LibOlmPKDecryption) lastError() error { return convertError(C.GoString(C.olm_pk_decryption_last_error((*C.OlmPkDecryption)(p.int)))) }