on master help sign in
Implement direct libsodium P/Invoke bindings for iOS and configure native HTTP handler
095199eiko
client/Crypto.fs+145−14
@@ -3,7 +3,6 @@ namespace Skrepka
3open System
3open System
4open System.Text
4open System.Text
5open System.Security.Cryptography
5open System.Security.Cryptography
6−open Sodium
7
6
8module Crypto =
7module Crypto =
9
8
@@ -19,15 +18,122 @@ module Crypto =
19 try Some(Convert.FromHexString(hex))
18 try Some(Convert.FromHexString(hex))
20 with :? FormatException -> None
19 with :? FormatException -> None
21
20
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+
22 let identityFromPrivKey (privKey: byte[]) : Identity =
119 let identityFromPrivKey (privKey: byte[]) : Identity =
23 { PrivKey = privKey; PubKeyHex = toHex privKey.[32..63] }
120 { PrivKey = privKey; PubKeyHex = toHex privKey.[32..63] }
24
121
25 let generateIdentity () : Identity =
122 let generateIdentity () : Identity =
123+#if MOBILE
124+ let _, sk = generateSignKeyPair ()
125+ sk |> identityFromPrivKey
126+#else
26− PublicKeyAuth.GenerateKeyPair().PrivateKey |> identityFromPrivKey
127+ Sodium.PublicKeyAuth.GenerateKeyPair().PrivateKey |> identityFromPrivKey
128+#endif
27
129
28 let signChallenge (privKey: byte[]) (challenge: string) : string =
130 let signChallenge (privKey: byte[]) (challenge: string) : string =
29 let message = Encoding.UTF8.GetBytes(challenge)
131 let message = Encoding.UTF8.GetBytes(challenge)
132+#if MOBILE
133+ signDetached message privKey |> toHex
134+#else
30− PublicKeyAuth.SignDetached(message, privKey) |> toHex
135+ Sodium.PublicKeyAuth.SignDetached(message, privKey) |> toHex
136+#endif
31
137
32 let private deriveKey ephPub recipientX25519Pub rawSecret =
138 let private deriveKey ephPub recipientX25519Pub rawSecret =
33 let salt = Array.append ephPub recipientX25519Pub
139 let salt = Array.append ephPub recipientX25519Pub
@@ -45,16 +151,28 @@ module Crypto =
45
151
46 let encrypt (senderPrivKey: byte[]) (recipientEd25519Pub: byte[]) (plaintext: string) : byte[] =
152 let encrypt (senderPrivKey: byte[]) (recipientEd25519Pub: byte[]) (plaintext: string) : byte[] =
47 let senderPubKey = senderPrivKey.[32..63]
153 let senderPubKey = senderPrivKey.[32..63]
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
48− let ephKp = PublicKeyBox.GenerateKeyPair()
165+ let ephKp = Sodium.PublicKeyBox.GenerateKeyPair()
49 let recipientX25519Pub =
166 let recipientX25519Pub =
50− PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
167+ Sodium.PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
51− let rawSecret = ScalarMult.Mult(ephKp.PrivateKey, recipientX25519Pub)
168+ let rawSecret = Sodium.ScalarMult.Mult(ephKp.PrivateKey, recipientX25519Pub)
52 let key = deriveKey ephKp.PublicKey recipientX25519Pub rawSecret
169 let key = deriveKey ephKp.PublicKey recipientX25519Pub rawSecret
53− let nonce = SodiumCore.GetRandomBytes(24)
170+ let nonce = Sodium.SodiumCore.GetRandomBytes(24)
54 let ptBytes = Encoding.UTF8.GetBytes(plaintext)
171 let ptBytes = Encoding.UTF8.GetBytes(plaintext)
55− let ciphertext = SecretAeadXChaCha20Poly1305.Encrypt(ptBytes, nonce, key)
172+ let ciphertext = Sodium.SecretAeadXChaCha20Poly1305.Encrypt(ptBytes, nonce, key)
56− let signature = PublicKeyAuth.SignDetached(ciphertext, senderPrivKey)
173+ let signature = Sodium.PublicKeyAuth.SignDetached(ciphertext, senderPrivKey)
57 Array.concat [| ephKp.PublicKey; nonce; ciphertext; senderPubKey; signature |]
174 Array.concat [| ephKp.PublicKey; nonce; ciphertext; senderPubKey; signature |]
175+#endif
58
176
59 let decrypt (recipientPrivKey: byte[]) (blob: byte[]) : (string * string) option =
177 let decrypt (recipientPrivKey: byte[]) (blob: byte[]) : (string * string) option =
60 if blob.Length < minBlobLen then
178 if blob.Length < minBlobLen then
@@ -67,17 +185,30 @@ module Crypto =
67 let senderPub = blob.[blob.Length - trailerLen .. blob.Length - sigLen - 1]
185 let senderPub = blob.[blob.Length - trailerLen .. blob.Length - sigLen - 1]
68 let signature = blob.[blob.Length - sigLen ..]
186 let signature = blob.[blob.Length - sigLen ..]
69
187
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
70− if not (PublicKeyAuth.VerifyDetached(signature, ciphertext, senderPub)) then
200+ if not (Sodium.PublicKeyAuth.VerifyDetached(signature, ciphertext, senderPub)) then
71 None
201 None
72 else
202 else
73 let recipientX25519Priv =
203 let recipientX25519Priv =
74− PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(recipientPrivKey)
204+ Sodium.PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(recipientPrivKey)
75− let rawSecret = ScalarMult.Mult(recipientX25519Priv, ephPub)
205+ let rawSecret = Sodium.ScalarMult.Mult(recipientX25519Priv, ephPub)
76 let recipientEd25519Pub = recipientPrivKey.[32..63]
206 let recipientEd25519Pub = recipientPrivKey.[32..63]
77 let recipientX25519Pub =
207 let recipientX25519Pub =
78− PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
208+ Sodium.PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
79 let key = deriveKey ephPub recipientX25519Pub rawSecret
209 let key = deriveKey ephPub recipientX25519Pub rawSecret
80− let plaintext = SecretAeadXChaCha20Poly1305.Decrypt(ciphertext, nonce, key)
210+ let plaintext = Sodium.SecretAeadXChaCha20Poly1305.Decrypt(ciphertext, nonce, key)
81 Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
211 Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
212+#endif
82 with _ ->
213 with _ ->
83 None
214 None
client/Skrepka.fsproj+2−1
@@ -24,6 +24,7 @@
24 <RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
24 <RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
25 <ValidateXcodeVersion>false</ValidateXcodeVersion>
25 <ValidateXcodeVersion>false</ValidateXcodeVersion>
26 <UseInterpreter>true</UseInterpreter>
26 <UseInterpreter>true</UseInterpreter>
27+ <UseNativeHttpHandler>true</UseNativeHttpHandler>
27 </PropertyGroup>
28 </PropertyGroup>
28
29
29 <ItemGroup>
30 <ItemGroup>
@@ -111,7 +112,7 @@
111 <PackageReference Include="FSharp.Core" Version="11.0.100-preview3.26159.112" />
112 <PackageReference Include="FSharp.Core" Version="11.0.100-preview3.26159.112" />
112 <PackageReference Include="Fabulous" Version="3.0.0-pre22" />
113 <PackageReference Include="Fabulous" Version="3.0.0-pre22" />
113 <PackageReference Include="Fabulous.Avalonia" Version="3.0.0-pre13" />
114 <PackageReference Include="Fabulous.Avalonia" Version="3.0.0-pre13" />
114− <PackageReference Include="Sodium.Core" Version="1.4.1-preview.1" />
115+ <PackageReference Include="Sodium.Core" Version="1.4.0" />
115 <PackageReference Include="Dapper.FSharp" Version="4.10.0" />
116 <PackageReference Include="Dapper.FSharp" Version="4.10.0" />
116 <PackageReference Include="Microsoft.Data.Sqlite" Version="11.0.0-preview.2.26159.112" />
117 <PackageReference Include="Microsoft.Data.Sqlite" Version="11.0.0-preview.2.26159.112" />
117 </ItemGroup>
118 </ItemGroup>