mirror of
https://github.com/unclebob/more-speech.git
synced 2024-09-30 02:21:02 +00:00
Tentative ability to send DM. Waiting for confirmation. uses "D @xxx" style.
This commit is contained in:
parent
4b5c1deab2
commit
e9d4bda3f2
@ -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));
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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`!()\[\]{};:'\".,<>?«»“”‘’]))")
|
||||
|
@ -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
|
||||
|
@ -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))))
|
||||
|
@ -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))
|
||||
|
@ -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 " ")
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user