Compare commits

...

139 Commits

Author SHA1 Message Date
Moxie Marlinspike
503d1ef452 Bump version to 2.2.0
// FREEBIE
2014-10-24 16:51:15 -07:00
Moxie Marlinspike
9accd92757 Updated language translations.
// FREEBIE
2014-10-24 13:18:52 -07:00
agrajaghh
306da92031 Test case and fix for deferred jobs.
Closes #2033
2014-10-24 12:08:03 -07:00
Moxie Marlinspike
e7b6a852c5 Fix bug caused by 1mod8 compatibility issue.
In the switch from v3, we bind identities in the message MAC
instead of doing the 1mod8 trick.  Since identity keys were
never set as 1mod8, it seemed like we could just remove it.

However, PreKeys are durable.  If an old client upgrades to v3,
it has a bunch of keys that *were* set to 1mod8 floating around.
The Curve25519 donna code re-sets the private key bits on every
operation, which results in a different key, and breaks the output
of an agreement.

So now we don't intentionally generate keys with 1mod8, but we
have to remove the donna code to honor existing 1mod8 keys for
the rest of time.  Trevor is squarely to blame.

// FREEBIE
2014-10-23 17:11:46 -07:00
Moxie Marlinspike
9dfaf19516 Merge pull request #2028 from mcginty/revert-generic-contenttype
revert content-type generics
2014-10-22 19:37:45 -07:00
Jake McGinty
34aece0b43 revert content-type generics
// FREEBIE
2014-10-22 19:37:04 -07:00
Moxie Marlinspike
9768de2d5e Short circuit self-send.
// FREEBIE
2014-10-22 18:28:03 -07:00
Moxie Marlinspike
c3eb0ea9db Check job requirements are satisfied in between retry iterations.
// FREEBIE
2014-10-21 13:50:38 -07:00
Moxie Marlinspike
4cdc0a3e61 Make signed prekey generation happen through the job queue. 2014-10-21 12:47:08 -07:00
Moxie Marlinspike
b568ce70b2 Make delivery receipts work correctly for groups. 2014-10-20 21:06:34 -07:00
Moxie Marlinspike
18b0601990 Dependency updates and gradle housekeeping. 2014-10-20 21:06:09 -07:00
Moxie Marlinspike
b308996885 Switch to using our own JobManager.
// FREEBIE
2014-10-20 19:13:06 -07:00
Moxie Marlinspike
73d896f378 Index shouldn't be unique. 2014-10-20 19:02:42 -07:00
Moxie Marlinspike
36ec1d84a1 Implement delivery receipts.
1) Support a "receipt" push message type.

2) Identify messages by timestamp.

3) Introduce a JobManager to handle the queue for network
   dependent jobs.
2014-10-20 19:02:42 -07:00
Moxie Marlinspike
8d6b9ae43e Incorporate PR feedback. Add license and eliminate duplicate code.
// FREEBIE
2014-10-20 18:38:52 -07:00
Moxie Marlinspike
a95cc0eba2 Switch to byte array.
// FREEBIE
2014-10-20 18:35:45 -07:00
Moxie Marlinspike
58d101ff2e Support for job "group ids."
A job can specify a group id, and jobs with the same group id
will run sequentially.
2014-10-20 18:35:45 -07:00
Moxie Marlinspike
544f06451f Persistent job queue, derivative of android-priority-jobqueue.
// FREEBIE
2014-10-20 18:35:45 -07:00
Moxie Marlinspike
20cf775b1e Fix up routing activity actions. 2014-10-20 14:55:34 -07:00
Moxie Marlinspike
5fcc135f81 Make sure senderkeys encrypt is correctly initialized. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
9a0ed659f7 Initial support for sender keys. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
54612159be Update ed25519 extract and tests 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
355d0be78a Introduce new simultaneous initiate strategy.
1) Fix bugs that prevented decrypt() from being non-transactional
   in some cases.

2) Introduce a new unified storage interface.

3) Transition simultaneous initiate from the "needs refresh"
   strategy to one that uses session state resurrection and
   promotion.
2014-10-20 12:25:40 -07:00
Moxie Marlinspike
73b75a4a27 Fix build. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
c4209a65e3 Don't assert on bad padding. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
9dce376780 Correctly handle formatting when "one time PreKey" is absent. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
07c61394e9 When processing PreKeyBundle, archive current session if it exists. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
3e287f930d Better thread safety for session building <-> use. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
7b1a37bd91 Make registration ID optionally extended. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
2db44a1578 Make generated PreKeyIds exclude both 0 and Medium.MAX_VALUE. 2014-10-20 12:25:40 -07:00
Moxie Marlinspike
006c9aae7b Only remove unsigned prekey if bundled message decrypts properly. 2014-10-20 12:25:39 -07:00
Moxie Marlinspike
b147a90463 This exception is never thrown. 2014-10-20 12:25:39 -07:00
Moxie Marlinspike
741171c49f Switch to CBC mode with a derived IV.
1) Since we're not CPU or space constrained (and are in fact
   padding), and since keystream reuse would be more catastrophic
   than IV reuse without chosen plaintext.
2014-10-20 12:25:39 -07:00
Moxie Marlinspike
c375ed8638 MIPS NDK support. Apparently there are mips devices... 2014-10-20 12:25:39 -07:00
Moxie Marlinspike
238f29c90a Updated to latest of Trevor's ref10-extract 2014-10-20 12:25:39 -07:00
Moxie Marlinspike
084f27a2e8 omg trevor 2014-10-20 12:25:39 -07:00
Moxie Marlinspike
27b5bf54cc Remove 1 mod 8. 2014-10-20 12:25:39 -07:00
Moxie Marlinspike
eda393b11c Minor refactoring and renaming. 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
c330eef7b9 Make PreKeyWhisperMessage decrypt more reliably atomic. 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
1eb3884b7a Update to latest ref10-extract ed25519 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
5ea3b3038e Remove verification tag.
1) Remove verification tag from PreKeyWhisperMessage.

2) Include sender and recipient identity keys in the MAC of
   each WhisperMessage.
2014-10-20 12:25:38 -07:00
Moxie Marlinspike
641ac9aed9 Rename axolotl terminology.
1) ephemeralKey -> ratchetKey

2) Have the caller specify Alice/Bob orientation.

3) Reorganize verification tag.

4) Remove verification tag from key exchange messages, replace
   with signatures in both directions.
2014-10-20 12:25:38 -07:00
Moxie Marlinspike
82bd75fb75 Fix padding problem. 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
c94a7b1eff Make sure "previous counter" is never negative. 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
4caebdcd06 Update tests for new API. 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
b3cece27d6 Update SessionCipher javadocs. 2014-10-20 12:25:38 -07:00
Moxie Marlinspike
819982af7b Rearrange decrypt API.
1) Change SessionBuilder to only establish sessions via
   KeyExchangeMessage and PreKeyBundles.

2) Change SessionCipher to decrypt either WhisperMessage
   or PreKeyWhisperMessage items, automatically building
   a session for the latter.

3) Change SessionCipher to tear down new sessions built
   with PreKeyWhisperMessages if the embedded WhsiperMessage
   fails to decrypt.
2014-10-20 12:25:38 -07:00
Moxie Marlinspike
42cf53e487 Rename "pendingPreKey" to "unacknowledgedPreKeyMessage" 2014-10-20 12:23:15 -07:00
Moxie Marlinspike
e0d2398ca5 Rename InitializationParameters -> AxolotlParameters 2014-10-20 12:23:14 -07:00
Moxie Marlinspike
3f299936bf Only create signed prekey if push registered. 2014-10-20 12:23:14 -07:00
Moxie Marlinspike
540592d71f Upgrade libaxolotl to the latest gradle plugin. 2014-10-20 12:23:14 -07:00
Moxie Marlinspike
5a9e5672d3 Updated README 2014-10-20 12:23:11 -07:00
Moxie Marlinspike
0a23b5fcd5 Added helper method for generating signed PreKeys. 2014-10-20 12:23:10 -07:00
Moxie Marlinspike
f0c22d593f Simplify/clarify internal interfaces and introduce optional types. 2014-10-20 12:23:10 -07:00
Moxie Marlinspike
5f5ddd7c26 Generate SignedPreKey records, improve SignedPreKey cleanup. 2014-10-20 12:23:08 -07:00
Moxie Marlinspike
144f269059 Upgrade curve25519-donna to latest. 2014-10-20 12:17:24 -07:00
Moxie Marlinspike
0d532afd8e Rename 'device key' to 'signed prekey'. 2014-10-20 12:17:24 -07:00
Moxie Marlinspike
07fd17ccda Add padding for push messages.
1) Use 'bit padding.'

1) By default, pad at 160 byte increments.
2014-10-20 12:17:23 -07:00
Moxie Marlinspike
fcaa3f0d73 Simplify HKDF interface. 2014-10-20 12:17:23 -07:00
Moxie Marlinspike
64b40df15b Add V3 support for KeyExchangeMessage case.
1) V3 KeyExchangeMessages can now contain signatures and
   verification tags.
2014-10-20 12:17:23 -07:00
Moxie Marlinspike
77ff9cece8 Add a 'verification tag' to incoming PreKeyWhisperMessage bundles. 2014-10-20 12:17:23 -07:00
Moxie Marlinspike
6326ef73f3 Split HKDF secret derivation and parsing. 2014-10-20 12:17:23 -07:00
Moxie Marlinspike
f29d1e6269 Add support for a compliant HKDF implementation. 2014-10-20 12:17:23 -07:00
Moxie Marlinspike
d6c5e92c9d Collapse RatchetingSessionV2 and RatchetingSessionV3. 2014-10-20 12:17:23 -07:00
Moxie Marlinspike
811479d168 Add first cut of protocol v3 support.
1) Use the new /v2/keys API for storing/retrieving prekey bundles.

2) For sessions built with PreKeyBundle and PreKeyWhisperMessage,
   use a v3 ratcheting session when available.
2014-10-20 12:17:23 -07:00
Moxie Marlinspike
2ed8d333d9 Add ed25519 2014-10-20 12:14:18 -07:00
Moxie Marlinspike
79020cd33c Better FS Locking. 2014-10-20 12:14:18 -07:00
Moxie Marlinspike
c8757c2134 Make helper static. 2014-10-20 12:14:18 -07:00
Moxie Marlinspike
2a65257182 Add serialization helpers for IdentityKeyPair. 2014-10-20 12:14:18 -07:00
Moxie Marlinspike
931605a1c4 Move identity key verification into libaxolotol. With tests. 2014-10-20 12:14:18 -07:00
Moxie Marlinspike
81ae9af2e4 Add "last resort" PreKey generation to KeyHelper. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
e84a0948e9 Update README 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
5239b3e8f8 Add basic README. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
a601c56af1 Collapse SessionRecord, SessionState, and PreKeyRecord interfaces. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
5a3c19fe3e Javadocs, and some minor refactoring. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
af45e5d544 SessionBuilder improvements, more extensive SessionBuilder tests. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
72af8b11c2 Move session construction and KeyExchangeMessage into libaxolotl.
1) Add plain two-way key exchange support libaxolotl by moving
   all the KeyExchangeMessage code there.

2) Move the bulk of KeyExchangeProcessor code to libaxolotl
   for setting up sessions based on retrieved prekeys, received
   prekeybundles, or exchanged key exchange messages.
2014-10-20 12:14:17 -07:00
Moxie Marlinspike
a1db221caf Collapse KeyExchangeMessage and KeyExchangeProcessor interfaces. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
14b8f97de2 Reorganize session store load/store operations. 2014-10-20 12:14:17 -07:00
Moxie Marlinspike
d902c12941 Break core ratchet out into libaxolotol.
1) Break the core cryptography functions out into libaxolotol.

2) The objective for this code is a Java library that isn't
   dependent on any Android functions.  However, while the
   code has been separated from any Android functionality,
   it is still an 'android library project' because of the
   JNI.
2014-10-20 12:10:02 -07:00
Moxie Marlinspike
fe3d91c40c Bump version to 2.1.10
// FREEBIE
2014-10-18 14:19:45 -07:00
Jake McGinty
31167d11dd use wildcard media types, don't crash on preview fail
// FREEBIE
2014-10-17 17:59:56 -07:00
Jake McGinty
6e3751a0c5 sane fallback for media selection on exception
Fixes #1763
// FREEBIE
2014-10-17 17:41:42 -07:00
Jake McGinty
b5941fb3fd actually fallback when mms local params unset
// FREEBIE
2014-10-15 17:14:12 -07:00
Moxie Marlinspike
cfccd367ad Bump version to 2.1.9
// FREEBIE
2014-10-13 19:02:37 -07:00
Moxie Marlinspike
2c8c6a410c Updated language translations.
// FREEBIE
2014-10-13 18:11:20 -07:00
Jake McGinty
2075bba86c switch back to BufferedInputStream
// FREEBIE
2014-10-13 15:38:27 -07:00
Jake McGinty
1d7b47c982 add CM APN list, remove non-MMS APN info
Fixes missing Verizon APN. Overall, smaller DB size but more APN info.
// FREEBIE
2014-10-13 12:33:54 -07:00
Jake McGinty
89fb80fcc5 MmsConnection refactor
- Use Apache HttpClient v4.x, only library that seems to like HTTP proxies
- Remove custom redirect logic in favor of library's

Fixes #1904
// FREEBIE
2014-10-13 12:26:38 -07:00
Moxie Marlinspike
f1d230ce6e Merge pull request #1964 from mcginty/maven-https
use https maven central
2014-10-01 17:16:11 -07:00
Jake McGinty
f51a5aa7eb use https maven central
// FREEBIE
2014-09-30 21:46:13 -07:00
Moxie Marlinspike
cf9c9bbfd7 Bumping version to 2.1.8
// FREEBIE
2014-09-15 19:34:30 -07:00
Jake McGinty
b8a3e87f3d custom redirect logic
// FREEBIE
2014-09-15 14:13:27 -07:00
Moxie Marlinspike
e5bad2746f Merge pull request #1917 from mcginty/mccmnc-crash
don't crash if we can't get an mccmnc
2014-09-15 09:32:28 -07:00
Jake McGinty
7316f17674 don't crash if we can't get an mccmnc
Fixes #1916
// FREEBIE
2014-09-14 17:46:07 -07:00
Veeti Paananen
609e69a801 Show date and year for ancient (> 1 year old) messages
The timestamp shown for very old messages only includes the time without
a date at all. Fix it. FREEBIE.
2014-09-08 15:08:46 -07:00
Moxie Marlinspike
d979593cbb Bump version to 2.1.7 2014-09-06 15:38:55 -07:00
Moxie Marlinspike
0808e00d0e Updated translations.
// FREEBIE
2014-09-06 14:54:32 -07:00
Moxie Marlinspike
2045c828be Merge pull request #1892 from mcginty/bitmap-memory
fallback to imprecise scaling if low memory
2014-09-06 14:53:56 -07:00
Jake McGinty
1b3bd32805 fallback to rough scaling if low memory
// FREEBIE
2014-09-05 13:00:26 -07:00
Moxie Marlinspike
877f2a25a9 Merge pull request #1887 from mcginty/mms-3xx
allow url client to follow redirects before checking response code
2014-08-29 15:23:00 -07:00
Jake McGinty
eb462f0345 force redirects, get inputstream first 2014-08-29 16:34:25 -05:00
Moxie Marlinspike
102b40543d Merge pull request #1886 from mcginty/mms-bad-pdu-logging
enable more verbose PDU parse logging
2014-08-29 14:33:25 -07:00
Jake McGinty
12077c6dad enable more verbose PDU parse logging
// FREEBIE
2014-08-29 16:29:36 -05:00
Jake McGinty
7441c191a7 Format outgoing MMS using SMIL.
// FREEBIE

Closes #1879
2014-08-29 14:29:24 -07:00
Lukas Barth
b355991b0b Remove superfluous repetition
//FREEBIE
2014-08-29 15:33:19 +02:00
Moxie Marlinspike
350eb438c0 Bump version to 2.1.6
// FREEBIE
2014-08-20 16:48:12 -07:00
Moxie Marlinspike
da7bbf4144 Merge pull request #1862 from mcginty/db-localization-ignore
disable localized collators in ApnDB
2014-08-20 14:39:50 -07:00
Jake McGinty
a0842f329f disable localized collators in ApnDB
// FREEBIE
2014-08-20 13:41:59 -07:00
Moxie Marlinspike
567224b4a6 Merge pull request #1861 from mcginty/cursor-leak
prevent another cursor leak
2014-08-20 12:32:24 -07:00
Jake McGinty
9b495d8c83 prevent another cursor leak
// FREEBIE
2014-08-20 12:29:40 -07:00
Moxie Marlinspike
8c55ea09d9 Updated language translations.
// FREEBIE
2014-08-20 10:53:16 -07:00
Moxie Marlinspike
5264ebed67 Avoid leaking cursor.
// FREEBIE

Closes #1838
2014-08-20 10:36:39 -07:00
Jake McGinty
7f51f9fd5b apntool and ApnDatabase
// FREEBIE
2014-08-20 10:36:39 -07:00
Moxie Marlinspike
40495a2261 Merge pull request #1854 from mcginty/content-description
improve some content descriptions
2014-08-19 21:00:20 -07:00
Jake McGinty
a6c1fdd914 improve some content descriptions
// FREEBIE
2014-08-19 19:10:15 -07:00
Jake McGinty
020d61dd6e migrate nav panel items to actionbar
Fixes #1819
Closes #1839
// FREEBIE
2014-08-19 15:20:57 -07:00
Jake McGinty
dee23b266f update support-v4 library
Fixes #1832
// FREEBIE
2014-08-14 23:29:25 -07:00
Moxie Marlinspike
4cf57fbb55 Bump version to 2.1.5
// FREEBIE
2014-08-13 16:47:47 -07:00
Moxie Marlinspike
85da05397a Update language translations.
// FREEBIE
2014-08-13 15:14:08 -07:00
Moxie Marlinspike
f4e9c4a710 Minor MMS cleanup.
// FREEBIE

