more fixes and tweaks to keyer, 17, 44 and 59.

This commit is contained in:
fiatjaf
2024-09-14 23:23:53 -03:00
parent 46a0c95b96
commit f976296e01
4 changed files with 137 additions and 223 deletions

View File

@@ -18,9 +18,10 @@ type KeySigner struct {
func (ks KeySigner) SignEvent(ctx context.Context, evt *nostr.Event) error { return evt.Sign(ks.sk) }
func (ks KeySigner) GetPublicKey(ctx context.Context) string { return ks.pk }
func (ks KeySigner) Encrypt(ctx context.Context, plaintext string, recipient string) (c64 string, err error) {
func (ks KeySigner) Encrypt(ctx context.Context, plaintext string, recipient string) (string, error) {
ck, ok := ks.conversationKeys[recipient]
if !ok {
var err error
ck, err = nip44.GenerateConversationKey(recipient, ks.sk)
if err != nil {
return "", err
@@ -30,7 +31,7 @@ func (ks KeySigner) Encrypt(ctx context.Context, plaintext string, recipient str
return nip44.Encrypt(plaintext, ck)
}
func (ks KeySigner) Decrypt(ctx context.Context, base64ciphertext string, sender string) (plaintext string, err error) {
func (ks KeySigner) Decrypt(ctx context.Context, base64ciphertext string, sender string) (string, error) {
ck, ok := ks.conversationKeys[sender]
if !ok {
var err error
@@ -40,5 +41,5 @@ func (ks KeySigner) Decrypt(ctx context.Context, base64ciphertext string, sender
}
ks.conversationKeys[sender] = ck
}
return nip44.Encrypt(plaintext, ck)
return nip44.Decrypt(base64ciphertext, ck)
}

View File

@@ -37,23 +37,30 @@ func PrepareMessage(
kr keyer.Keyer,
recipientPubKey string,
modify func(*nostr.Event),
) (nostr.Event, error) {
) (toUs nostr.Event, toThem nostr.Event, err error) {
ourPubkey := kr.GetPublicKey(ctx)
rumor := nostr.Event{
Kind: 14,
Content: content,
Tags: tags,
CreatedAt: nostr.Now(),
PubKey: kr.GetPublicKey(ctx),
PubKey: ourPubkey,
}
rumor.ID = rumor.GetID()
return nip59.GiftWrap(
wraps, err := nip59.GiftWrap(
rumor,
recipientPubKey,
[]string{ourPubkey, recipientPubKey},
func(s string) (string, error) { return kr.Encrypt(ctx, s, recipientPubKey) },
func(e *nostr.Event) error { return kr.SignEvent(ctx, e) },
modify,
)
if err != nil {
return nostr.Event{}, nostr.Event{}, err
}
return wraps[0], wraps[1], nil
}
// ListenForMessages returns a channel with the rumors already decrypted and checked

View File

@@ -5,189 +5,108 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"
"strings"
"testing"
"github.com/nbd-wtf/go-nostr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func assertCryptPriv(t *testing.T, sk1 string, sk2 string, conversationKey string, salt string, plaintext string, expected string) {
var (
k1 [32]byte
s []byte
actual string
decrypted string
ok bool
err error
)
k1, err = hexDecode32Array(conversationKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok {
return
}
if ok = assertConversationKeyGenerationSec(t, sk1, sk2, conversationKey); !ok {
return
}
s, err = hex.DecodeString(salt)
if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok {
return
}
actual, err = Encrypt(plaintext, k1, WithCustomNonce(s))
if ok = assert.NoError(t, err, "encryption failed: %v", err); !ok {
return
}
if ok = assert.Equalf(t, expected, actual, "wrong encryption"); !ok {
return
}
decrypted, err = Decrypt(expected, k1)
if ok = assert.NoErrorf(t, err, "decryption failed: %v", err); !ok {
return
}
assert.Equal(t, decrypted, plaintext, "wrong decryption")
k1, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for conversation key: %v", err)
assertConversationKeyGenerationSec(t, sk1, sk2, conversationKey)
customNonce, err := hex.DecodeString(salt)
require.NoErrorf(t, err, "hex decode failed for salt: %v", err)
actual, err := Encrypt(plaintext, k1, WithCustomNonce(customNonce))
require.NoError(t, err, "encryption failed: %v", err)
require.Equalf(t, expected, actual, "wrong encryption")
decrypted, err := Decrypt(expected, k1)
require.NoErrorf(t, err, "decryption failed: %v", err)
require.Equal(t, decrypted, plaintext, "wrong decryption")
}
func assertDecryptFail(t *testing.T, conversationKey string, _ string, ciphertext string, msg string) {
var (
k1 [32]byte
ok bool
err error
)
k1, err = hexDecode32Array(conversationKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok {
return
}
k1, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for conversation key: %v", err)
_, err = Decrypt(ciphertext, k1)
assert.ErrorContains(t, err, msg)
require.ErrorContains(t, err, msg)
}
func assertConversationKeyFail(t *testing.T, priv string, pub string, msg string) {
_, err := GenerateConversationKey(pub, priv)
assert.ErrorContains(t, err, msg)
require.ErrorContains(t, err, msg)
}
func assertConversationKeyGeneration(t *testing.T, priv string, pub string, conversationKey string) bool {
var (
actualConversationKey [32]byte
expectedConversationKey [32]byte
ok bool
err error
)
expectedConversationKey, err = hexDecode32Array(conversationKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok {
return false
}
actualConversationKey, err = GenerateConversationKey(pub, priv)
if ok = assert.NoErrorf(t, err, "conversation key generation failed: %v", err); !ok {
return false
}
if ok = assert.Equalf(t, expectedConversationKey, actualConversationKey, "wrong conversation key"); !ok {
return false
}
return true
func assertConversationKeyGenerationPub(t *testing.T, priv string, pub string, conversationKey string) {
expectedConversationKey, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for conversation key: %v", err)
actualConversationKey, err := GenerateConversationKey(pub, priv)
require.NoErrorf(t, err, "conversation key generation failed: %v", err)
require.Equalf(t, expectedConversationKey, actualConversationKey, "wrong conversation key")
}
func assertConversationKeyGenerationSec(t *testing.T, sk1 string, sk2 string, conversationKey string) bool {
func assertConversationKeyGenerationSec(t *testing.T, sk1 string, sk2 string, conversationKey string) {
pub2, err := nostr.GetPublicKey(sk2)
if ok := assert.NoErrorf(t, err, "failed to derive pubkey from sk2: %v", err); !ok {
return false
}
return assertConversationKeyGeneration(t, sk1, pub2, conversationKey)
}
func assertConversationKeyGenerationPub(t *testing.T, sk string, pub string, conversationKey string) bool {
return assertConversationKeyGeneration(t, sk, pub, conversationKey)
require.NoErrorf(t, err, "failed to derive pubkey from sk2: %v", err)
assertConversationKeyGenerationPub(t, sk1, pub2, conversationKey)
}
func assertMessageKeyGeneration(t *testing.T, conversationKey string, salt string, chachaKey string, chachaSalt string, hmacKey string) bool {
var (
convKey [32]byte
convSalt []byte
actualChaChaKey []byte
expectedChaChaKey []byte
actualChaChaNonce []byte
expectedChaChaNonce []byte
actualHmacKey []byte
expectedHmacKey []byte
ok bool
err error
)
convKey, err = hexDecode32Array(conversationKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for convKey: %v", err); !ok {
return false
}
convSalt, err = hex.DecodeString(salt)
if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok {
return false
}
expectedChaChaKey, err = hex.DecodeString(chachaKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for chacha key: %v", err); !ok {
return false
}
expectedChaChaNonce, err = hex.DecodeString(chachaSalt)
if ok = assert.NoErrorf(t, err, "hex decode failed for chacha nonce: %v", err); !ok {
return false
}
expectedHmacKey, err = hex.DecodeString(hmacKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for hmac key: %v", err); !ok {
return false
}
actualChaChaKey, actualChaChaNonce, actualHmacKey, err = messageKeys(convKey, convSalt)
if ok = assert.NoErrorf(t, err, "message key generation failed: %v", err); !ok {
return false
}
if ok = assert.Equalf(t, expectedChaChaKey, actualChaChaKey, "wrong chacha key"); !ok {
return false
}
if ok = assert.Equalf(t, expectedChaChaNonce, actualChaChaNonce, "wrong chacha nonce"); !ok {
return false
}
if ok = assert.Equalf(t, expectedHmacKey, actualHmacKey, "wrong hmac key"); !ok {
return false
}
convKey, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for convKey: %v", err)
convNonce, err := hexDecode32Array(salt)
require.NoErrorf(t, err, "hex decode failed for nonce: %v", err)
expectedChaChaKey, err := hex.DecodeString(chachaKey)
require.NoErrorf(t, err, "hex decode failed for chacha key: %v", err)
expectedChaChaNonce, err := hex.DecodeString(chachaSalt)
require.NoErrorf(t, err, "hex decode failed for chacha nonce: %v", err)
expectedHmacKey, err := hex.DecodeString(hmacKey)
require.NoErrorf(t, err, "hex decode failed for hmac key: %v", err)
actualChaChaKey, actualChaChaNonce, actualHmacKey, err := messageKeys(convKey, convNonce)
require.NoErrorf(t, err, "message key generation failed: %v", err)
require.Equalf(t, expectedChaChaKey, actualChaChaKey, "wrong chacha key")
require.Equalf(t, expectedChaChaNonce, actualChaChaNonce, "wrong chacha nonce")
require.Equalf(t, expectedHmacKey, actualHmacKey, "wrong hmac key")
return true
}
func assertCryptLong(t *testing.T, conversationKey string, salt string, pattern string, repeat int, plaintextSha256 string, payloadSha256 string) {
var (
convKey [32]byte
convSalt []byte
plaintext string
actualPlaintextSha256 string
actualPayload string
actualPayloadSha256 string
h hash.Hash
ok bool
err error
)
convKey, err = hexDecode32Array(conversationKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for convKey: %v", err); !ok {
return
}
convSalt, err = hex.DecodeString(salt)
if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok {
return
}
plaintext = ""
convKey, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for convKey: %v", err)
customNonce, err := hex.DecodeString(salt)
require.NoErrorf(t, err, "hex decode failed for salt: %v", err)
plaintext := ""
for i := 0; i < repeat; i++ {
plaintext += pattern
}
h = sha256.New()
h := sha256.New()
h.Write([]byte(plaintext))
actualPlaintextSha256 = hex.EncodeToString(h.Sum(nil))
if ok = assert.Equalf(t, plaintextSha256, actualPlaintextSha256, "invalid plaintext sha256 hash: %v", err); !ok {
return
}
actualPayload, err = Encrypt(plaintext, convKey, WithCustomNonce(convSalt))
if ok = assert.NoErrorf(t, err, "encryption failed: %v", err); !ok {
return
}
actualPlaintextSha256 := hex.EncodeToString(h.Sum(nil))
require.Equalf(t, plaintextSha256, actualPlaintextSha256, "invalid plaintext sha256 hash: %v", err)
actualPayload, err := Encrypt(plaintext, convKey, WithCustomNonce(customNonce))
require.NoErrorf(t, err, "encryption failed: %v", err)
h.Reset()
h.Write([]byte(actualPayload))
actualPayloadSha256 = hex.EncodeToString(h.Sum(nil))
if ok = assert.Equalf(t, payloadSha256, actualPayloadSha256, "invalid payload sha256 hash: %v", err); !ok {
return
}
actualPayloadSha256 := hex.EncodeToString(h.Sum(nil))
require.Equalf(t, payloadSha256, actualPayloadSha256, "invalid payload sha256 hash: %v", err)
}
func TestCryptPriv001(t *testing.T) {
@@ -1162,38 +1081,23 @@ func TestMaxLength(t *testing.T) {
)
}
func assertCryptPub(t *testing.T, sk1 string, pub2 string, conversationKey string, salt string, plaintext string, expected string) {
var (
k1 [32]byte
s []byte
actual string
decrypted string
ok bool
err error
)
k1, err = hexDecode32Array(conversationKey)
if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok {
return
}
if ok = assertConversationKeyGenerationPub(t, sk1, pub2, conversationKey); !ok {
return
}
s, err = hex.DecodeString(salt)
if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok {
return
}
actual, err = Encrypt(plaintext, k1, WithCustomNonce(s))
if ok = assert.NoError(t, err, "encryption failed: %v", err); !ok {
return
}
if ok = assert.Equalf(t, expected, actual, "wrong encryption"); !ok {
return
}
decrypted, err = Decrypt(expected, k1)
if ok = assert.NoErrorf(t, err, "decryption failed: %v", err); !ok {
return
}
assert.Equal(t, decrypted, plaintext, "wrong decryption")
func assertCryptPub(t *testing.T, sk1 string, pub2 string, conversationKey string, customNonce string, plaintext string, expected string) {
k1, err := hexDecode32Array(conversationKey)
require.NoErrorf(t, err, "hex decode failed for conversation key: %v", err)
assertConversationKeyGenerationPub(t, sk1, pub2, conversationKey)
s, err := hex.DecodeString(customNonce)
require.NoErrorf(t, err, "hex decode failed for salt: %v", err)
actual, err := Encrypt(plaintext, k1, WithCustomNonce(s))
require.NoError(t, err, "encryption failed: %v", err)
require.Equalf(t, expected, actual, "wrong encryption")
decrypted, err := Decrypt(expected, k1)
require.NoErrorf(t, err, "decryption failed: %v", err)
require.Equal(t, decrypted, plaintext, "wrong decryption")
}
func hexDecode32Array(hexString string) (res [32]byte, err error) {

View File

@@ -13,15 +13,15 @@ import (
// signs that (after potentially applying a modify function, which can be nil otherwise), yielding a 'gift-wrap'.
func GiftWrap(
rumor nostr.Event,
recipientPublicKey string,
recipients []string, // return one giftwrap addressed to each of these
encrypt func(plaintext string) (string, error),
sign func(*nostr.Event) error,
modify func(*nostr.Event),
) (nostr.Event, error) {
) ([]nostr.Event, error) {
rumor.Sig = ""
rumorCiphertext, err := encrypt(rumor.String())
if err != nil {
return nostr.Event{}, err
return nil, err
}
seal := nostr.Event{
@@ -31,37 +31,39 @@ func GiftWrap(
Tags: make(nostr.Tags, 0),
}
if err := sign(&seal); err != nil {
return nostr.Event{}, err
return nil, err
}
nonceKey := nostr.GeneratePrivateKey()
temporaryConversationKey, err := nip44.GenerateConversationKey(recipientPublicKey, nonceKey)
if err != nil {
return nostr.Event{}, err
}
sealCiphertext, err := nip44.Encrypt(seal.String(), temporaryConversationKey)
if err != nil {
return nostr.Event{}, err
results := make([]nostr.Event, len(recipients))
for i, recipient := range recipients {
nonceKey := nostr.GeneratePrivateKey()
temporaryConversationKey, err := nip44.GenerateConversationKey(recipient, nonceKey)
if err != nil {
return nil, err
}
sealCiphertext, err := nip44.Encrypt(seal.String(), temporaryConversationKey)
if err != nil {
return nil, err
}
gw := nostr.Event{
Kind: 1059,
Content: sealCiphertext,
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
Tags: nostr.Tags{
nostr.Tag{"p", recipient},
},
}
if modify != nil {
modify(&gw)
}
if err := gw.Sign(nonceKey); err != nil {
return nil, err
}
results[i] = gw
}
gw := nostr.Event{
Kind: 1059,
Content: sealCiphertext,
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
Tags: nostr.Tags{
nostr.Tag{"p", recipientPublicKey},
},
}
if modify != nil {
modify(&gw)
}
if err := gw.Sign(nonceKey); err != nil {
return gw, err
}
return gw, nil
return results, nil
}
func GiftUnwrap(
@@ -70,13 +72,13 @@ func GiftUnwrap(
) (rumor nostr.Event, err error) {
jseal, err := decrypt(gw.PubKey, gw.Content)
if err != nil {
return rumor, err
return rumor, fmt.Errorf("failed to decrypt seal: %w", err)
}
var seal nostr.Event
err = easyjson.Unmarshal([]byte(jseal), &seal)
if err != nil {
return rumor, err
return rumor, fmt.Errorf("seal is invalid json: %w", err)
}
if ok, _ := seal.CheckSignature(); !ok {
@@ -85,12 +87,12 @@ func GiftUnwrap(
jrumor, err := decrypt(seal.PubKey, seal.Content)
if err != nil {
return rumor, err
return rumor, fmt.Errorf("failed to decrypt rumor: %w", err)
}
err = easyjson.Unmarshal([]byte(jrumor), &rumor)
if err != nil {
return rumor, err
return rumor, fmt.Errorf("rumor is invalid json: %w", err)
}
rumor.PubKey = seal.PubKey