@@ -3,7 +3,6 @@ namespace Skrepka
33open System
44open System.Text
55open System.Security.Cryptography
6−open Sodium
76
87module Crypto =
98
@@ -19,15 +18,122 @@ module Crypto =
1918 try Some(Convert.FromHexString(hex))
2019 with :? FormatException -> None
2120
21+#if MOBILE
22+ open System.Runtime.InteropServices
23+
24+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
25+ extern int private sodium_init()
26+
27+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
28+ extern void private randombytes_buf(byte[] buf, unativeint size)
29+
30+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
31+ extern int private crypto_sign_keypair(byte[] pk, byte[] sk)
32+
33+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
34+ extern int private crypto_sign_detached(byte[] out, nativeint& siglen, byte[] m, uint64 mlen, byte[] sk)
35+
36+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
37+ extern int private crypto_sign_verify_detached(byte[] s, byte[] m, uint64 mlen, byte[] pk)
38+
39+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
40+ extern int private crypto_sign_ed25519_pk_to_curve25519(byte[] x25519_pk, byte[] ed25519_pk)
41+
42+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
43+ extern int private crypto_sign_ed25519_sk_to_curve25519(byte[] x25519_sk, byte[] ed25519_sk)
44+
45+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
46+ extern int private crypto_box_keypair(byte[] pk, byte[] sk)
47+
48+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
49+ extern int private crypto_scalarmult(byte[] q, byte[] n, byte[] p)
50+
51+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
52+ extern int private crypto_aead_xchacha20poly1305_ietf_encrypt(byte[] c, nativeint& clen, byte[] m, uint64 mlen, byte[] ad, uint64 adlen, nativeint nsec, byte[] npub, byte[] k)
53+
54+ [<DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)>]
55+ extern int private crypto_aead_xchacha20poly1305_ietf_decrypt(byte[] m, nativeint& mlen, nativeint nsec, byte[] c, uint64 clen, byte[] ad, uint64 adlen, byte[] npub, byte[] k)
56+
57+ do
58+ if sodium_init() < 0 then
59+ failwith "sodium_init() failed"
60+
61+ let private getRandomBytes n =
62+ let buf = Array.zeroCreate n
63+ randombytes_buf(buf, unativeint n)
64+ buf
65+
66+ let private generateSignKeyPair () =
67+ let pk = Array.zeroCreate 32
68+ let sk = Array.zeroCreate 64
69+ if crypto_sign_keypair(pk, sk) <> 0 then failwith "crypto_sign_keypair failed"
70+ pk, sk
71+
72+ let private signDetached (message: byte[]) (sk: byte[]) =
73+ let out = Array.zeroCreate 64
74+ let mutable siglen = 0n
75+ if crypto_sign_detached(out, &siglen, message, uint64 message.Length, sk) <> 0 then
76+ failwith "crypto_sign_detached failed"
77+ out
78+
79+ let private verifyDetached (signature: byte[]) (message: byte[]) (pk: byte[]) =
80+ crypto_sign_verify_detached(signature, message, uint64 message.Length, pk) = 0
81+
82+ let private ed25519PkToCurve25519 (ed: byte[]) =
83+ let x = Array.zeroCreate 32
84+ if crypto_sign_ed25519_pk_to_curve25519(x, ed) <> 0 then failwith "pk_to_curve25519 failed"
85+ x
86+
87+ let private ed25519SkToCurve25519 (ed: byte[]) =
88+ let x = Array.zeroCreate 32
89+ if crypto_sign_ed25519_sk_to_curve25519(x, ed) <> 0 then failwith "sk_to_curve25519 failed"
90+ x
91+
92+ let private generateBoxKeyPair () =
93+ let pk = Array.zeroCreate 32
94+ let sk = Array.zeroCreate 32
95+ if crypto_box_keypair(pk, sk) <> 0 then failwith "crypto_box_keypair failed"
96+ pk, sk
97+
98+ let private scalarMult (n: byte[]) (p: byte[]) =
99+ let q = Array.zeroCreate 32
100+ if crypto_scalarmult(q, n, p) <> 0 then failwith "crypto_scalarmult failed"
101+ q
102+
103+ let private aeadEncrypt (pt: byte[]) (nonce: byte[]) (key: byte[]) =
104+ let ct = Array.zeroCreate (pt.Length + 16)
105+ let mutable ctlen = 0n
106+ if crypto_aead_xchacha20poly1305_ietf_encrypt(ct, &ctlen, pt, uint64 pt.Length, null, 0UL, 0n, nonce, key) <> 0 then
107+ failwith "aead encrypt failed"
108+ ct.[.. int ctlen - 1]
109+
110+ let private aeadDecrypt (ct: byte[]) (nonce: byte[]) (key: byte[]) =
111+ let pt = Array.zeroCreate ct.Length
112+ let mutable ptlen = 0n
113+ if crypto_aead_xchacha20poly1305_ietf_decrypt(pt, &ptlen, 0n, ct, uint64 ct.Length, null, 0UL, nonce, key) <> 0 then
114+ failwith "aead decrypt failed"
115+ pt.[.. int ptlen - 1]
116+
117+#endif
118+
22119 let identityFromPrivKey (privKey: byte[]) : Identity =
23120 { PrivKey = privKey; PubKeyHex = toHex privKey.[32..63] }
24121
25122 let generateIdentity () : Identity =
26− PublicKeyAuth.GenerateKeyPair().PrivateKey |> identityFromPrivKey
123+#if MOBILE
124+ let _, sk = generateSignKeyPair ()
125+ sk |> identityFromPrivKey
126+#else
127+ Sodium.PublicKeyAuth.GenerateKeyPair().PrivateKey |> identityFromPrivKey
128+#endif
27129
28130 let signChallenge (privKey: byte[]) (challenge: string) : string =
29131 let message = Encoding.UTF8.GetBytes(challenge)
30− PublicKeyAuth.SignDetached(message, privKey) |> toHex
132+#if MOBILE
133+ signDetached message privKey |> toHex
134+#else
135+ Sodium.PublicKeyAuth.SignDetached(message, privKey) |> toHex
136+#endif
31137
32138 let private deriveKey ephPub recipientX25519Pub rawSecret =
33139 let salt = Array.append ephPub recipientX25519Pub
@@ -45,16 +151,28 @@ module Crypto =
45151
46152 let encrypt (senderPrivKey: byte[]) (recipientEd25519Pub: byte[]) (plaintext: string) : byte[] =
47153 let senderPubKey = senderPrivKey.[32..63]
48− let ephKp = PublicKeyBox.GenerateKeyPair()
154+#if MOBILE
155+ let ephPub, ephPriv = generateBoxKeyPair ()
156+ let recipientX25519Pub = ed25519PkToCurve25519 recipientEd25519Pub
157+ let rawSecret = scalarMult ephPriv recipientX25519Pub
158+ let key = deriveKey ephPub recipientX25519Pub rawSecret
159+ let nonce = getRandomBytes 24
160+ let ptBytes = Encoding.UTF8.GetBytes(plaintext)
161+ let ciphertext = aeadEncrypt ptBytes nonce key
162+ let signature = signDetached ciphertext senderPrivKey
163+ Array.concat [| ephPub; nonce; ciphertext; senderPubKey; signature |]
164+#else
165+ let ephKp = Sodium.PublicKeyBox.GenerateKeyPair()
49166 let recipientX25519Pub =
50− PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
51− let rawSecret = ScalarMult.Mult(ephKp.PrivateKey, recipientX25519Pub)
167+ Sodium.PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
168+ let rawSecret = Sodium.ScalarMult.Mult(ephKp.PrivateKey, recipientX25519Pub)
52169 let key = deriveKey ephKp.PublicKey recipientX25519Pub rawSecret
53− let nonce = SodiumCore.GetRandomBytes(24)
170+ let nonce = Sodium.SodiumCore.GetRandomBytes(24)
54171 let ptBytes = Encoding.UTF8.GetBytes(plaintext)
55− let ciphertext = SecretAeadXChaCha20Poly1305.Encrypt(ptBytes, nonce, key)
56− let signature = PublicKeyAuth.SignDetached(ciphertext, senderPrivKey)
172+ let ciphertext = Sodium.SecretAeadXChaCha20Poly1305.Encrypt(ptBytes, nonce, key)
173+ let signature = Sodium.PublicKeyAuth.SignDetached(ciphertext, senderPrivKey)
57174 Array.concat [| ephKp.PublicKey; nonce; ciphertext; senderPubKey; signature |]
175+#endif
58176
59177 let decrypt (recipientPrivKey: byte[]) (blob: byte[]) : (string * string) option =
60178 if blob.Length < minBlobLen then
@@ -67,17 +185,30 @@ module Crypto =
67185 let senderPub = blob.[blob.Length - trailerLen .. blob.Length - sigLen - 1]
68186 let signature = blob.[blob.Length - sigLen ..]
69187
70− if not (PublicKeyAuth.VerifyDetached(signature, ciphertext, senderPub)) then
188+#if MOBILE
189+ if not (verifyDetached signature ciphertext senderPub) then
190+ None
191+ else
192+ let recipientX25519Priv = ed25519SkToCurve25519 recipientPrivKey
193+ let rawSecret = scalarMult recipientX25519Priv ephPub
194+ let recipientEd25519Pub = recipientPrivKey.[32..63]
195+ let recipientX25519Pub = ed25519PkToCurve25519 recipientEd25519Pub
196+ let key = deriveKey ephPub recipientX25519Pub rawSecret
197+ let plaintext = aeadDecrypt ciphertext nonce key
198+ Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
199+#else
200+ if not (Sodium.PublicKeyAuth.VerifyDetached(signature, ciphertext, senderPub)) then
71201 None
72202 else
73203 let recipientX25519Priv =
74− PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(recipientPrivKey)
75− let rawSecret = ScalarMult.Mult(recipientX25519Priv, ephPub)
204+ Sodium.PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(recipientPrivKey)
205+ let rawSecret = Sodium.ScalarMult.Mult(recipientX25519Priv, ephPub)
76206 let recipientEd25519Pub = recipientPrivKey.[32..63]
77207 let recipientX25519Pub =
78− PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
208+ Sodium.PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
79209 let key = deriveKey ephPub recipientX25519Pub rawSecret
80− let plaintext = SecretAeadXChaCha20Poly1305.Decrypt(ciphertext, nonce, key)
210+ let plaintext = Sodium.SecretAeadXChaCha20Poly1305.Decrypt(ciphertext, nonce, key)
81211 Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
212+#endif
82213 with _ ->
83214 None