LoadSignedPreKeys();
41 |
42 | /**
43 | * Store a local SignedPreKeyRecord.
44 | *
45 | * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
46 | * @param record the SignedPreKeyRecord.
47 | */
48 | void StoreSignedPreKey(uint signedPreKeyId, SignedPreKeyRecord record);
49 |
50 | /**
51 | * @param signedPreKeyId A SignedPreKeyRecord ID.
52 | * @return true if the store has a record for the signedPreKeyId, otherwise false.
53 | */
54 | bool ContainsSignedPreKey(uint signedPreKeyId);
55 |
56 | /**
57 | * Delete a SignedPreKeyRecord from local storage.
58 | *
59 | * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
60 | */
61 | void RemoveSignedPreKey(uint signedPreKeyId);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/groups/state/SenderKeyStore.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | namespace libsignal.groups.state
19 | {
20 | public interface SenderKeyStore
21 | {
22 |
23 | /**
24 | * Commit to storage the {@link org.whispersystems.libsignal.groups.state.SenderKeyRecord} for a
25 | * given (groupId + senderId + deviceId) tuple.
26 | *
27 | * @param senderKeyName the (groupId + senderId + deviceId) tuple.
28 | * @param record the current SenderKeyRecord for the specified senderKeyName.
29 | */
30 | void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record);
31 |
32 | /**
33 | * Returns a copy of the {@link org.whispersystems.libsignal.groups.state.SenderKeyRecord}
34 | * corresponding to the (groupId + senderId + deviceId) tuple, or a new SenderKeyRecord if
35 | * one does not currently exist.
36 | *
37 | * It is important that implementations return a copy of the current durable information. The
38 | * returned SenderKeyRecord may be modified, but those changes should not have an effect on the
39 | * durable session state (what is returned by subsequent calls to this method) without the
40 | * store method being called here first.
41 | *
42 | * @param senderKeyName The (groupId + senderId + deviceId) tuple.
43 | * @return a copy of the SenderKeyRecord corresponding to the (groupId + senderId + deviceId tuple, or
44 | * a new SenderKeyRecord if one does not currently exist.
45 | */
46 |
47 | SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/ecc/DjbECPublicKey.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using System.Linq;
20 | using libsignal.util;
21 |
22 | namespace libsignal.ecc
23 | {
24 | public class DjbECPublicKey : ECPublicKey
25 | {
26 | private readonly byte[] publicKey;
27 |
28 | public DjbECPublicKey(byte[] publicKey)
29 | {
30 | this.publicKey = publicKey;
31 | }
32 |
33 |
34 | public byte[] serialize()
35 | {
36 | byte[] type = { (byte)Curve.DJB_TYPE };
37 | return ByteUtil.combine(type, publicKey);
38 | }
39 |
40 |
41 | public int getType()
42 | {
43 | return Curve.DJB_TYPE;
44 | }
45 |
46 |
47 | public override bool Equals(Object other)
48 | {
49 | if (other == null) return false;
50 | if (!(other is DjbECPublicKey)) return false;
51 |
52 | DjbECPublicKey that = (DjbECPublicKey)other;
53 | return Enumerable.SequenceEqual(this.publicKey, that.publicKey);
54 | }
55 |
56 |
57 | public override int GetHashCode()
58 | {
59 | return string.Join(",", publicKey).GetHashCode();
60 | }
61 |
62 |
63 | public int CompareTo(Object another)
64 | {
65 | byte[] theirs = ((DjbECPublicKey)another).publicKey;
66 | String theirString = string.Join(",", theirs.Select(y => y.ToString()));
67 | String ourString = string.Join(",", publicKey.Select(y => y.ToString()));
68 | return ourString.CompareTo(theirString);
69 | }
70 |
71 | public byte[] getPublicKey()
72 | {
73 | return publicKey;
74 | }
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/impl/InMemorySignedPreKeyStore.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 |
21 | namespace libsignal.state.impl
22 | {
23 | public class InMemorySignedPreKeyStore : SignedPreKeyStore
24 | {
25 |
26 | private readonly IDictionary store = new Dictionary();
27 |
28 |
29 | public SignedPreKeyRecord LoadSignedPreKey(uint signedPreKeyId)
30 | {
31 | try
32 | {
33 | if (!store.ContainsKey(signedPreKeyId))
34 | {
35 | throw new InvalidKeyIdException("No such signedprekeyrecord! " + signedPreKeyId);
36 | }
37 |
38 | byte[] record;
39 | store.TryGetValue(signedPreKeyId, out record); // get()
40 |
41 | return new SignedPreKeyRecord(record);
42 | }
43 | catch (Exception e)
44 | {
45 | throw new Exception(e.Message);
46 | }
47 | }
48 |
49 |
50 | public List LoadSignedPreKeys()
51 | {
52 | try
53 | {
54 | List results = new List();
55 |
56 | foreach (byte[] serialized in store.Values) //values()
57 | {
58 | results.Add(new SignedPreKeyRecord(serialized));
59 | }
60 |
61 | return results;
62 | }
63 | catch (Exception e)
64 | {
65 | throw new Exception(e.Message);
66 | }
67 | }
68 |
69 |
70 | public void StoreSignedPreKey(uint signedPreKeyId, SignedPreKeyRecord record)
71 | {
72 | store[signedPreKeyId] = record.serialize();
73 | }
74 |
75 |
76 | public bool ContainsSignedPreKey(uint signedPreKeyId)
77 | {
78 | return store.ContainsKey(signedPreKeyId);
79 | }
80 |
81 |
82 | public void RemoveSignedPreKey(uint signedPreKeyId)
83 | {
84 | store.Remove(signedPreKeyId);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/IdentityKeyPair.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using Google.ProtocolBuffers;
19 | using libsignal.ecc;
20 | using static libsignal.state.StorageProtos;
21 |
22 | namespace libsignal
23 | {
24 | /**
25 | * Holder for public and private identity key pair.
26 | *
27 | * @author
28 | */
29 | public class IdentityKeyPair
30 | {
31 |
32 | private readonly IdentityKey publicKey;
33 | private readonly ECPrivateKey privateKey;
34 |
35 | public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey)
36 | {
37 | this.publicKey = publicKey;
38 | this.privateKey = privateKey;
39 | }
40 |
41 | public IdentityKeyPair(byte[] serialized)
42 | {
43 | try
44 | {
45 | IdentityKeyPairStructure structure = IdentityKeyPairStructure.ParseFrom(serialized);
46 | this.publicKey = new IdentityKey(structure.PublicKey.ToByteArray(), 0);
47 | this.privateKey = Curve.decodePrivatePoint(structure.PrivateKey.ToByteArray());
48 | }
49 | catch (InvalidProtocolBufferException e)
50 | {
51 | throw new InvalidKeyException(e);
52 | }
53 | }
54 |
55 | public IdentityKey getPublicKey()
56 | {
57 | return publicKey;
58 | }
59 |
60 | public ECPrivateKey getPrivateKey()
61 | {
62 | return privateKey;
63 | }
64 |
65 | public byte[] serialize()
66 | {
67 | return IdentityKeyPairStructure.CreateBuilder()
68 | .SetPublicKey(ByteString.CopyFrom(publicKey.serialize()))
69 | .SetPrivateKey(ByteString.CopyFrom(privateKey.serialize()))
70 | .Build().ToByteArray();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/ratchet/ChainKey.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.kdf;
19 | using libsignal.util;
20 | using System;
21 | using System.Text;
22 |
23 | namespace libsignal.ratchet
24 | {
25 | public class ChainKey
26 | {
27 |
28 | private static readonly byte[] MESSAGE_KEY_SEED = { 0x01 };
29 | private static readonly byte[] CHAIN_KEY_SEED = { 0x02 };
30 |
31 | private readonly HKDF kdf;
32 | private readonly byte[] key;
33 | private readonly uint index;
34 |
35 | public ChainKey(HKDF kdf, byte[] key, uint index)
36 | {
37 | this.kdf = kdf;
38 | this.key = key;
39 | this.index = index;
40 | }
41 |
42 | public byte[] getKey()
43 | {
44 | return key;
45 | }
46 |
47 | public uint getIndex()
48 | {
49 | return index;
50 | }
51 |
52 | public ChainKey getNextChainKey()
53 | {
54 | byte[] nextKey = getBaseMaterial(CHAIN_KEY_SEED);
55 | return new ChainKey(kdf, nextKey, index + 1);
56 | }
57 |
58 | public MessageKeys getMessageKeys()
59 | {
60 | byte[] inputKeyMaterial = getBaseMaterial(MESSAGE_KEY_SEED);
61 | byte[] keyMaterialBytes = kdf.deriveSecrets(inputKeyMaterial, Encoding.UTF8.GetBytes("WhisperMessageKeys"), DerivedMessageSecrets.SIZE);
62 | DerivedMessageSecrets keyMaterial = new DerivedMessageSecrets(keyMaterialBytes);
63 |
64 | return new MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), index);
65 | }
66 |
67 | private byte[] getBaseMaterial(byte[] seed)
68 | {
69 | try
70 | {
71 | return Sign.sha256sum(key, seed);
72 | }
73 | catch (InvalidKeyException e)
74 | {
75 | throw new Exception(e.Message);
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/groups/ratchet/SenderChainKey.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.util;
19 |
20 | namespace libsignal.groups.ratchet
21 | {
22 | /**
23 | * Each SenderKey is a "chain" of keys, each derived from the previous.
24 | *
25 | * At any given point in time, the state of a SenderKey can be represented
26 | * as the current chain key value, along with its iteration count. From there,
27 | * subsequent iterations can be derived, as well as individual message keys from
28 | * each chain key.
29 | *
30 | * @author
31 | */
32 | public class SenderChainKey
33 | {
34 |
35 | private static readonly byte[] MESSAGE_KEY_SEED = { 0x01 };
36 | private static readonly byte[] CHAIN_KEY_SEED = { 0x02 };
37 |
38 | private readonly uint iteration;
39 | private readonly byte[] chainKey;
40 |
41 | public SenderChainKey(uint iteration, byte[] chainKey)
42 | {
43 | this.iteration = iteration;
44 | this.chainKey = chainKey;
45 | }
46 |
47 | public uint getIteration()
48 | {
49 | return iteration;
50 | }
51 |
52 | public SenderMessageKey getSenderMessageKey()
53 | {
54 | return new SenderMessageKey(iteration, getDerivative(MESSAGE_KEY_SEED, chainKey));
55 | }
56 |
57 | public SenderChainKey getNext()
58 | {
59 | return new SenderChainKey(iteration + 1, getDerivative(CHAIN_KEY_SEED, chainKey));
60 | }
61 |
62 | public byte[] getSeed()
63 | {
64 | return chainKey;
65 | }
66 |
67 | private byte[] getDerivative(byte[] seed, byte[] key)
68 | {
69 | // try
70 | //{
71 | return Sign.sha256sum(key, seed);
72 | /*}
73 | catch (NoSuchAlgorithmException | InvalidKeyException e) {
74 | throw new AssertionError(e);
75 | }*/
76 | }
77 |
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/PreKeyRecord.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using Google.ProtocolBuffers;
19 | using libsignal.ecc;
20 | using System;
21 | using static libsignal.state.StorageProtos;
22 |
23 | namespace libsignal.state
24 | {
25 | public class PreKeyRecord
26 | {
27 |
28 | private PreKeyRecordStructure structure;
29 |
30 | public PreKeyRecord(uint id, ECKeyPair keyPair)
31 | {
32 | this.structure = PreKeyRecordStructure.CreateBuilder()
33 | .SetId(id)
34 | .SetPublicKey(ByteString.CopyFrom(keyPair.getPublicKey()
35 | .serialize()))
36 | .SetPrivateKey(ByteString.CopyFrom(keyPair.getPrivateKey()
37 | .serialize()))
38 | .Build();
39 | }
40 |
41 | public PreKeyRecord(byte[] serialized)
42 | {
43 | this.structure = PreKeyRecordStructure.ParseFrom(serialized);
44 | }
45 |
46 |
47 |
48 | public uint getId()
49 | {
50 | return this.structure.Id;
51 | }
52 |
53 | public ECKeyPair getKeyPair()
54 | {
55 | try
56 | {
57 | ECPublicKey publicKey = Curve.decodePoint(this.structure.PublicKey.ToByteArray(), 0);
58 | ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.PrivateKey.ToByteArray());
59 |
60 | return new ECKeyPair(publicKey, privateKey);
61 | }
62 | catch (InvalidKeyException e)
63 | {
64 | throw new Exception(e.Message);
65 | }
66 | }
67 |
68 | public byte[] serialize()
69 | {
70 | return this.structure.ToByteArray();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/IdentityKeyStore.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 |
20 | namespace libsignal.state
21 | {
22 | /**
23 | * Provides an interface to identity information.
24 | *
25 | * @author
26 | */
27 | public interface IdentityKeyStore
28 | {
29 |
30 | /**
31 | * Get the local client's identity key pair.
32 | *
33 | * @return The local client's persistent identity key pair.
34 | */
35 | IdentityKeyPair GetIdentityKeyPair();
36 |
37 | /**
38 | * Return the local client's registration ID.
39 | *
40 | * Clients should maintain a registration ID, a random number
41 | * between 1 and 16380 that's generated once at install time.
42 | *
43 | * @return the local client's registration ID.
44 | */
45 | uint GetLocalRegistrationId();
46 |
47 | /**
48 | * Save a remote client's identity key
49 | *
50 | * Store a remote client's identity key as trusted.
51 | *
52 | * @param name The name of the remote client.
53 | * @param identityKey The remote client's identity key.
54 | */
55 | bool SaveIdentity(String name, IdentityKey identityKey);
56 |
57 |
58 | /**
59 | * Verify a remote client's identity key.
60 | *
61 | * Determine whether a remote client's identity is trusted. Convention is
62 | * that the TextSecure protocol is 'trust on first use.' This means that
63 | * an identity key is considered 'trusted' if there is no entry for the recipient
64 | * in the local store, or if it matches the saved key for a recipient in the local
65 | * store. Only if it mismatches an entry in the local store is it considered
66 | * 'untrusted.'
67 | *
68 | * @param name The name of the remote client.
69 | * @param identityKey The identity key to verify.
70 | * @return true if trusted, false if untrusted.
71 | */
72 | bool IsTrustedIdentity(String name, IdentityKey identityKey);
73 |
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/impl/InMemorySessionStore.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 |
21 | namespace libsignal.state.impl
22 | {
23 | public class InMemorySessionStore : SessionStore
24 | {
25 |
26 | static object Lock = new object();
27 |
28 | private IDictionary sessions = new Dictionary();
29 |
30 | public InMemorySessionStore() { }
31 |
32 | //[MethodImpl(MethodImplOptions.Synchronized)]
33 | public SessionRecord LoadSession(SignalProtocolAddress remoteAddress)
34 | {
35 | try
36 | {
37 | if (ContainsSession(remoteAddress))
38 | {
39 | byte[] session;
40 | sessions.TryGetValue(remoteAddress, out session); // get()
41 |
42 | return new SessionRecord(session);
43 | }
44 | else
45 | {
46 | return new SessionRecord();
47 | }
48 | }
49 | catch (Exception e)
50 | {
51 | throw new Exception(e.Message);
52 | }
53 | }
54 |
55 |
56 | public List GetSubDeviceSessions(String name)
57 | {
58 | List deviceIds = new List();
59 |
60 | foreach (SignalProtocolAddress key in sessions.Keys) //keySet()
61 | {
62 | if (key.getName().Equals(name) &&
63 | key.getDeviceId() != 1)
64 | {
65 | deviceIds.Add(key.getDeviceId());
66 | }
67 | }
68 |
69 | return deviceIds;
70 | }
71 |
72 |
73 | public void StoreSession(SignalProtocolAddress address, SessionRecord record)
74 | {
75 | sessions[address] = record.serialize();
76 | }
77 |
78 |
79 | public bool ContainsSession(SignalProtocolAddress address)
80 | {
81 | return sessions.ContainsKey(address);
82 | }
83 |
84 |
85 | public void DeleteSession(SignalProtocolAddress address)
86 | {
87 | sessions.Remove(address);
88 | }
89 |
90 |
91 | public void DeleteAllSessions(String name)
92 | {
93 | foreach (SignalProtocolAddress key in sessions.Keys) // keySet()
94 | {
95 | if (key.getName().Equals(name))
96 | {
97 | sessions.Remove(key);
98 | }
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/SignedPreKeyRecord.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using Google.ProtocolBuffers;
19 | using libsignal.ecc;
20 | using System;
21 | using static libsignal.state.StorageProtos;
22 |
23 | namespace libsignal.state
24 | {
25 | public class SignedPreKeyRecord
26 | {
27 |
28 | private SignedPreKeyRecordStructure structure;
29 |
30 | public SignedPreKeyRecord(uint id, ulong timestamp, ECKeyPair keyPair, byte[] signature)
31 | {
32 | this.structure = SignedPreKeyRecordStructure.CreateBuilder()
33 | .SetId(id)
34 | .SetPublicKey(ByteString.CopyFrom(keyPair.getPublicKey()
35 | .serialize()))
36 | .SetPrivateKey(ByteString.CopyFrom(keyPair.getPrivateKey()
37 | .serialize()))
38 | .SetSignature(ByteString.CopyFrom(signature))
39 | .SetTimestamp(timestamp)
40 | .Build();
41 | }
42 |
43 | public SignedPreKeyRecord(byte[] serialized)
44 | {
45 | this.structure = SignedPreKeyRecordStructure.ParseFrom(serialized);
46 | }
47 |
48 | public uint getId()
49 | {
50 | return this.structure.Id;
51 | }
52 |
53 | public ulong getTimestamp()
54 | {
55 | return this.structure.Timestamp;
56 | }
57 |
58 | public ECKeyPair getKeyPair()
59 | {
60 | try
61 | {
62 | ECPublicKey publicKey = Curve.decodePoint(this.structure.PublicKey.ToByteArray(), 0);
63 | ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.PrivateKey.ToByteArray());
64 |
65 | return new ECKeyPair(publicKey, privateKey);
66 | }
67 | catch (InvalidKeyException e)
68 | {
69 | throw new Exception(e.Message);
70 | }
71 | }
72 |
73 | public byte[] getSignature()
74 | {
75 | return this.structure.Signature.ToByteArray();
76 | }
77 |
78 | public byte[] serialize()
79 | {
80 | return this.structure.ToByteArray();
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/SessionStore.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 |
21 | namespace libsignal.state
22 | {
23 | /**
24 | * The interface to the durable store of session state information
25 | * for remote clients.
26 | *
27 | * @author
28 | */
29 | public interface SessionStore
30 | {
31 |
32 | /**
33 | * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
34 | * or a new SessionRecord if one does not currently exist.
35 | *
36 | * It is important that implementations return a copy of the current durable information. The
37 | * returned SessionRecord may be modified, but those changes should not have an effect on the
38 | * durable session state (what is returned by subsequent calls to this method) without the
39 | * store method being called here first.
40 | *
41 | * @param address The name and device ID of the remote client.
42 | * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
43 | * a new SessionRecord if one does not currently exist.
44 | */
45 | SessionRecord LoadSession(SignalProtocolAddress address);
46 |
47 | /**
48 | * Returns all known devices with active sessions for a recipient
49 | *
50 | * @param name the name of the client.
51 | * @return all known sub-devices with active sessions.
52 | */
53 | List GetSubDeviceSessions(String name);
54 |
55 | /**
56 | * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
57 | * @param address the address of the remote client.
58 | * @param record the current SessionRecord for the remote client.
59 | */
60 | void StoreSession(SignalProtocolAddress address, SessionRecord record);
61 |
62 | /**
63 | * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
64 | * @param address the address of the remote client.
65 | * @return true if a {@link SessionRecord} exists, false otherwise.
66 | */
67 | bool ContainsSession(SignalProtocolAddress address);
68 |
69 | /**
70 | * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
71 | *
72 | * @param address the address of the remote client.
73 | */
74 | void DeleteSession(SignalProtocolAddress address);
75 |
76 | /**
77 | * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
78 | *
79 | * @param name the name of the remote client.
80 | */
81 | void DeleteAllSessions(String name);
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/Protobuf/LocalStorageProtocol.proto:
--------------------------------------------------------------------------------
1 | package textsecure;
2 |
3 |
4 | message SessionStructure {
5 | message Chain {
6 | optional bytes senderRatchetKey = 1;
7 | optional bytes senderRatchetKeyPrivate = 2;
8 |
9 | message ChainKey {
10 | optional uint32 index = 1;
11 | optional bytes key = 2;
12 | }
13 |
14 | optional ChainKey chainKey = 3;
15 |
16 | message MessageKey {
17 | optional uint32 index = 1;
18 | optional bytes cipherKey = 2;
19 | optional bytes macKey = 3;
20 | optional bytes iv = 4;
21 | }
22 |
23 | repeated MessageKey messageKeys = 4;
24 | }
25 |
26 | message PendingKeyExchange {
27 | optional uint32 sequence = 1;
28 | optional bytes localBaseKey = 2;
29 | optional bytes localBaseKeyPrivate = 3;
30 | optional bytes localRatchetKey = 4;
31 | optional bytes localRatchetKeyPrivate = 5;
32 | optional bytes localIdentityKey = 7;
33 | optional bytes localIdentityKeyPrivate = 8;
34 | }
35 |
36 | message PendingPreKey {
37 | optional uint32 preKeyId = 1;
38 | optional int32 signedPreKeyId = 3;
39 | optional bytes baseKey = 2;
40 | }
41 |
42 | optional uint32 sessionVersion = 1;
43 | optional bytes localIdentityPublic = 2;
44 | optional bytes remoteIdentityPublic = 3;
45 |
46 | optional bytes rootKey = 4;
47 | optional uint32 previousCounter = 5;
48 |
49 | optional Chain senderChain = 6;
50 | repeated Chain receiverChains = 7;
51 |
52 | optional PendingKeyExchange pendingKeyExchange = 8;
53 | optional PendingPreKey pendingPreKey = 9;
54 |
55 | optional uint32 remoteRegistrationId = 10;
56 | optional uint32 localRegistrationId = 11;
57 |
58 | optional bool needsRefresh = 12;
59 | optional bytes aliceBaseKey = 13;
60 | }
61 |
62 | message RecordStructure {
63 | optional SessionStructure currentSession = 1;
64 | repeated SessionStructure previousSessions = 2;
65 | }
66 |
67 | message PreKeyRecordStructure {
68 | optional uint32 id = 1;
69 | optional bytes publicKey = 2;
70 | optional bytes privateKey = 3;
71 | }
72 |
73 | message SignedPreKeyRecordStructure {
74 | optional uint32 id = 1;
75 | optional bytes publicKey = 2;
76 | optional bytes privateKey = 3;
77 | optional bytes signature = 4;
78 | optional fixed64 timestamp = 5;
79 | }
80 |
81 | message IdentityKeyPairStructure {
82 | optional bytes publicKey = 1;
83 | optional bytes privateKey = 2;
84 | }
85 |
86 | message SenderKeyStateStructure {
87 | message SenderChainKey {
88 | optional uint32 iteration = 1;
89 | optional bytes seed = 2;
90 | }
91 |
92 | message SenderMessageKey {
93 | optional uint32 iteration = 1;
94 | optional bytes seed = 2;
95 | }
96 |
97 | message SenderSigningKey {
98 | optional bytes public = 1;
99 | optional bytes private = 2;
100 | }
101 |
102 | optional uint32 senderKeyId = 1;
103 | optional SenderChainKey senderChainKey = 2;
104 | optional SenderSigningKey senderSigningKey = 3;
105 | repeated SenderMessageKey senderMessageKeys = 4;
106 | }
107 |
108 | message SenderKeyRecordStructure {
109 | repeated SenderKeyStateStructure senderKeyStates = 1;
110 | }
--------------------------------------------------------------------------------
/signal-protocol-pcl/kdf/HKDF.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.util;
19 | using System;
20 | using System.IO;
21 |
22 | namespace libsignal.kdf
23 | {
24 | public abstract class HKDF
25 | {
26 |
27 | private static readonly int HASH_OUTPUT_SIZE = 32;
28 |
29 | public static HKDF createFor(uint messageVersion)
30 | {
31 | switch (messageVersion)
32 | {
33 | case 2: return new HKDFv2();
34 | case 3: return new HKDFv3();
35 | default: throw new Exception("Unknown version: " + messageVersion);
36 | }
37 | }
38 |
39 | public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] info, int outputLength)
40 | {
41 | byte[] salt = new byte[HASH_OUTPUT_SIZE];
42 | return deriveSecrets(inputKeyMaterial, salt, info, outputLength);
43 | }
44 |
45 | public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] salt, byte[] info, int outputLength)
46 | {
47 | byte[] prk = extract(salt, inputKeyMaterial);
48 | return expand(prk, info, outputLength);
49 | }
50 |
51 | private byte[] extract(byte[] salt, byte[] inputKeyMaterial)
52 | {
53 | try
54 | {
55 | return Sign.sha256sum(salt, inputKeyMaterial);
56 | }
57 | catch (Exception e)
58 | {
59 | throw new Exception(e.Message);
60 | }
61 | }
62 |
63 | private byte[] expand(byte[] prk, byte[] info, int outputSize)
64 | {
65 | try
66 | {
67 | int iterations = (int)Math.Ceiling((double)outputSize / (double)HASH_OUTPUT_SIZE);
68 | byte[] mixin = new byte[0];
69 | MemoryStream results = new MemoryStream();
70 | int remainingBytes = outputSize;
71 |
72 | for (int i = getIterationStartOffset(); i < iterations + getIterationStartOffset(); i++)
73 | {
74 | MemoryStream msg = new MemoryStream();
75 | msg.Write(mixin, 0, mixin.Length);
76 | if (info != null)
77 | {
78 | msg.Write(info, 0, info.Length);
79 | }
80 | byte[] ib = BitConverter.GetBytes(i);
81 | msg.Write(ib, 0, 1);
82 |
83 | byte[] stepResult = Sign.sha256sum(prk, msg.ToArray());
84 | int stepSize = Math.Min(remainingBytes, stepResult.Length);
85 |
86 | results.Write(stepResult, 0, stepSize);
87 |
88 | mixin = stepResult;
89 | remainingBytes -= stepSize;
90 | }
91 |
92 | return results.ToArray();
93 | }
94 | catch (Exception e)
95 | {
96 | throw new Exception(e.Message);
97 | }
98 | }
99 |
100 | protected abstract int getIterationStartOffset();
101 |
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/groups/state/SenderKeyRecord.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc;
19 | using libsignal.groups.state;
20 | using libsignal.state;
21 | using System.Collections.Generic;
22 | using static libsignal.state.StorageProtos;
23 |
24 | namespace libsignal.groups
25 | {
26 | /**
27 | * A durable representation of a set of SenderKeyStates for a specific
28 | * SenderKeyName.
29 | *
30 | * @author
31 | */
32 | public class SenderKeyRecord
33 | {
34 | private static readonly int MAX_STATES = 5;
35 |
36 | private LinkedList senderKeyStates = new LinkedList();
37 |
38 | public SenderKeyRecord() { }
39 |
40 | public SenderKeyRecord(byte[] serialized)
41 | {
42 | SenderKeyRecordStructure senderKeyRecordStructure = SenderKeyRecordStructure.ParseFrom(serialized);
43 |
44 | foreach (StorageProtos.SenderKeyStateStructure structure in senderKeyRecordStructure.SenderKeyStatesList)
45 | {
46 | this.senderKeyStates.AddFirst(new SenderKeyState(structure));
47 | }
48 | }
49 |
50 | public bool isEmpty()
51 | {
52 | return senderKeyStates.Count == 0;
53 | }
54 |
55 | public SenderKeyState getSenderKeyState()
56 | {
57 | if (!isEmpty())
58 | {
59 | return senderKeyStates.First.Value;
60 | }
61 | else
62 | {
63 | throw new InvalidKeyIdException("No key state in record!");
64 | }
65 | }
66 |
67 | public SenderKeyState getSenderKeyState(uint keyId)
68 | {
69 | foreach (SenderKeyState state in senderKeyStates)
70 | {
71 | if (state.getKeyId() == keyId)
72 | {
73 | return state;
74 | }
75 | }
76 |
77 | throw new InvalidKeyIdException("No keys for: " + keyId);
78 | }
79 |
80 | public void addSenderKeyState(uint id, uint iteration, byte[] chainKey, ECPublicKey signatureKey)
81 | {
82 | senderKeyStates.AddFirst(new SenderKeyState(id, iteration, chainKey, signatureKey));
83 |
84 | if (senderKeyStates.Count > MAX_STATES)
85 | {
86 | senderKeyStates.RemoveLast();
87 | }
88 | }
89 |
90 | public void setSenderKeyState(uint id, uint iteration, byte[] chainKey, ECKeyPair signatureKey)
91 | {
92 | senderKeyStates.Clear();
93 | senderKeyStates.AddFirst(new SenderKeyState(id, iteration, chainKey, signatureKey));
94 | }
95 |
96 | public byte[] serialize()
97 | {
98 | SenderKeyRecordStructure.Builder recordStructure = SenderKeyRecordStructure.CreateBuilder();
99 |
100 | foreach (SenderKeyState senderKeyState in senderKeyStates)
101 | {
102 | recordStructure.AddSenderKeyStates(senderKeyState.getStructure());
103 | }
104 |
105 | return recordStructure.Build().ToByteArray();
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/PreKeyBundle.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc;
19 |
20 | namespace libsignal.state
21 | {
22 | /**
23 | * A class that contains a remote PreKey and collection
24 | * of associated items.
25 | *
26 | * @author Moxie Marlinspike
27 | */
28 | public class PreKeyBundle
29 | {
30 |
31 | private uint registrationId;
32 |
33 | private uint deviceId;
34 |
35 | private uint preKeyId;
36 | private ECPublicKey preKeyPublic;
37 |
38 | private uint signedPreKeyId;
39 | private ECPublicKey signedPreKeyPublic;
40 | private byte[] signedPreKeySignature;
41 |
42 | private IdentityKey identityKey;
43 |
44 | public PreKeyBundle(uint registrationId, uint deviceId, uint preKeyId, ECPublicKey preKeyPublic,
45 | uint signedPreKeyId, ECPublicKey signedPreKeyPublic, byte[] signedPreKeySignature,
46 | IdentityKey identityKey)
47 | {
48 | this.registrationId = registrationId;
49 | this.deviceId = deviceId;
50 | this.preKeyId = preKeyId;
51 | this.preKeyPublic = preKeyPublic;
52 | this.signedPreKeyId = signedPreKeyId;
53 | this.signedPreKeyPublic = signedPreKeyPublic;
54 | this.signedPreKeySignature = signedPreKeySignature;
55 | this.identityKey = identityKey;
56 | }
57 |
58 | /**
59 | * @return the device ID this PreKey belongs to.
60 | */
61 | public uint getDeviceId()
62 | {
63 | return deviceId;
64 | }
65 |
66 | /**
67 | * @return the unique key ID for this PreKey.
68 | */
69 | public uint getPreKeyId()
70 | {
71 | return preKeyId;
72 | }
73 |
74 | /**
75 | * @return the public key for this PreKey.
76 | */
77 | public ECPublicKey getPreKey()
78 | {
79 | return preKeyPublic;
80 | }
81 |
82 | /**
83 | * @return the unique key ID for this signed prekey.
84 | */
85 | public uint getSignedPreKeyId()
86 | {
87 | return signedPreKeyId;
88 | }
89 |
90 | /**
91 | * @return the signed prekey for this PreKeyBundle.
92 | */
93 | public ECPublicKey getSignedPreKey()
94 | {
95 | return signedPreKeyPublic;
96 | }
97 |
98 | /**
99 | * @return the signature over the signed prekey.
100 | */
101 | public byte[] getSignedPreKeySignature()
102 | {
103 | return signedPreKeySignature;
104 | }
105 |
106 | /**
107 | * @return the {@link org.whispersystems.libsignal.IdentityKey} of this PreKeys owner.
108 | */
109 | public IdentityKey getIdentityKey()
110 | {
111 | return identityKey;
112 | }
113 |
114 | /**
115 | * @return the registration ID associated with this PreKey.
116 | */
117 | public uint getRegistrationId()
118 | {
119 | return registrationId;
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/ecc/Curve.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | namespace libsignal.ecc
19 | {
20 | public class Curve
21 | {
22 | public const int DJB_TYPE = 0x05;
23 |
24 | public static bool isNative()
25 | {
26 | return Curve25519.getInstance(Curve25519ProviderType.BEST).isNative();
27 | }
28 |
29 | public static ECKeyPair generateKeyPair()
30 | {
31 | Curve25519KeyPair keyPair = Curve25519.getInstance(Curve25519ProviderType.BEST).generateKeyPair();
32 |
33 | return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()),
34 | new DjbECPrivateKey(keyPair.getPrivateKey()));
35 | }
36 |
37 | public static ECPublicKey decodePoint(byte[] bytes, int offset)
38 | {
39 | int type = bytes[offset] & 0xFF;
40 |
41 | switch (type)
42 | {
43 | case Curve.DJB_TYPE:
44 | byte[] keyBytes = new byte[32];
45 | System.Buffer.BlockCopy(bytes, offset + 1, keyBytes, 0, keyBytes.Length);
46 | return new DjbECPublicKey(keyBytes);
47 | default:
48 | throw new InvalidKeyException("Bad key type: " + type);
49 | }
50 | }
51 |
52 | public static ECPrivateKey decodePrivatePoint(byte[] bytes)
53 | {
54 | return new DjbECPrivateKey(bytes);
55 | }
56 |
57 | public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey)
58 | {
59 | if (publicKey.getType() != privateKey.getType())
60 | {
61 | throw new InvalidKeyException("Public and private keys must be of the same type!");
62 | }
63 |
64 | if (publicKey.getType() == DJB_TYPE)
65 | {
66 | return Curve25519.getInstance(Curve25519ProviderType.BEST)
67 | .calculateAgreement(((DjbECPublicKey)publicKey).getPublicKey(),
68 | ((DjbECPrivateKey)privateKey).getPrivateKey());
69 | }
70 | else
71 | {
72 | throw new InvalidKeyException("Unknown type: " + publicKey.getType());
73 | }
74 | }
75 |
76 | public static bool verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature)
77 | {
78 | if (signingKey.getType() == DJB_TYPE)
79 | {
80 | return Curve25519.getInstance(Curve25519ProviderType.BEST)
81 | .verifySignature(((DjbECPublicKey)signingKey).getPublicKey(), message, signature);
82 | }
83 | else
84 | {
85 | throw new InvalidKeyException("Unknown type: " + signingKey.getType());
86 | }
87 | }
88 |
89 | public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message)
90 | {
91 | if (signingKey.getType() == DJB_TYPE)
92 | {
93 | return Curve25519.getInstance(Curve25519ProviderType.BEST)
94 | .calculateSignature(((DjbECPrivateKey)signingKey).getPrivateKey(), message);
95 | }
96 | else
97 | {
98 | throw new InvalidKeyException("Unknown type: " + signingKey.getType());
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/signal-protocol-tests/ratchet/RootKeyTest.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc;
19 | using libsignal.kdf;
20 | using libsignal.ratchet;
21 | using libsignal.util;
22 | using Microsoft.VisualStudio.TestTools.UnitTesting;
23 |
24 | namespace libsignal_test
25 | {
26 | [TestClass]
27 | public class RootKeyTest
28 | {
29 | [TestMethod, TestCategory("libsignal.ratchet")]
30 | public void testRootKeyDerivationV2()
31 | {
32 | byte[] rootKeySeed =
33 | {
34 | 0x7b, 0xa6, 0xde, 0xbc, 0x2b,
35 | 0xc1, 0xbb, 0xf9, 0x1a, 0xbb,
36 | 0xc1, 0x36, 0x74, 0x04, 0x17,
37 | 0x6c, 0xa6, 0x23, 0x09, 0x5b,
38 | 0x7e, 0xc6, 0x6b, 0x45, 0xf6,
39 | 0x02, 0xd9, 0x35, 0x38, 0x94,
40 | 0x2d, 0xcc
41 | };
42 |
43 | byte[] alicePublic =
44 | {
45 | 0x05, 0xee, 0x4f, 0xa6, 0xcd,
46 | 0xc0, 0x30, 0xdf, 0x49, 0xec,
47 | 0xd0, 0xba, 0x6c, 0xfc, 0xff,
48 | 0xb2, 0x33, 0xd3, 0x65, 0xa2,
49 | 0x7f, 0xad, 0xbe, 0xff, 0x77,
50 | 0xe9, 0x63, 0xfc, 0xb1, 0x62,
51 | 0x22, 0xe1, 0x3a
52 | };
53 |
54 | byte[] alicePrivate =
55 | {
56 | 0x21, 0x68, 0x22, 0xec, 0x67,
57 | 0xeb, 0x38, 0x04, 0x9e, 0xba,
58 | 0xe7, 0xb9, 0x39, 0xba, 0xea,
59 | 0xeb, 0xb1, 0x51, 0xbb, 0xb3,
60 | 0x2d, 0xb8, 0x0f, 0xd3, 0x89,
61 | 0x24, 0x5a, 0xc3, 0x7a, 0x94,
62 | 0x8e, 0x50
63 | };
64 |
65 | byte[] bobPublic =
66 | {
67 | 0x05, 0xab, 0xb8, 0xeb, 0x29,
68 | 0xcc, 0x80, 0xb4, 0x71, 0x09,
69 | 0xa2, 0x26, 0x5a, 0xbe, 0x97,
70 | 0x98, 0x48, 0x54, 0x06, 0xe3,
71 | 0x2d, 0xa2, 0x68, 0x93, 0x4a,
72 | 0x95, 0x55, 0xe8, 0x47, 0x57,
73 | 0x70, 0x8a, 0x30
74 | };
75 |
76 | byte[] nextRoot =
77 | {
78 | 0xb1, 0x14, 0xf5, 0xde, 0x28,
79 | 0x01, 0x19, 0x85, 0xe6, 0xeb,
80 | 0xa2, 0x5d, 0x50, 0xe7, 0xec,
81 | 0x41, 0xa9, 0xb0, 0x2f, 0x56,
82 | 0x93, 0xc5, 0xc7, 0x88, 0xa6,
83 | 0x3a, 0x06, 0xd2, 0x12, 0xa2,
84 | 0xf7, 0x31
85 | };
86 |
87 | byte[] nextChain =
88 | {
89 | 0x9d, 0x7d, 0x24, 0x69, 0xbc,
90 | 0x9a, 0xe5, 0x3e, 0xe9, 0x80,
91 | 0x5a, 0xa3, 0x26, 0x4d, 0x24,
92 | 0x99, 0xa3, 0xac, 0xe8, 0x0f,
93 | 0x4c, 0xca, 0xe2, 0xda, 0x13,
94 | 0x43, 0x0c, 0x5c, 0x55, 0xb5,
95 | 0xca, 0x5f
96 | };
97 |
98 | ECPublicKey alicePublicKey = Curve.decodePoint(alicePublic, 0);
99 | ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(alicePrivate);
100 | ECKeyPair aliceKeyPair = new ECKeyPair(alicePublicKey, alicePrivateKey);
101 |
102 | ECPublicKey bobPublicKey = Curve.decodePoint(bobPublic, 0);
103 | RootKey rootKey = new RootKey(HKDF.createFor(2), rootKeySeed);
104 |
105 | Pair rootKeyChainKeyPair = rootKey.createChain(bobPublicKey, aliceKeyPair);
106 | RootKey nextRootKey = rootKeyChainKeyPair.first();
107 | ChainKey nextChainKey = rootKeyChainKeyPair.second();
108 |
109 | CollectionAssert.AreEqual(rootKey.getKeyBytes(), rootKeySeed);
110 | CollectionAssert.AreEqual(nextRootKey.getKeyBytes(), nextRoot);
111 | CollectionAssert.AreEqual(nextChainKey.getKey(), nextChain);
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/impl/InMemorySignalProtocolStore.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 |
21 | namespace libsignal.state.impl
22 | {
23 | public class InMemorySignalProtocolStore : SignalProtocolStore
24 | {
25 |
26 | private readonly InMemoryPreKeyStore preKeyStore = new InMemoryPreKeyStore();
27 | private readonly InMemorySessionStore sessionStore = new InMemorySessionStore();
28 | private readonly InMemorySignedPreKeyStore signedPreKeyStore = new InMemorySignedPreKeyStore();
29 |
30 | private readonly InMemoryIdentityKeyStore identityKeyStore;
31 |
32 | public InMemorySignalProtocolStore(IdentityKeyPair identityKeyPair, uint registrationId)
33 | {
34 | this.identityKeyStore = new InMemoryIdentityKeyStore(identityKeyPair, registrationId);
35 | }
36 |
37 |
38 | public IdentityKeyPair GetIdentityKeyPair()
39 | {
40 | return identityKeyStore.GetIdentityKeyPair();
41 | }
42 |
43 |
44 | public uint GetLocalRegistrationId()
45 | {
46 | return identityKeyStore.GetLocalRegistrationId();
47 | }
48 |
49 |
50 | public bool SaveIdentity(String name, IdentityKey identityKey)
51 | {
52 | identityKeyStore.SaveIdentity(name, identityKey);
53 | return true;
54 | }
55 |
56 |
57 | public bool IsTrustedIdentity(String name, IdentityKey identityKey)
58 | {
59 | return identityKeyStore.IsTrustedIdentity(name, identityKey);
60 | }
61 |
62 |
63 | public PreKeyRecord LoadPreKey(uint preKeyId)
64 | {
65 | return preKeyStore.LoadPreKey(preKeyId);
66 | }
67 |
68 |
69 | public void StorePreKey(uint preKeyId, PreKeyRecord record)
70 | {
71 | preKeyStore.StorePreKey(preKeyId, record);
72 | }
73 |
74 |
75 | public bool ContainsPreKey(uint preKeyId)
76 | {
77 | return preKeyStore.ContainsPreKey(preKeyId);
78 | }
79 |
80 |
81 | public void RemovePreKey(uint preKeyId)
82 | {
83 | preKeyStore.RemovePreKey(preKeyId);
84 | }
85 |
86 |
87 | public SessionRecord LoadSession(SignalProtocolAddress address)
88 | {
89 | return sessionStore.LoadSession(address);
90 | }
91 |
92 |
93 | public List GetSubDeviceSessions(String name)
94 | {
95 | return sessionStore.GetSubDeviceSessions(name);
96 | }
97 |
98 |
99 | public void StoreSession(SignalProtocolAddress address, SessionRecord record)
100 | {
101 | sessionStore.StoreSession(address, record);
102 | }
103 |
104 |
105 | public bool ContainsSession(SignalProtocolAddress address)
106 | {
107 | return sessionStore.ContainsSession(address);
108 | }
109 |
110 |
111 | public void DeleteSession(SignalProtocolAddress address)
112 | {
113 | sessionStore.DeleteSession(address);
114 | }
115 |
116 |
117 | public void DeleteAllSessions(String name)
118 | {
119 | sessionStore.DeleteAllSessions(name);
120 | }
121 |
122 |
123 | public SignedPreKeyRecord LoadSignedPreKey(uint signedPreKeyId)
124 | {
125 | return signedPreKeyStore.LoadSignedPreKey(signedPreKeyId);
126 | }
127 |
128 |
129 | public List LoadSignedPreKeys()
130 | {
131 | return signedPreKeyStore.LoadSignedPreKeys();
132 | }
133 |
134 |
135 | public void StoreSignedPreKey(uint signedPreKeyId, SignedPreKeyRecord record)
136 | {
137 | signedPreKeyStore.StoreSignedPreKey(signedPreKeyId, record);
138 | }
139 |
140 |
141 | public bool ContainsSignedPreKey(uint signedPreKeyId)
142 | {
143 | return signedPreKeyStore.ContainsSignedPreKey(signedPreKeyId);
144 | }
145 |
146 |
147 | public void RemoveSignedPreKey(uint signedPreKeyId)
148 | {
149 | signedPreKeyStore.RemoveSignedPreKey(signedPreKeyId);
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/groups/GroupSessionBuilder.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.groups.state;
19 | using libsignal.protocol;
20 | using libsignal.util;
21 | using System;
22 |
23 | namespace libsignal.groups
24 | {
25 | /**
26 | * GroupSessionBuilder is responsible for setting up group SenderKey encrypted sessions.
27 | *
28 | * Once a session has been established, {@link org.whispersystems.libsignal.groups.GroupCipher}
29 | * can be used to encrypt/decrypt messages in that session.
30 | *
31 | * The built sessions are unidirectional: they can be used either for sending or for receiving,
32 | * but not both.
33 | *
34 | * Sessions are constructed per (groupId + senderId + deviceId) tuple. Remote logical users
35 | * are identified by their senderId, and each logical recipientId can have multiple physical
36 | * devices.
37 | *
38 | * @author
39 | */
40 |
41 | public class GroupSessionBuilder
42 | {
43 |
44 | private readonly SenderKeyStore senderKeyStore;
45 |
46 | public GroupSessionBuilder(SenderKeyStore senderKeyStore)
47 | {
48 | this.senderKeyStore = senderKeyStore;
49 | }
50 |
51 | /**
52 | * Construct a group session for receiving messages from senderKeyName.
53 | *
54 | * @param senderKeyName The (groupId, senderId, deviceId) tuple associated with the SenderKeyDistributionMessage.
55 | * @param senderKeyDistributionMessage A received SenderKeyDistributionMessage.
56 | */
57 | public void process(SenderKeyName senderKeyName, SenderKeyDistributionMessage senderKeyDistributionMessage)
58 | {
59 | lock (GroupCipher.LOCK)
60 | {
61 | SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);
62 | senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(),
63 | senderKeyDistributionMessage.getIteration(),
64 | senderKeyDistributionMessage.getChainKey(),
65 | senderKeyDistributionMessage.getSignatureKey());
66 | senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord);
67 | }
68 | }
69 |
70 | /**
71 | * Construct a group session for sending messages.
72 | *
73 | * @param senderKeyName The (groupId, senderId, deviceId) tuple. In this case, 'senderId' should be the caller.
74 | * @return A SenderKeyDistributionMessage that is individually distributed to each member of the group.
75 | */
76 | public SenderKeyDistributionMessage create(SenderKeyName senderKeyName)
77 | {
78 | lock (GroupCipher.LOCK)
79 | {
80 | try
81 | {
82 | SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);
83 |
84 | if (senderKeyRecord.isEmpty())
85 | {
86 | senderKeyRecord.setSenderKeyState(KeyHelper.generateSenderKeyId(),
87 | 0,
88 | KeyHelper.generateSenderKey(),
89 | KeyHelper.generateSenderSigningKey());
90 | senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord);
91 | }
92 |
93 | SenderKeyState state = senderKeyRecord.getSenderKeyState();
94 |
95 | return new SenderKeyDistributionMessage(state.getKeyId(),
96 | state.getSenderChainKey().getIteration(),
97 | state.getSenderChainKey().getSeed(),
98 | state.getSigningKeyPublic());
99 |
100 | }
101 | catch (Exception e) when (e is InvalidKeyIdException || e is InvalidKeyException)
102 | {
103 | throw new Exception(e.Message);
104 | }
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/protocol/SenderKeyDistributionMessage.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using Google.ProtocolBuffers;
19 | using libsignal.ecc;
20 | using libsignal.util;
21 | using System;
22 |
23 | namespace libsignal.protocol
24 | {
25 | public partial class SenderKeyDistributionMessage : CiphertextMessage
26 | {
27 |
28 | private readonly uint id;
29 | private readonly uint iteration;
30 | private readonly byte[] chainKey;
31 | private readonly ECPublicKey signatureKey;
32 | private readonly byte[] serialized;
33 |
34 | public SenderKeyDistributionMessage(uint id, uint iteration, byte[] chainKey, ECPublicKey signatureKey)
35 | {
36 | byte[] version = { ByteUtil.intsToByteHighAndLow((int)CURRENT_VERSION, (int)CURRENT_VERSION) };
37 | byte[] protobuf = WhisperProtos.SenderKeyDistributionMessage.CreateBuilder()
38 | .SetId(id)
39 | .SetIteration(iteration)
40 | .SetChainKey(ByteString.CopyFrom(chainKey))
41 | .SetSigningKey(ByteString.CopyFrom(signatureKey.serialize()))
42 | .Build().ToByteArray();
43 |
44 | this.id = id;
45 | this.iteration = iteration;
46 | this.chainKey = chainKey;
47 | this.signatureKey = signatureKey;
48 | this.serialized = ByteUtil.combine(version, protobuf);
49 | }
50 |
51 | public SenderKeyDistributionMessage(byte[] serialized)
52 | {
53 | try
54 | {
55 | byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.Length - 1);
56 | byte version = messageParts[0][0];
57 | byte[] message = messageParts[1];
58 |
59 | if (ByteUtil.highBitsToInt(version) < CiphertextMessage.CURRENT_VERSION)
60 | {
61 | throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version));
62 | }
63 |
64 | if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION)
65 | {
66 | throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version));
67 | }
68 |
69 | WhisperProtos.SenderKeyDistributionMessage distributionMessage = WhisperProtos.SenderKeyDistributionMessage.ParseFrom(message);
70 |
71 | if (!distributionMessage.HasId ||
72 | !distributionMessage.HasIteration ||
73 | !distributionMessage.HasChainKey ||
74 | !distributionMessage.HasSigningKey)
75 | {
76 | throw new InvalidMessageException("Incomplete message.");
77 | }
78 |
79 | this.serialized = serialized;
80 | this.id = distributionMessage.Id;
81 | this.iteration = distributionMessage.Iteration;
82 | this.chainKey = distributionMessage.ChainKey.ToByteArray();
83 | this.signatureKey = Curve.decodePoint(distributionMessage.SigningKey.ToByteArray(), 0);
84 | }
85 | catch (Exception e)
86 | {
87 | //InvalidProtocolBufferException | InvalidKey
88 | throw new InvalidMessageException(e);
89 | }
90 | }
91 |
92 | public override byte[] serialize()
93 | {
94 | return serialized;
95 | }
96 |
97 |
98 | public override uint getType()
99 | {
100 | return SENDERKEY_DISTRIBUTION_TYPE;
101 | }
102 |
103 | public uint getIteration()
104 | {
105 | return iteration;
106 | }
107 |
108 | public byte[] getChainKey()
109 | {
110 | return chainKey;
111 | }
112 |
113 | public ECPublicKey getSignatureKey()
114 | {
115 | return signatureKey;
116 | }
117 |
118 | public uint getId()
119 | {
120 | return id;
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/ratchet/SymmetricSignalProtocolParameters.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc;
19 | using System;
20 |
21 | namespace libsignal.ratchet
22 | {
23 | public class SymmetricSignalProtocolParameters
24 | {
25 |
26 | private readonly ECKeyPair ourBaseKey;
27 | private readonly ECKeyPair ourRatchetKey;
28 | private readonly IdentityKeyPair ourIdentityKey;
29 |
30 | private readonly ECPublicKey theirBaseKey;
31 | private readonly ECPublicKey theirRatchetKey;
32 | private readonly IdentityKey theirIdentityKey;
33 |
34 | SymmetricSignalProtocolParameters(ECKeyPair ourBaseKey, ECKeyPair ourRatchetKey,
35 | IdentityKeyPair ourIdentityKey, ECPublicKey theirBaseKey,
36 | ECPublicKey theirRatchetKey, IdentityKey theirIdentityKey)
37 | {
38 | this.ourBaseKey = ourBaseKey;
39 | this.ourRatchetKey = ourRatchetKey;
40 | this.ourIdentityKey = ourIdentityKey;
41 | this.theirBaseKey = theirBaseKey;
42 | this.theirRatchetKey = theirRatchetKey;
43 | this.theirIdentityKey = theirIdentityKey;
44 |
45 | if (ourBaseKey == null || ourRatchetKey == null || ourIdentityKey == null ||
46 | theirBaseKey == null || theirRatchetKey == null || theirIdentityKey == null)
47 | {
48 | throw new Exception("Null values!");
49 | }
50 | }
51 |
52 | public ECKeyPair getOurBaseKey()
53 | {
54 | return ourBaseKey;
55 | }
56 |
57 | public ECKeyPair getOurRatchetKey()
58 | {
59 | return ourRatchetKey;
60 | }
61 |
62 | public IdentityKeyPair getOurIdentityKey()
63 | {
64 | return ourIdentityKey;
65 | }
66 |
67 | public ECPublicKey getTheirBaseKey()
68 | {
69 | return theirBaseKey;
70 | }
71 |
72 | public ECPublicKey getTheirRatchetKey()
73 | {
74 | return theirRatchetKey;
75 | }
76 |
77 | public IdentityKey getTheirIdentityKey()
78 | {
79 | return theirIdentityKey;
80 | }
81 |
82 | public static Builder newBuilder()
83 | {
84 | return new Builder();
85 | }
86 |
87 | public class Builder
88 | {
89 | private ECKeyPair ourBaseKey;
90 | private ECKeyPair ourRatchetKey;
91 | private IdentityKeyPair ourIdentityKey;
92 |
93 | private ECPublicKey theirBaseKey;
94 | private ECPublicKey theirRatchetKey;
95 | private IdentityKey theirIdentityKey;
96 |
97 | public Builder setOurBaseKey(ECKeyPair ourBaseKey)
98 | {
99 | this.ourBaseKey = ourBaseKey;
100 | return this;
101 | }
102 |
103 | public Builder setOurRatchetKey(ECKeyPair ourRatchetKey)
104 | {
105 | this.ourRatchetKey = ourRatchetKey;
106 | return this;
107 | }
108 |
109 | public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey)
110 | {
111 | this.ourIdentityKey = ourIdentityKey;
112 | return this;
113 | }
114 |
115 | public Builder setTheirBaseKey(ECPublicKey theirBaseKey)
116 | {
117 | this.theirBaseKey = theirBaseKey;
118 | return this;
119 | }
120 |
121 | public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey)
122 | {
123 | this.theirRatchetKey = theirRatchetKey;
124 | return this;
125 | }
126 |
127 | public Builder setTheirIdentityKey(IdentityKey theirIdentityKey)
128 | {
129 | this.theirIdentityKey = theirIdentityKey;
130 | return this;
131 | }
132 |
133 | public SymmetricSignalProtocolParameters create()
134 | {
135 | return new SymmetricSignalProtocolParameters(ourBaseKey, ourRatchetKey, ourIdentityKey,
136 | theirBaseKey, theirRatchetKey, theirIdentityKey);
137 | }
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Microsoft Azure ApplicationInsights config file
170 | ApplicationInsights.config
171 |
172 | # Windows Store app package directory
173 | AppPackages/
174 | BundleArtifacts/
175 |
176 | # Visual Studio cache files
177 | # files ending in .cache can be ignored
178 | *.[Cc]ache
179 | # but keep track of directories ending in .cache
180 | !*.[Cc]ache/
181 |
182 | # Others
183 | ClientBin/
184 | [Ss]tyle[Cc]op.*
185 | ~$*
186 | *~
187 | *.dbmdl
188 | *.dbproj.schemaview
189 | *.pfx
190 | *.publishsettings
191 | node_modules/
192 | orleans.codegen.cs
193 |
194 | # RIA/Silverlight projects
195 | Generated_Code/
196 |
197 | # Backup & report files from converting an old project file
198 | # to a newer Visual Studio version. Backup files are not needed,
199 | # because we have git ;-)
200 | _UpgradeReport_Files/
201 | Backup*/
202 | UpgradeLog*.XML
203 | UpgradeLog*.htm
204 |
205 | # SQL Server files
206 | *.mdf
207 | *.ldf
208 |
209 | # Business Intelligence projects
210 | *.rdl.data
211 | *.bim.layout
212 | *.bim_*.settings
213 |
214 | # Microsoft Fakes
215 | FakesAssemblies/
216 |
217 | # GhostDoc plugin setting file
218 | *.GhostDoc.xml
219 |
220 | # Node.js Tools for Visual Studio
221 | .ntvs_analysis.dat
222 |
223 | # Visual Studio 6 build log
224 | *.plg
225 |
226 | # Visual Studio 6 workspace options file
227 | *.opt
228 |
229 | # Visual Studio LightSwitch build output
230 | **/*.HTMLClient/GeneratedArtifacts
231 | **/*.DesktopClient/GeneratedArtifacts
232 | **/*.DesktopClient/ModelManifest.xml
233 | **/*.Server/GeneratedArtifacts
234 | **/*.Server/ModelManifest.xml
235 | _Pvt_Extensions
236 |
237 | # LightSwitch generated files
238 | GeneratedArtifacts/
239 | ModelManifest.xml
240 |
241 | # Paket dependency manager
242 | .paket/paket.exe
243 |
244 | # FAKE - F# Make
245 | .fake/
246 | /signal-porting-tool
247 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/fingerprint/NumericFingerprintGenerator.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal;
19 | using libsignal.util;
20 | using PCLCrypto;
21 | using System;
22 | using System.Diagnostics;
23 | using System.Text;
24 | using static PCLCrypto.WinRTCrypto;
25 |
26 | namespace org.whispersystems.libsignal.fingerprint
27 | {
28 |
29 | public class NumericFingerprintGenerator : FingerprintGenerator
30 | {
31 |
32 | private static readonly int VERSION = 0;
33 |
34 | private readonly long iterations;
35 |
36 | /**
37 | * Construct a fingerprint generator for 60 digit numerics.
38 | *
39 | * @param iterations The number of internal iterations to perform in the process of
40 | * generating a fingerprint. This needs to be constant, and synchronized
41 | * across all clients.
42 | *
43 | * The higher the iteration count, the higher the security level:
44 | *
45 | * - 1024 ~ 109.7 bits
46 | * - 1400 > 110 bits
47 | * - 5200 > 112 bits
48 | */
49 | public NumericFingerprintGenerator(long iterations)
50 | {
51 | this.iterations = iterations;
52 | }
53 |
54 | public object MessageDigest { get; private set; }
55 |
56 | /**
57 | * Generate a scannable and displayble fingerprint.
58 | *
59 | * @param localStableIdentifier The client's "stable" identifier.
60 | * @param localIdentityKey The client's identity key.
61 | * @param remoteStableIdentifier The remote party's "stable" identifier.
62 | * @param remoteIdentityKey The remote party's identity key.
63 | * @return A unique fingerprint for this conversation.
64 | */
65 |
66 | public Fingerprint createFor(string localStableIdentifier, IdentityKey localIdentityKey,
67 | string remoteStableIdentifier, IdentityKey remoteIdentityKey)
68 | {
69 | DisplayableFingerprint displayableFingerprint = new DisplayableFingerprint(getDisplayStringFor(localStableIdentifier, localIdentityKey),
70 | getDisplayStringFor(remoteStableIdentifier, remoteIdentityKey));
71 |
72 | ScannableFingerprint scannableFingerprint = new ScannableFingerprint(VERSION,
73 | localStableIdentifier, localIdentityKey,
74 | remoteStableIdentifier, remoteIdentityKey);
75 |
76 | return new Fingerprint(displayableFingerprint, scannableFingerprint);
77 | }
78 |
79 | private string getDisplayStringFor(string stableIdentifier, IdentityKey identityKey)
80 | {
81 | try
82 | {
83 | IHashAlgorithmProvider digest = HashAlgorithmProvider.OpenAlgorithm(PCLCrypto.HashAlgorithm.Sha512);
84 |
85 | byte[] publicKey = identityKey.getPublicKey().serialize();
86 | byte[] hash = ByteUtil.combine(ByteUtil.shortToByteArray(VERSION),
87 | publicKey, Encoding.UTF8.GetBytes(stableIdentifier));
88 |
89 | for (int i = 0; i < iterations; i++)
90 | {
91 | hash = digest.HashData(ByteUtil.combine(new byte[][] { hash, publicKey }));
92 | }
93 |
94 | return getEncodedChunk(hash, 0) +
95 | getEncodedChunk(hash, 5) +
96 | getEncodedChunk(hash, 10) +
97 | getEncodedChunk(hash, 15) +
98 | getEncodedChunk(hash, 20) +
99 | getEncodedChunk(hash, 25);
100 | }
101 | catch (Exception e)
102 | {
103 | Debug.Assert(false, e.Message);
104 | throw e;
105 | }
106 | }
107 |
108 | private String getEncodedChunk(byte[] hash, int offset)
109 | {
110 | long chunk = ByteUtil.byteArray5ToLong(hash, offset) % 100000;
111 | return chunk.ToString().PadLeft(5, '0');
112 | }
113 |
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/signal-protocol-pcl/ratchet/BobSignalProtocolParameters.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc;
19 | using Strilanc.Value;
20 | using System;
21 |
22 | namespace libsignal.ratchet
23 | {
24 | public class BobSignalProtocolParameters
25 | {
26 |
27 | private readonly IdentityKeyPair ourIdentityKey;
28 | private readonly ECKeyPair ourSignedPreKey;
29 | private readonly May ourOneTimePreKey;
30 | private readonly ECKeyPair ourRatchetKey;
31 |
32 | private readonly IdentityKey theirIdentityKey;
33 | private readonly ECPublicKey theirBaseKey;
34 |
35 | BobSignalProtocolParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourSignedPreKey,
36 | ECKeyPair ourRatchetKey, May ourOneTimePreKey,
37 | IdentityKey theirIdentityKey, ECPublicKey theirBaseKey)
38 | {
39 | this.ourIdentityKey = ourIdentityKey;
40 | this.ourSignedPreKey = ourSignedPreKey;
41 | this.ourRatchetKey = ourRatchetKey;
42 | this.ourOneTimePreKey = ourOneTimePreKey;
43 | this.theirIdentityKey = theirIdentityKey;
44 | this.theirBaseKey = theirBaseKey;
45 |
46 | if (ourIdentityKey == null || ourSignedPreKey == null || ourRatchetKey == null ||
47 | ourOneTimePreKey == null || theirIdentityKey == null || theirBaseKey == null)
48 | {
49 | throw new Exception("Null value!");
50 | }
51 | }
52 |
53 | public IdentityKeyPair getOurIdentityKey()
54 | {
55 | return ourIdentityKey;
56 | }
57 |
58 | public ECKeyPair getOurSignedPreKey()
59 | {
60 | return ourSignedPreKey;
61 | }
62 |
63 | public May getOurOneTimePreKey()
64 | {
65 | return ourOneTimePreKey;
66 | }
67 |
68 | public IdentityKey getTheirIdentityKey()
69 | {
70 | return theirIdentityKey;
71 | }
72 |
73 | public ECPublicKey getTheirBaseKey()
74 | {
75 | return theirBaseKey;
76 | }
77 |
78 | public static Builder newBuilder()
79 | {
80 | return new Builder();
81 | }
82 |
83 | public ECKeyPair getOurRatchetKey()
84 | {
85 | return ourRatchetKey;
86 | }
87 |
88 | public class Builder
89 | {
90 | private IdentityKeyPair ourIdentityKey;
91 | private ECKeyPair ourSignedPreKey;
92 | private May ourOneTimePreKey;
93 | private ECKeyPair ourRatchetKey;
94 |
95 | private IdentityKey theirIdentityKey;
96 | private ECPublicKey theirBaseKey;
97 |
98 | public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey)
99 | {
100 | this.ourIdentityKey = ourIdentityKey;
101 | return this;
102 | }
103 |
104 | public Builder setOurSignedPreKey(ECKeyPair ourSignedPreKey)
105 | {
106 | this.ourSignedPreKey = ourSignedPreKey;
107 | return this;
108 | }
109 |
110 | public Builder setOurOneTimePreKey(May ourOneTimePreKey)
111 | {
112 | this.ourOneTimePreKey = ourOneTimePreKey;
113 | return this;
114 | }
115 |
116 | public Builder setTheirIdentityKey(IdentityKey theirIdentityKey)
117 | {
118 | this.theirIdentityKey = theirIdentityKey;
119 | return this;
120 | }
121 |
122 | public Builder setTheirBaseKey(ECPublicKey theirBaseKey)
123 | {
124 | this.theirBaseKey = theirBaseKey;
125 | return this;
126 | }
127 |
128 | public Builder setOurRatchetKey(ECKeyPair ourRatchetKey)
129 | {
130 | this.ourRatchetKey = ourRatchetKey;
131 | return this;
132 | }
133 |
134 | public BobSignalProtocolParameters create()
135 | {
136 | return new BobSignalProtocolParameters(ourIdentityKey, ourSignedPreKey, ourRatchetKey,
137 | ourOneTimePreKey, theirIdentityKey, theirBaseKey);
138 | }
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/ratchet/AliceSignalProtocolParameters.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc;
19 | using Strilanc.Value;
20 | using System;
21 |
22 | namespace libsignal.ratchet
23 | {
24 | public class AliceSignalProtocolParameters
25 | {
26 |
27 | private readonly IdentityKeyPair ourIdentityKey;
28 | private readonly ECKeyPair ourBaseKey;
29 |
30 | private readonly IdentityKey theirIdentityKey;
31 | private readonly ECPublicKey theirSignedPreKey;
32 | private readonly May theirOneTimePreKey;
33 | private readonly ECPublicKey theirRatchetKey;
34 |
35 | private AliceSignalProtocolParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourBaseKey,
36 | IdentityKey theirIdentityKey, ECPublicKey theirSignedPreKey,
37 | ECPublicKey theirRatchetKey, May theirOneTimePreKey)
38 | {
39 | this.ourIdentityKey = ourIdentityKey;
40 | this.ourBaseKey = ourBaseKey;
41 | this.theirIdentityKey = theirIdentityKey;
42 | this.theirSignedPreKey = theirSignedPreKey;
43 | this.theirRatchetKey = theirRatchetKey;
44 | this.theirOneTimePreKey = theirOneTimePreKey;
45 |
46 | if (ourIdentityKey == null || ourBaseKey == null || theirIdentityKey == null ||
47 | theirSignedPreKey == null || theirRatchetKey == null || theirOneTimePreKey == null)
48 | {
49 | throw new Exception("Null values!");
50 | }
51 | }
52 |
53 | public IdentityKeyPair getOurIdentityKey()
54 | {
55 | return ourIdentityKey;
56 | }
57 |
58 | public ECKeyPair getOurBaseKey()
59 | {
60 | return ourBaseKey;
61 | }
62 |
63 | public IdentityKey getTheirIdentityKey()
64 | {
65 | return theirIdentityKey;
66 | }
67 |
68 | public ECPublicKey getTheirSignedPreKey()
69 | {
70 | return theirSignedPreKey;
71 | }
72 |
73 | public May getTheirOneTimePreKey()
74 | {
75 | return theirOneTimePreKey;
76 | }
77 |
78 | public static Builder newBuilder()
79 | {
80 | return new Builder();
81 | }
82 |
83 | public ECPublicKey getTheirRatchetKey()
84 | {
85 | return theirRatchetKey;
86 | }
87 |
88 | public class Builder
89 | {
90 | private IdentityKeyPair ourIdentityKey;
91 | private ECKeyPair ourBaseKey;
92 |
93 | private IdentityKey theirIdentityKey;
94 | private ECPublicKey theirSignedPreKey;
95 | private ECPublicKey theirRatchetKey;
96 | private May theirOneTimePreKey;
97 |
98 | public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey)
99 | {
100 | this.ourIdentityKey = ourIdentityKey;
101 | return this;
102 | }
103 |
104 | public Builder setOurBaseKey(ECKeyPair ourBaseKey)
105 | {
106 | this.ourBaseKey = ourBaseKey;
107 | return this;
108 | }
109 |
110 | public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey)
111 | {
112 | this.theirRatchetKey = theirRatchetKey;
113 | return this;
114 | }
115 |
116 | public Builder setTheirIdentityKey(IdentityKey theirIdentityKey)
117 | {
118 | this.theirIdentityKey = theirIdentityKey;
119 | return this;
120 | }
121 |
122 | public Builder setTheirSignedPreKey(ECPublicKey theirSignedPreKey)
123 | {
124 | this.theirSignedPreKey = theirSignedPreKey;
125 | return this;
126 | }
127 |
128 | public Builder setTheirOneTimePreKey(May theirOneTimePreKey)
129 | {
130 | this.theirOneTimePreKey = theirOneTimePreKey;
131 | return this;
132 | }
133 |
134 | public AliceSignalProtocolParameters create()
135 | {
136 | return new AliceSignalProtocolParameters(ourIdentityKey, ourBaseKey, theirIdentityKey,
137 | theirSignedPreKey, theirRatchetKey, theirOneTimePreKey);
138 | }
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/fingerprint/ScannableFingerprint.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using Google.ProtocolBuffers;
19 | using libsignal;
20 | using libsignal.util;
21 | using System.Text;
22 | using static libsignal.fingerprint.FingerprintProtos;
23 |
24 | namespace org.whispersystems.libsignal.fingerprint
25 | {
26 |
27 | public class ScannableFingerprint
28 | {
29 |
30 | private readonly CombinedFingerprint combinedFingerprint;
31 |
32 | public ScannableFingerprint(int version,
33 | string localStableIdentifier, IdentityKey localIdentityKey,
34 | string remoteStableIdentifier, IdentityKey remoteIdentityKey)
35 | {
36 | this.combinedFingerprint = CombinedFingerprint.CreateBuilder()
37 | .SetVersion((uint)version)
38 | .SetLocalFingerprint(FingerprintData.CreateBuilder()
39 | .SetIdentifier(ByteString.CopyFrom(Encoding.UTF8.GetBytes(localStableIdentifier)))
40 | .SetPublicKey(ByteString.CopyFrom(localIdentityKey.serialize())))
41 | .SetRemoteFingerprint(FingerprintData.CreateBuilder()
42 | .SetIdentifier(ByteString.CopyFrom(Encoding.UTF8.GetBytes(remoteStableIdentifier)))
43 | .SetPublicKey(ByteString.CopyFrom(remoteIdentityKey.serialize())))
44 | .Build();
45 | }
46 |
47 | /**
48 | * @return A byte string to be displayed in a QR code.
49 | */
50 | public byte[] getSerialized()
51 | {
52 | return combinedFingerprint.ToByteArray();
53 | }
54 |
55 | /**
56 | * Compare a scanned QR code with what we expect.
57 | *
58 | * @param scannedFingerprintData The scanned data
59 | * @return True if matching, otehrwise false.
60 | * @throws FingerprintVersionMismatchException if the scanned fingerprint is the wrong version.
61 | * @throws FingerprintIdentifierMismatchException if the scanned fingerprint is for the wrong stable identifier.
62 | */
63 | public bool compareTo(byte[] scannedFingerprintData)
64 | /* throws FingerprintVersionMismatchException,
65 | FingerprintIdentifierMismatchException,
66 | FingerprintParsingException */
67 | {
68 | try
69 | {
70 | CombinedFingerprint scannedFingerprint = CombinedFingerprint.ParseFrom(scannedFingerprintData);
71 |
72 | if (!scannedFingerprint.HasRemoteFingerprint || !scannedFingerprint.HasLocalFingerprint ||
73 | !scannedFingerprint.HasVersion || scannedFingerprint.Version != combinedFingerprint.Version)
74 | {
75 | throw new FingerprintVersionMismatchException();
76 | }
77 |
78 | if (!combinedFingerprint.LocalFingerprint.Identifier.Equals(scannedFingerprint.RemoteFingerprint.Identifier) ||
79 | !combinedFingerprint.RemoteFingerprint.Identifier.Equals(scannedFingerprint.LocalFingerprint.Identifier))
80 | {
81 | throw new FingerprintIdentifierMismatchException(combinedFingerprint.LocalFingerprint.Identifier.ToStringUtf8(),
82 | combinedFingerprint.RemoteFingerprint.Identifier.ToStringUtf8(),
83 | scannedFingerprint.LocalFingerprint.Identifier.ToStringUtf8(),
84 | scannedFingerprint.RemoteFingerprint.Identifier.ToStringUtf8());
85 | }
86 |
87 | return ByteUtil.isEqual(combinedFingerprint.LocalFingerprint.ToByteArray(), scannedFingerprint.RemoteFingerprint.ToByteArray()) &&
88 | ByteUtil.isEqual(combinedFingerprint.RemoteFingerprint.ToByteArray(), scannedFingerprint.LocalFingerprint.ToByteArray());
89 | }
90 | catch (InvalidProtocolBufferException e)
91 | {
92 | throw new FingerprintParsingException(e);
93 | }
94 | }
95 | }
96 | }
--------------------------------------------------------------------------------
/signal-protocol-pcl/state/SessionRecord.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System.Collections.Generic;
19 | using System.Linq;
20 | using static libsignal.state.StorageProtos;
21 |
22 | namespace libsignal.state
23 | {
24 | /**
25 | * A SessionRecord encapsulates the state of an ongoing session.
26 | *
27 | * @author Moxie Marlinspike
28 | */
29 | public class SessionRecord
30 | {
31 |
32 | private static int ARCHIVED_STATES_MAX_LENGTH = 40;
33 |
34 | private SessionState sessionState = new SessionState();
35 | private LinkedList previousStates = new LinkedList();
36 | private bool fresh = false;
37 |
38 | public SessionRecord()
39 | {
40 | this.fresh = true;
41 | }
42 |
43 | public SessionRecord(SessionState sessionState)
44 | {
45 | this.sessionState = sessionState;
46 | this.fresh = false;
47 | }
48 |
49 | public SessionRecord(byte[] serialized)
50 | {
51 | RecordStructure record = RecordStructure.ParseFrom(serialized);
52 | this.sessionState = new SessionState(record.CurrentSession);
53 | this.fresh = false;
54 |
55 | foreach (SessionStructure previousStructure in record.PreviousSessionsList)
56 | {
57 | previousStates.AddLast(new SessionState(previousStructure)); // add -> AddLast (java)
58 | }
59 | }
60 |
61 | public bool hasSessionState(uint version, byte[] aliceBaseKey)
62 | {
63 | if (sessionState.getSessionVersion() == version &&
64 | Enumerable.SequenceEqual(aliceBaseKey, sessionState.getAliceBaseKey()))
65 | {
66 | return true;
67 | }
68 |
69 | foreach (SessionState state in previousStates)
70 | {
71 | if (state.getSessionVersion() == version &&
72 | Enumerable.SequenceEqual(aliceBaseKey, state.getAliceBaseKey()))
73 | {
74 | return true;
75 | }
76 | }
77 |
78 | return false;
79 | }
80 |
81 | public SessionState getSessionState()
82 | {
83 | return sessionState;
84 | }
85 |
86 | /**
87 | * @return the list of all currently maintained "previous" session states.
88 | */
89 | public LinkedList getPreviousSessionStates()
90 | {
91 | return previousStates;
92 | }
93 |
94 |
95 | public bool isFresh()
96 | {
97 | return fresh;
98 | }
99 |
100 | /**
101 | * Move the current {@link SessionState} into the list of "previous" session states,
102 | * and replace the current {@link org.whispersystems.libsignal.state.SessionState}
103 | * with a fresh reset instance.
104 | */
105 | public void archiveCurrentState()
106 | {
107 | promoteState(new SessionState());
108 | }
109 |
110 | public void promoteState(SessionState promotedState)
111 | {
112 | this.previousStates.AddFirst(sessionState);
113 | this.sessionState = promotedState;
114 |
115 | if (previousStates.Count > ARCHIVED_STATES_MAX_LENGTH)
116 | {
117 | previousStates.RemoveLast();
118 | }
119 | }
120 |
121 | public void setState(SessionState sessionState)
122 | {
123 | this.sessionState = sessionState;
124 | }
125 |
126 | /**
127 | * @return a serialized version of the current SessionRecord.
128 | */
129 | public byte[] serialize()
130 | {
131 | List previousStructures = new List();
132 |
133 | foreach (SessionState previousState in previousStates)
134 | {
135 | previousStructures.Add(previousState.getStructure());
136 | }
137 |
138 | RecordStructure record = RecordStructure.CreateBuilder()
139 | .SetCurrentSession(sessionState.getStructure())
140 | .AddRangePreviousSessions(previousStructures)
141 | /*.AddAllPreviousSessions(previousStructures)*/
142 | .Build();
143 |
144 | return record.ToByteArray();
145 | }
146 |
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/util/HMAC.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using static PCLCrypto.WinRTCrypto;
21 | using PCLCrypto;
22 |
23 | namespace libsignal.util
24 | {
25 | public class Sign
26 | {
27 | public static byte[] sha256sum(byte[] key, byte[] message)
28 | {
29 | IMacAlgorithmProvider provider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
30 | ICryptographicKey hmacKey = provider.CreateKey(key);
31 | byte [] hmac = CryptographicEngine.Sign(hmacKey, message);
32 | return hmac;
33 | }
34 | }
35 |
36 | public class Sha256
37 | {
38 | public static byte[] Sign(byte[] key, byte[] message)
39 | {
40 | IMacAlgorithmProvider provider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
41 | ICryptographicKey hmacKey = provider.CreateKey(key);
42 | byte[] hmac = CryptographicEngine.Sign(hmacKey, message);
43 | return hmac;
44 | }
45 |
46 | public static bool Verify(byte[] key, byte[] message, byte[] signature)
47 | {
48 | IMacAlgorithmProvider provider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
49 |
50 | ICryptographicKey hmacKey = provider.CreateKey(key);
51 | return CryptographicEngine.VerifySignature(hmacKey, message, signature);
52 | }
53 | }
54 |
55 | ///
56 | /// Encryption helpers
57 | ///
58 | public class Encrypt
59 | {
60 | ///
61 | /// Computes PKCS5 for the message
62 | ///
63 | /// plaintext
64 | /// PKCS5 of the message
65 | public static byte[] aesCbcPkcs5(byte[] message, byte[] key, byte[] iv)
66 | {
67 | ISymmetricKeyAlgorithmProvider objAlg = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); // PKCS5
68 | ICryptographicKey ckey = objAlg.CreateSymmetricKey(key);
69 | byte [] result = CryptographicEngine.Encrypt(ckey, message, iv);
70 | return result;
71 | }
72 |
73 | public static byte[] aesCtr(byte[] message, byte[] key, uint counter)
74 | {
75 | ISymmetricKeyAlgorithmProvider objAlg = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); // CRT
76 | ICryptographicKey ckey = objAlg.CreateSymmetricKey(key);
77 |
78 | byte[] ivBytes = new byte[16];
79 | ByteUtil.intToByteArray(ivBytes, 0, (int)counter);
80 |
81 | byte [] result = CryptographicEngine.Encrypt(ckey, message, ivBytes);
82 | return result;
83 | }
84 |
85 |
86 | }
87 |
88 | ///
89 | /// Decryption helpers
90 | ///
91 | public class Decrypt
92 | {
93 | public static byte[] aesCbcPkcs5(byte[] message, byte[] key, byte[] iv)
94 | {
95 | ISymmetricKeyAlgorithmProvider objAlg = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
96 | ICryptographicKey ckey = objAlg.CreateSymmetricKey(key);
97 |
98 | if (message.Length % objAlg.BlockLength != 0) throw new Exception("Invalid ciphertext length");
99 |
100 | byte [] result = CryptographicEngine.Decrypt(ckey, message, iv);
101 | return result;
102 | }
103 |
104 | public static byte[] aesCtr(byte[] message, byte[] key, uint counter)
105 | {
106 | ISymmetricKeyAlgorithmProvider objAlg = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
107 | ICryptographicKey ckey = objAlg.CreateSymmetricKey(key);
108 |
109 | byte[] ivBytes = new byte[16];
110 | ByteUtil.intToByteArray(ivBytes, 0, (int)counter);
111 |
112 | byte [] result = CryptographicEngine.Decrypt(ckey, message, ivBytes);
113 | return result;
114 | }
115 | }
116 |
117 | public static class CryptoHelper
118 | {
119 | ///
120 | /// TODO: dead code?
121 | ///
122 | public static void Shuffle(this IList list)
123 | {
124 | Random rng = new Random();
125 | int n = list.Count;
126 | while (n > 1)
127 | {
128 | n--;
129 | int k = rng.Next(n + 1);
130 | T value = list[k];
131 | list[k] = list[n];
132 | list[n] = value;
133 | }
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/protocol/SenderKeyMessage.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using Google.ProtocolBuffers;
19 | using libsignal.ecc;
20 | using libsignal.util;
21 | using System;
22 |
23 | namespace libsignal.protocol
24 | {
25 | public partial class SenderKeyMessage : CiphertextMessage
26 | {
27 |
28 | private static readonly int SIGNATURE_LENGTH = 64;
29 |
30 | private readonly uint messageVersion;
31 | private readonly uint keyId;
32 | private readonly uint iteration;
33 | private readonly byte[] ciphertext;
34 | private readonly byte[] serialized;
35 |
36 | public SenderKeyMessage(byte[] serialized)
37 | {
38 | try
39 | {
40 | byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.Length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH);
41 | byte version = messageParts[0][0];
42 | byte[] message = messageParts[1];
43 | byte[] signature = messageParts[2];
44 |
45 | if (ByteUtil.highBitsToInt(version) < 3)
46 | {
47 | throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version));
48 | }
49 |
50 | if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION)
51 | {
52 | throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version));
53 | }
54 |
55 | WhisperProtos.SenderKeyMessage senderKeyMessage = WhisperProtos.SenderKeyMessage.ParseFrom(message);
56 |
57 | if (!senderKeyMessage.HasId ||
58 | !senderKeyMessage.HasIteration ||
59 | !senderKeyMessage.HasCiphertext)
60 | {
61 | throw new InvalidMessageException("Incomplete message.");
62 | }
63 |
64 | this.serialized = serialized;
65 | this.messageVersion = (uint)ByteUtil.highBitsToInt(version);
66 | this.keyId = senderKeyMessage.Id;
67 | this.iteration = senderKeyMessage.Iteration;
68 | this.ciphertext = senderKeyMessage.Ciphertext.ToByteArray();
69 | }
70 | catch (/*InvalidProtocolBufferException | Parse*/Exception e)
71 | {
72 | throw new InvalidMessageException(e);
73 | }
74 | }
75 |
76 | public SenderKeyMessage(uint keyId, uint iteration, byte[] ciphertext, ECPrivateKey signatureKey)
77 | {
78 | byte[] version = { ByteUtil.intsToByteHighAndLow((int)CURRENT_VERSION, (int)CURRENT_VERSION) };
79 | byte[] message = WhisperProtos.SenderKeyMessage.CreateBuilder()
80 | .SetId(keyId)
81 | .SetIteration(iteration)
82 | .SetCiphertext(ByteString.CopyFrom(ciphertext))
83 | .Build().ToByteArray();
84 |
85 | byte[] signature = getSignature(signatureKey, ByteUtil.combine(version, message));
86 |
87 | this.serialized = ByteUtil.combine(version, message, signature);
88 | this.messageVersion = CURRENT_VERSION;
89 | this.keyId = keyId;
90 | this.iteration = iteration;
91 | this.ciphertext = ciphertext;
92 | }
93 |
94 | public uint getKeyId()
95 | {
96 | return keyId;
97 | }
98 |
99 | public uint getIteration()
100 | {
101 | return iteration;
102 | }
103 |
104 | public byte[] getCipherText()
105 | {
106 | return ciphertext;
107 | }
108 |
109 | public void verifySignature(ECPublicKey signatureKey)
110 | {
111 | try
112 | {
113 | byte[][] parts = ByteUtil.split(serialized, serialized.Length - SIGNATURE_LENGTH, SIGNATURE_LENGTH);
114 |
115 | if (!Curve.verifySignature(signatureKey, parts[0], parts[1]))
116 | {
117 | throw new InvalidMessageException("Invalid signature!");
118 | }
119 |
120 | }
121 | catch (InvalidKeyException e)
122 | {
123 | throw new InvalidMessageException(e);
124 | }
125 | }
126 |
127 | private byte[] getSignature(ECPrivateKey signatureKey, byte[] serialized)
128 | {
129 | try
130 | {
131 | return Curve.calculateSignature(signatureKey, serialized);
132 | }
133 | catch (InvalidKeyException e)
134 | {
135 | throw new Exception(e.Message);
136 | }
137 | }
138 |
139 | public override byte[] serialize()
140 | {
141 | return serialized;
142 | }
143 |
144 |
145 | public override uint getType()
146 | {
147 | return CiphertextMessage.SENDERKEY_TYPE;
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/signal-protocol-tests/ratchet/ChainKeyTest.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.kdf;
19 | using libsignal.ratchet;
20 | using Microsoft.VisualStudio.TestTools.UnitTesting;
21 |
22 | namespace libsignal_test
23 | {
24 | [TestClass]
25 | public class ChainKeyTest
26 | {
27 | [TestMethod, TestCategory("libsignal.ratchet")]
28 | public void testChainKeyDerivationV2()
29 | {
30 | byte[] seed =
31 | {
32 | 0x8a, 0xb7, 0x2d, 0x6f, 0x4c,
33 | 0xc5, 0xac, 0x0d, 0x38, 0x7e,
34 | 0xaf, 0x46, 0x33, 0x78, 0xdd,
35 | 0xb2, 0x8e, 0xdd, 0x07, 0x38,
36 | 0x5b, 0x1c, 0xb0, 0x12, 0x50,
37 | 0xc7, 0x15, 0x98, 0x2e, 0x7a,
38 | 0xd4, 0x8f
39 | };
40 |
41 | byte[] messageKey =
42 | {
43 | 0x02, 0xa9, 0xaa, 0x6c, 0x7d,
44 | 0xbd, 0x64, 0xf9, 0xd3, 0xaa,
45 | 0x92, 0xf9, 0x2a, 0x27, 0x7b,
46 | 0xf5, 0x46, 0x09, 0xda, 0xdf,
47 | 0x0b, 0x00, 0x82, 0x8a, 0xcf,
48 | 0xc6, 0x1e, 0x3c, 0x72, 0x4b,
49 | 0x84, 0xa7
50 | };
51 |
52 | byte[] macKey =
53 | {
54 | 0xbf, 0xbe, 0x5e, 0xfb, 0x60,
55 | 0x30, 0x30, 0x52, 0x67, 0x42,
56 | 0xe3, 0xee, 0x89, 0xc7, 0x02,
57 | 0x4e, 0x88, 0x4e, 0x44, 0x0f,
58 | 0x1f, 0xf3, 0x76, 0xbb, 0x23,
59 | 0x17, 0xb2, 0xd6, 0x4d, 0xeb,
60 | 0x7c, 0x83
61 | };
62 |
63 | byte[] nextChainKey =
64 | {
65 | 0x28, 0xe8, 0xf8, 0xfe, 0xe5,
66 | 0x4b, 0x80, 0x1e, 0xef, 0x7c,
67 | 0x5c, 0xfb, 0x2f, 0x17, 0xf3,
68 | 0x2c, 0x7b, 0x33, 0x44, 0x85,
69 | 0xbb, 0xb7, 0x0f, 0xac, 0x6e,
70 | 0xc1, 0x03, 0x42, 0xa2, 0x46,
71 | 0xd1, 0x5d
72 | };
73 |
74 | ChainKey chainKey = new ChainKey(HKDF.createFor(2), seed, 0);
75 |
76 | Assert.AreEqual(seed, chainKey.getKey());
77 | CollectionAssert.AreEqual(messageKey, chainKey.getMessageKeys().getCipherKey());
78 | CollectionAssert.AreEqual(macKey, chainKey.getMessageKeys().getMacKey());
79 | CollectionAssert.AreEqual(nextChainKey, chainKey.getNextChainKey().getKey());
80 | Assert.AreEqual(0, chainKey.getIndex());
81 | Assert.AreEqual(0, chainKey.getMessageKeys().getCounter());
82 | Assert.AreEqual(1, chainKey.getNextChainKey().getIndex());
83 | Assert.AreEqual(1, chainKey.getNextChainKey().getMessageKeys().getCounter());
84 | }
85 |
86 | [TestMethod, TestCategory("libsignal.ratchet")]
87 | public void testChainKeyDerivationV3()
88 | {
89 | byte[] seed =
90 | {
91 | 0x8a, 0xb7, 0x2d, 0x6f, 0x4c,
92 | 0xc5, 0xac, 0x0d, 0x38, 0x7e,
93 | 0xaf, 0x46, 0x33, 0x78, 0xdd,
94 | 0xb2, 0x8e, 0xdd, 0x07, 0x38,
95 | 0x5b, 0x1c, 0xb0, 0x12, 0x50,
96 | 0xc7, 0x15, 0x98, 0x2e, 0x7a,
97 | 0xd4, 0x8f
98 | };
99 |
100 | byte[] messageKey =
101 | {
102 | /* 0x02*/
103 | 0xbf, 0x51, 0xe9, 0xd7,
104 | 0x5e, 0x0e, 0x31, 0x03, 0x10,
105 | 0x51, 0xf8, 0x2a, 0x24, 0x91,
106 | 0xff, 0xc0, 0x84, 0xfa, 0x29,
107 | 0x8b, 0x77, 0x93, 0xbd, 0x9d,
108 | 0xb6, 0x20, 0x05, 0x6f, 0xeb,
109 | 0xf4, 0x52, 0x17
110 | };
111 |
112 | byte[] macKey =
113 | {
114 | 0xc6, 0xc7, 0x7d, 0x6a, 0x73,
115 | 0xa3, 0x54, 0x33, 0x7a, 0x56,
116 | 0x43, 0x5e, 0x34, 0x60, 0x7d,
117 | 0xfe, 0x48, 0xe3, 0xac, 0xe1,
118 | 0x4e, 0x77, 0x31, 0x4d, 0xc6,
119 | 0xab, 0xc1, 0x72, 0xe7, 0xa7,
120 | 0x03, 0x0b
121 | };
122 |
123 | byte[] nextChainKey =
124 | {
125 | 0x28, 0xe8, 0xf8, 0xfe, 0xe5,
126 | 0x4b, 0x80, 0x1e, 0xef, 0x7c,
127 | 0x5c, 0xfb, 0x2f, 0x17, 0xf3,
128 | 0x2c, 0x7b, 0x33, 0x44, 0x85,
129 | 0xbb, 0xb7, 0x0f, 0xac, 0x6e,
130 | 0xc1, 0x03, 0x42, 0xa2, 0x46,
131 | 0xd1, 0x5d
132 | };
133 |
134 | ChainKey chainKey = new ChainKey(HKDF.createFor(3), seed, 0);
135 |
136 | CollectionAssert.AreEqual(seed, chainKey.getKey());
137 | CollectionAssert.AreEqual(messageKey, chainKey.getMessageKeys().getCipherKey());
138 | CollectionAssert.AreEqual(macKey, chainKey.getMessageKeys().getMacKey());
139 | CollectionAssert.AreEqual(nextChainKey, chainKey.getNextChainKey().getKey());
140 | Assert.AreEqual(0, chainKey.getIndex());
141 | Assert.AreEqual(0, chainKey.getMessageKeys().getCounter());
142 | Assert.AreEqual(1, chainKey.getNextChainKey().getIndex());
143 | Assert.AreEqual(1, chainKey.getNextChainKey().getMessageKeys().getCounter());
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/signal-protocol-pcl/ecc/Curve25519.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 smndtrl, langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using libsignal.ecc.impl;
19 | using static PCLCrypto.WinRTCrypto;
20 |
21 | namespace libsignal.ecc
22 | {
23 | ///
24 | /// Choose between various implementations of Curve25519 (native, managed, etc).
25 | ///
26 | public enum Curve25519ProviderType
27 | {
28 | ///
29 | /// Attempt to provide a native implementation. If one is not available, error out (TODO, break apart managed and native implementations in NuGet packages where we can dynamically use what is best based on the current environment).
30 | ///
31 | BEST = 0x05,
32 | ///
33 | /// Explicitly use the native implementation
34 | ///
35 | NATIVE
36 | }
37 |
38 | class Curve25519
39 | {
40 | private static Curve25519 instance;
41 | private ICurve25519Provider provider;
42 |
43 | private Curve25519() { }
44 |
45 | ///
46 | /// Accesses the currently in use Curve25519 provider, according to the type requested.
47 | ///
48 | /// Type of provider requested.
49 | /// Provider
50 | public static Curve25519 getInstance(Curve25519ProviderType type)
51 | {
52 | if (instance == null)
53 | {
54 | instance = new Curve25519();
55 | switch (type)
56 | {
57 | case Curve25519ProviderType.NATIVE:
58 | {
59 | instance.provider = (ICurve25519Provider)new Curve25519NativeProvider();
60 | break;
61 | }
62 | case Curve25519ProviderType.BEST:
63 | {
64 | instance.provider = (ICurve25519Provider)new Curve25519ManagedProvider(
65 | org.whispersystems.curve25519.Curve25519.BEST);
66 | break;
67 | }
68 | }
69 | }
70 | return instance;
71 | }
72 |
73 | ///
74 | /// is backed by a WinRT implementation of curve25519. Returns true for native.
75 | ///
76 | /// True. Backed by a native provider.
77 | public bool isNative()
78 | {
79 | return provider.isNative();
80 | }
81 |
82 | ///
83 | /// Generates a Curve25519 keypair.
84 | ///
85 | /// A randomly generated Curve25519 keypair.
86 | public Curve25519KeyPair generateKeyPair()
87 | {
88 | byte[] random = CryptographicBuffer.GenerateRandom(32);
89 | byte[] privateKey = provider.generatePrivateKey(random);
90 | byte[] publicKey = provider.generatePublicKey(privateKey);
91 |
92 | return new Curve25519KeyPair(publicKey, privateKey);
93 | }
94 |
95 | ///
96 | /// Calculates an ECDH agreement.
97 | ///
98 | /// The Curve25519 (typically remote party's) public key.
99 | /// The Curve25519 (typically yours) private key.
100 | /// A 32-byte shared secret.
101 | public byte[] calculateAgreement(byte[] publicKey, byte[] privateKey)
102 | {
103 | return provider.calculateAgreement(privateKey, publicKey);
104 | }
105 |
106 | ///
107 | /// Calculates a Curve25519 signature.
108 | ///
109 | /// The private Curve25519 key to create the signature with.
110 | /// The message to sign.
111 | /// 64 byte signature
112 | public byte[] calculateSignature(byte[] privateKey, byte[] message)
113 | {
114 |
115 | byte[] random = CryptographicBuffer.GenerateRandom(64);
116 | return provider.calculateSignature(random, privateKey, message);
117 | }
118 |
119 | ///
120 | /// Verify a Curve25519 signature.
121 | ///
122 | /// The Curve25519 public key the signature belongs to.
123 | /// The message that was signed.
124 | /// The signature to verify.
125 | /// Boolean for if valid
126 | public bool verifySignature(byte[] publicKey, byte[] message, byte[] signature)
127 | {
128 | return provider.verifySignature(publicKey, message, signature);
129 | }
130 | }
131 |
132 | ///
133 | /// Curve25519 public and private key stored together.
134 | ///
135 | public class Curve25519KeyPair
136 | {
137 |
138 | private readonly byte[] publicKey;
139 | private readonly byte[] privateKey;
140 |
141 | ///
142 | /// Create a curve 25519 keypair from a public and private keys.
143 | ///
144 | /// 32 byte public key
145 | /// 32 byte private key
146 | public Curve25519KeyPair(byte[] publicKey, byte[] privateKey)
147 | {
148 | this.publicKey = publicKey;
149 | this.privateKey = privateKey;
150 | }
151 |
152 | ///
153 | /// Curve25519 public key
154 | ///
155 | ///
156 | public byte[] getPublicKey()
157 | {
158 | return publicKey;
159 | }
160 |
161 | ///
162 | /// Curve25519 private key
163 | ///
164 | ///
165 | public byte[] getPrivateKey()
166 | {
167 | return privateKey;
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/signal-protocol-tests/fingerprint/NumericFingerprintGeneratorTest.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2016 langboost
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | using System;
19 | using Microsoft.VisualStudio.TestTools.UnitTesting;
20 | using libsignal.ecc;
21 | using libsignal;
22 |
23 | namespace org.whispersystems.libsignal.fingerprint
24 | {
25 | [TestClass]
26 | public class NumericFingerprintGeneratorTest
27 | {
28 | [TestMethod]
29 | public void testMatchingFingerprints()
30 | {
31 | ECKeyPair aliceKeyPair = Curve.generateKeyPair();
32 | ECKeyPair bobKeyPair = Curve.generateKeyPair();
33 |
34 | IdentityKey aliceIdentityKey = new IdentityKey(aliceKeyPair.getPublicKey());
35 | IdentityKey bobIdentityKey = new IdentityKey(bobKeyPair.getPublicKey());
36 |
37 | NumericFingerprintGenerator generator = new NumericFingerprintGenerator(1024);
38 | Fingerprint aliceFingerprint = generator.createFor("+14152222222", aliceIdentityKey,
39 | "+14153333333", bobIdentityKey);
40 |
41 | Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
42 | "+14152222222", aliceIdentityKey);
43 |
44 | Assert.AreEqual(aliceFingerprint.getDisplayableFingerprint().getDisplayText(),
45 | bobFingerprint.getDisplayableFingerprint().getDisplayText());
46 |
47 | Assert.IsTrue(
48 | aliceFingerprint.getScannableFingerprint().compareTo(
49 | bobFingerprint.getScannableFingerprint().getSerialized()));
50 | Assert.IsTrue(
51 | bobFingerprint.getScannableFingerprint().compareTo(
52 | aliceFingerprint.getScannableFingerprint().getSerialized()));
53 |
54 | Assert.AreEqual(aliceFingerprint.getDisplayableFingerprint().getDisplayText().Length, 60);
55 | }
56 |
57 | [TestMethod]
58 | public void testMismatchingFingerprints()
59 | {
60 | ECKeyPair aliceKeyPair = Curve.generateKeyPair();
61 | ECKeyPair bobKeyPair = Curve.generateKeyPair();
62 | ECKeyPair mitmKeyPair = Curve.generateKeyPair();
63 |
64 | IdentityKey aliceIdentityKey = new IdentityKey(aliceKeyPair.getPublicKey());
65 | IdentityKey bobIdentityKey = new IdentityKey(bobKeyPair.getPublicKey());
66 | IdentityKey mitmIdentityKey = new IdentityKey(mitmKeyPair.getPublicKey());
67 |
68 | NumericFingerprintGenerator generator = new NumericFingerprintGenerator(1024);
69 | Fingerprint aliceFingerprint = generator.createFor("+14152222222", aliceIdentityKey,
70 | "+14153333333", mitmIdentityKey);
71 |
72 | Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
73 | "+14152222222", aliceIdentityKey);
74 |
75 | Assert.AreNotEqual(aliceFingerprint.getDisplayableFingerprint().getDisplayText(),
76 | bobFingerprint.getDisplayableFingerprint().getDisplayText());
77 |
78 | Assert.IsFalse(aliceFingerprint.getScannableFingerprint().compareTo(bobFingerprint.getScannableFingerprint().getSerialized()));
79 | Assert.IsFalse(bobFingerprint.getScannableFingerprint().compareTo(aliceFingerprint.getScannableFingerprint().getSerialized()));
80 | }
81 |
82 | [TestMethod]
83 | public void testMismatchingIdentifiers()
84 | {
85 | ECKeyPair aliceKeyPair = Curve.generateKeyPair();
86 | ECKeyPair bobKeyPair = Curve.generateKeyPair();
87 |
88 | IdentityKey aliceIdentityKey = new IdentityKey(aliceKeyPair.getPublicKey());
89 | IdentityKey bobIdentityKey = new IdentityKey(bobKeyPair.getPublicKey());
90 |
91 | NumericFingerprintGenerator generator = new NumericFingerprintGenerator(1024);
92 | Fingerprint aliceFingerprint = generator.createFor("+141512222222", aliceIdentityKey,
93 | "+14153333333", bobIdentityKey);
94 |
95 | Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
96 | "+14152222222", aliceIdentityKey);
97 |
98 | Assert.AreNotEqual(aliceFingerprint.getDisplayableFingerprint().getDisplayText(),
99 | bobFingerprint.getDisplayableFingerprint().getDisplayText());
100 |
101 | try
102 | {
103 | ;
104 | aliceFingerprint.getScannableFingerprint().compareTo(bobFingerprint.getScannableFingerprint().getSerialized());
105 | throw new Exception("Should mismatch!");
106 | }
107 | catch (FingerprintIdentifierMismatchException)
108 | {
109 | // good
110 | }
111 |
112 | try
113 | {
114 | bobFingerprint.getScannableFingerprint().compareTo(aliceFingerprint.getScannableFingerprint().getSerialized());
115 | throw new Exception("Should mismatch!");
116 | }
117 | catch (FingerprintIdentifierMismatchException)
118 | {
119 | // good
120 | }
121 | }
122 |
123 | }
124 | }
--------------------------------------------------------------------------------