Tentative ability to send DM. Waiting for confirmation. uses "D @xxx" style.

This commit is contained in:
Robert C. Martin 2022-09-18 11:25:21 -05:00
parent 4b5c1deab2
commit e9d4bda3f2
7 changed files with 107 additions and 17 deletions

View File

@ -25,10 +25,26 @@ public class SECP256K1 {
curve= new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH());
}
public static byte[] bytesFromBigInteger(BigInteger n) {
byte[] b = n.toByteArray();
if(b.length == 32) {
return b;
}
else if(b.length > 32) {
return java.util.Arrays.copyOfRange(b, b.length - 32, b.length);
}
else {
byte[] buf = new byte[32];
System.arraycopy(b, 0, buf, buf.length - b.length, b.length);
return buf;
}
}
public static BigInteger calculateKeyAgreement(BigInteger privKey, BigInteger theirPubKey) {
ECPrivateKeyParameters privKeyP = new ECPrivateKeyParameters(privKey, curve);
byte[] compressed = new byte[]{2};
byte[] val = Arrays.concatenate(compressed, theirPubKey.toByteArray());
byte[] val = Arrays.concatenate(compressed, bytesFromBigInteger(theirPubKey));
ECPoint ecPoint = curve.getCurve().decodePoint(val);
ECPublicKeyParameters pubKeyP = new ECPublicKeyParameters(ecPoint, curve);
@ -42,7 +58,7 @@ public class SECP256K1 {
byte[] iv = new byte[16];
r.nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.toByteArray(), "AES"), new IvParameterSpec(iv));
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(bytesFromBigInteger(key), "AES"), new IvParameterSpec(iv));
String ivBase64 = Base64.toBase64String(iv);
byte[] encryptedMsg = cipher.doFinal(msg.getBytes());
String encryptedMsgBase64 = Base64.toBase64String(encryptedMsg);
@ -56,7 +72,7 @@ public class SECP256K1 {
byte[] decodedMsg = Base64.decode(msgPart);
byte[] iv = Base64.decode(ivPart);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.toByteArray(), "AES"), new IvParameterSpec(iv));
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(bytesFromBigInteger(key), "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(decodedMsg));
}
}

View File

