Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
503d1ef452 | ||
|
|
9accd92757 | ||
|
|
306da92031 | ||
|
|
e7b6a852c5 | ||
|
|
9dfaf19516 | ||
|
|
34aece0b43 | ||
|
|
9768de2d5e | ||
|
|
c3eb0ea9db | ||
|
|
4cdc0a3e61 | ||
|
|
b568ce70b2 | ||
|
|
18b0601990 | ||
|
|
b308996885 | ||
|
|
73d896f378 | ||
|
|
36ec1d84a1 | ||
|
|
8d6b9ae43e | ||
|
|
a95cc0eba2 | ||
|
|
58d101ff2e | ||
|
|
544f06451f | ||
|
|
20cf775b1e | ||
|
|
5fcc135f81 | ||
|
|
9a0ed659f7 | ||
|
|
54612159be | ||
|
|
355d0be78a | ||
|
|
73b75a4a27 | ||
|
|
c4209a65e3 | ||
|
|
9dce376780 | ||
|
|
07c61394e9 | ||
|
|
3e287f930d | ||
|
|
7b1a37bd91 | ||
|
|
2db44a1578 | ||
|
|
006c9aae7b | ||
|
|
b147a90463 | ||
|
|
741171c49f | ||
|
|
c375ed8638 | ||
|
|
238f29c90a | ||
|
|
084f27a2e8 | ||
|
|
27b5bf54cc | ||
|
|
eda393b11c | ||
|
|
c330eef7b9 | ||
|
|
1eb3884b7a | ||
|
|
5ea3b3038e | ||
|
|
641ac9aed9 | ||
|
|
82bd75fb75 | ||
|
|
c94a7b1eff | ||
|
|
4caebdcd06 | ||
|
|
b3cece27d6 | ||
|
|
819982af7b | ||
|
|
42cf53e487 | ||
|
|
e0d2398ca5 | ||
|
|
3f299936bf | ||
|
|
540592d71f | ||
|
|
5a9e5672d3 | ||
|
|
0a23b5fcd5 | ||
|
|
f0c22d593f | ||
|
|
5f5ddd7c26 | ||
|
|
144f269059 | ||
|
|
0d532afd8e | ||
|
|
07fd17ccda | ||
|
|
fcaa3f0d73 | ||
|
|
64b40df15b | ||
|
|
77ff9cece8 | ||
|
|
6326ef73f3 | ||
|
|
f29d1e6269 | ||
|
|
d6c5e92c9d | ||
|
|
811479d168 | ||
|
|
2ed8d333d9 | ||
|
|
79020cd33c | ||
|
|
c8757c2134 | ||
|
|
2a65257182 | ||
|
|
931605a1c4 | ||
|
|
81ae9af2e4 | ||
|
|
e84a0948e9 | ||
|
|
5239b3e8f8 | ||
|
|
a601c56af1 | ||
|
|
5a3c19fe3e | ||
|
|
af45e5d544 | ||
|
|
72af8b11c2 | ||
|
|
a1db221caf | ||
|
|
14b8f97de2 | ||
|
|
d902c12941 | ||
|
|
fe3d91c40c | ||
|
|
31167d11dd | ||
|
|
6e3751a0c5 | ||
|
|
b5941fb3fd | ||
|
|
cfccd367ad | ||
|
|
2c8c6a410c | ||
|
|
2075bba86c | ||
|
|
1d7b47c982 | ||
|
|
89fb80fcc5 | ||
|
|
f1d230ce6e | ||
|
|
f51a5aa7eb | ||
|
|
cf9c9bbfd7 | ||
|
|
b8a3e87f3d | ||
|
|
e5bad2746f | ||
|
|
7316f17674 | ||
|
|
609e69a801 | ||
|
|
d979593cbb | ||
|
|
0808e00d0e | ||
|
|
2045c828be | ||
|
|
1b3bd32805 | ||
|
|
877f2a25a9 | ||
|
|
eb462f0345 | ||
|
|
102b40543d | ||
|
|
12077c6dad | ||
|
|
7441c191a7 | ||
|
|
b355991b0b | ||
|
|
350eb438c0 | ||
|
|
da7bbf4144 | ||
|
|
a0842f329f | ||
|
|
567224b4a6 | ||
|
|
9b495d8c83 | ||
|
|
8c55ea09d9 | ||
|
|
5264ebed67 | ||
|
|
7f51f9fd5b | ||
|
|
40495a2261 | ||
|
|
a6c1fdd914 | ||
|
|
020d61dd6e | ||
|
|
dee23b266f | ||
|
|
4cf57fbb55 | ||
|
|
85da05397a | ||
|
|
f4e9c4a710 | ||
|
|
28e14f47cf | ||
|
|
d2edda837a | ||
|
|
213715a0dc | ||
|
|
7b0479ff0f | ||
|
|
feabbb33d2 | ||
|
|
d3da409774 | ||
|
|
edb04138c2 | ||
|
|
8bf537bb09 | ||
|
|
b80593b5f5 | ||
|
|
b61e7839f4 | ||
|
|
7b41b1492e | ||
|
|
928b9687ef | ||
|
|
306c127803 | ||
|
|
239a11bfd2 | ||
|
|
de63b0dd96 | ||
|
|
eb4ac40051 | ||
|
|
dda4459e5a | ||
|
|
8726dd51be |
@@ -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">
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,2 @@
|
||||
*.db
|
||||
*.db.gz
|
||||
1759
apntool/apnlists/cyanogenmod.xml
Normal file
2218
apntool/apnlists/hangouts.xml
Normal file
81
apntool/apntool.py
Normal 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
@@ -0,0 +1,3 @@
|
||||
argparse>=1.2.1
|
||||
lxml>=3.3.3
|
||||
progressbar-latest>=2.4
|
||||
|
Before Width: | Height: | Size: 582 KiB After Width: | Height: | Size: 496 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 93 KiB |
BIN
assets/databases/apns.db
Normal file
|
Before Width: | Height: | Size: 522 KiB After Width: | Height: | Size: 445 KiB |
|
Before Width: | Height: | Size: 629 KiB After Width: | Height: | Size: 547 KiB |
|
Before Width: | Height: | Size: 764 KiB After Width: | Height: | Size: 660 KiB |
|
Before Width: | Height: | Size: 420 KiB After Width: | Height: | Size: 369 KiB |
|
Before Width: | Height: | Size: 429 KiB After Width: | Height: | Size: 363 KiB |
61
build.gradle
@@ -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
@@ -0,0 +1 @@
|
||||
/build
|
||||
19
jobqueue/build.gradle
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
public interface RunnableThrowable {
|
||||
|
||||
public void run() throws Throwable;
|
||||
|
||||
public void shouldThrow(Boolean value);
|
||||
}
|
||||
6
jobqueue/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.whispersystems.jobqueue">
|
||||
|
||||
<application />
|
||||
|
||||
</manifest>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
89
jobqueue/src/main/java/org/whispersystems/jobqueue/Job.java
Normal 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);
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
2
jobqueue/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<resources>
|
||||
</resources>
|
||||
2
libaxolotl/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/build
|
||||
/obj
|
||||
85
libaxolotl/README.md
Normal 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
@@ -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
@@ -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)
|
||||
|
||||
1
libaxolotl/jni/Application.mk
Normal file
@@ -0,0 +1 @@
|
||||
APP_ABI := armeabi armeabi-v7a x86 mips
|
||||
@@ -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;
|
||||
}
|
||||
109
libaxolotl/jni/curve25519-jni.c
Normal 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;
|
||||
}
|
||||
44
libaxolotl/jni/ed25519/additions/compare.c
Normal 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;
|
||||
}
|
||||
6
libaxolotl/jni/ed25519/additions/compare.h
Normal 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
|
||||
6
libaxolotl/jni/ed25519/additions/crypto_hash_sha512.h
Normal 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
|
||||
116
libaxolotl/jni/ed25519/additions/curve_sigs.c
Normal 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);
|
||||
}
|
||||
50
libaxolotl/jni/ed25519/additions/curve_sigs.h
Normal 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
|
||||
47
libaxolotl/jni/ed25519/additions/sign_modified.c
Normal 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;
|
||||
}
|
||||
17
libaxolotl/jni/ed25519/additions/zeroize.c
Normal 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);
|
||||
}
|
||||
12
libaxolotl/jni/ed25519/additions/zeroize.h
Normal 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
|
||||
4
libaxolotl/jni/ed25519/api.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#define CRYPTO_SECRETKEYBYTES 64
|
||||
#define CRYPTO_PUBLICKEYBYTES 32
|
||||
#define CRYPTO_BYTES 64
|
||||
#define CRYPTO_DETERMINISTIC 1
|
||||
1344
libaxolotl/jni/ed25519/base.h
Normal file
40
libaxolotl/jni/ed25519/base2.h
Normal 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 },
|
||||
},
|
||||
1
libaxolotl/jni/ed25519/d.h
Normal file
@@ -0,0 +1 @@
|
||||
-10913610,13857413,-15372611,6949391,114729,-8787816,-6275908,-3247719,-18696448,-12055116
|
||||
1
libaxolotl/jni/ed25519/d2.h
Normal file
@@ -0,0 +1 @@
|
||||
-21827239,-5839606,-30745221,13898782,229458,15978800,-12551817,-6495438,29715968,9444199
|
||||
56
libaxolotl/jni/ed25519/fe.h
Normal 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
|
||||
19
libaxolotl/jni/ed25519/fe_0.c
Normal 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;
|
||||
}
|
||||
19
libaxolotl/jni/ed25519/fe_1.c
Normal 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;
|
||||
}
|
||||
57
libaxolotl/jni/ed25519/fe_add.c
Normal 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;
|
||||
}
|
||||
63
libaxolotl/jni/ed25519/fe_cmov.c
Normal 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;
|
||||
}
|
||||
29
libaxolotl/jni/ed25519/fe_copy.c
Normal 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;
|
||||
}
|
||||
73
libaxolotl/jni/ed25519/fe_frombytes.c
Normal 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;
|
||||
}
|
||||
14
libaxolotl/jni/ed25519/fe_invert.c
Normal 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;
|
||||
}
|
||||
16
libaxolotl/jni/ed25519/fe_isnegative.c
Normal 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;
|
||||
}
|
||||
19
libaxolotl/jni/ed25519/fe_isnonzero.c
Normal 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);
|
||||
}
|
||||
253
libaxolotl/jni/ed25519/fe_mul.c
Normal 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;
|
||||
}
|
||||
45
libaxolotl/jni/ed25519/fe_neg.c
Normal 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;
|
||||
}
|
||||
13
libaxolotl/jni/ed25519/fe_pow22523.c
Normal 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;
|
||||
}
|
||||
149
libaxolotl/jni/ed25519/fe_sq.c
Normal 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;
|
||||
}
|
||||
160
libaxolotl/jni/ed25519/fe_sq2.c
Normal 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;
|
||||
}
|
||||
57
libaxolotl/jni/ed25519/fe_sub.c
Normal 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;
|
||||
}
|
||||
119
libaxolotl/jni/ed25519/fe_tobytes.c
Normal 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;
|
||||
}
|
||||
95
libaxolotl/jni/ed25519/ge.h
Normal 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
|
||||
11
libaxolotl/jni/ed25519/ge_add.c
Normal 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"
|
||||
}
|
||||
97
libaxolotl/jni/ed25519/ge_add.h
Normal 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 */
|
||||
96
libaxolotl/jni/ed25519/ge_double_scalarmult.c
Normal 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);
|
||||
}
|
||||
}
|
||||
50
libaxolotl/jni/ed25519/ge_frombytes.c
Normal 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;
|
||||
}
|
||||
11
libaxolotl/jni/ed25519/ge_madd.c
Normal 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"
|
||||
}
|
||||
88
libaxolotl/jni/ed25519/ge_madd.h
Normal 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 */
|
||||
11
libaxolotl/jni/ed25519/ge_msub.c
Normal 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"
|
||||
}
|
||||
88
libaxolotl/jni/ed25519/ge_msub.h
Normal 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 */
|
||||
12
libaxolotl/jni/ed25519/ge_p1p1_to_p2.c
Normal 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);
|
||||
}
|
||||
13
libaxolotl/jni/ed25519/ge_p1p1_to_p3.c
Normal 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);
|
||||
}
|
||||
8
libaxolotl/jni/ed25519/ge_p2_0.c
Normal 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);
|
||||
}
|
||||