olm/pk: make an interface

Signed-off-by: Sumner Evans <sumner@beeper.com>
This commit is contained in:
Sumner Evans 2024-03-08 15:01:44 -07:00
commit 3b65d98c0c
No known key found for this signature in database
GPG key ID: 8904527AB50022FD
10 changed files with 222 additions and 175 deletions

View file

@ -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,

View file

@ -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{

View file

@ -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
}

View file

@ -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")

View file

@ -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()
}

View file

@ -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) {

View file

@ -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
}

View file

@ -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()
}

View file

@ -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)

View file

@ -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))))
}