@ -1,9 +1,11 @@
(ns more-speech.nostr.events_spec
(:require [speclj.core :refer :all]
[more-speech.nostr.util :as util]
[more-speech.nostr.events :refer :all]
[more-speech.nostr.elliptic-signature :refer :all]
[more-speech.nostr.util :refer :all]
[more-speech.ui.swing.ui-context :refer :all]))
[more-speech.ui.swing.ui-context :refer :all])
(:import (ecdhJava SECP256K1)))
(defrecord event-handler-dummy []
event-handler
@ -317,6 +319,58 @@
(hex-string->bytes sig)))))
)
(context "compose direct messages (kind 4)"
(it "does not encrypt a regular message"
(should= ["message" 1] (encrypt-if-direct-message "message" [])))
(it "encrypts with shared keys"
(let [sender-private-key (util/make-private-key)
recipient-private-key (util/make-private-key)
sender-public-key (get-pub-key sender-private-key)
recipient-public-key (get-pub-key recipient-private-key)
outbound-shared-secret (SECP256K1/calculateKeyAgreement
(bytes->num sender-private-key)
(bytes->num recipient-public-key))
content "message"
encrypted-content (SECP256K1/encrypt outbound-shared-secret content)
inbound-shared-secret (SECP256K1/calculateKeyAgreement
(bytes->num recipient-private-key)
(bytes->num sender-public-key))]
(should= inbound-shared-secret outbound-shared-secret)
(should= content (SECP256K1/decrypt inbound-shared-secret encrypted-content))))
(it "encrypts a direct message"
(let [event-context (:event-context @ui-context)
sender-private-key (util/make-private-key)
recipient-private-key (util/make-private-key)
sender-public-key (get-pub-key sender-private-key)
recipient-public-key (get-pub-key recipient-private-key)
_ (reset! event-context {:keys {:private-key (bytes->hex-string sender-private-key)}})
tags [[:p (bytes->hex-string recipient-public-key)]]
content "D #[0] hi."
inbound-shared-secret (SECP256K1/calculateKeyAgreement
(bytes->num recipient-private-key)
(bytes->num sender-public-key))
[encrypted-message kind] (encrypt-if-direct-message content tags)]
(should= 4 kind)
(should= content (SECP256K1/decrypt inbound-shared-secret encrypted-message))))
(it "catches fake DMs with phoney #[xxx] in them."
(let [event-context (:event-context @ui-context)
sender-private-key (util/make-private-key)
recipient-private-key (util/make-private-key)
sender-public-key (get-pub-key sender-private-key)
_ (reset! event-context {:keys {:private-key (bytes->num sender-private-key)}})
tags [[:p "dummy"]]
content "D #[223] hi."
inbound-shared-secret (SECP256K1/calculateKeyAgreement
(bytes->num recipient-private-key)
(bytes->num sender-public-key))
[encrypted-message kind] (encrypt-if-direct-message content tags)]
(should= 1 kind)
(should= content encrypted-message)))
)
(context "compose kind-3 contact-list event"
(it "composes an simple contact list"
(let [private-key (num->bytes 64 42)

View File

@ -31,6 +31,7 @@
(def user-name-pattern #"\@[\w\-]+")
(def pubkey-pattern #"[0-9a-f]{64}+")
(def user-name-chars #"[\w\-]+")
(def reference-pattern #"\#\[\d+\]")
;; https://daringfireball.net/2010/07/improved_regex_for_matching_urls
(def url-pattern #"(?i)\b(?:(?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(?:(?:[^\s()<>]+|(?:\(?:[^\s()<>]+\)))*\))+(?:\(?:(?:[^\s()<>]+|(?:\(?:[^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))")

View File

@ -1,6 +1,7 @@
(ns more-speech.migrator
(:require [clojure.java.io :as io]
[clojure.set :as set]
[more-speech.nostr.util :as util]
[more-speech.config :refer [migration-filename]]
[more-speech.config :as config]
[more-speech.nostr.util :as util]
@ -9,7 +10,7 @@
[more-speech.data-storage :as data-storage]
[more-speech.data-storage :as data-storage]
[more-speech.user-configuration :as user-configuration])
(:import (java.security SecureRandom)))
)
(defn file-exists? [fname]
(.exists (io/file fname)))
@ -21,11 +22,7 @@
(when (file-exists? fname)
(io/delete-file fname)))
(defn make-private-key []
(let [gen (SecureRandom.)
key-bytes (byte-array 32)
_ (.nextBytes gen key-bytes)]
key-bytes))
;---The Migrations
@ -33,7 +30,7 @@
(when-not (file-exists? @config/private-directory)
(.mkdir (io/file @config/private-directory)))
(when-not (file-exists? @config/keys-filename)
(let [private-key (make-private-key)
(let [private-key (util/make-private-key)
public-key (ecc/get-pub-key private-key)
temp-user-name (str "more-speech-" (rand-int 100000))]
(spit @config/keys-filename {:name temp-user-name

View File

@ -10,7 +10,8 @@
[clojure.string :as string]
[more-speech.nostr.contact-list :as contact-list]
[more-speech.config :as config])
(:import (java.nio.charset StandardCharsets)))
(:import (java.nio.charset StandardCharsets)
(ecdhJava SECP256K1)))
(s/def ::id number?)
(s/def ::pubkey number?)
@ -421,6 +422,21 @@
tags]
))
(defn encrypt-if-direct-message [content tags]
(if (re-matches #"D \#\[\d+\].*" content)
(let [reference-digits (re-find #"\d+" content)
reference-index (Integer/parseInt reference-digits)
p-tag (get tags reference-index)]
(if (nil? p-tag)
[content 1]
(let [recipient-key (hex-string->num (second p-tag))
sender-key (hex-string->num (get-in (get-event-state) [:keys :private-key]))
shared-secret (SECP256K1/calculateKeyAgreement sender-key recipient-key)
encrypted-content (SECP256K1/encrypt shared-secret content)]
[encrypted-content 4])))
[content 1])
)
(defn compose-text-event
([subject text]
(compose-text-event subject text nil))
@ -433,7 +449,8 @@
(make-subject-tag subject)
[[:client (str "more-speech - " config/version)]])
[content tags] (emplace-references text tags)
body {:kind 1
[content kind] (encrypt-if-direct-message content tags)
body {:kind kind
:tags tags
:content content}]
(body->event body))))

View File

@ -1,5 +1,5 @@
(ns more-speech.nostr.util
(:import (java.security MessageDigest)))
(:import (java.security MessageDigest SecureRandom)))
(defn num->bytes
"Returns the byte-array representation of n.
@ -69,3 +69,9 @@
(doseq [i (range (alength a))]
(aset result i (byte (bit-xor (aget a i) (aget b i)))))
result))
(defn make-private-key []
(let [gen (SecureRandom.)
key-bytes (byte-array 32)
_ (.nextBytes gen key-bytes)]
key-bytes))

View File

@ -62,9 +62,8 @@
(defn replace-references [event]
(let [padded-content (str " " (:content event) " ")
pattern #"\#\[\d+\]"
references (re-seq pattern padded-content)
segments (string/split padded-content pattern)
references (re-seq config/reference-pattern padded-content)
segments (string/split padded-content config/reference-pattern)
referents (mapv (partial lookup-reference event) references)
referents (conj referents " ")
]