Closes #1827
2014-08-13 14:40:19 -07:00
Jake McGinty
28e14f47cf use URLConnection for MMS because it's better
// FREEBIE
2014-08-13 12:36:47 -07:00
Jake McGinty
d2edda837a Update Play services and add checksum
Quoting Google employee: "5.0.77 had some serious issues, team didn't want
anyone using it"

// FREEBIE
2014-08-12 20:12:52 -07:00
Jake McGinty
213715a0dc braces around loop, proper spacing
// FREEBIE
2014-08-12 18:23:18 -07:00
Moxie Marlinspike
7b0479ff0f requestRouteToHost of URL rather than MMSC on MMS Download
// FREEBIE

Closes #1806
2014-08-12 17:31:28 -07:00
rymdhund
feabbb33d2 MMS Fixes
1) Respect proxyIfPossible flag and make sure to try all mms APNs

2) Reorder mmsc connection process
2014-08-12 17:30:19 -07:00
rymdhund
d3da409774 Add time to saved media filenames
see #1689
// FREEBIE
2014-08-12 16:09:26 -07:00
Moxie Marlinspike
edb04138c2 Merge pull request #1825 from mcginty/resource-removal
remove old, unused resources
2014-08-12 13:57:49 -07:00
Jake McGinty
8bf537bb09 remove old, unused resources
// FREEBIE
2014-08-12 12:12:08 -07:00
Moxie Marlinspike
b80593b5f5 Merge pull request #1821 from mcginty/mms-npe
prevent MMS notification NPE
2014-08-11 11:08:44 -07:00
Jake McGinty
b61e7839f4 prevent MMS notification NPE
// FREEBIE
2014-08-10 21:15:55 -07:00
Drew Hintz
7b41b1492e Shrink png files using pngout. Saves 608kB. FREEBIE. 2014-08-07 17:56:08 -07:00
Moxie Marlinspike
928b9687ef Bump version to 2.1.4
// FREEBIE
2014-08-04 20:48:02 -07:00
Jake McGinty
306c127803 fix EmojiLRU concurrency exception
// FREEBIE
2014-08-04 16:51:44 -07:00
Jake McGinty
239a11bfd2 fix unused argument
// FREEBIE
2014-08-04 15:30:52 -07:00
Jake McGinty
de63b0dd96 make reminder a custom view, make push reminder
// FREEBIE
2014-08-04 15:25:53 -07:00
Jake McGinty
eb4ac40051 don't use old emoji recents, broken
Fixes #1782
// FREEBIE
2014-08-04 15:23:43 -07:00
Moxie Marlinspike
dda4459e5a Bump version to 2.1.3
// FREEBIE
2014-08-02 23:43:45 -07:00
Moxie Marlinspike
8726dd51be Can't toast on this thread.
// FREEBIE
2014-08-02 23:15:57 -07:00
772 changed files with 49730 additions and 15108 deletions

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.thoughtcrime.securesms"
android:versionCode="74"
android:versionName="2.1.2">
android:versionCode="83"
android:versionName="2.2.0">
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
android:label="Access to TextSecure Secrets"
@@ -37,7 +37,7 @@
android:protectionLevel="signature" />
<uses-permission android:name="org.thoughtcrime.securesms.permission.C2D_MESSAGE" />
<application android:name="org.thoughtcrime.securesms.ApplicationListener"
<application android:name=".ApplicationContext"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/TextSecure.LightTheme">

View File

@@ -0,0 +1,92 @@
package org.thoughtcrime.securesms.service;
import android.test.AndroidTestCase;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.SignedPreKeyEntity;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class PreKeyServiceTest extends AndroidTestCase {
public void testSignedPreKeyRotationNotRegistered() throws IOException {
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(null);
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore,
pushServiceSocket);
cleanTask.run();
verify(pushServiceSocket).getCurrentSignedPreKey();
verifyNoMoreInteractions(signedPreKeyStore);
}
public void testSignedPreKeyEviction() throws Exception {
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class);
when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133);
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity);
final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]);
List<SignedPreKeyRecord> records = new LinkedList<SignedPreKeyRecord>() {{
add(new SignedPreKeyRecord(1, 10, Curve.generateKeyPair(true), new byte[32]));
add(new SignedPreKeyRecord(2, 11, Curve.generateKeyPair(true), new byte[32]));
add(new SignedPreKeyRecord(3, System.currentTimeMillis() - 90, Curve.generateKeyPair(true), new byte[64]));
add(new SignedPreKeyRecord(4, System.currentTimeMillis() - 100, Curve.generateKeyPair(true), new byte[64]));
add(currentRecord);
}};
when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records);
when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord);
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket);
cleanTask.run();
verify(signedPreKeyStore).removeSignedPreKey(eq(1));
verify(signedPreKeyStore).removeSignedPreKey(eq(2));
verify(signedPreKeyStore, times(2)).removeSignedPreKey(anyInt());
}
public void testSignedPreKeyNoEviction() throws Exception {
SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class);
PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class);
SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class);
when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133);
when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity);
final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]);
List<SignedPreKeyRecord> records = new LinkedList<SignedPreKeyRecord>() {{
add(currentRecord);
}};
when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records);
when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord);
PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket);
cleanTask.run();
verify(signedPreKeyStore, never()).removeSignedPreKey(anyInt());
}
}

View File

@@ -0,0 +1,28 @@
package org.thoughtcrime.securesms.service;
import android.content.Intent;
import android.test.InstrumentationTestCase;
import static org.fest.assertions.api.Assertions.*;
public class MmsReceiverTest extends InstrumentationTestCase {
private MmsReceiver mmsReceiver;
public void setUp() throws Exception {
super.setUp();
mmsReceiver = new MmsReceiver(getInstrumentation().getContext());
}
public void tearDown() throws Exception {
}
public void testProcessMalformedData() throws Exception {
Intent intent = new Intent();
intent.setAction(SendReceiveService.RECEIVE_MMS_ACTION);
intent.putExtra("data", new byte[]{0x00});
mmsReceiver.process(null, intent);
}
}

2
apntool/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.db
*.db.gz

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

81
apntool/apntool.py Normal file
View File

@@ -0,0 +1,81 @@
import sys
import argparse
import sqlite3
import gzip
from progressbar import ProgressBar, Counter, Timer
from lxml import etree
parser = argparse.ArgumentParser(prog='apntool', description="""Process Android's apn xml files and drop them into an easily
queryable SQLite db. Tested up to version 9 of their APN file.""")
parser.add_argument('-v', '--version', action='version', version='%(prog)s v1.0')
parser.add_argument('-i', '--input', help='the xml file to parse', default='apns.xml', required=False)
parser.add_argument('-o', '--output', help='the sqlite db output file', default='apns.db', required=False)
parser.add_argument('--quiet', help='do not show progress or verbose instructions', action='store_true', required=False)
parser.add_argument('--no-gzip', help="do not gzip after creation", action='store_true', required=False)
args = parser.parse_args()
try:
connection = sqlite3.connect(args.output)
cursor = connection.cursor()
cursor.execute('SELECT SQLITE_VERSION()')
version = cursor.fetchone()
if not args.quiet:
print("SQLite version: %s" % version)
print("Opening %s" % args.input)
cursor.execute("PRAGMA legacy_file_format=ON")
cursor.execute("PRAGMA journal_mode=DELETE")
cursor.execute("PRAGMA page_size=32768")
cursor.execute("VACUUM")
cursor.execute("DROP TABLE IF EXISTS apns")
cursor.execute("""CREATE TABLE apns(_id INTEGER PRIMARY KEY, mccmnc TEXT, mcc TEXT, mnc TEXT, carrier TEXT, apn TEXT,
mmsc TEXT, port INTEGER, type TEXT, protocol TEXT, bearer TEXT, roaming_protocol TEXT,
carrier_enabled INTEGER, mmsproxy TEXT, mmsport INTEGER, proxy TEXT, mvno_match_data TEXT,
mvno_type TEXT, authtype INTEGER, user TEXT, password TEXT, server TEXT)""")
apns = etree.parse(args.input)
root = apns.getroot()
pbar = ProgressBar(widgets=['Processed: ', Counter(), ' apns (', Timer(), ')'], maxval=len(list(root))).start() if not args.quiet else None
count = 0
for apn in root.iter("apn"):
if apn.get("mmsc") == None:
continue
sqlvars = ["?" for x in apn.attrib.keys()] + ["?"]
mccmnc = "%s%s" % (apn.get("mcc"), apn.get("mnc"))
values = [apn.get(attrib) for attrib in apn.attrib.keys()] + [mccmnc]
keys = apn.attrib.keys() + ["mccmnc"]
cursor.execute("SELECT 1 FROM apns WHERE mccmnc = ? AND apn = ?", [mccmnc, apn.get("apn")])
if cursor.fetchone() == None:
statement = "INSERT INTO apns (%s) VALUES (%s)" % (", ".join(keys), ", ".join(sqlvars))
cursor.execute(statement, values)
count += 1
if not args.quiet:
pbar.update(count)
if not args.quiet:
pbar.finish()
connection.commit()
print("Successfully written to %s" % args.output)
if not args.no_gzip:
gzipped_file = "%s.gz" % (args.output,)
with open(args.output, 'rb') as orig:
with gzip.open(gzipped_file, 'wb') as gzipped:
gzipped.writelines(orig)
print("Successfully gzipped to %s" % gzipped_file)
if not args.quiet:
print("\nTo include this in the distribution, copy it to the project's assets/databases/ directory.")
print("If you support API 10 or lower, you must use the gzipped version to avoid corruption.")
except sqlite3.Error, e:
if connection:
connection.rollback()
print("Error: %s" % e.args[0])
sys.exit(1)
finally:
if connection:
connection.close()

3
apntool/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
argparse>=1.2.1
lxml>=3.3.3
progressbar-latest>=2.4

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 KiB

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 93 KiB

BIN
assets/databases/apns.db Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 KiB

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 629 KiB

After

Width:  |  Height:  |  Size: 547 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

After

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

After

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 KiB

After

Width:  |  Height:  |  Size: 363 KiB

View File

@@ -1,9 +1,11 @@
buildscript {
repositories {
mavenCentral()
maven {
url "https://repo1.maven.org/maven2"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
classpath 'com.android.tools.build:gradle:0.12.2'
classpath files('libs/gradle-witness.jar')
}
}
@@ -12,37 +14,51 @@ apply plugin: 'com.android.application'
apply plugin: 'witness'
repositories {
mavenCentral()
maven {
url "https://repo1.maven.org/maven2"
}
maven {
url "https://raw.github.com/whispersystems/maven/master/gcm-client/releases/"
}
maven {
url "https://raw.github.com/whispersystems/maven/master/gson/releases/"
}
maven {
url "https://raw.github.com/whispersystems/maven/master/smil/releases/"
}
}
dependencies {
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
compile 'com.android.support:support-v4:19.1.0'
compile 'com.android.support:support-v4:20.0.0'
compile 'se.emilsjolander:stickylistheaders:2.2.0'
compile 'com.google.android.gms:play-services:5.0.77'
compile 'com.google.android.gms:play-services:5.0.89'
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
compile 'org.w3c:smil:1.0.0'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
androidTestCompile 'com.squareup:fest-android:1.0.8'
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
compile project(':library')
compile project(':jobqueue')
}
dependencyVerification {
verify = [
'com.actionbarsherlock:actionbarsherlock:5ab04d74101f70024b222e3ff9c87bee151ec43331b4a2134b6cc08cf8565819',
'com.android.support:support-v4:3f40fa7b3a4ead01ce15dce9453b061646e7fe2e7c51cb75ca01ee1e77037f3f',
'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6',
'com.astuetz:pagerslidingtabstrip:f1641396732c7132a7abb837e482e5ee2b0ebb8d10813fc52bbaec2c15c184c2',
'com.google.protobuf:protobuf-java:ad9769a22989e688a46af4d3accc348cc501ced22118033230542bc916e33f0b',
'com.madgag:sc-light-jdk15on:931f39d351429fb96c2f749e7ecb1a256a8ebbf5edca7995c9cc085b94d1841d',
'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab',
'org.whispersystems:gson:08f4f7498455d1539c9233e5aac18e9b1805815ef29221572996508eb512fe51',
'com.actionbarsherlock:actionbarsherlock:5ab04d74101f70024b222e3ff9c87bee151ec43331b4a2134b6cc08cf8565819',
'com.android.support:support-v4:81f2b1c2c94efd5a4ec7fcd97b6cdcd00e87a933905c5c86103c7319eb024572',
'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6',
'com.google.android.gms:play-services:38f326e525830f1d70f60f594ceafcbdf5b312287ddbecd338fd1ed7958a4b1e',
'com.astuetz:pagerslidingtabstrip:f1641396732c7132a7abb837e482e5ee2b0ebb8d10813fc52bbaec2c15c184c2',
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
'com.android.support:support-annotations:1aa96ef0cc4a445bfc2f93ccf762305bc57fa107b12afe9d11f3863ae8a11036',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
'com.madgag:sc-light-jdk15on:931f39d351429fb96c2f749e7ecb1a256a8ebbf5edca7995c9cc085b94d1841d',
'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab',
'org.whispersystems:gson:08f4f7498455d1539c9233e5aac18e9b1805815ef29221572996508eb512fe51',
]
}
@@ -55,6 +71,11 @@ android {
targetSdkVersion 19
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
android {
sourceSets {
main {
@@ -67,10 +88,10 @@ android {
assets.srcDirs = ['assets']
}
androidTest {
java.srcDirs = ['androidTest']
resources.srcDirs = ['androidTest']
aidl.srcDirs = ['androidTest']
renderscript.srcDirs = ['androidTest']
java.srcDirs = ['androidTest/java']
resources.srcDirs = ['androidTest/java']
aidl.srcDirs = ['androidTest/java']
renderscript.srcDirs = ['androidTest/java']
}
}
}
@@ -89,6 +110,12 @@ android {
}
}
tasks.whenTaskAdded { task ->
if (task.name.equals("lint")) {
task.enabled = false
}
}
def Properties props = new Properties()
def propFile = new File('signing.properties')

1
jobqueue/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

19
jobqueue/build.gradle Normal file
View File

@@ -0,0 +1,19 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 20
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "org.whispersystems.jobqueue"
minSdkVersion 9
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}

View File

@@ -0,0 +1,193 @@
package org.whispersystems.jobqueue;
import android.test.AndroidTestCase;
import org.whispersystems.jobqueue.jobs.PersistentTestJob;
import org.whispersystems.jobqueue.jobs.RequirementDeferringTestJob;
import org.whispersystems.jobqueue.jobs.RequirementTestJob;
import org.whispersystems.jobqueue.jobs.TestJob;
import org.whispersystems.jobqueue.persistence.JavaJobSerializer;
import org.whispersystems.jobqueue.util.MockRequirement;
import org.whispersystems.jobqueue.util.MockRequirementProvider;
import org.whispersystems.jobqueue.util.PersistentMockRequirement;
import org.whispersystems.jobqueue.util.PersistentRequirement;
import org.whispersystems.jobqueue.util.PersistentResult;
import org.whispersystems.jobqueue.util.RunnableThrowable;
import java.io.IOException;
public class JobManagerTest extends AndroidTestCase {
public void testTransientJobExecution() throws InterruptedException {
TestJob testJob = new TestJob();
JobManager jobManager = new JobManager(getContext(), "transient-test", null, null, 1);
jobManager.add(testJob);
assertTrue(testJob.isAdded());
assertTrue(testJob.isRan());
}
public void testTransientRequirementJobExecution() throws InterruptedException {
MockRequirementProvider provider = new MockRequirementProvider();
MockRequirement requirement = new MockRequirement(false);
TestJob testJob = new RequirementTestJob(requirement);
JobManager jobManager = new JobManager(getContext(), "transient-requirement-test",
provider, null, 1);
jobManager.add(testJob);
assertTrue(testJob.isAdded());
assertTrue(!testJob.isRan());
requirement.setPresent(true);
provider.fireChange();
assertTrue(testJob.isRan());
}
public void testTransientRequirementDeferringJobExecution() throws InterruptedException {
final Object lock = new Object();
RunnableThrowable waitRunnable = new RunnableThrowable() {
public Boolean shouldThrow = false;
@Override
public void run() throws Exception {
try {
synchronized (lock) {
lock.wait();
if (shouldThrow) {
throw new Exception();
}
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
@Override
public void shouldThrow(Boolean value) {
shouldThrow = value;
}
};
MockRequirementProvider provider = new MockRequirementProvider();
MockRequirement requirement = new MockRequirement(false);
RequirementDeferringTestJob testJob = new RequirementDeferringTestJob(requirement, 5, waitRunnable);
JobManager jobManager = new JobManager(getContext(), "transient-requirement-test",
provider, null, 1);
jobManager.add(testJob);
waitRunnable.shouldThrow(true);
requirement.setPresent(true);
provider.fireChange();
assertTrue(testJob.isRan());
assertTrue(!testJob.isFinished());
synchronized (lock) { lock.notifyAll(); }
assertTrue(!testJob.isFinished());
requirement.setPresent(false);
provider.fireChange();
assertTrue(!testJob.isFinished());
synchronized (lock) { lock.notifyAll(); }
assertTrue(!testJob.isFinished());
waitRunnable.shouldThrow(false);
requirement.setPresent(true);
provider.fireChange();
assertTrue(!testJob.isFinished());
synchronized (lock) { lock.notifyAll(); }
assertTrue(testJob.isFinished());
}
public void testPersistentJobExecuton() throws InterruptedException {
PersistentMockRequirement requirement = new PersistentMockRequirement();
PersistentTestJob testJob = new PersistentTestJob(requirement);
JobManager jobManager = new JobManager(getContext(), "persistent-requirement-test3",
null, new JavaJobSerializer(getContext()), 1);
PersistentResult.getInstance().reset();
PersistentRequirement.getInstance().setPresent(false);
jobManager.add(testJob);
assertTrue(PersistentResult.getInstance().isAdded());
assertTrue(!PersistentResult.getInstance().isRan());
PersistentRequirement.getInstance().setPresent(true);
jobManager = new JobManager(getContext(), "persistent-requirement-test3", null,
new JavaJobSerializer(getContext()), 1);
assertTrue(PersistentResult.getInstance().isRan());
}
public void testEncryptedJobExecuton() throws InterruptedException {
EncryptionKeys keys = new EncryptionKeys(new byte[30]);
PersistentMockRequirement requirement = new PersistentMockRequirement();
PersistentTestJob testJob = new PersistentTestJob(requirement, keys);
JobManager jobManager = new JobManager(getContext(), "persistent-requirement-test4",
null, new JavaJobSerializer(getContext()), 1);
jobManager.setEncryptionKeys(keys);
PersistentResult.getInstance().reset();
PersistentRequirement.getInstance().setPresent(false);
jobManager.add(testJob);
assertTrue(PersistentResult.getInstance().isAdded());
assertTrue(!PersistentResult.getInstance().isRan());
PersistentRequirement.getInstance().setPresent(true);
jobManager = new JobManager(getContext(), "persistent-requirement-test4", null, new JavaJobSerializer(getContext()), 1);
assertTrue(!PersistentResult.getInstance().isRan());
jobManager.setEncryptionKeys(keys);
assertTrue(PersistentResult.getInstance().isRan());
}
public void testGroupIdExecution() throws InterruptedException {
final Object lock = new Object();
Runnable waitRunnable = new Runnable() {
@Override
public void run() {
try {
synchronized (lock) {
lock.wait();
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
};
TestJob testJobOne = new TestJob(JobParameters.newBuilder().withGroupId("foo").create(), waitRunnable);
TestJob testJobTwo = new TestJob(JobParameters.newBuilder().withGroupId("foo").create());
TestJob testJobThree = new TestJob(JobParameters.newBuilder().withGroupId("bar").create());
JobManager jobManager = new JobManager(getContext(), "transient-test", null, null, 3);
jobManager.add(testJobOne);
jobManager.add(testJobTwo);
jobManager.add(testJobThree);
assertTrue(testJobOne.isAdded());
assertTrue(testJobTwo.isAdded());
assertTrue(testJobThree.isAdded());
assertTrue(testJobOne.isRan());
assertTrue(!testJobTwo.isRan());
assertTrue(testJobThree.isRan());
synchronized (lock) {
lock.notifyAll();
}
assertTrue(testJobTwo.isRan());
}
}

View File

@@ -0,0 +1,39 @@
package org.whispersystems.jobqueue.jobs;
import org.whispersystems.jobqueue.EncryptionKeys;
import org.whispersystems.jobqueue.Job;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.Requirement;
import org.whispersystems.jobqueue.util.PersistentResult;
public class PersistentTestJob extends Job {
public PersistentTestJob(Requirement requirement) {
super(JobParameters.newBuilder().withRequirement(requirement).withPersistence().create());
}
public PersistentTestJob(Requirement requirement, EncryptionKeys keys) {
super(JobParameters.newBuilder().withRequirement(requirement).withPersistence().withEncryption(keys).create());
}
@Override
public void onAdded() {
PersistentResult.getInstance().onAdded();;
}
@Override
public void onRun() throws Throwable {
PersistentResult.getInstance().onRun();
}
@Override
public void onCanceled() {
PersistentResult.getInstance().onCanceled();
}
@Override
public boolean onShouldRetry(Throwable throwable) {
return false;
}
}

View File

@@ -0,0 +1,51 @@
package org.whispersystems.jobqueue.jobs;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.Requirement;
import org.whispersystems.jobqueue.util.RunnableThrowable;
import java.io.IOException;
public class RequirementDeferringTestJob extends TestJob {
private final Object FINISHED_LOCK = new Object();
private boolean finished = false;
private RunnableThrowable runnable;
public RequirementDeferringTestJob(Requirement requirement, int retryCount, RunnableThrowable runnable) {
super(JobParameters.newBuilder().withRequirement(requirement).withRetryCount(retryCount).create());
this.runnable = runnable;
}
@Override
public void onRun() throws Throwable {
synchronized (RAN_LOCK) {
this.ran = true;
}
if (runnable != null)
runnable.run();
synchronized (FINISHED_LOCK) {
this.finished = true;
}
}
@Override
public boolean onShouldRetry(Throwable throwable) {
if (throwable instanceof Exception) {
return true;
}
return false;
}
public boolean isFinished() throws InterruptedException {
synchronized (FINISHED_LOCK) {
if (!finished) FINISHED_LOCK.wait(1000);
return finished;
}
}
}

View File

@@ -0,0 +1,12 @@
package org.whispersystems.jobqueue.jobs;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.Requirement;
public class RequirementTestJob extends TestJob {
public RequirementTestJob(Requirement requirement) {
super(JobParameters.newBuilder().withRequirement(requirement).create());
}
}

View File

@@ -0,0 +1,81 @@
package org.whispersystems.jobqueue.jobs;
import org.whispersystems.jobqueue.Job;
import org.whispersystems.jobqueue.JobParameters;
public class TestJob extends Job {
private final Object ADDED_LOCK = new Object();
protected final Object RAN_LOCK = new Object();
private final Object CANCELED_LOCK = new Object();
private boolean added = false;
protected boolean ran = false;
private boolean canceled = false;
private Runnable runnable;
public TestJob() {
this(JobParameters.newBuilder().create());
}
public TestJob(JobParameters parameters) {
super(parameters);
}
public TestJob(JobParameters parameters, Runnable runnable) {
super(parameters);
this.runnable = runnable;
}
@Override
public void onAdded() {
synchronized (ADDED_LOCK) {
this.added = true;
this.ADDED_LOCK.notifyAll();
}
}
@Override
public void onRun() throws Throwable {
synchronized (RAN_LOCK) {
this.ran = true;
}
if (runnable != null)
runnable.run();
}
@Override
public void onCanceled() {
synchronized (CANCELED_LOCK) {
this.canceled = true;
}
}
@Override
public boolean onShouldRetry(Throwable throwable) {
return false;
}
public boolean isAdded() throws InterruptedException {
synchronized (ADDED_LOCK) {
if (!added) ADDED_LOCK.wait(1000);
return added;
}
}
public boolean isRan() throws InterruptedException {
synchronized (RAN_LOCK) {
if (!ran) RAN_LOCK.wait(1000);
return ran;
}
}
public boolean isCanceled() throws InterruptedException {
synchronized (CANCELED_LOCK) {
if (!canceled) CANCELED_LOCK.wait(1000);
return canceled;
}
}
}

View File

@@ -0,0 +1,23 @@
package org.whispersystems.jobqueue.util;
import org.whispersystems.jobqueue.requirements.Requirement;
import java.util.concurrent.atomic.AtomicBoolean;
public class MockRequirement implements Requirement {
private AtomicBoolean present;
public MockRequirement(boolean present) {
this.present = new AtomicBoolean(present);
}
public void setPresent(boolean present) {
this.present.set(present);
}
@Override
public boolean isPresent() {
return present.get();
}
}

View File

@@ -0,0 +1,18 @@
package org.whispersystems.jobqueue.util;
import org.whispersystems.jobqueue.requirements.RequirementListener;
import org.whispersystems.jobqueue.requirements.RequirementProvider;
public class MockRequirementProvider implements RequirementProvider {
private RequirementListener listener;
public void fireChange() {
listener.onRequirementStatusChanged();
}
@Override
public void setListener(RequirementListener listener) {
this.listener = listener;
}
}

View File

@@ -0,0 +1,10 @@
package org.whispersystems.jobqueue.util;
import org.whispersystems.jobqueue.requirements.Requirement;
public class PersistentMockRequirement implements Requirement {
@Override
public boolean isPresent() {
return PersistentRequirement.getInstance().isPresent();
}
}

View File

@@ -0,0 +1,22 @@
package org.whispersystems.jobqueue.util;
import java.util.concurrent.atomic.AtomicBoolean;
public class PersistentRequirement {
private AtomicBoolean present = new AtomicBoolean(false);
private static final PersistentRequirement instance = new PersistentRequirement();
public static PersistentRequirement getInstance() {
return instance;
}
public void setPresent(boolean present) {
this.present.set(present);
}
public boolean isPresent() {
return present.get();
}
}

View File

@@ -0,0 +1,73 @@
package org.whispersystems.jobqueue.util;
public class PersistentResult {
private final Object ADDED_LOCK = new Object();
private final Object RAN_LOCK = new Object();
private final Object CANCELED_LOCK = new Object();
private boolean added = false;
private boolean ran = false;
private boolean canceled = false;
private static final PersistentResult instance = new PersistentResult();
public static PersistentResult getInstance() {
return instance;
}
public void onAdded() {
synchronized (ADDED_LOCK) {
this.added = true;
this.ADDED_LOCK.notifyAll();
}
}
public void onRun() throws Throwable {
synchronized (RAN_LOCK) {
this.ran = true;
}
}
public void onCanceled() {
synchronized (CANCELED_LOCK) {
this.canceled = true;
}
}
public boolean isAdded() throws InterruptedException {
synchronized (ADDED_LOCK) {
if (!added) ADDED_LOCK.wait(1000);
return added;
}
}
public boolean isRan() throws InterruptedException {
synchronized (RAN_LOCK) {
if (!ran) RAN_LOCK.wait(1000);
return ran;
}
}
public boolean isCanceled() throws InterruptedException {
synchronized (CANCELED_LOCK) {
if (!canceled) CANCELED_LOCK.wait(1000);
return canceled;
}
}
public void reset() {
synchronized (ADDED_LOCK) {
this.added = false;
}
synchronized (RAN_LOCK) {
this.ran = false;
}
synchronized (CANCELED_LOCK) {
this.canceled = false;
}
}
}

View File

@@ -0,0 +1,8 @@
package org.whispersystems.jobqueue.util;
public interface RunnableThrowable {
public void run() throws Throwable;
public void shouldThrow(Boolean value);
}

View File

@@ -0,0 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.whispersystems.jobqueue">
<application />
</manifest>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Open Whisper Systems
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,24 +14,17 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
package org.whispersystems.jobqueue;
import android.app.Application;
public class EncryptionKeys {
import org.thoughtcrime.securesms.crypto.PRNGFixes;
private transient final byte[] encoded;
/**
* Will be called once when the TextSecure process is created.
*
* We're using this as an insertion point to patch up the Android PRNG disaster.
*
* @author Moxie Marlinspike
*/
public class ApplicationListener extends Application {
@Override
public void onCreate() {
PRNGFixes.apply();
public EncryptionKeys(byte[] encoded) {
this.encoded = encoded;
}
public byte[] getEncoded() {
return encoded;
}
}

View File

@@ -0,0 +1,89 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue;
import org.whispersystems.jobqueue.requirements.Requirement;
import java.io.Serializable;
import java.util.List;
public abstract class Job implements Serializable {
private final JobParameters parameters;
private transient long persistentId;
private transient int runIteration;
public Job(JobParameters parameters) {
this.parameters = parameters;
}
public List<Requirement> getRequirements() {
return parameters.getRequirements();
}
public boolean isRequirementsMet() {
for (Requirement requirement : parameters.getRequirements()) {
if (!requirement.isPresent()) return false;
}
return true;
}
public String getGroupId() {
return parameters.getGroupId();
}
public boolean isPersistent() {
return parameters.isPersistent();
}
public EncryptionKeys getEncryptionKeys() {
return parameters.getEncryptionKeys();
}
public void setEncryptionKeys(EncryptionKeys keys) {
parameters.setEncryptionKeys(keys);
}
public int getRetryCount() {
return parameters.getRetryCount();
}
public void setPersistentId(long persistentId) {
this.persistentId = persistentId;
}
public long getPersistentId() {
return persistentId;
}
public int getRunIteration() {
return runIteration;
}
public void setRunIteration(int runIteration) {
this.runIteration = runIteration;
}
public abstract void onAdded();
public abstract void onRun() throws Throwable;
public abstract void onCanceled();
public abstract boolean onShouldRetry(Throwable throwable);
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue;
import org.whispersystems.jobqueue.persistence.PersistentStorage;
public class JobConsumer extends Thread {
enum JobResult {
SUCCESS,
FAILURE,
DEFERRED
}
private final JobQueue jobQueue;
private final PersistentStorage persistentStorage;
public JobConsumer(String name, JobQueue jobQueue, PersistentStorage persistentStorage) {
super(name);
this.jobQueue = jobQueue;
this.persistentStorage = persistentStorage;
}
@Override
public void run() {
while (true) {
Job job = jobQueue.getNext();
JobResult result;
if ((result = runJob(job)) != JobResult.DEFERRED) {
if (result == JobResult.FAILURE) {
job.onCanceled();
}
if (job.isPersistent()) {
persistentStorage.remove(job.getPersistentId());
}
} else {
jobQueue.add(job);
}
if (job.getGroupId() != null) {
jobQueue.setGroupIdAvailable(job.getGroupId());
}
}
}
private JobResult runJob(Job job) {
int retryCount = job.getRetryCount();
int runIteration = job.getRunIteration();
for (;runIteration<retryCount;runIteration++) {
try {
job.onRun();
return JobResult.SUCCESS;
} catch (Throwable throwable) {
if (!job.onShouldRetry(throwable)) {
return JobResult.FAILURE;
} else if (!job.isRequirementsMet()) {
job.setRunIteration(runIteration+1);
return JobResult.DEFERRED;
}
}
}
return JobResult.FAILURE;
}
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue;
import android.content.Context;
import android.util.Log;
import org.whispersystems.jobqueue.persistence.JobSerializer;
import org.whispersystems.jobqueue.persistence.PersistentStorage;
import org.whispersystems.jobqueue.requirements.RequirementListener;
import org.whispersystems.jobqueue.requirements.RequirementProvider;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
public class JobManager implements RequirementListener {
private final JobQueue jobQueue = new JobQueue();
private final Executor eventExecutor = Executors.newSingleThreadExecutor();
private final AtomicBoolean hasLoadedEncrypted = new AtomicBoolean(false);
private final PersistentStorage persistentStorage;
public JobManager(Context context, String name,
RequirementProvider requirementProvider,
JobSerializer jobSerializer, int consumers)
{
this.persistentStorage = new PersistentStorage(context, name, jobSerializer);
eventExecutor.execute(new LoadTask(null));
if (requirementProvider != null) {
requirementProvider.setListener(this);
}
for (int i=0;i<consumers;i++) {
new JobConsumer("JobConsumer-" + i, jobQueue, persistentStorage).start();
}
}
public void setEncryptionKeys(EncryptionKeys keys) {
if (hasLoadedEncrypted.compareAndSet(false, true)) {
eventExecutor.execute(new LoadTask(keys));
}
}
public void add(final Job job) {
eventExecutor.execute(new Runnable() {
@Override
public void run() {
try {
if (job.isPersistent()) {
persistentStorage.store(job);
}
job.onAdded();
jobQueue.add(job);
} catch (IOException e) {
Log.w("JobManager", e);
job.onCanceled();
}
}
});
}
@Override
public void onRequirementStatusChanged() {
eventExecutor.execute(new Runnable() {
@Override
public void run() {
jobQueue.onRequirementStatusChanged();
}
});
}
private class LoadTask implements Runnable {
private final EncryptionKeys keys;
public LoadTask(EncryptionKeys keys) {
this.keys = keys;
}
@Override
public void run() {
List<Job> pendingJobs;
if (keys == null) pendingJobs = persistentStorage.getAllUnencrypted();
else pendingJobs = persistentStorage.getAllEncrypted(keys);
jobQueue.addAll(pendingJobs);
}
}
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue;
import org.whispersystems.jobqueue.requirements.Requirement;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
public class JobParameters implements Serializable {
private transient EncryptionKeys encryptionKeys;
private final List<Requirement> requirements;
private final boolean isPersistent;
private final int retryCount;
private final String groupId;
private JobParameters(List<Requirement> requirements,
boolean isPersistent, String groupId,
EncryptionKeys encryptionKeys,
int retryCount)
{
this.requirements = requirements;
this.isPersistent = isPersistent;
this.groupId = groupId;
this.encryptionKeys = encryptionKeys;
this.retryCount = retryCount;
}
public List<Requirement> getRequirements() {
return requirements;
}
public boolean isPersistent() {
return isPersistent;
}
public EncryptionKeys getEncryptionKeys() {
return encryptionKeys;
}
public void setEncryptionKeys(EncryptionKeys encryptionKeys) {
this.encryptionKeys = encryptionKeys;
}
public int getRetryCount() {
return retryCount;
}
public static Builder newBuilder() {
return new Builder();
}
public String getGroupId() {
return groupId;
}
public static class Builder {
private List<Requirement> requirements = new LinkedList<>();
private boolean isPersistent = false;
private EncryptionKeys encryptionKeys = null;
private int retryCount = 100;
private String groupId = null;
public Builder withRequirement(Requirement requirement) {
this.requirements.add(requirement);
return this;
}
public Builder withPersistence() {
this.isPersistent = true;
return this;
}
public Builder withEncryption(EncryptionKeys encryptionKeys) {
this.encryptionKeys = encryptionKeys;
return this;
}
public Builder withRetryCount(int retryCount) {
this.retryCount = retryCount;
return this;
}
public Builder withGroupId(String groupId) {
this.groupId = groupId;
return this;
}
public JobParameters create() {
return new JobParameters(requirements, isPersistent, groupId, encryptionKeys, retryCount);
}
}
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
public class JobQueue {
private final Set<String> activeGroupIds = new HashSet<>();
private final LinkedList<Job> jobQueue = new LinkedList<>();
public synchronized void onRequirementStatusChanged() {
notifyAll();
}
public synchronized void add(Job job) {
jobQueue.add(job);
notifyAll();
}
public synchronized void addAll(List<Job> jobs) {
jobQueue.addAll(jobs);
notifyAll();
}
public synchronized Job getNext() {
try {
Job nextAvailableJob;
while ((nextAvailableJob = getNextAvailableJob()) == null) {
wait();
}
return nextAvailableJob;
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
public synchronized void setGroupIdAvailable(String groupId) {
if (groupId != null) {
activeGroupIds.remove(groupId);
notifyAll();
}
}
private Job getNextAvailableJob() {
if (jobQueue.isEmpty()) return null;
ListIterator<Job> iterator = jobQueue.listIterator();
while (iterator.hasNext()) {
Job job = iterator.next();
if (job.isRequirementsMet() && isGroupIdAvailable(job.getGroupId())) {
iterator.remove();
setGroupIdUnavailable(job.getGroupId());
return job;
}
}
return null;
}
private boolean isGroupIdAvailable(String groupId) {
return groupId == null || !activeGroupIds.contains(groupId);
}
private void setGroupIdUnavailable(String groupId) {
if (groupId != null) {
activeGroupIds.add(groupId);
}
}
}

View File

@@ -1,6 +1,5 @@
/**
* Copyright (C) 2011 Whisper Systems
* Copyright (C) 2013 Open Whisper Systems
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,17 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.textsecure.storage;
package org.whispersystems.jobqueue.dependencies;
import android.content.Context;
public class LocalKeyRecord {
public static void delete(Context context, CanonicalRecipient recipient) {
Record.delete(context, Record.SESSIONS_DIRECTORY, getFileNameForRecipient(recipient));
}
private static String getFileNameForRecipient(CanonicalRecipient recipient) {
return recipient.getRecipientId() + "-local";
}
public interface ContextDependent {
public void setContext(Context context);
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.persistence;
import android.content.Context;
import android.util.Base64;
import org.whispersystems.jobqueue.EncryptionKeys;
import org.whispersystems.jobqueue.Job;
import org.whispersystems.jobqueue.dependencies.ContextDependent;
import org.whispersystems.jobqueue.requirements.Requirement;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class JavaJobSerializer implements JobSerializer {
private final Context context;
public JavaJobSerializer(Context context) {
this.context = context;
}
@Override
public String serialize(Job job) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(job);
return Base64.encodeToString(baos.toByteArray(), Base64.NO_WRAP);
}
@Override
public Job deserialize(EncryptionKeys keys, boolean encrypted, String serialized) throws IOException {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(serialized, Base64.NO_WRAP));
ObjectInputStream ois = new ObjectInputStream(bais);
Job job = (Job)ois.readObject();
if (job instanceof ContextDependent) {
((ContextDependent)job).setContext(context);
}
for (Requirement requirement : job.getRequirements()) {
if (requirement instanceof ContextDependent) {
((ContextDependent)requirement).setContext(context);
}
}
job.setEncryptionKeys(keys);
return job;
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.persistence;
import org.whispersystems.jobqueue.EncryptionKeys;
import org.whispersystems.jobqueue.Job;
import java.io.IOException;
public interface JobSerializer {
public String serialize(Job job) throws IOException;
public Job deserialize(EncryptionKeys keys, boolean encrypted, String serialized) throws IOException;
}

View File

@@ -0,0 +1,128 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.persistence;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import org.whispersystems.jobqueue.EncryptionKeys;
import org.whispersystems.jobqueue.Job;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public class PersistentStorage {
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "queue";
private static final String ID = "_id";
private static final String ITEM = "item";
private static final String ENCRYPTED = "encrypted";
private static final String DATABASE_CREATE = String.format("CREATE TABLE %s (%s INTEGER PRIMARY KEY, %s TEXT NOT NULL, %s INTEGER DEFAULT 0);",
TABLE_NAME, ID, ITEM, ENCRYPTED);
private final DatabaseHelper databaseHelper;
private final JobSerializer jobSerializer;
public PersistentStorage(Context context, String name, JobSerializer serializer) {
this.databaseHelper = new DatabaseHelper(context, "_jobqueue-" + name);
this.jobSerializer = serializer;
}
public void store(Job job) throws IOException {
ContentValues contentValues = new ContentValues();
contentValues.put(ITEM, jobSerializer.serialize(job));
contentValues.put(ENCRYPTED, job.getEncryptionKeys() != null);
long id = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
job.setPersistentId(id);
}
// public List<Job> getAll(EncryptionKeys keys) {
// return getJobs(keys, null);
// }
public List<Job> getAllUnencrypted() {
return getJobs(null, ENCRYPTED + " = 0");
}
public List<Job> getAllEncrypted(EncryptionKeys keys) {
return getJobs(keys, ENCRYPTED + " = 1");
}
private List<Job> getJobs(EncryptionKeys keys, String where) {
List<Job> results = new LinkedList<>();
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, where, null, null, null, ID + " ASC", null);
while (cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
String item = cursor.getString(cursor.getColumnIndexOrThrow(ITEM));
boolean encrypted = cursor.getInt(cursor.getColumnIndexOrThrow(ENCRYPTED)) == 1;
try{
Job job = jobSerializer.deserialize(keys, encrypted, item);
job.setPersistentId(id);
results.add(job);
} catch (IOException e) {
Log.w("PersistentStore", e);
remove(id);
}
}
} finally {
if (cursor != null)
cursor.close();
}
return results;
}
public void remove(long id) {
databaseHelper.getWritableDatabase()
.delete(TABLE_NAME, ID + " = ?", new String[] {String.valueOf(id)});
}
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context, String name) {
super(context, name, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.requirements;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import org.whispersystems.jobqueue.dependencies.ContextDependent;
public class NetworkRequirement implements Requirement, ContextDependent {
private transient Context context;
public NetworkRequirement(Context context) {
this.context = context;
}
public NetworkRequirement() {}
@Override
public boolean isPresent() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnectedOrConnecting();
}
@Override
public void setContext(Context context) {
this.context = context;
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.requirements;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
public class NetworkRequirementProvider implements RequirementProvider {
private RequirementListener listener;
private final NetworkRequirement requirement;
public NetworkRequirementProvider(Context context) {
this.requirement = new NetworkRequirement(context);
context.getApplicationContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (listener == null) {
return;
}
if (requirement.isPresent()) {
listener.onRequirementStatusChanged();
}
}
}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
@Override
public void setListener(RequirementListener listener) {
this.listener = listener;
}
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.requirements;
import java.io.Serializable;
public interface Requirement extends Serializable {
public boolean isPresent();
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.jobqueue.requirements;
public interface RequirementListener {
public void onRequirementStatusChanged();
}

View File

@@ -1,6 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
*
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -10,12 +10,12 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.textsecure.crypto;
package org.whispersystems.jobqueue.requirements;
public interface SerializableKey {
public byte[] serialize();
public interface RequirementProvider {
public void setListener(RequirementListener listener);
}

View File

@@ -0,0 +1,2 @@
<resources>
</resources>

2
libaxolotl/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/obj

85
libaxolotl/README.md Normal file
View File

@@ -0,0 +1,85 @@
# Overview
This is a ratcheting forward secrecy protocol that works in synchronous and asynchronous messaging
environments. The protocol overview is available [here](https://github.com/trevp/axolotl/wiki),
and the details of the wire format are available [here](https://github.com/WhisperSystems/TextSecure/wiki/ProtocolV2).
## PreKeys
This protocol uses a concept called 'PreKeys'. A PreKey is an ECPublicKey and an associated unique
ID which are stored together by a server. PreKeys can also be signed.
At install time, clients generate a single signed PreKey, as well as a large list of unsigned
PreKeys, and transmit all of them to the server.
## Sessions
The axolotl protocol is session-oriented. Clients establish a "session," which is then used for
all subsequent encrypt/decrypt operations. There is no need to ever tear down a session once one
has been established.
Sessions are established in one of three ways:
1. PreKeyBundles. A client that wishes to send a message to a recipient can establish a session by
retrieving a PreKeyBundle for that recipient from the server.
1. PreKeyWhisperMessages. A client can receive a PreKeyWhisperMessage from a recipient and use it
to establish a session.
1. KeyExchangeMessages. Two clients can exchange KeyExchange messages to establish a session.
## State
An established session encapsulates a lot of state between two clients. That state is maintained
in durable records which need to be kept for the life of the session.
State is kept in the following places:
1. Identity State. Clients will need to maintain the state of their own identity key pair, as well
as identity keys received from other clients.
1. PreKey State. Clients will need to maintain the state of their generated PreKeys.
1. Signed PreKey States. Clients will need to maintain the state of their signed PreKeys.
1. Session State. Clients will need to maintain the state of the sessions they have established.
# Using libaxolotl
## Install time
At install time, a libaxolotl client needs to generate its identity keys, registration id, and
prekeys.
IdentityKeyPair identityKeyPair = KeyHelper.generateIdentityKeyPair();
int registrationId = KeyHelper.generateRegistrationId();
List<PreKeyRecord> preKeys = KeyHelper.generatePreKeys(startId, 100);
PreKeyRecord lastResortKey = KeyHelper.generateLastResortKey();
SignedPreKeyRecord signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, 5);
// Store identityKeyPair somewhere durable and safe.
// Store registrationId somewhere durable and safe.
// Store preKeys in PreKeyStore.
// Store signed prekey in SignedPreKeyStore.
## Building a session
A libaxolotl client needs to implement four interfaces: IdentityKeyStore, PreKeyStore,
SignedPreKeyStore, and SessionStore. These will manage loading and storing of identity,
prekeys, signed prekeys, and session state.
Once those are implemented, building a session is fairly straightforward:
SessionStore sessionStore = new MySessionStore();
PreKeyStore preKeyStore = new MyPreKeyStore();
SignedPreKeyStore signedPreKeyStore = new MySignedPreKeyStore();
IdentityKeyStore identityStore = new MyIdentityKeyStore();
// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
identityStore, recipientId, deviceId);
// Build a session with a PreKey retrieved from the server.
sessionBuilder.process(retrievedPreKey);
SessionCipher sessionCipher = new SessionCipher(sessionStore, recipientId, deviceId);
CiphertextMessage message = sessionCipher.encrypt("Hello world!".getBytes("UTF-8"));
deliver(message.serialize());

44
libaxolotl/build.gradle Normal file
View File

@@ -0,0 +1,44 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
}
}
apply plugin: 'com.android.library'
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.protobuf:protobuf-java:2.5.0'
}
android {
compileSdkVersion 19
buildToolsVersion '19.1.0'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
android {
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
}
tasks.whenTaskAdded { task ->
if (task.name.equals("lint")) {
task.enabled = false
}
}

27
libaxolotl/jni/Android.mk Normal file
View File

@@ -0,0 +1,27 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcurve25519-donna
LOCAL_SRC_FILES := curve25519-donna.c
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libcurve25519-ref10
LOCAL_SRC_FILES := $(wildcard ed25519/*.c) $(wildcard ed25519/additions/*.c) $(wildcard ed25519/nacl_sha512/*.c)
LOCAL_C_INCLUDES := ed25519/nacl_includes ed25519/additions ed25519/sha512 ed25519
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libcurve25519
LOCAL_SRC_FILES := curve25519-jni.c
LOCAL_C_INCLUDES := ed25519/additions
LOCAL_STATIC_LIBRARIES := libcurve25519-donna libcurve25519-ref10
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1 @@
APP_ABI := armeabi armeabi-v7a x86 mips

View File

@@ -43,8 +43,7 @@
*
* This is, almost, a clean room reimplementation from the curve25519 paper. It
* uses many of the tricks described therein. Only the crecip function is taken
* from the sample implementation.
*/
* from the sample implementation. */
#include <string.h>
#include <stdint.h>
@@ -63,25 +62,23 @@ typedef int64_t limb;
* significant first. The value of the field element is:
* x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
*
* i.e. the limbs are 26, 25, 26, 25, ... bits wide.
*/
* i.e. the limbs are 26, 25, 26, 25, ... bits wide. */
/* Sum two numbers: output += in */
static void fsum(limb *output, const limb *in) {
unsigned i;
for (i = 0; i < 10; i += 2) {
output[0+i] = (output[0+i] + in[0+i]);
output[1+i] = (output[1+i] + in[1+i]);
output[0+i] = output[0+i] + in[0+i];
output[1+i] = output[1+i] + in[1+i];
}
}
/* Find the difference of two numbers: output = in - output
* (note the order of the arguments!)
*/
* (note the order of the arguments!). */
static void fdifference(limb *output, const limb *in) {
unsigned i;
for (i = 0; i < 10; ++i) {
output[i] = (in[i] - output[i]);
output[i] = in[i] - output[i];
}
}
@@ -97,7 +94,8 @@ static void fscalar_product(limb *output, const limb *in, const limb scalar) {
*
* output must be distinct to both inputs. The inputs are reduced coefficient
* form, the output is not.
*/
*
* output[x] <= 14 * the largest product of the input limbs. */
static void fproduct(limb *output, const limb *in2, const limb *in) {
output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);
output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) +
@@ -201,9 +199,15 @@ static void fproduct(limb *output, const limb *in2, const limb *in) {
output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);
}
/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */
/* Reduce a long form to a short form by taking the input mod 2^255 - 19.
*
* On entry: |output[i]| < 14*2^54
* On exit: |output[0..8]| < 280*2^54 */
static void freduce_degree(limb *output) {
/* Each of these shifts and adds ends up multiplying the value by 19. */
/* Each of these shifts and adds ends up multiplying the value by 19.
*
* For output[0..8], the absolute entry value is < 14*2^54 and we add, at
* most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */
output[8] += output[18] << 4;
output[8] += output[18] << 1;
output[8] += output[18];
@@ -237,11 +241,13 @@ static void freduce_degree(limb *output) {
#error "This code only works on a two's complement system"
#endif
/* return v / 2^26, using only shifts and adds. */
/* return v / 2^26, using only shifts and adds.
*
* On entry: v can take any value. */
static inline limb
div_by_2_26(const limb v)
{
/* High word of v; no shift needed*/
/* High word of v; no shift needed. */
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
/* Set to all 1s if v was negative; else set to 0s. */
const int32_t sign = ((int32_t) highword) >> 31;
@@ -251,7 +257,9 @@ div_by_2_26(const limb v)
return (v + roundoff) >> 26;
}
/* return v / (2^25), using only shifts and adds. */
/* return v / (2^25), using only shifts and adds.
*
* On entry: v can take any value. */
static inline limb
div_by_2_25(const limb v)
{
@@ -265,6 +273,9 @@ div_by_2_25(const limb v)
return (v + roundoff) >> 25;
}
/* return v / (2^25), using only shifts and adds.
*
* On entry: v can take any value. */
static inline s32
div_s32_by_2_25(const s32 v)
{
@@ -274,8 +285,7 @@ div_s32_by_2_25(const s32 v)
/* Reduce all coefficients of the short form input so that |x| < 2^26.
*
* On entry: |output[i]| < 2^62
*/
* On entry: |output[i]| < 280*2^54 */
static void freduce_coefficients(limb *output) {
unsigned i;
@@ -283,56 +293,65 @@ static void freduce_coefficients(limb *output) {
for (i = 0; i < 10; i += 2) {
limb over = div_by_2_26(output[i]);
/* The entry condition (that |output[i]| < 280*2^54) means that over is, at
* most, 280*2^28 in the first iteration of this loop. This is added to the
* next limb and we can approximate the resulting bound of that limb by
* 281*2^54. */
output[i] -= over << 26;
output[i+1] += over;
/* For the first iteration, |output[i+1]| < 281*2^54, thus |over| <
* 281*2^29. When this is added to the next limb, the resulting bound can
* be approximated as 281*2^54.
*
* For subsequent iterations of the loop, 281*2^54 remains a conservative
* bound and no overflow occurs. */
over = div_by_2_25(output[i+1]);
output[i+1] -= over << 25;
output[i+2] += over;
}
/* Now |output[10]| < 2 ^ 38 and all other coefficients are reduced. */
/* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */
output[0] += output[10] << 4;
output[0] += output[10] << 1;
output[0] += output[10];
output[10] = 0;
/* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19 * 2^38
* So |over| will be no more than 77825 */
/* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29
* So |over| will be no more than 2^16. */
{
limb over = div_by_2_26(output[0]);
output[0] -= over << 26;
output[1] += over;
}
/* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 77825
* So |over| will be no more than 1. */
{
/* output[1] fits in 32 bits, so we can use div_s32_by_2_25 here. */
s32 over32 = div_s32_by_2_25((s32) output[1]);
output[1] -= over32 << 25;
output[2] += over32;
}
/* Finally, output[0,1,3..9] are reduced, and output[2] is "nearly reduced":
* we have |output[2]| <= 2^26. This is good enough for all of our math,
* but it will require an extra freduce_coefficients before fcontract. */
/* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The
* bound on |output[1]| is sufficient to meet our needs. */
}
/* A helpful wrapper around fproduct: output = in * in2.
*
* output must be distinct to both inputs. The output is reduced degree and
* reduced coefficient.
*/
* On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.
*
* output must be distinct to both inputs. The output is reduced degree
* (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */
static void
fmul(limb *output, const limb *in, const limb *in2) {
limb t[19];
fproduct(t, in, in2);
/* |t[i]| < 14*2^54 */
freduce_degree(t);
freduce_coefficients(t);
/* |t[i]| < 2^26 */
memcpy(output, t, sizeof(limb) * 10);
}
/* Square a number: output = in**2
*
* output must be distinct from the input. The inputs are reduced coefficient
* form, the output is not.
*
* output[x] <= 14 * the largest product of the input limbs. */
static void fsquare_inner(limb *output, const limb *in) {
output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);
output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);
@@ -391,12 +410,23 @@ static void fsquare_inner(limb *output, const limb *in) {
output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);
}
/* fsquare sets output = in^2.
*
* On entry: The |in| argument is in reduced coefficients form and |in[i]| <
* 2^27.
*
* On exit: The |output| argument is in reduced coefficients form (indeed, one
* need only provide storage for 10 limbs) and |out[i]| < 2^26. */
static void
fsquare(limb *output, const limb *in) {
limb t[19];
fsquare_inner(t, in);
/* |t[i]| < 14*2^54 because the largest product of two limbs will be <
* 2^(27+27) and fsquare_inner adds together, at most, 14 of those
* products. */
freduce_degree(t);
freduce_coefficients(t);
/* |t[i]| < 2^26 */
memcpy(output, t, sizeof(limb) * 10);
}
@@ -417,7 +447,7 @@ fexpand(limb *output, const u8 *input) {
F(6, 19, 1, 0x3ffffff);
F(7, 22, 3, 0x1ffffff);
F(8, 25, 4, 0x3ffffff);
F(9, 28, 6, 0x3ffffff);
F(9, 28, 6, 0x1ffffff);
#undef F
}
@@ -425,60 +455,143 @@ fexpand(limb *output, const u8 *input) {
#error "This code only works when >> does sign-extension on negative numbers"
#endif
/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */
static s32 s32_eq(s32 a, s32 b) {
a = ~(a ^ b);
a &= a << 16;
a &= a << 8;
a &= a << 4;
a &= a << 2;
a &= a << 1;
return a >> 31;
}
/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are
* both non-negative. */
static s32 s32_gte(s32 a, s32 b) {
a -= b;
/* a >= 0 iff a >= b. */
return ~(a >> 31);
}
/* Take a fully reduced polynomial form number and contract it into a
* little-endian, 32-byte array
*/
* little-endian, 32-byte array.
*
* On entry: |input_limbs[i]| < 2^26 */
static void
fcontract(u8 *output, limb *input) {
fcontract(u8 *output, limb *input_limbs) {
int i;
int j;
s32 input[10];
s32 mask;
/* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */
for (i = 0; i < 10; i++) {
input[i] = input_limbs[i];
}
for (j = 0; j < 2; ++j) {
for (i = 0; i < 9; ++i) {
if ((i & 1) == 1) {
/* This calculation is a time-invariant way to make input[i] positive
by borrowing from the next-larger limb.
*/
const s32 mask = (s32)(input[i]) >> 31;
const s32 carry = -(((s32)(input[i]) & mask) >> 25);
input[i] = (s32)(input[i]) + (carry << 25);
input[i+1] = (s32)(input[i+1]) - carry;
/* This calculation is a time-invariant way to make input[i]
* non-negative by borrowing from the next-larger limb. */
const s32 mask = input[i] >> 31;
const s32 carry = -((input[i] & mask) >> 25);
input[i] = input[i] + (carry << 25);
input[i+1] = input[i+1] - carry;
} else {
const s32 mask = (s32)(input[i]) >> 31;
const s32 carry = -(((s32)(input[i]) & mask) >> 26);
input[i] = (s32)(input[i]) + (carry << 26);
input[i+1] = (s32)(input[i+1]) - carry;
const s32 mask = input[i] >> 31;
const s32 carry = -((input[i] & mask) >> 26);
input[i] = input[i] + (carry << 26);
input[i+1] = input[i+1] - carry;
}
}
/* There's no greater limb for input[9] to borrow from, but we can multiply
* by 19 and borrow from input[0], which is valid mod 2^255-19. */
{
const s32 mask = (s32)(input[9]) >> 31;
const s32 carry = -(((s32)(input[9]) & mask) >> 25);
input[9] = (s32)(input[9]) + (carry << 25);
input[0] = (s32)(input[0]) - (carry * 19);
const s32 mask = input[9] >> 31;
const s32 carry = -((input[9] & mask) >> 25);
input[9] = input[9] + (carry << 25);
input[0] = input[0] - (carry * 19);
}
/* After the first iteration, input[1..9] are non-negative and fit within
* 25 or 26 bits, depending on position. However, input[0] may be
* negative. */
}
/* The first borrow-propagation pass above ended with every limb
except (possibly) input[0] non-negative.
Since each input limb except input[0] is decreased by at most 1
by a borrow-propagation pass, the second borrow-propagation pass
could only have wrapped around to decrease input[0] again if the
first pass left input[0] negative *and* input[1] through input[9]
were all zero. In that case, input[1] is now 2^25 - 1, and this
last borrow-propagation step will leave input[1] non-negative.
*/
If input[0] was negative after the first pass, then it was because of a
carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most,
one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19.
In the second pass, each limb is decreased by at most one. Thus the second
borrow-propagation pass could only have wrapped around to decrease
input[0] again if the first pass left input[0] negative *and* input[1]
through input[9] were all zero. In that case, input[1] is now 2^25 - 1,
and this last borrow-propagation step will leave input[1] non-negative. */
{
const s32 mask = (s32)(input[0]) >> 31;
const s32 carry = -(((s32)(input[0]) & mask) >> 26);
input[0] = (s32)(input[0]) + (carry << 26);
input[1] = (s32)(input[1]) - carry;
const s32 mask = input[0] >> 31;
const s32 carry = -((input[0] & mask) >> 26);
input[0] = input[0] + (carry << 26);
input[1] = input[1] - carry;
}
/* Both passes through the above loop, plus the last 0-to-1 step, are
necessary: if input[9] is -1 and input[0] through input[8] are 0,
negative values will remain in the array until the end.
*/
/* All input[i] are now non-negative. However, there might be values between
* 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */
for (j = 0; j < 2; j++) {
for (i = 0; i < 9; i++) {
if ((i & 1) == 1) {
const s32 carry = input[i] >> 25;
input[i] &= 0x1ffffff;
input[i+1] += carry;
} else {
const s32 carry = input[i] >> 26;
input[i] &= 0x3ffffff;
input[i+1] += carry;
}
}
{
const s32 carry = input[9] >> 25;
input[9] &= 0x1ffffff;
input[0] += 19*carry;
}
}
/* If the first carry-chain pass, just above, ended up with a carry from
* input[9], and that caused input[0] to be out-of-bounds, then input[0] was
* < 2^26 + 2*19, because the carry was, at most, two.
*
* If the second pass carried from input[9] again then input[0] is < 2*19 and
* the input[9] -> input[0] carry didn't push input[0] out of bounds. */
/* It still remains the case that input might be between 2^255-19 and 2^255.
* In this case, input[1..9] must take their maximum value and input[0] must
* be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
mask = s32_gte(input[0], 0x3ffffed);
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
mask &= s32_eq(input[i], 0x1ffffff);
} else {
mask &= s32_eq(input[i], 0x3ffffff);
}
}
/* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus
* this conditionally subtracts 2^255-19. */
input[0] -= mask & 0x3ffffed;
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
input[i] -= mask & 0x1ffffff;
} else {
input[i] -= mask & 0x3ffffff;
}
}
input[1] <<= 2;
input[2] <<= 3;
@@ -516,7 +629,9 @@ fcontract(u8 *output, limb *input) {
* x z: short form, destroyed
* xprime zprime: short form, destroyed
* qmqp: short form, preserved
*/
*
* On entry and exit, the absolute value of the limbs of all inputs and outputs
* are < 2^26. */
static void fmonty(limb *x2, limb *z2, /* output 2Q */
limb *x3, limb *z3, /* output Q + Q' */
limb *x, limb *z, /* input Q */
@@ -527,43 +642,69 @@ static void fmonty(limb *x2, limb *z2, /* output 2Q */
memcpy(origx, x, 10 * sizeof(limb));
fsum(x, z);
fdifference(z, origx); // does x - z
/* |x[i]| < 2^27 */
fdifference(z, origx); /* does x - z */
/* |z[i]| < 2^27 */
memcpy(origxprime, xprime, sizeof(limb) * 10);
fsum(xprime, zprime);
/* |xprime[i]| < 2^27 */
fdifference(zprime, origxprime);
/* |zprime[i]| < 2^27 */
fproduct(xxprime, xprime, z);
/* |xxprime[i]| < 14*2^54: the largest product of two limbs will be <
* 2^(27+27) and fproduct adds together, at most, 14 of those products.
* (Approximating that to 2^58 doesn't work out.) */
fproduct(zzprime, x, zprime);
/* |zzprime[i]| < 14*2^54 */
freduce_degree(xxprime);
freduce_coefficients(xxprime);
/* |xxprime[i]| < 2^26 */
freduce_degree(zzprime);
freduce_coefficients(zzprime);
/* |zzprime[i]| < 2^26 */
memcpy(origxprime, xxprime, sizeof(limb) * 10);
fsum(xxprime, zzprime);
/* |xxprime[i]| < 2^27 */
fdifference(zzprime, origxprime);
/* |zzprime[i]| < 2^27 */
fsquare(xxxprime, xxprime);
/* |xxxprime[i]| < 2^26 */
fsquare(zzzprime, zzprime);
/* |zzzprime[i]| < 2^26 */
fproduct(zzprime, zzzprime, qmqp);
/* |zzprime[i]| < 14*2^52 */
freduce_degree(zzprime);
freduce_coefficients(zzprime);
/* |zzprime[i]| < 2^26 */
memcpy(x3, xxxprime, sizeof(limb) * 10);
memcpy(z3, zzprime, sizeof(limb) * 10);
fsquare(xx, x);
/* |xx[i]| < 2^26 */
fsquare(zz, z);
/* |zz[i]| < 2^26 */
fproduct(x2, xx, zz);
/* |x2[i]| < 14*2^52 */
freduce_degree(x2);
freduce_coefficients(x2);
/* |x2[i]| < 2^26 */
fdifference(zz, xx); // does zz = xx - zz
/* |zz[i]| < 2^27 */
memset(zzz + 10, 0, sizeof(limb) * 9);
fscalar_product(zzz, zz, 121665);
/* |zzz[i]| < 2^(27+17) */
/* No need to call freduce_degree here:
fscalar_product doesn't increase the degree of its input. */
freduce_coefficients(zzz);
/* |zzz[i]| < 2^26 */
fsum(zzz, xx);
/* |zzz[i]| < 2^27 */
fproduct(z2, zz, zzz);
/* |z2[i]| < 14*2^(26+27) */
freduce_degree(z2);
freduce_coefficients(z2);
/* |z2|i| < 2^26 */
}
/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
@@ -574,8 +715,7 @@ static void fmonty(limb *x2, limb *z2, /* output 2Q */
* wrong results. Also, the two limb arrays must be in reduced-coefficient,
* reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
* and all all values in a[0..9],b[0..9] must have magnitude less than
* INT32_MAX.
*/
* INT32_MAX. */
static void
swap_conditional(limb a[19], limb b[19], limb iswap) {
unsigned i;
@@ -592,8 +732,7 @@ swap_conditional(limb a[19], limb b[19], limb iswap) {
*
* resultx/resultz: the x coordinate of the resulting curve point (short form)
* n: a little endian, 32-byte number
* q: a point of the curve (short form)
*/
* q: a point of the curve (short form) */
static void
cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
@@ -711,8 +850,6 @@ crecip(limb *out, const limb *z) {
/* 2^255 - 21 */ fmul(out,t1,z11);
}
int curve25519_donna(u8 *, const u8 *, const u8 *);
int
curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
limb bp[10], x[10], z[11], zmone[10];
@@ -720,12 +857,14 @@ curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
int i;
for (i = 0; i < 32; ++i) e[i] = secret[i];
// e[0] &= 248;
// e[31] &= 127;
// e[31] |= 64;
fexpand(bp, basepoint);
cmult(x, z, e, bp);
crecip(zmone, z);
fmul(z, x, zmone);
freduce_coefficients(z);
fcontract(mypublic, z);
return 0;
}

View File

@@ -0,0 +1,109 @@
/**
* Copyright (C) 2013-2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdint.h>
#include <jni.h>
#include "curve25519-donna.h"
#include "curve_sigs.h"
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_generatePrivateKey
(JNIEnv *env, jclass clazz, jbyteArray random)
{
uint8_t* privateKey = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
privateKey[0] &= 248;
privateKey[31] &= 127;
privateKey[31] |= 64;
(*env)->ReleaseByteArrayElements(env, random, privateKey, 0);
return random;
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_generatePublicKey
(JNIEnv *env, jclass clazz, jbyteArray privateKey)
{
static const uint8_t basepoint[32] = {9};
jbyteArray publicKey = (*env)->NewByteArray(env, 32);
uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
curve25519_donna(publicKeyBytes, privateKeyBytes, basepoint);
(*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
return publicKey;
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateAgreement
(JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey)
{
jbyteArray sharedKey = (*env)->NewByteArray(env, 32);
uint8_t* sharedKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, sharedKey, 0);
uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
curve25519_donna(sharedKeyBytes, privateKeyBytes, publicKeyBytes);
(*env)->ReleaseByteArrayElements(env, sharedKey, sharedKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
return sharedKey;
}
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateSignature
(JNIEnv *env, jclass clazz, jbyteArray random, jbyteArray privateKey, jbyteArray message)
{
jbyteArray signature = (*env)->NewByteArray(env, 64);
uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 0);
uint8_t* randomBytes = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
uint8_t* messageBytes = (uint8_t*)(*env)->GetByteArrayElements(env, message, 0);
jsize messageLength = (*env)->GetArrayLength(env, message);
int result = curve25519_sign(signatureBytes, privateKeyBytes, messageBytes, messageLength, randomBytes);
(*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0);
(*env)->ReleaseByteArrayElements(env, random, randomBytes, 0);
(*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, message, messageBytes, 0);
if (result == 0) return signature;
else (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/AssertionError"), "Signature failed!");
}
JNIEXPORT jboolean JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_verifySignature
(JNIEnv *env, jclass clazz, jbyteArray publicKey, jbyteArray message, jbyteArray signature)
{
uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 0);
uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
uint8_t* messageBytes = (uint8_t*)(*env)->GetByteArrayElements(env, message, 0);
jsize messageLength = (*env)->GetArrayLength(env, message);
jboolean result = (curve25519_verify(signatureBytes, publicKeyBytes, messageBytes, messageLength) == 0);
(*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0);
(*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
(*env)->ReleaseByteArrayElements(env, message, messageBytes, 0);
return result;
}

View File

@@ -0,0 +1,44 @@
#include <string.h>
#include "compare.h"
/* Const-time comparison from SUPERCOP, but here it's only used for
signature verification, so doesn't need to be const-time. But
copied the nacl version anyways. */
int crypto_verify_32_ref(const unsigned char *x, const unsigned char *y)
{
unsigned int differentbits = 0;
#define F(i) differentbits |= x[i] ^ y[i];
F(0)
F(1)
F(2)
F(3)
F(4)
F(5)
F(6)
F(7)
F(8)
F(9)
F(10)
F(11)
F(12)
F(13)
F(14)
F(15)
F(16)
F(17)
F(18)
F(19)
F(20)
F(21)
F(22)
F(23)
F(24)
F(25)
F(26)
F(27)
F(28)
F(29)
F(30)
F(31)
return (1 & ((differentbits - 1) >> 8)) - 1;
}

View File

@@ -0,0 +1,6 @@
#ifndef __COMPARE_H__
#define __COMPARE_H__
int crypto_verify_32_ref(const unsigned char *b1, const unsigned char *b2);
#endif

View File

@@ -0,0 +1,6 @@
#ifndef crypto_hash_sha512_H
#define crypto_hash_sha512_H
extern int crypto_hash_sha512(unsigned char *,const unsigned char *,unsigned long long);
#endif

View File

@@ -0,0 +1,116 @@
#include <string.h>
#include "ge.h"
#include "curve_sigs.h"
#include "crypto_sign.h"
void curve25519_keygen(unsigned char* curve25519_pubkey_out,
const unsigned char* curve25519_privkey_in)
{
ge_p3 ed; /* Ed25519 pubkey point */
fe ed_y, ed_y_plus_one, one_minus_ed_y, inv_one_minus_ed_y;
fe mont_x;
/* Perform a fixed-base multiplication of the Edwards base point,
(which is efficient due to precalculated tables), then convert
to the Curve25519 montgomery-format public key. In particular,
convert Curve25519's "montgomery" x-coordinate into an Ed25519
"edwards" y-coordinate:
mont_x = (ed_y + 1) / (1 - ed_y)
with projective coordinates:
mont_x = (ed_y + ed_z) / (ed_z - ed_y)
NOTE: ed_y=1 is converted to mont_x=0 since fe_invert is mod-exp
*/
ge_scalarmult_base(&ed, curve25519_privkey_in);
fe_add(ed_y_plus_one, ed.Y, ed.Z);
fe_sub(one_minus_ed_y, ed.Z, ed.Y);
fe_invert(inv_one_minus_ed_y, one_minus_ed_y);
fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y);
fe_tobytes(curve25519_pubkey_out, mont_x);
}
int curve25519_sign(unsigned char* signature_out,
const unsigned char* curve25519_privkey,
const unsigned char* msg, const unsigned long msg_len,
const unsigned char* random)
{
ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */
unsigned char ed_pubkey[32]; /* Ed25519 encoded pubkey */
unsigned char sigbuf[MAX_MSG_LEN + 128]; /* working buffer */
unsigned char sign_bit = 0;
if (msg_len > MAX_MSG_LEN) {
memset(signature_out, 0, 64);
return -1;
}
/* Convert the Curve25519 privkey to an Ed25519 public key */
ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey);
ge_p3_tobytes(ed_pubkey, &ed_pubkey_point);
sign_bit = ed_pubkey[31] & 0x80;
/* Perform an Ed25519 signature with explicit private key */
crypto_sign_modified(sigbuf, msg, msg_len, curve25519_privkey,
ed_pubkey, random);
memmove(signature_out, sigbuf, 64);
/* Encode the sign bit into signature (in unused high bit of S) */
signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */
signature_out[63] |= sign_bit;
return 0;
}
int curve25519_verify(const unsigned char* signature,
const unsigned char* curve25519_pubkey,
const unsigned char* msg, const unsigned long msg_len)
{
fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one;
fe one;
fe ed_y;
unsigned char ed_pubkey[32];
unsigned long long some_retval;
unsigned char verifybuf[MAX_MSG_LEN + 64]; /* working buffer */
unsigned char verifybuf2[MAX_MSG_LEN + 64]; /* working buffer #2 */
if (msg_len > MAX_MSG_LEN) {
return -1;
}
/* Convert the Curve25519 public key into an Ed25519 public key. In
particular, convert Curve25519's "montgomery" x-coordinate into an
Ed25519 "edwards" y-coordinate:
ed_y = (mont_x - 1) / (mont_x + 1)
NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp
Then move the sign bit into the pubkey from the signature.
*/
fe_frombytes(mont_x, curve25519_pubkey);
fe_1(one);
fe_sub(mont_x_minus_one, mont_x, one);
fe_add(mont_x_plus_one, mont_x, one);
fe_invert(inv_mont_x_plus_one, mont_x_plus_one);
fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one);
fe_tobytes(ed_pubkey, ed_y);
/* Copy the sign bit, and remove it from signature */
ed_pubkey[31] &= 0x7F; /* bit should be zero already, but just in case */
ed_pubkey[31] |= (signature[63] & 0x80);
memmove(verifybuf, signature, 64);
verifybuf[63] &= 0x7F;
memmove(verifybuf+64, msg, msg_len);
/* Then perform a normal Ed25519 verification, return 0 on success */
/* The below call has a strange API: */
/* verifybuf = R || S || message */
/* verifybuf2 = internal to next call gets a copy of verifybuf, S gets
replaced with pubkey for hashing, then the whole thing gets zeroized
(if bad sig), or contains a copy of msg (good sig) */
return crypto_sign_open(verifybuf2, &some_retval, verifybuf, 64 + msg_len, ed_pubkey);
}

View File

@@ -0,0 +1,50 @@
#ifndef __CURVE_SIGS_H__
#define __CURVE_SIGS_H__
#define MAX_MSG_LEN 256
void curve25519_keygen(unsigned char* curve25519_pubkey_out, /* 32 bytes */
const unsigned char* curve25519_privkey_in); /* 32 bytes */
/* returns 0 on success */
int curve25519_sign(unsigned char* signature_out, /* 64 bytes */
const unsigned char* curve25519_privkey, /* 32 bytes */
const unsigned char* msg, const unsigned long msg_len,
const unsigned char* random); /* 64 bytes */
/* returns 0 on success */
int curve25519_verify(const unsigned char* signature, /* 64 bytes */
const unsigned char* curve25519_pubkey, /* 32 bytes */
const unsigned char* msg, const unsigned long msg_len);
/* helper function - modified version of crypto_sign() to use
explicit private key. In particular:
sk : private key
pk : public key
msg : message
prefix : 0xFE || [0xFF]*31
random : 64 bytes random
q : main subgroup order
The prefix is chosen to distinguish the two SHA512 uses below, since
prefix is an invalid encoding for R (it would encode a "field element"
of 2^255 - 2). 0xFF*32 is set aside for use in ECDH protocols, which
is why the first byte here ix 0xFE.
sig_nonce = SHA512(prefix || sk || msg || random) % q
R = g^sig_nonce
M = SHA512(R || pk || m)
S = sig_nonce + (m * sk)
signature = (R || S)
*/
int crypto_sign_modified(
unsigned char *sm,
const unsigned char *m,unsigned long long mlen,
const unsigned char *sk, /* Curve/Ed25519 private key */
const unsigned char *pk, /* Ed25519 public key */
const unsigned char *random /* 64 bytes random to hash into nonce */
);
#endif

View File

@@ -0,0 +1,47 @@
#include <string.h>
#include "crypto_sign.h"
#include "crypto_hash_sha512.h"
#include "ge.h"
#include "sc.h"
#include "zeroize.h"
/* NEW: Compare to pristine crypto_sign()
Uses explicit private key for nonce derivation and as scalar,
instead of deriving both from a master key.
*/
int crypto_sign_modified(
unsigned char *sm,
const unsigned char *m,unsigned long long mlen,
const unsigned char *sk, const unsigned char* pk,
const unsigned char* random
)
{
unsigned char nonce[64];
unsigned char hram[64];
ge_p3 R;
int count=0;
memmove(sm + 64,m,mlen);
memmove(sm + 32,sk,32); /* NEW: Use privkey directly for nonce derivation */
/* NEW : add prefix to separate hash uses - see .h */
sm[0] = 0xFE;
for (count = 1; count < 32; count++)
sm[count] = 0xFF;
/* NEW: add suffix of random data */
memmove(sm + mlen + 64, random, 64);
crypto_hash_sha512(nonce,sm,mlen + 128);
memmove(sm + 32,pk,32);
sc_reduce(nonce);
ge_scalarmult_base(&R,nonce);
ge_p3_tobytes(sm,&R);
crypto_hash_sha512(hram,sm,mlen + 64);
sc_reduce(hram);
sc_muladd(sm + 32,hram,sk,nonce); /* NEW: Use privkey directly */
return 0;
}

View File

@@ -0,0 +1,17 @@
#include "zeroize.h"
void zeroize(unsigned char* b, size_t len)
{
size_t count = 0;
unsigned long retval = 0;
volatile unsigned char *p = b;
for (count = 0; count < len; count++)
p[count] = 0;
}
void zeroize_stack()
{
unsigned char m[ZEROIZE_STACK_SIZE];
zeroize(m, sizeof m);
}

View File

@@ -0,0 +1,12 @@
#ifndef __ZEROIZE_H__
#define __ZEROIZE_H__
#include <stdlib.h>
#define ZEROIZE_STACK_SIZE 2048
void zeroize(unsigned char* b, size_t len);
void zeroize_stack();
#endif

View File

@@ -0,0 +1,4 @@
#define CRYPTO_SECRETKEYBYTES 64
#define CRYPTO_PUBLICKEYBYTES 32
#define CRYPTO_BYTES 64
#define CRYPTO_DETERMINISTIC 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
{
{ 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 },
{ -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 },
{ -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 },
},
{
{ 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 },
{ 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 },
{ 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 },
},
{
{ 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 },
{ 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 },
{ 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 },
},
{
{ 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 },
{ -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 },
{ 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 },
},
{
{ -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 },
{ -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 },
{ 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 },
},
{
{ -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 },
{ 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 },
{ 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 },
},
{
{ -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 },
{ -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 },
{ -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 },
},
{
{ -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 },
{ -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 },
{ -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 },
},

View File

@@ -0,0 +1 @@
-10913610,13857413,-15372611,6949391,114729,-8787816,-6275908,-3247719,-18696448,-12055116

View File

@@ -0,0 +1 @@
-21827239,-5839606,-30745221,13898782,229458,15978800,-12551817,-6495438,29715968,9444199

View File

@@ -0,0 +1,56 @@
#ifndef FE_H
#define FE_H
#include "crypto_int32.h"
typedef crypto_int32 fe[10];
/*
fe means field element.
Here the field is \Z/(2^255-19).
An element t, entries t[0]...t[9], represents the integer
t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
Bounds on each t[i] vary depending on context.
*/
#define fe_frombytes crypto_sign_ed25519_ref10_fe_frombytes
#define fe_tobytes crypto_sign_ed25519_ref10_fe_tobytes
#define fe_copy crypto_sign_ed25519_ref10_fe_copy
#define fe_isnonzero crypto_sign_ed25519_ref10_fe_isnonzero
#define fe_isnegative crypto_sign_ed25519_ref10_fe_isnegative
#define fe_0 crypto_sign_ed25519_ref10_fe_0
#define fe_1 crypto_sign_ed25519_ref10_fe_1
#define fe_cswap crypto_sign_ed25519_ref10_fe_cswap
#define fe_cmov crypto_sign_ed25519_ref10_fe_cmov
#define fe_add crypto_sign_ed25519_ref10_fe_add
#define fe_sub crypto_sign_ed25519_ref10_fe_sub
#define fe_neg crypto_sign_ed25519_ref10_fe_neg
#define fe_mul crypto_sign_ed25519_ref10_fe_mul
#define fe_sq crypto_sign_ed25519_ref10_fe_sq
#define fe_sq2 crypto_sign_ed25519_ref10_fe_sq2
#define fe_mul121666 crypto_sign_ed25519_ref10_fe_mul121666
#define fe_invert crypto_sign_ed25519_ref10_fe_invert
#define fe_pow22523 crypto_sign_ed25519_ref10_fe_pow22523
extern void fe_frombytes(fe,const unsigned char *);
extern void fe_tobytes(unsigned char *,const fe);
extern void fe_copy(fe,const fe);
extern int fe_isnonzero(const fe);
extern int fe_isnegative(const fe);
extern void fe_0(fe);
extern void fe_1(fe);
extern void fe_cswap(fe,fe,unsigned int);
extern void fe_cmov(fe,const fe,unsigned int);
extern void fe_add(fe,const fe,const fe);
extern void fe_sub(fe,const fe,const fe);
extern void fe_neg(fe,const fe);
extern void fe_mul(fe,const fe,const fe);
extern void fe_sq(fe,const fe);
extern void fe_sq2(fe,const fe);
extern void fe_mul121666(fe,const fe);
extern void fe_invert(fe,const fe);
extern void fe_pow22523(fe,const fe);
#endif

View File

@@ -0,0 +1,19 @@
#include "fe.h"
/*
h = 0
*/
void fe_0(fe h)
{
h[0] = 0;
h[1] = 0;
h[2] = 0;
h[3] = 0;
h[4] = 0;
h[5] = 0;
h[6] = 0;
h[7] = 0;
h[8] = 0;
h[9] = 0;
}

View File

@@ -0,0 +1,19 @@
#include "fe.h"
/*
h = 1
*/
void fe_1(fe h)
{
h[0] = 1;
h[1] = 0;
h[2] = 0;
h[3] = 0;
h[4] = 0;
h[5] = 0;
h[6] = 0;
h[7] = 0;
h[8] = 0;
h[9] = 0;
}

View File

@@ -0,0 +1,57 @@
#include "fe.h"
/*
h = f + g
Can overlap h with f or g.
Preconditions:
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
Postconditions:
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
void fe_add(fe h,const fe f,const fe g)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 g0 = g[0];
crypto_int32 g1 = g[1];
crypto_int32 g2 = g[2];
crypto_int32 g3 = g[3];
crypto_int32 g4 = g[4];
crypto_int32 g5 = g[5];
crypto_int32 g6 = g[6];
crypto_int32 g7 = g[7];
crypto_int32 g8 = g[8];
crypto_int32 g9 = g[9];
crypto_int32 h0 = f0 + g0;
crypto_int32 h1 = f1 + g1;
crypto_int32 h2 = f2 + g2;
crypto_int32 h3 = f3 + g3;
crypto_int32 h4 = f4 + g4;
crypto_int32 h5 = f5 + g5;
crypto_int32 h6 = f6 + g6;
crypto_int32 h7 = f7 + g7;
crypto_int32 h8 = f8 + g8;
crypto_int32 h9 = f9 + g9;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,63 @@
#include "fe.h"
/*
Replace (f,g) with (g,g) if b == 1;
replace (f,g) with (f,g) if b == 0.
Preconditions: b in {0,1}.
*/
void fe_cmov(fe f,const fe g,unsigned int b)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 g0 = g[0];
crypto_int32 g1 = g[1];
crypto_int32 g2 = g[2];
crypto_int32 g3 = g[3];
crypto_int32 g4 = g[4];
crypto_int32 g5 = g[5];
crypto_int32 g6 = g[6];
crypto_int32 g7 = g[7];
crypto_int32 g8 = g[8];
crypto_int32 g9 = g[9];
crypto_int32 x0 = f0 ^ g0;
crypto_int32 x1 = f1 ^ g1;
crypto_int32 x2 = f2 ^ g2;
crypto_int32 x3 = f3 ^ g3;
crypto_int32 x4 = f4 ^ g4;
crypto_int32 x5 = f5 ^ g5;
crypto_int32 x6 = f6 ^ g6;
crypto_int32 x7 = f7 ^ g7;
crypto_int32 x8 = f8 ^ g8;
crypto_int32 x9 = f9 ^ g9;
b = -b;
x0 &= b;
x1 &= b;
x2 &= b;
x3 &= b;
x4 &= b;
x5 &= b;
x6 &= b;
x7 &= b;
x8 &= b;
x9 &= b;
f[0] = f0 ^ x0;
f[1] = f1 ^ x1;
f[2] = f2 ^ x2;
f[3] = f3 ^ x3;
f[4] = f4 ^ x4;
f[5] = f5 ^ x5;
f[6] = f6 ^ x6;
f[7] = f7 ^ x7;
f[8] = f8 ^ x8;
f[9] = f9 ^ x9;
}

View File

@@ -0,0 +1,29 @@
#include "fe.h"
/*
h = f
*/
void fe_copy(fe h,const fe f)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
h[0] = f0;
h[1] = f1;
h[2] = f2;
h[3] = f3;
h[4] = f4;
h[5] = f5;
h[6] = f6;
h[7] = f7;
h[8] = f8;
h[9] = f9;
}

View File

@@ -0,0 +1,73 @@
#include "fe.h"
#include "crypto_int64.h"
#include "crypto_uint64.h"
static crypto_uint64 load_3(const unsigned char *in)
{
crypto_uint64 result;
result = (crypto_uint64) in[0];
result |= ((crypto_uint64) in[1]) << 8;
result |= ((crypto_uint64) in[2]) << 16;
return result;
}
static crypto_uint64 load_4(const unsigned char *in)
{
crypto_uint64 result;
result = (crypto_uint64) in[0];
result |= ((crypto_uint64) in[1]) << 8;
result |= ((crypto_uint64) in[2]) << 16;
result |= ((crypto_uint64) in[3]) << 24;
return result;
}
/*
Ignores top bit of h.
*/
void fe_frombytes(fe h,const unsigned char *s)
{
crypto_int64 h0 = load_4(s);
crypto_int64 h1 = load_3(s + 4) << 6;
crypto_int64 h2 = load_3(s + 7) << 5;
crypto_int64 h3 = load_3(s + 10) << 3;
crypto_int64 h4 = load_3(s + 13) << 2;
crypto_int64 h5 = load_4(s + 16);
crypto_int64 h6 = load_3(s + 20) << 7;
crypto_int64 h7 = load_3(s + 23) << 5;
crypto_int64 h8 = load_3(s + 26) << 4;
crypto_int64 h9 = (load_3(s + 29) & 8388607) << 2;
crypto_int64 carry0;
crypto_int64 carry1;
crypto_int64 carry2;
crypto_int64 carry3;
crypto_int64 carry4;
crypto_int64 carry5;
crypto_int64 carry6;
crypto_int64 carry7;
crypto_int64 carry8;
crypto_int64 carry9;
carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,14 @@
#include "fe.h"
void fe_invert(fe out,const fe z)
{
fe t0;
fe t1;
fe t2;
fe t3;
int i;
#include "pow225521.h"
return;
}

View File

@@ -0,0 +1,16 @@
#include "fe.h"
/*
return 1 if f is in {1,3,5,...,q-2}
return 0 if f is in {0,2,4,...,q-1}
Preconditions:
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
int fe_isnegative(const fe f)
{
unsigned char s[32];
fe_tobytes(s,f);
return s[0] & 1;
}

View File

@@ -0,0 +1,19 @@
#include "fe.h"
#include "crypto_verify_32.h"
/*
return 1 if f == 0
return 0 if f != 0
Preconditions:
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
static const unsigned char zero[32];
int fe_isnonzero(const fe f)
{
unsigned char s[32];
fe_tobytes(s,f);
return crypto_verify_32(s,zero);
}

View File

@@ -0,0 +1,253 @@
#include "fe.h"
#include "crypto_int64.h"
/*
h = f * g
Can overlap h with f or g.
Preconditions:
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
Postconditions:
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*/
/*
Notes on implementation strategy:
Using schoolbook multiplication.
Karatsuba would save a little in some cost models.
Most multiplications by 2 and 19 are 32-bit precomputations;
cheaper than 64-bit postcomputations.
There is one remaining multiplication by 19 in the carry chain;
one *19 precomputation can be merged into this,
but the resulting data flow is considerably less clean.
There are 12 carries below.
10 of them are 2-way parallelizable and vectorizable.
Can get away with 11 carries, but then data flow is much deeper.
With tighter constraints on inputs can squeeze carries into int32.
*/
void fe_mul(fe h,const fe f,const fe g)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 g0 = g[0];
crypto_int32 g1 = g[1];
crypto_int32 g2 = g[2];
crypto_int32 g3 = g[3];
crypto_int32 g4 = g[4];
crypto_int32 g5 = g[5];
crypto_int32 g6 = g[6];
crypto_int32 g7 = g[7];
crypto_int32 g8 = g[8];
crypto_int32 g9 = g[9];
crypto_int32 g1_19 = 19 * g1; /* 1.959375*2^29 */
crypto_int32 g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
crypto_int32 g3_19 = 19 * g3;
crypto_int32 g4_19 = 19 * g4;
crypto_int32 g5_19 = 19 * g5;
crypto_int32 g6_19 = 19 * g6;
crypto_int32 g7_19 = 19 * g7;
crypto_int32 g8_19 = 19 * g8;
crypto_int32 g9_19 = 19 * g9;
crypto_int32 f1_2 = 2 * f1;
crypto_int32 f3_2 = 2 * f3;
crypto_int32 f5_2 = 2 * f5;
crypto_int32 f7_2 = 2 * f7;
crypto_int32 f9_2 = 2 * f9;
crypto_int64 f0g0 = f0 * (crypto_int64) g0;
crypto_int64 f0g1 = f0 * (crypto_int64) g1;
crypto_int64 f0g2 = f0 * (crypto_int64) g2;
crypto_int64 f0g3 = f0 * (crypto_int64) g3;
crypto_int64 f0g4 = f0 * (crypto_int64) g4;
crypto_int64 f0g5 = f0 * (crypto_int64) g5;
crypto_int64 f0g6 = f0 * (crypto_int64) g6;
crypto_int64 f0g7 = f0 * (crypto_int64) g7;
crypto_int64 f0g8 = f0 * (crypto_int64) g8;
crypto_int64 f0g9 = f0 * (crypto_int64) g9;
crypto_int64 f1g0 = f1 * (crypto_int64) g0;
crypto_int64 f1g1_2 = f1_2 * (crypto_int64) g1;
crypto_int64 f1g2 = f1 * (crypto_int64) g2;
crypto_int64 f1g3_2 = f1_2 * (crypto_int64) g3;
crypto_int64 f1g4 = f1 * (crypto_int64) g4;
crypto_int64 f1g5_2 = f1_2 * (crypto_int64) g5;
crypto_int64 f1g6 = f1 * (crypto_int64) g6;
crypto_int64 f1g7_2 = f1_2 * (crypto_int64) g7;
crypto_int64 f1g8 = f1 * (crypto_int64) g8;
crypto_int64 f1g9_38 = f1_2 * (crypto_int64) g9_19;
crypto_int64 f2g0 = f2 * (crypto_int64) g0;
crypto_int64 f2g1 = f2 * (crypto_int64) g1;
crypto_int64 f2g2 = f2 * (crypto_int64) g2;
crypto_int64 f2g3 = f2 * (crypto_int64) g3;
crypto_int64 f2g4 = f2 * (crypto_int64) g4;
crypto_int64 f2g5 = f2 * (crypto_int64) g5;
crypto_int64 f2g6 = f2 * (crypto_int64) g6;
crypto_int64 f2g7 = f2 * (crypto_int64) g7;
crypto_int64 f2g8_19 = f2 * (crypto_int64) g8_19;
crypto_int64 f2g9_19 = f2 * (crypto_int64) g9_19;
crypto_int64 f3g0 = f3 * (crypto_int64) g0;
crypto_int64 f3g1_2 = f3_2 * (crypto_int64) g1;
crypto_int64 f3g2 = f3 * (crypto_int64) g2;
crypto_int64 f3g3_2 = f3_2 * (crypto_int64) g3;
crypto_int64 f3g4 = f3 * (crypto_int64) g4;
crypto_int64 f3g5_2 = f3_2 * (crypto_int64) g5;
crypto_int64 f3g6 = f3 * (crypto_int64) g6;
crypto_int64 f3g7_38 = f3_2 * (crypto_int64) g7_19;
crypto_int64 f3g8_19 = f3 * (crypto_int64) g8_19;
crypto_int64 f3g9_38 = f3_2 * (crypto_int64) g9_19;
crypto_int64 f4g0 = f4 * (crypto_int64) g0;
crypto_int64 f4g1 = f4 * (crypto_int64) g1;
crypto_int64 f4g2 = f4 * (crypto_int64) g2;
crypto_int64 f4g3 = f4 * (crypto_int64) g3;
crypto_int64 f4g4 = f4 * (crypto_int64) g4;
crypto_int64 f4g5 = f4 * (crypto_int64) g5;
crypto_int64 f4g6_19 = f4 * (crypto_int64) g6_19;
crypto_int64 f4g7_19 = f4 * (crypto_int64) g7_19;
crypto_int64 f4g8_19 = f4 * (crypto_int64) g8_19;
crypto_int64 f4g9_19 = f4 * (crypto_int64) g9_19;
crypto_int64 f5g0 = f5 * (crypto_int64) g0;
crypto_int64 f5g1_2 = f5_2 * (crypto_int64) g1;
crypto_int64 f5g2 = f5 * (crypto_int64) g2;
crypto_int64 f5g3_2 = f5_2 * (crypto_int64) g3;
crypto_int64 f5g4 = f5 * (crypto_int64) g4;
crypto_int64 f5g5_38 = f5_2 * (crypto_int64) g5_19;
crypto_int64 f5g6_19 = f5 * (crypto_int64) g6_19;
crypto_int64 f5g7_38 = f5_2 * (crypto_int64) g7_19;
crypto_int64 f5g8_19 = f5 * (crypto_int64) g8_19;
crypto_int64 f5g9_38 = f5_2 * (crypto_int64) g9_19;
crypto_int64 f6g0 = f6 * (crypto_int64) g0;
crypto_int64 f6g1 = f6 * (crypto_int64) g1;
crypto_int64 f6g2 = f6 * (crypto_int64) g2;
crypto_int64 f6g3 = f6 * (crypto_int64) g3;
crypto_int64 f6g4_19 = f6 * (crypto_int64) g4_19;
crypto_int64 f6g5_19 = f6 * (crypto_int64) g5_19;
crypto_int64 f6g6_19 = f6 * (crypto_int64) g6_19;
crypto_int64 f6g7_19 = f6 * (crypto_int64) g7_19;
crypto_int64 f6g8_19 = f6 * (crypto_int64) g8_19;
crypto_int64 f6g9_19 = f6 * (crypto_int64) g9_19;
crypto_int64 f7g0 = f7 * (crypto_int64) g0;
crypto_int64 f7g1_2 = f7_2 * (crypto_int64) g1;
crypto_int64 f7g2 = f7 * (crypto_int64) g2;
crypto_int64 f7g3_38 = f7_2 * (crypto_int64) g3_19;
crypto_int64 f7g4_19 = f7 * (crypto_int64) g4_19;
crypto_int64 f7g5_38 = f7_2 * (crypto_int64) g5_19;
crypto_int64 f7g6_19 = f7 * (crypto_int64) g6_19;
crypto_int64 f7g7_38 = f7_2 * (crypto_int64) g7_19;
crypto_int64 f7g8_19 = f7 * (crypto_int64) g8_19;
crypto_int64 f7g9_38 = f7_2 * (crypto_int64) g9_19;
crypto_int64 f8g0 = f8 * (crypto_int64) g0;
crypto_int64 f8g1 = f8 * (crypto_int64) g1;
crypto_int64 f8g2_19 = f8 * (crypto_int64) g2_19;
crypto_int64 f8g3_19 = f8 * (crypto_int64) g3_19;
crypto_int64 f8g4_19 = f8 * (crypto_int64) g4_19;
crypto_int64 f8g5_19 = f8 * (crypto_int64) g5_19;
crypto_int64 f8g6_19 = f8 * (crypto_int64) g6_19;
crypto_int64 f8g7_19 = f8 * (crypto_int64) g7_19;
crypto_int64 f8g8_19 = f8 * (crypto_int64) g8_19;
crypto_int64 f8g9_19 = f8 * (crypto_int64) g9_19;
crypto_int64 f9g0 = f9 * (crypto_int64) g0;
crypto_int64 f9g1_38 = f9_2 * (crypto_int64) g1_19;
crypto_int64 f9g2_19 = f9 * (crypto_int64) g2_19;
crypto_int64 f9g3_38 = f9_2 * (crypto_int64) g3_19;
crypto_int64 f9g4_19 = f9 * (crypto_int64) g4_19;
crypto_int64 f9g5_38 = f9_2 * (crypto_int64) g5_19;
crypto_int64 f9g6_19 = f9 * (crypto_int64) g6_19;
crypto_int64 f9g7_38 = f9_2 * (crypto_int64) g7_19;
crypto_int64 f9g8_19 = f9 * (crypto_int64) g8_19;
crypto_int64 f9g9_38 = f9_2 * (crypto_int64) g9_19;
crypto_int64 h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38;
crypto_int64 h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19;
crypto_int64 h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38;
crypto_int64 h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19;
crypto_int64 h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38;
crypto_int64 h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19;
crypto_int64 h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38;
crypto_int64 h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19;
crypto_int64 h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38;
crypto_int64 h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ;
crypto_int64 carry0;
crypto_int64 carry1;
crypto_int64 carry2;
crypto_int64 carry3;
crypto_int64 carry4;
crypto_int64 carry5;
crypto_int64 carry6;
crypto_int64 carry7;
crypto_int64 carry8;
crypto_int64 carry9;
/*
|h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
|h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9
*/
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
/* |h0| <= 2^25 */
/* |h4| <= 2^25 */
/* |h1| <= 1.71*2^59 */
/* |h5| <= 1.71*2^59 */
carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
/* |h1| <= 2^24; from now on fits into int32 */
/* |h5| <= 2^24; from now on fits into int32 */
/* |h2| <= 1.41*2^60 */
/* |h6| <= 1.41*2^60 */
carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
/* |h2| <= 2^25; from now on fits into int32 unchanged */
/* |h6| <= 2^25; from now on fits into int32 unchanged */
/* |h3| <= 1.71*2^59 */
/* |h7| <= 1.71*2^59 */
carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
/* |h3| <= 2^24; from now on fits into int32 unchanged */
/* |h7| <= 2^24; from now on fits into int32 unchanged */
/* |h4| <= 1.72*2^34 */
/* |h8| <= 1.41*2^60 */
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
/* |h4| <= 2^25; from now on fits into int32 unchanged */
/* |h8| <= 2^25; from now on fits into int32 unchanged */
/* |h5| <= 1.01*2^24 */
/* |h9| <= 1.71*2^59 */
carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
/* |h9| <= 2^24; from now on fits into int32 unchanged */
/* |h0| <= 1.1*2^39 */
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
/* |h0| <= 2^25; from now on fits into int32 unchanged */
/* |h1| <= 1.01*2^24 */
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,45 @@
#include "fe.h"
/*
h = -f
Preconditions:
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
Postconditions:
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*/
void fe_neg(fe h,const fe f)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 h0 = -f0;
crypto_int32 h1 = -f1;
crypto_int32 h2 = -f2;
crypto_int32 h3 = -f3;
crypto_int32 h4 = -f4;
crypto_int32 h5 = -f5;
crypto_int32 h6 = -f6;
crypto_int32 h7 = -f7;
crypto_int32 h8 = -f8;
crypto_int32 h9 = -f9;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,13 @@
#include "fe.h"
void fe_pow22523(fe out,const fe z)
{
fe t0;
fe t1;
fe t2;
int i;
#include "pow22523.h"
return;
}

View File

@@ -0,0 +1,149 @@
#include "fe.h"
#include "crypto_int64.h"
/*
h = f * f
Can overlap h with f.
Preconditions:
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
Postconditions:
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*/
/*
See fe_mul.c for discussion of implementation strategy.
*/
void fe_sq(fe h,const fe f)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 f0_2 = 2 * f0;
crypto_int32 f1_2 = 2 * f1;
crypto_int32 f2_2 = 2 * f2;
crypto_int32 f3_2 = 2 * f3;
crypto_int32 f4_2 = 2 * f4;
crypto_int32 f5_2 = 2 * f5;
crypto_int32 f6_2 = 2 * f6;
crypto_int32 f7_2 = 2 * f7;
crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */
crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */
crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */
crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */
crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */
crypto_int64 f0f0 = f0 * (crypto_int64) f0;
crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1;
crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2;
crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3;
crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4;
crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5;
crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6;
crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7;
crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8;
crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9;
crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1;
crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2;
crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2;
crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4;
crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2;
crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6;
crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2;
crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8;
crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38;
crypto_int64 f2f2 = f2 * (crypto_int64) f2;
crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3;
crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4;
crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5;
crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6;
crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7;
crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19;
crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38;
crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3;
crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4;
crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2;
crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6;
crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38;
crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19;
crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38;
crypto_int64 f4f4 = f4 * (crypto_int64) f4;
crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5;
crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19;
crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38;
crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19;
crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38;
crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38;
crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19;
crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38;
crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19;
crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38;
crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19;
crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38;
crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19;
crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38;
crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38;
crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19;
crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38;
crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19;
crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38;
crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38;
crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
crypto_int64 carry0;
crypto_int64 carry1;
crypto_int64 carry2;
crypto_int64 carry3;
crypto_int64 carry4;
crypto_int64 carry5;
crypto_int64 carry6;
crypto_int64 carry7;
crypto_int64 carry8;
crypto_int64 carry9;
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,160 @@
#include "fe.h"
#include "crypto_int64.h"
/*
h = 2 * f * f
Can overlap h with f.
Preconditions:
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
Postconditions:
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*/
/*
See fe_mul.c for discussion of implementation strategy.
*/
void fe_sq2(fe h,const fe f)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 f0_2 = 2 * f0;
crypto_int32 f1_2 = 2 * f1;
crypto_int32 f2_2 = 2 * f2;
crypto_int32 f3_2 = 2 * f3;
crypto_int32 f4_2 = 2 * f4;
crypto_int32 f5_2 = 2 * f5;
crypto_int32 f6_2 = 2 * f6;
crypto_int32 f7_2 = 2 * f7;
crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */
crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */
crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */
crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */
crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */
crypto_int64 f0f0 = f0 * (crypto_int64) f0;
crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1;
crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2;
crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3;
crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4;
crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5;
crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6;
crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7;
crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8;
crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9;
crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1;
crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2;
crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2;
crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4;
crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2;
crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6;
crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2;
crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8;
crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38;
crypto_int64 f2f2 = f2 * (crypto_int64) f2;
crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3;
crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4;
crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5;
crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6;
crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7;
crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19;
crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38;
crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3;
crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4;
crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2;
crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6;
crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38;
crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19;
crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38;
crypto_int64 f4f4 = f4 * (crypto_int64) f4;
crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5;
crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19;
crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38;
crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19;
crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38;
crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38;
crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19;
crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38;
crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19;
crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38;
crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19;
crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38;
crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19;
crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38;
crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38;
crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19;
crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38;
crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19;
crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38;
crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38;
crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
crypto_int64 carry0;
crypto_int64 carry1;
crypto_int64 carry2;
crypto_int64 carry3;
crypto_int64 carry4;
crypto_int64 carry5;
crypto_int64 carry6;
crypto_int64 carry7;
crypto_int64 carry8;
crypto_int64 carry9;
h0 += h0;
h1 += h1;
h2 += h2;
h3 += h3;
h4 += h4;
h5 += h5;
h6 += h6;
h7 += h7;
h8 += h8;
h9 += h9;
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,57 @@
#include "fe.h"
/*
h = f - g
Can overlap h with f or g.
Preconditions:
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
Postconditions:
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
void fe_sub(fe h,const fe f,const fe g)
{
crypto_int32 f0 = f[0];
crypto_int32 f1 = f[1];
crypto_int32 f2 = f[2];
crypto_int32 f3 = f[3];
crypto_int32 f4 = f[4];
crypto_int32 f5 = f[5];
crypto_int32 f6 = f[6];
crypto_int32 f7 = f[7];
crypto_int32 f8 = f[8];
crypto_int32 f9 = f[9];
crypto_int32 g0 = g[0];
crypto_int32 g1 = g[1];
crypto_int32 g2 = g[2];
crypto_int32 g3 = g[3];
crypto_int32 g4 = g[4];
crypto_int32 g5 = g[5];
crypto_int32 g6 = g[6];
crypto_int32 g7 = g[7];
crypto_int32 g8 = g[8];
crypto_int32 g9 = g[9];
crypto_int32 h0 = f0 - g0;
crypto_int32 h1 = f1 - g1;
crypto_int32 h2 = f2 - g2;
crypto_int32 h3 = f3 - g3;
crypto_int32 h4 = f4 - g4;
crypto_int32 h5 = f5 - g5;
crypto_int32 h6 = f6 - g6;
crypto_int32 h7 = f7 - g7;
crypto_int32 h8 = f8 - g8;
crypto_int32 h9 = f9 - g9;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}

View File

@@ -0,0 +1,119 @@
#include "fe.h"
/*
Preconditions:
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
Write p=2^255-19; q=floor(h/p).
Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
Proof:
Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
Then 0<y<1.
Write r=h-pq.
Have 0<=r<=p-1=2^255-20.
Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
Write x=r+19(2^-255)r+y.
Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
*/
void fe_tobytes(unsigned char *s,const fe h)
{
crypto_int32 h0 = h[0];
crypto_int32 h1 = h[1];
crypto_int32 h2 = h[2];
crypto_int32 h3 = h[3];
crypto_int32 h4 = h[4];
crypto_int32 h5 = h[5];
crypto_int32 h6 = h[6];
crypto_int32 h7 = h[7];
crypto_int32 h8 = h[8];
crypto_int32 h9 = h[9];
crypto_int32 q;
crypto_int32 carry0;
crypto_int32 carry1;
crypto_int32 carry2;
crypto_int32 carry3;
crypto_int32 carry4;
crypto_int32 carry5;
crypto_int32 carry6;
crypto_int32 carry7;
crypto_int32 carry8;
crypto_int32 carry9;
q = (19 * h9 + (((crypto_int32) 1) << 24)) >> 25;
q = (h0 + q) >> 26;
q = (h1 + q) >> 25;
q = (h2 + q) >> 26;
q = (h3 + q) >> 25;
q = (h4 + q) >> 26;
q = (h5 + q) >> 25;
q = (h6 + q) >> 26;
q = (h7 + q) >> 25;
q = (h8 + q) >> 26;
q = (h9 + q) >> 25;
/* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
h0 += 19 * q;
/* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26;
carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25;
carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26;
carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25;
carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26;
carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25;
carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26;
carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25;
carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26;
carry9 = h9 >> 25; h9 -= carry9 << 25;
/* h10 = carry9 */
/*
Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
Have h0+...+2^230 h9 between 0 and 2^255-1;
evidently 2^255 h10-2^255 q = 0.
Goal: Output h0+...+2^230 h9.
*/
s[0] = h0 >> 0;
s[1] = h0 >> 8;
s[2] = h0 >> 16;
s[3] = (h0 >> 24) | (h1 << 2);
s[4] = h1 >> 6;
s[5] = h1 >> 14;
s[6] = (h1 >> 22) | (h2 << 3);
s[7] = h2 >> 5;
s[8] = h2 >> 13;
s[9] = (h2 >> 21) | (h3 << 5);
s[10] = h3 >> 3;
s[11] = h3 >> 11;
s[12] = (h3 >> 19) | (h4 << 6);
s[13] = h4 >> 2;
s[14] = h4 >> 10;
s[15] = h4 >> 18;
s[16] = h5 >> 0;
s[17] = h5 >> 8;
s[18] = h5 >> 16;
s[19] = (h5 >> 24) | (h6 << 1);
s[20] = h6 >> 7;
s[21] = h6 >> 15;
s[22] = (h6 >> 23) | (h7 << 3);
s[23] = h7 >> 5;
s[24] = h7 >> 13;
s[25] = (h7 >> 21) | (h8 << 4);
s[26] = h8 >> 4;
s[27] = h8 >> 12;
s[28] = (h8 >> 20) | (h9 << 6);
s[29] = h9 >> 2;
s[30] = h9 >> 10;
s[31] = h9 >> 18;
}

View File

@@ -0,0 +1,95 @@
#ifndef GE_H
#define GE_H
/*
ge means group element.
Here the group is the set of pairs (x,y) of field elements (see fe.h)
satisfying -x^2 + y^2 = 1 + d x^2y^2
where d = -121665/121666.
Representations:
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
ge_precomp (Duif): (y+x,y-x,2dxy)
*/
#include "fe.h"
typedef struct {
fe X;
fe Y;
fe Z;
} ge_p2;
typedef struct {
fe X;
fe Y;
fe Z;
fe T;
} ge_p3;
typedef struct {
fe X;
fe Y;
fe Z;
fe T;
} ge_p1p1;
typedef struct {
fe yplusx;
fe yminusx;
fe xy2d;
} ge_precomp;
typedef struct {
fe YplusX;
fe YminusX;
fe Z;
fe T2d;
} ge_cached;
#define ge_frombytes_negate_vartime crypto_sign_ed25519_ref10_ge_frombytes_negate_vartime
#define ge_tobytes crypto_sign_ed25519_ref10_ge_tobytes
#define ge_p3_tobytes crypto_sign_ed25519_ref10_ge_p3_tobytes
#define ge_p2_0 crypto_sign_ed25519_ref10_ge_p2_0
#define ge_p3_0 crypto_sign_ed25519_ref10_ge_p3_0
#define ge_precomp_0 crypto_sign_ed25519_ref10_ge_precomp_0
#define ge_p3_to_p2 crypto_sign_ed25519_ref10_ge_p3_to_p2
#define ge_p3_to_cached crypto_sign_ed25519_ref10_ge_p3_to_cached
#define ge_p1p1_to_p2 crypto_sign_ed25519_ref10_ge_p1p1_to_p2
#define ge_p1p1_to_p3 crypto_sign_ed25519_ref10_ge_p1p1_to_p3
#define ge_p2_dbl crypto_sign_ed25519_ref10_ge_p2_dbl
#define ge_p3_dbl crypto_sign_ed25519_ref10_ge_p3_dbl
#define ge_madd crypto_sign_ed25519_ref10_ge_madd
#define ge_msub crypto_sign_ed25519_ref10_ge_msub
#define ge_add crypto_sign_ed25519_ref10_ge_add
#define ge_sub crypto_sign_ed25519_ref10_ge_sub
#define ge_scalarmult_base crypto_sign_ed25519_ref10_ge_scalarmult_base
#define ge_double_scalarmult_vartime crypto_sign_ed25519_ref10_ge_double_scalarmult_vartime
extern void ge_tobytes(unsigned char *,const ge_p2 *);
extern void ge_p3_tobytes(unsigned char *,const ge_p3 *);
extern int ge_frombytes_negate_vartime(ge_p3 *,const unsigned char *);
extern void ge_p2_0(ge_p2 *);
extern void ge_p3_0(ge_p3 *);
extern void ge_precomp_0(ge_precomp *);
extern void ge_p3_to_p2(ge_p2 *,const ge_p3 *);
extern void ge_p3_to_cached(ge_cached *,const ge_p3 *);
extern void ge_p1p1_to_p2(ge_p2 *,const ge_p1p1 *);
extern void ge_p1p1_to_p3(ge_p3 *,const ge_p1p1 *);
extern void ge_p2_dbl(ge_p1p1 *,const ge_p2 *);
extern void ge_p3_dbl(ge_p1p1 *,const ge_p3 *);
extern void ge_madd(ge_p1p1 *,const ge_p3 *,const ge_precomp *);
extern void ge_msub(ge_p1p1 *,const ge_p3 *,const ge_precomp *);
extern void ge_add(ge_p1p1 *,const ge_p3 *,const ge_cached *);
extern void ge_sub(ge_p1p1 *,const ge_p3 *,const ge_cached *);
extern void ge_scalarmult_base(ge_p3 *,const unsigned char *);
extern void ge_double_scalarmult_vartime(ge_p2 *,const unsigned char *,const ge_p3 *,const unsigned char *);
#endif

View File

@@ -0,0 +1,11 @@
#include "ge.h"
/*
r = p + q
*/
void ge_add(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q)
{
fe t0;
#include "ge_add.h"
}

View File

@@ -0,0 +1,97 @@
/* qhasm: enter ge_add */
/* qhasm: fe X1 */
/* qhasm: fe Y1 */
/* qhasm: fe Z1 */
/* qhasm: fe Z2 */
/* qhasm: fe T1 */
/* qhasm: fe ZZ */
/* qhasm: fe YpX2 */
/* qhasm: fe YmX2 */
/* qhasm: fe T2d2 */
/* qhasm: fe X3 */
/* qhasm: fe Y3 */
/* qhasm: fe Z3 */
/* qhasm: fe T3 */
/* qhasm: fe YpX1 */
/* qhasm: fe YmX1 */
/* qhasm: fe A */
/* qhasm: fe B */
/* qhasm: fe C */
/* qhasm: fe D */
/* qhasm: YpX1 = Y1+X1 */
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
fe_add(r->X,p->Y,p->X);
/* qhasm: YmX1 = Y1-X1 */
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
fe_sub(r->Y,p->Y,p->X);
/* qhasm: A = YpX1*YpX2 */
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YpX2=fe#15); */
/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YpX2=q->YplusX); */
fe_mul(r->Z,r->X,q->YplusX);
/* qhasm: B = YmX1*YmX2 */
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YmX2=fe#16); */
/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YmX2=q->YminusX); */
fe_mul(r->Y,r->Y,q->YminusX);
/* qhasm: C = T2d2*T1 */
/* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */
/* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */
fe_mul(r->T,q->T2d,p->T);
/* qhasm: ZZ = Z1*Z2 */
/* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */
/* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */
fe_mul(r->X,p->Z,q->Z);
/* qhasm: D = 2*ZZ */
/* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */
/* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */
fe_add(t0,r->X,r->X);
/* qhasm: X3 = A-B */
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
fe_sub(r->X,r->Z,r->Y);
/* qhasm: Y3 = A+B */
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
fe_add(r->Y,r->Z,r->Y);
/* qhasm: Z3 = D+C */
/* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */
/* asm 2: fe_add(>Z3=r->Z,<D=t0,<C=r->T); */
fe_add(r->Z,t0,r->T);
/* qhasm: T3 = D-C */
/* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */
/* asm 2: fe_sub(>T3=r->T,<D=t0,<C=r->T); */
fe_sub(r->T,t0,r->T);
/* qhasm: return */

View File

@@ -0,0 +1,96 @@
#include "ge.h"
static void slide(signed char *r,const unsigned char *a)
{
int i;
int b;
int k;
for (i = 0;i < 256;++i)
r[i] = 1 & (a[i >> 3] >> (i & 7));
for (i = 0;i < 256;++i)
if (r[i]) {
for (b = 1;b <= 6 && i + b < 256;++b) {
if (r[i + b]) {
if (r[i] + (r[i + b] << b) <= 15) {
r[i] += r[i + b] << b; r[i + b] = 0;
} else if (r[i] - (r[i + b] << b) >= -15) {
r[i] -= r[i + b] << b;
for (k = i + b;k < 256;++k) {
if (!r[k]) {
r[k] = 1;
break;
}
r[k] = 0;
}
} else
break;
}
}
}
}
static ge_precomp Bi[8] = {
#include "base2.h"
} ;
/*
r = a * A + b * B
where a = a[0]+256*a[1]+...+256^31 a[31].
and b = b[0]+256*b[1]+...+256^31 b[31].
B is the Ed25519 base point (x,4/5) with x positive.
*/
void ge_double_scalarmult_vartime(ge_p2 *r,const unsigned char *a,const ge_p3 *A,const unsigned char *b)
{
signed char aslide[256];
signed char bslide[256];
ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
ge_p1p1 t;
ge_p3 u;
ge_p3 A2;
int i;
slide(aslide,a);
slide(bslide,b);
ge_p3_to_cached(&Ai[0],A);
ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t);
ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u);
ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u);
ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u);
ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u);
ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u);
ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u);
ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u);
ge_p2_0(r);
for (i = 255;i >= 0;--i) {
if (aslide[i] || bslide[i]) break;
}
for (;i >= 0;--i) {
ge_p2_dbl(&t,r);
if (aslide[i] > 0) {
ge_p1p1_to_p3(&u,&t);
ge_add(&t,&u,&Ai[aslide[i]/2]);
} else if (aslide[i] < 0) {
ge_p1p1_to_p3(&u,&t);
ge_sub(&t,&u,&Ai[(-aslide[i])/2]);
}
if (bslide[i] > 0) {
ge_p1p1_to_p3(&u,&t);
ge_madd(&t,&u,&Bi[bslide[i]/2]);
} else if (bslide[i] < 0) {
ge_p1p1_to_p3(&u,&t);
ge_msub(&t,&u,&Bi[(-bslide[i])/2]);
}
ge_p1p1_to_p2(r,&t);
}
}

View File

@@ -0,0 +1,50 @@
#include "ge.h"
static const fe d = {
#include "d.h"
} ;
static const fe sqrtm1 = {
#include "sqrtm1.h"
} ;
int ge_frombytes_negate_vartime(ge_p3 *h,const unsigned char *s)
{
fe u;
fe v;
fe v3;
fe vxx;
fe check;
fe_frombytes(h->Y,s);
fe_1(h->Z);
fe_sq(u,h->Y);
fe_mul(v,u,d);
fe_sub(u,u,h->Z); /* u = y^2-1 */
fe_add(v,v,h->Z); /* v = dy^2+1 */
fe_sq(v3,v);
fe_mul(v3,v3,v); /* v3 = v^3 */
fe_sq(h->X,v3);
fe_mul(h->X,h->X,v);
fe_mul(h->X,h->X,u); /* x = uv^7 */
fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */
fe_mul(h->X,h->X,v3);
fe_mul(h->X,h->X,u); /* x = uv^3(uv^7)^((q-5)/8) */
fe_sq(vxx,h->X);
fe_mul(vxx,vxx,v);
fe_sub(check,vxx,u); /* vx^2-u */
if (fe_isnonzero(check)) {
fe_add(check,vxx,u); /* vx^2+u */
if (fe_isnonzero(check)) return -1;
fe_mul(h->X,h->X,sqrtm1);
}
if (fe_isnegative(h->X) == (s[31] >> 7))
fe_neg(h->X,h->X);
fe_mul(h->T,h->X,h->Y);
return 0;
}

View File

@@ -0,0 +1,11 @@
#include "ge.h"
/*
r = p + q
*/
void ge_madd(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q)
{
fe t0;
#include "ge_madd.h"
}

View File

@@ -0,0 +1,88 @@
/* qhasm: enter ge_madd */
/* qhasm: fe X1 */
/* qhasm: fe Y1 */
/* qhasm: fe Z1 */
/* qhasm: fe T1 */
/* qhasm: fe ypx2 */
/* qhasm: fe ymx2 */
/* qhasm: fe xy2d2 */
/* qhasm: fe X3 */
/* qhasm: fe Y3 */
/* qhasm: fe Z3 */
/* qhasm: fe T3 */
/* qhasm: fe YpX1 */
/* qhasm: fe YmX1 */
/* qhasm: fe A */
/* qhasm: fe B */
/* qhasm: fe C */
/* qhasm: fe D */
/* qhasm: YpX1 = Y1+X1 */
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
fe_add(r->X,p->Y,p->X);
/* qhasm: YmX1 = Y1-X1 */
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
fe_sub(r->Y,p->Y,p->X);
/* qhasm: A = YpX1*ypx2 */
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ypx2=fe#15); */
/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<ypx2=q->yplusx); */
fe_mul(r->Z,r->X,q->yplusx);
/* qhasm: B = YmX1*ymx2 */
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ymx2=fe#16); */
/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<ymx2=q->yminusx); */
fe_mul(r->Y,r->Y,q->yminusx);
/* qhasm: C = xy2d2*T1 */
/* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */
/* asm 2: fe_mul(>C=r->T,<xy2d2=q->xy2d,<T1=p->T); */
fe_mul(r->T,q->xy2d,p->T);
/* qhasm: D = 2*Z1 */
/* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */
/* asm 2: fe_add(>D=t0,<Z1=p->Z,<Z1=p->Z); */
fe_add(t0,p->Z,p->Z);
/* qhasm: X3 = A-B */
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
fe_sub(r->X,r->Z,r->Y);
/* qhasm: Y3 = A+B */
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
fe_add(r->Y,r->Z,r->Y);
/* qhasm: Z3 = D+C */
/* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */
/* asm 2: fe_add(>Z3=r->Z,<D=t0,<C=r->T); */
fe_add(r->Z,t0,r->T);
/* qhasm: T3 = D-C */
/* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */
/* asm 2: fe_sub(>T3=r->T,<D=t0,<C=r->T); */
fe_sub(r->T,t0,r->T);
/* qhasm: return */

View File

@@ -0,0 +1,11 @@
#include "ge.h"
/*
r = p - q
*/
void ge_msub(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q)
{
fe t0;
#include "ge_msub.h"
}

View File

@@ -0,0 +1,88 @@
/* qhasm: enter ge_msub */
/* qhasm: fe X1 */
/* qhasm: fe Y1 */
/* qhasm: fe Z1 */
/* qhasm: fe T1 */
/* qhasm: fe ypx2 */
/* qhasm: fe ymx2 */
/* qhasm: fe xy2d2 */
/* qhasm: fe X3 */
/* qhasm: fe Y3 */
/* qhasm: fe Z3 */
/* qhasm: fe T3 */
/* qhasm: fe YpX1 */
/* qhasm: fe YmX1 */
/* qhasm: fe A */
/* qhasm: fe B */
/* qhasm: fe C */
/* qhasm: fe D */
/* qhasm: YpX1 = Y1+X1 */
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
fe_add(r->X,p->Y,p->X);
/* qhasm: YmX1 = Y1-X1 */
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
fe_sub(r->Y,p->Y,p->X);
/* qhasm: A = YpX1*ymx2 */
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ymx2=fe#16); */
/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<ymx2=q->yminusx); */
fe_mul(r->Z,r->X,q->yminusx);
/* qhasm: B = YmX1*ypx2 */
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ypx2=fe#15); */
/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<ypx2=q->yplusx); */
fe_mul(r->Y,r->Y,q->yplusx);
/* qhasm: C = xy2d2*T1 */
/* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */
/* asm 2: fe_mul(>C=r->T,<xy2d2=q->xy2d,<T1=p->T); */
fe_mul(r->T,q->xy2d,p->T);
/* qhasm: D = 2*Z1 */
/* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */
/* asm 2: fe_add(>D=t0,<Z1=p->Z,<Z1=p->Z); */
fe_add(t0,p->Z,p->Z);
/* qhasm: X3 = A-B */
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
fe_sub(r->X,r->Z,r->Y);
/* qhasm: Y3 = A+B */
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
fe_add(r->Y,r->Z,r->Y);
/* qhasm: Z3 = D-C */
/* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */
/* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */
fe_sub(r->Z,t0,r->T);
/* qhasm: T3 = D+C */
/* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */
/* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */
fe_add(r->T,t0,r->T);
/* qhasm: return */

View File

@@ -0,0 +1,12 @@
#include "ge.h"
/*
r = p
*/
extern void ge_p1p1_to_p2(ge_p2 *r,const ge_p1p1 *p)
{
fe_mul(r->X,p->X,p->T);
fe_mul(r->Y,p->Y,p->Z);
fe_mul(r->Z,p->Z,p->T);
}

View File

@@ -0,0 +1,13 @@
#include "ge.h"
/*
r = p
*/
extern void ge_p1p1_to_p3(ge_p3 *r,const ge_p1p1 *p)
{
fe_mul(r->X,p->X,p->T);
fe_mul(r->Y,p->Y,p->Z);
fe_mul(r->Z,p->Z,p->T);
fe_mul(r->T,p->X,p->Y);
}

View File

@@ -0,0 +1,8 @@
#include "ge.h"
void ge_p2_0(ge_p2 *h)
{
fe_0(h->X);
fe_1(h->Y);
fe_1(h->Z);
}

Some files were not shown because too many files have changed in this diff Show More