├── .gitignore ├── .jscsrc ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── build ├── components_concat.js ├── curve25519_compiled.js ├── curve25519_concat.js ├── protoText.js └── protobufs_concat.js ├── dist ├── libsignal-protocol-worker.js └── libsignal-protocol.js ├── native ├── curve25519-donna.c └── ed25519 │ ├── additions │ ├── compare.c │ ├── compare.h │ ├── curve_sigs.c │ ├── curve_sigs.h │ ├── sha512.c │ ├── sha512.h │ └── sign_modified.c │ ├── api.h │ ├── base.h │ ├── base2.h │ ├── d.h │ ├── d2.h │ ├── fe.h │ ├── fe_0.c │ ├── fe_1.c │ ├── fe_add.c │ ├── fe_cmov.c │ ├── fe_copy.c │ ├── fe_frombytes.c │ ├── fe_invert.c │ ├── fe_isnegative.c │ ├── fe_isnonzero.c │ ├── fe_mul.c │ ├── fe_neg.c │ ├── fe_pow22523.c │ ├── fe_sq.c │ ├── fe_sq2.c │ ├── fe_sub.c │ ├── fe_tobytes.c │ ├── ge.h │ ├── ge_add.c │ ├── ge_add.h │ ├── ge_double_scalarmult.c │ ├── ge_frombytes.c │ ├── ge_madd.c │ ├── ge_madd.h │ ├── ge_msub.c │ ├── ge_msub.h │ ├── ge_p1p1_to_p2.c │ ├── ge_p1p1_to_p3.c │ ├── ge_p2_0.c │ ├── ge_p2_dbl.c │ ├── ge_p2_dbl.h │ ├── ge_p3_0.c │ ├── ge_p3_dbl.c │ ├── ge_p3_to_cached.c │ ├── ge_p3_to_p2.c │ ├── ge_p3_tobytes.c │ ├── ge_precomp_0.c │ ├── ge_scalarmult_base.c │ ├── ge_sub.c │ ├── ge_sub.h │ ├── ge_tobytes.c │ ├── main │ └── main.c │ ├── nacl_includes │ ├── crypto_hash_sha512.h │ ├── crypto_int32.h │ ├── crypto_int64.h │ ├── crypto_sign.h │ ├── crypto_sign_edwards25519sha512batch.h │ ├── crypto_uint32.h │ ├── crypto_uint64.h │ └── crypto_verify_32.h │ ├── open.c │ ├── pow22523.h │ ├── pow225521.h │ ├── sc.h │ ├── sc_muladd.c │ ├── sc_reduce.c │ ├── sha512 │ ├── LICENSE.txt │ ├── md_helper.c │ ├── sha2big.c │ ├── sph_sha2.h │ └── sph_types.h │ ├── sign.c │ └── sqrtm1.h ├── package.json ├── protos └── WhisperTextProtocol.proto ├── src ├── Curve.js ├── KeyHelper.js ├── NumericFingerprint.js ├── SessionBuilder.js ├── SessionCipher.js ├── SessionLock.js ├── SessionRecord.js ├── SignalProtocolAddress.js ├── crypto.js ├── curve25519_worker.js ├── curve25519_worker_manager.js ├── curve25519_wrapper.js ├── helpers.js └── protobufs.js └── test ├── IdentityKeyStore_test.js ├── InMemorySignalProtocolStore.js ├── KeyHelperTest.js ├── NumericFingerprintTest.js ├── PreKeyStore_test.js ├── SessionBuilderTest.js ├── SessionCipherTest.js ├── SessionStore_test.js ├── SignalProtocolAddressTest.js ├── SignalProtocolStore_test.js ├── SignedPreKeyStore_test.js ├── _test.js ├── crypto_test.js ├── helpers_test.js ├── index.html ├── protos ├── temp_helpers.js ├── test.js └── testvectors.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowMixedSpacesAndTabs": true, 3 | "disallowTrailingWhitespace": true, 4 | "disallowNewlineBeforeBlockStatements": true, 5 | "requireCommaBeforeLineBreak": true, 6 | "requireSemicolons": true, 7 | "requireSpaceBeforeBlockStatements": true, 8 | "disallowSpacesInNamedFunctionExpression": { 9 | "beforeOpeningRoundBrace": true 10 | }, 11 | "requireSpacesInNamedFunctionExpression": { 12 | "beforeOpeningCurlyBrace": true 13 | }, 14 | "requireCurlyBraces": [ 15 | "if", 16 | "else", 17 | "for", 18 | "while", 19 | "do", 20 | "try", 21 | "catch" 22 | ], 23 | "requireSpaceAfterKeywords": [ 24 | "if", 25 | "else", 26 | "for", 27 | "while", 28 | "case", 29 | "try", 30 | "typeof", 31 | "return" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr" : 50, 3 | "bitwise" : false, 4 | "camelcase" : false, 5 | "curly" : false, 6 | "eqeqeq" : false, 7 | "forin" : false, 8 | "freeze" : false, 9 | "immed" : false, 10 | "indent" : 4, 11 | "latedef" : false, 12 | "newcap" : false, 13 | "noarg" : false, 14 | "noempty" : false, 15 | "nonbsp" : false, 16 | "nonew" : false, 17 | "plusplus" : false, 18 | "quotmark" : false, 19 | "undef" : false, 20 | "unused" : false, 21 | "strict" : false, 22 | "maxparams" : false, 23 | "maxdepth" : false, 24 | "maxstatements" : false, 25 | "maxcomplexity" : false, 26 | "maxlen" : false, 27 | "asi" : false, 28 | "boss" : false, 29 | "debug" : false, 30 | "eqnull" : false, 31 | "es5" : false, 32 | "esnext" : false, 33 | "moz" : false, 34 | "evil" : false, 35 | "expr" : false, 36 | "funcscope" : false, 37 | "globalstrict" : false, 38 | "iterator" : false, 39 | "lastsemic" : false, 40 | "laxbreak" : false, 41 | "laxcomma" : false, 42 | "loopfunc" : false, 43 | "multistr" : false, 44 | "noyield" : false, 45 | "notypeof" : false, 46 | "proto" : true, 47 | "scripturl" : false, 48 | "shadow" : false, 49 | "sub" : false, 50 | "supernew" : false, 51 | "validthis" : false, 52 | "browser" : false, 53 | "browserify" : false, 54 | "couch" : false, 55 | "devel" : false, 56 | "dojo" : false, 57 | "jasmine" : false, 58 | "jquery" : false, 59 | "mocha" : false, 60 | "mootools" : false, 61 | "node" : false, 62 | "nonstandard" : false, 63 | "prototypejs" : false, 64 | "qunit" : false, 65 | "rhino" : false, 66 | "shelljs" : false, 67 | "worker" : false, 68 | "wsh" : false, 69 | "yui" : false, 70 | "globals" : {} 71 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | install: 5 | - npm install 6 | script: 7 | - npm test 8 | env: 9 | global: 10 | - secure: rdRRKiD0Ndfcy66gFOJP/8FZhx9bVk1VDUnMnavyLMbOKKV2jH4qkoFlYqr73D/EDADL8QepsKdGc51VtYewED2Ki98KNv+CmoXPfURqj97mAH3HycV4soinqC46elEpcIbhoOL2skyBRv8F2VwRyWzQiHKTlXrkL4anxzfF0xyVAo07Sy010AOgCLHU+j+kg1VTSwKtUSHJY5zunX+oNDJVQTPGKtWJ4eODggLv4eSRI8+fu9Z/AA+tV5BBU1d7Ey3GY9TD0Ady4HMZmvGGIQ76M6ULb++cXlAOGLR9Vv4YeCS1TnhLoZQGEJ+UFRR1hpVF4m3ignhqSkJ5+lvpFQAAKg0gS0GwNseR2XFNGusLwyUazQRk7u00s808lJ5GutL+rLGM2eZCAZprwe2SMxrAW5GLm6u/YMZycxGyBA/ikP11X9v5Zx1m6bNRJdRmfRiPFD5Il+Q29i1DWVVawvjkrcRH9GPtNxES64jS0K7hU28DQYq2vgWEP5AY6Uwsz2bK0wEswfffk4VDFps73cx39EMRH3xP4PpIRxsGddfYejcBH+9rGH+eaHzpQ8Q3zNsn7muYjZXcVPIu6Pze5mUmsox++Kq2llTXTy/WJ6MeDv2E/6AE7ArXs61E1lKDb+vcciWRBuD9gy5SaFQ6RJ/3cn4QLCWXNw4TUabeYrE= 11 | - secure: RjI8HiaKzer4Fg08Lmt7SNcnYpctL+kqHIhRBXgYhq6cjU4YpyFYn2pplOPKHEBLBMsL44u0cvJ6XGPMpUy16rKRphID4CzE0w6rkI2MJaPt2uKV+0TpllYDHZEiOMnuFAphp0JfJgLVVqfEQhNaLddlmArG2b8++CGjsq6QPPN2SYupdvwLTL8S2nQ8+zlmcU2wcl5WjgfEe6jPAEyP5KMUw0E8XmTisWUq1xSuzi5895s6UJ6sSPHrTChRErUcXznWevu+EbgtKxCGgmcRv3Ex/64/tzgsEUI1AFKcpfwdkXD2YQU0ZKCGRVICi0sXSM1BJd9PQB1afZKoNW+Qky7+LbsEhS+1xO9rxu0Iv2h+Eq8BnrZHvH6dwFqc/0O+4jvUhJiuRLS9+wYm0JsSCdiOLkdOpYdKN72IYMwB64gCDL0//FCZtuubZj1knBIL3pt8gh84YKl1XCrVBfZ/Tw374D9t+nvsBeFAhrVi6DCUFSuzGEY+1/MiX3nbwLAewUX7ez7MRq2A4JYHS/hrmz+VId4spT6SJ/erjS9Abt9d4Rrp/Jp8/craqmdIYscVv7N+ske/bQaSMX7XY2ri/+HuSiKUITSTWhMM9xJS3oT5HNuevxMug/3bwpYqXulBf2ikaDIVGdl+NQSE4E4ItUOUDrGVFuCu0EjoAAKksjg= 12 | sudo: false 13 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | var child_process = require('child_process'); 2 | var util = require('util'); 3 | 4 | module.exports = function(grunt) { 5 | 'use strict'; 6 | 7 | grunt.initConfig({ 8 | pkg: grunt.file.readJSON('package.json'), 9 | concat: { 10 | components: { 11 | src: [ 12 | 'node_modules/long/dist/long.js', 13 | 'node_modules/bytebuffer/dist/ByteBufferAB.js', 14 | 'node_modules/protobufjs/dist/protobuf.js' 15 | ], 16 | dest: 'build/components_concat.js', 17 | }, 18 | curve25519: { 19 | src: [ 20 | 'build/curve25519_compiled.js', 21 | 'src/curve25519_wrapper.js', 22 | ], 23 | dest: 'build/curve25519_concat.js' 24 | }, 25 | protos: { 26 | src: [ 27 | 'protos/WhisperTextProtocol.proto' 28 | ], 29 | dest: 'build/protoText.js', 30 | options: { 31 | banner: 'var Internal = Internal || {};\n\nInternal.protoText = function() {\n\tvar protoText = {};\n\n', 32 | footer: '\n\treturn protoText;\n}();', 33 | process: function(src, file) { 34 | var res = "\tprotoText['" + file + "'] = \n"; 35 | var lines = src.match(/[^\r\n]+/g); 36 | for (var i in lines) { 37 | res += "\t\t'" + lines[i] + "\\n' +\n"; 38 | } 39 | return res + "''\t;\n"; 40 | } 41 | } 42 | }, 43 | protos_concat: { 44 | src: [ 45 | 'build/protoText.js', 46 | 'src/protobufs.js', 47 | ], 48 | dest: 'build/protobufs_concat.js' 49 | }, 50 | 51 | worker: { 52 | src: [ 53 | 'build/curve25519_concat.js', 54 | 'src/curve25519_worker.js', 55 | ], 56 | dest: 'dist/libsignal-protocol-worker.js', 57 | options: { 58 | banner: ';(function(){\nvar Internal = {};\nvar libsignal = {};\n', 59 | footer: '\n})();' 60 | } 61 | 62 | }, 63 | libsignalprotocol: { 64 | src: [ 65 | 'build/curve25519_concat.js', 66 | 'src/curve25519_worker_manager.js', 67 | 'build/components_concat.js', 68 | 69 | 'src/Curve.js', 70 | 'src/crypto.js', 71 | 'src/helpers.js', 72 | 'src/KeyHelper.js', 73 | 'build/protobufs_concat.js', 74 | 'src/SessionRecord.js', 75 | 'src/SignalProtocolAddress.js', 76 | 'src/SessionBuilder.js', 77 | 'src/SessionCipher.js', 78 | 'src/SessionLock.js', 79 | 'src/NumericFingerprint.js' 80 | ], 81 | dest: 'dist/libsignal-protocol.js', 82 | options: { 83 | banner: ';(function(){\nvar Internal = {};\nwindow.libsignal = {};\n', 84 | footer: '\n})();' 85 | } 86 | 87 | }, 88 | test: { 89 | src: [ 90 | 'node_modules/mocha/mocha.js', 91 | 'node_modules/chai/chai.js', 92 | 'node_modules/jquery/dist/jquery.js', 93 | 'node_modules/blanket/dist/mocha/blanket_mocha.js', 94 | 'test/_test.js' 95 | ], 96 | dest: 'test/test.js', 97 | options: { 98 | banner: 'var Internal = {};\nwindow.libsignal = {};\n' 99 | } 100 | } 101 | }, 102 | compile: { 103 | curve25519_compiled: { 104 | src_files: [ 105 | 'native/ed25519/additions/*.c', 106 | 'native/curve25519-donna.c', 107 | 'native/ed25519/*.c', 108 | 'native/ed25519/sha512/sha2big.c' 109 | ], 110 | methods: [ 111 | 'curve25519_donna', 112 | 'curve25519_sign', 113 | 'curve25519_verify', 114 | 'crypto_sign_ed25519_ref10_ge_scalarmult_base', 115 | 'sph_sha512_init', 116 | 'malloc' 117 | ] 118 | } 119 | }, 120 | 121 | jshint: { 122 | files: [ 123 | 'Gruntfile.js', 124 | 'src/**/*.js' 125 | ], // TODO add 'test/**/*.js' 126 | options: { jshintrc: '.jshintrc' }, 127 | }, 128 | jscs: { 129 | all: { 130 | src: [ 131 | 'Gruntfile.js', 132 | 'src/**/*.js' 133 | ] 134 | } 135 | }, 136 | watch: { 137 | jshint: { 138 | files: ['<%= jshint.files %>', '.jshintrc'], 139 | tasks: ['jshint'] 140 | }, 141 | worker: { 142 | files: ['<%= concat.worker.src %>'], 143 | tasks: ['concat:worker'] 144 | }, 145 | libsignalprotocol: { 146 | files: ['<%= concat.libsignalprotocol.src %>'], 147 | tasks: ['concat:libsignalprotocol'] 148 | }, 149 | protos: { 150 | files: ['<%= concat.protos.src %>'], 151 | tasks: ['concat:protos_concat'] 152 | }, 153 | protos_concat: { 154 | files: ['<%= concat.protos_concat.src %>'], 155 | tasks: ['concat:protos_concat'] 156 | } 157 | }, 158 | 159 | connect: { 160 | server: { 161 | options: { 162 | base: '.', 163 | port: 9998 164 | } 165 | } 166 | }, 167 | 'saucelabs-mocha': { 168 | all: { 169 | options: { 170 | urls: ['http://127.0.0.1:9998/test/index.html'], 171 | build: process.env.TRAVIS_JOB_ID, 172 | browsers: [ 173 | { browserName: 'chrome', version: '41' }, 174 | { platform: 'linux', browserName: 'firefox', version: '34' } 175 | ], 176 | testname: 'libsignal-protocol tests', 177 | 'max-duration': 300, 178 | statusCheckAttempts: 200 179 | } 180 | } 181 | } 182 | }); 183 | 184 | Object.keys(grunt.config.get('pkg').devDependencies).forEach(function(key) { 185 | if (/^grunt(?!(-cli)?$)/.test(key)) { // ignore grunt and grunt-cli 186 | grunt.loadNpmTasks(key); 187 | } 188 | }); 189 | 190 | grunt.registerMultiTask('compile', 'Compile the C libraries with emscripten.', function() { 191 | var callback = this.async(); 192 | var outfile = 'build/' + this.target + '.js'; 193 | 194 | var exported_functions = this.data.methods.map(function(name) { 195 | return "'_" + name + "'"; 196 | }); 197 | var flags = [ 198 | '-O1', 199 | '-Qunused-arguments', 200 | '-o', outfile, 201 | '-Inative/ed25519/nacl_includes -Inative/ed25519 -Inative/ed25519/sha512', 202 | '-s', "EXPORTED_FUNCTIONS=\"[" + exported_functions.join(',') + "]\""]; 203 | var command = [].concat('emcc', this.data.src_files, flags).join(' '); 204 | grunt.log.writeln('Compiling via emscripten to ' + outfile); 205 | 206 | var exitCode = 0; 207 | grunt.verbose.subhead(command); 208 | grunt.verbose.writeln(util.format('Expecting exit code %d', exitCode)); 209 | 210 | var child = child_process.exec(command); 211 | child.stdout.on('data', function (d) { grunt.log.write(d); }); 212 | child.stderr.on('data', function (d) { grunt.log.error(d); }); 213 | child.on('exit', function(code) { 214 | if (code !== exitCode) { 215 | grunt.log.error(util.format('Exited with code: %d.', code)); 216 | return callback(false); 217 | } 218 | 219 | grunt.verbose.ok(util.format('Exited with code: %d.', code)); 220 | callback(true); 221 | }); 222 | }); 223 | 224 | grunt.registerTask('dev', ['connect', 'watch']); 225 | grunt.registerTask('test', ['jshint', 'jscs', 'connect', 'saucelabs-mocha']); 226 | grunt.registerTask('default', ['concat']); 227 | grunt.registerTask('build', ['compile', 'concat']); 228 | 229 | }; 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Deprecation Warning**: It is recommended that the TypeScript interface of [libsignal-client](https://github.com/signalapp/libsignal-client) be used for all new applications. This library is no longer used by us or maintained. 2 | 3 | # libsignal-protocol-javascript 4 | 5 | [![Build Status](https://travis-ci.org/signalapp/libsignal-protocol-javascript.svg?branch=master)](https://travis-ci.org/signalapp/libsignal-protocol-javascript) 6 | 7 | 8 | Signal Protocol implementation for the browser based on 9 | [libsignal-protocol-java](https://github.com/signalapp/libsignal-protocol-java). 10 | 11 | ``` 12 | /dist # Distributables 13 | /build # Intermediate build files 14 | /src # JS source files 15 | /native # C source files for curve25519 16 | /protos # Protobuf definitions 17 | /test # Tests 18 | ``` 19 | 20 | ## Overview 21 | A ratcheting forward secrecy protocol that works in synchronous and 22 | asynchronous messaging environments. 23 | 24 | ### PreKeys 25 | 26 | This protocol uses a concept called 'PreKeys'. A PreKey is an ECPublicKey and 27 | an associated unique ID which are stored together by a server. PreKeys can also 28 | be signed. 29 | 30 | At install time, clients generate a single signed PreKey, as well as a large 31 | list of unsigned PreKeys, and transmit all of them to the server. 32 | 33 | ### Sessions 34 | 35 | Signal Protocol is session-oriented. Clients establish a "session," which is 36 | then used for all subsequent encrypt/decrypt operations. There is no need to 37 | ever tear down a session once one has been established. 38 | 39 | Sessions are established in one of two ways: 40 | 41 | 1. PreKeyBundles. A client that wishes to send a message to a recipient can 42 | establish a session by retrieving a PreKeyBundle for that recipient from the 43 | server. 44 | 1. PreKeySignalMessages. A client can receive a PreKeySignalMessage from a 45 | recipient and use it to establish a session. 46 | 47 | ### State 48 | 49 | An established session encapsulates a lot of state between two clients. That 50 | state is maintained in durable records which need to be kept for the life of 51 | the session. 52 | 53 | State is kept in the following places: 54 | 55 | * Identity State. Clients will need to maintain the state of their own identity 56 | key pair, as well as identity keys received from other clients. 57 | * PreKey State. Clients will need to maintain the state of their generated 58 | PreKeys. 59 | * Signed PreKey States. Clients will need to maintain the state of their signed 60 | PreKeys. 61 | * Session State. Clients will need to maintain the state of the sessions they 62 | have established. 63 | 64 | ## Requirements 65 | 66 | This implementation currently depends on the presence of the following 67 | types/interfaces, which are available in most modern browsers. 68 | 69 | * [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) 70 | * [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) 71 | * [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 72 | * [WebCrypto](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) with support for: 73 | - AES-CBC 74 | - HMAC SHA-256 75 | 76 | ## Usage 77 | 78 | Include `dist/libsignal-protocol.js` in your webpage. 79 | 80 | ### Install time 81 | 82 | At install time, a libsignal client needs to generate its identity keys, 83 | registration id, and prekeys. 84 | 85 | ```js 86 | var KeyHelper = libsignal.KeyHelper; 87 | 88 | var registrationId = KeyHelper.generateRegistrationId(); 89 | // Store registrationId somewhere durable and safe. 90 | 91 | KeyHelper.generateIdentityKeyPair().then(function(identityKeyPair) { 92 | // keyPair -> { pubKey: ArrayBuffer, privKey: ArrayBuffer } 93 | // Store identityKeyPair somewhere durable and safe. 94 | }); 95 | 96 | KeyHelper.generatePreKey(keyId).then(function(preKey) { 97 | store.storePreKey(preKey.keyId, preKey.keyPair); 98 | }); 99 | 100 | KeyHelper.generateSignedPreKey(identityKeyPair, keyId).then(function(signedPreKey) { 101 | store.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair); 102 | }); 103 | 104 | // Register preKeys and signedPreKey with the server 105 | ``` 106 | 107 | ### Building a session 108 | 109 | A libsignal client needs to implement a storage interface that will manage 110 | loading and storing of identity, prekeys, signed prekeys, and session state. 111 | See `test/InMemorySignalProtocolStore.js` for an example. 112 | 113 | Once this is implemented, building a session is fairly straightforward: 114 | 115 | ```js 116 | var store = new MySignalProtocolStore(); 117 | var address = new libsignal.SignalProtocolAddress(recipientId, deviceId); 118 | 119 | // Instantiate a SessionBuilder for a remote recipientId + deviceId tuple. 120 | var sessionBuilder = new libsignal.SessionBuilder(store, address); 121 | 122 | // Process a prekey fetched from the server. Returns a promise that resolves 123 | // once a session is created and saved in the store, or rejects if the 124 | // identityKey differs from a previously seen identity for this address. 125 | var promise = sessionBuilder.processPreKey({ 126 | registrationId: , 127 | identityKey: , 128 | signedPreKey: { 129 | keyId : , 130 | publicKey : , 131 | signature : 132 | }, 133 | preKey: { 134 | keyId : , 135 | publicKey : 136 | } 137 | }); 138 | 139 | promise.then(function onsuccess() { 140 | // encrypt messages 141 | }); 142 | 143 | promise.catch(function onerror(error) { 144 | // handle identity key conflict 145 | }); 146 | ``` 147 | 148 | ### Encrypting 149 | 150 | Once you have a session established with an address, you can encrypt messages 151 | using SessionCipher. 152 | 153 | ```js 154 | var plaintext = "Hello world"; 155 | var sessionCipher = new libsignal.SessionCipher(store, address); 156 | sessionCipher.encrypt(plaintext).then(function(ciphertext) { 157 | // ciphertext -> { type: , body: } 158 | handle(ciphertext.type, ciphertext.body); 159 | }); 160 | ``` 161 | 162 | ### Decrypting 163 | 164 | Ciphertexts come in two flavors: WhisperMessage and PreKeyWhisperMessage. 165 | 166 | ```js 167 | var address = new SignalProtocolAddress(recipientId, deviceId); 168 | var sessionCipher = new SessionCipher(store, address); 169 | 170 | // Decrypt a PreKeyWhisperMessage by first establishing a new session. 171 | // Returns a promise that resolves when the message is decrypted or 172 | // rejects if the identityKey differs from a previously seen identity for this 173 | // address. 174 | sessionCipher.decryptPreKeyWhisperMessage(ciphertext).then(function(plaintext) { 175 | // handle plaintext ArrayBuffer 176 | }).catch(function(error) { 177 | // handle identity key conflict 178 | }); 179 | 180 | // Decrypt a normal message using an existing session 181 | var sessionCipher = new SessionCipher(store, address); 182 | sessionCipher.decryptWhisperMessage(ciphertext).then(function(plaintext) { 183 | // handle plaintext ArrayBuffer 184 | }); 185 | ``` 186 | 187 | ## Building 188 | 189 | To compile curve25519 from C source files in `/native`, install 190 | [emscripten](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html). 191 | 192 | ``` 193 | grunt compile 194 | ``` 195 | 196 | ## License 197 | 198 | Copyright 2015-2018 Open Whisper Systems 199 | 200 | Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html 201 | -------------------------------------------------------------------------------- /build/protoText.js: -------------------------------------------------------------------------------- 1 | var Internal = Internal || {}; 2 | 3 | Internal.protoText = function() { 4 | var protoText = {}; 5 | 6 | protoText['protos/WhisperTextProtocol.proto'] = 7 | 'package textsecure;\n' + 8 | 'option java_package = "org.whispersystems.libsignal.protocol";\n' + 9 | 'option java_outer_classname = "WhisperProtos";\n' + 10 | 'message WhisperMessage {\n' + 11 | ' optional bytes ephemeralKey = 1;\n' + 12 | ' optional uint32 counter = 2;\n' + 13 | ' optional uint32 previousCounter = 3;\n' + 14 | ' optional bytes ciphertext = 4; // PushMessageContent\n' + 15 | '}\n' + 16 | 'message PreKeyWhisperMessage {\n' + 17 | ' optional uint32 registrationId = 5;\n' + 18 | ' optional uint32 preKeyId = 1;\n' + 19 | ' optional uint32 signedPreKeyId = 6;\n' + 20 | ' optional bytes baseKey = 2;\n' + 21 | ' optional bytes identityKey = 3;\n' + 22 | ' optional bytes message = 4; // WhisperMessage\n' + 23 | '}\n' + 24 | 'message KeyExchangeMessage {\n' + 25 | ' optional uint32 id = 1;\n' + 26 | ' optional bytes baseKey = 2;\n' + 27 | ' optional bytes ephemeralKey = 3;\n' + 28 | ' optional bytes identityKey = 4;\n' + 29 | ' optional bytes baseKeySignature = 5;\n' + 30 | '}\n' + 31 | '' ; 32 | 33 | return protoText; 34 | }(); -------------------------------------------------------------------------------- /build/protobufs_concat.js: -------------------------------------------------------------------------------- 1 | var Internal = Internal || {}; 2 | 3 | Internal.protoText = function() { 4 | var protoText = {}; 5 | 6 | protoText['protos/WhisperTextProtocol.proto'] = 7 | 'package textsecure;\n' + 8 | 'option java_package = "org.whispersystems.libsignal.protocol";\n' + 9 | 'option java_outer_classname = "WhisperProtos";\n' + 10 | 'message WhisperMessage {\n' + 11 | ' optional bytes ephemeralKey = 1;\n' + 12 | ' optional uint32 counter = 2;\n' + 13 | ' optional uint32 previousCounter = 3;\n' + 14 | ' optional bytes ciphertext = 4; // PushMessageContent\n' + 15 | '}\n' + 16 | 'message PreKeyWhisperMessage {\n' + 17 | ' optional uint32 registrationId = 5;\n' + 18 | ' optional uint32 preKeyId = 1;\n' + 19 | ' optional uint32 signedPreKeyId = 6;\n' + 20 | ' optional bytes baseKey = 2;\n' + 21 | ' optional bytes identityKey = 3;\n' + 22 | ' optional bytes message = 4; // WhisperMessage\n' + 23 | '}\n' + 24 | 'message KeyExchangeMessage {\n' + 25 | ' optional uint32 id = 1;\n' + 26 | ' optional bytes baseKey = 2;\n' + 27 | ' optional bytes ephemeralKey = 3;\n' + 28 | ' optional bytes identityKey = 4;\n' + 29 | ' optional bytes baseKeySignature = 5;\n' + 30 | '}\n' + 31 | '' ; 32 | 33 | return protoText; 34 | }(); 35 | /* vim: ts=4:sw=4 */ 36 | var Internal = Internal || {}; 37 | 38 | Internal.protobuf = function() { 39 | 'use strict'; 40 | 41 | function loadProtoBufs(filename) { 42 | return dcodeIO.ProtoBuf.loadProto(Internal.protoText['protos/' + filename]).build('textsecure'); 43 | } 44 | 45 | var protocolMessages = loadProtoBufs('WhisperTextProtocol.proto'); 46 | 47 | return { 48 | WhisperMessage : protocolMessages.WhisperMessage, 49 | PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage 50 | }; 51 | }(); 52 | -------------------------------------------------------------------------------- /native/ed25519/additions/compare.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "compare.h" 3 | 4 | /* Const-time comparison from SUPERCOP, but here it's only used for 5 | signature verification, so doesn't need to be const-time. But 6 | copied the nacl version anyways. */ 7 | int crypto_verify_32_ref(const unsigned char *x, const unsigned char *y) 8 | { 9 | unsigned int differentbits = 0; 10 | #define F(i) differentbits |= x[i] ^ y[i]; 11 | F(0) 12 | F(1) 13 | F(2) 14 | F(3) 15 | F(4) 16 | F(5) 17 | F(6) 18 | F(7) 19 | F(8) 20 | F(9) 21 | F(10) 22 | F(11) 23 | F(12) 24 | F(13) 25 | F(14) 26 | F(15) 27 | F(16) 28 | F(17) 29 | F(18) 30 | F(19) 31 | F(20) 32 | F(21) 33 | F(22) 34 | F(23) 35 | F(24) 36 | F(25) 37 | F(26) 38 | F(27) 39 | F(28) 40 | F(29) 41 | F(30) 42 | F(31) 43 | return (1 & ((differentbits - 1) >> 8)) - 1; 44 | } 45 | -------------------------------------------------------------------------------- /native/ed25519/additions/compare.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMPARE_H__ 2 | #define __COMPARE_H__ 3 | 4 | int crypto_verify_32_ref(const unsigned char *b1, const unsigned char *b2); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /native/ed25519/additions/curve_sigs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ge.h" 3 | #include "curve_sigs.h" 4 | #include "crypto_sign.h" 5 | 6 | void curve25519_keygen(unsigned char* curve25519_pubkey_out, 7 | unsigned char* curve25519_privkey_in) 8 | { 9 | ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */ 10 | unsigned char ed_pubkey[32]; /* privkey followed by pubkey */ 11 | fe ed_y, one, ed_y_plus_one, one_minus_ed_y, inv_one_minus_ed_y; 12 | fe mont_x; 13 | 14 | /* Perform a fixed-base multiplication of the Edwards base point, 15 | (which is efficient due to precalculated tables), then convert 16 | to the Curve25519 montgomery-format public key. In particular, 17 | convert Curve25519's "montgomery" x-coordinate into an Ed25519 18 | "edwards" y-coordinate: 19 | 20 | mont_x = (ed_y +1 1) / (1 - ed_y) 21 | */ 22 | 23 | ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey_in); 24 | ge_p3_tobytes(ed_pubkey, &ed_pubkey_point); 25 | ed_pubkey[31] = ed_pubkey[31] & 0x7F; /* Mask off sign bit */ 26 | fe_frombytes(ed_y, ed_pubkey); 27 | 28 | fe_1(one); 29 | fe_add(ed_y_plus_one, ed_y, one); 30 | fe_sub(one_minus_ed_y, one, ed_y); 31 | fe_invert(inv_one_minus_ed_y, one_minus_ed_y); 32 | fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y); 33 | fe_tobytes(curve25519_pubkey_out, mont_x); 34 | } 35 | 36 | void curve25519_sign(unsigned char* signature_out, 37 | unsigned char* curve25519_privkey, 38 | unsigned char* msg, unsigned long msg_len) 39 | { 40 | ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */ 41 | unsigned char ed_keypair[64]; /* privkey followed by pubkey */ 42 | unsigned char sigbuf[msg_len + 64]; /* working buffer */ 43 | unsigned long long sigbuf_out_len = 0; 44 | unsigned char sign_bit = 0; 45 | 46 | /* Convert the Curve25519 privkey to an Ed25519 keypair */ 47 | memmove(ed_keypair, curve25519_privkey, 32); 48 | ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey); 49 | ge_p3_tobytes(ed_keypair + 32, &ed_pubkey_point); 50 | sign_bit = ed_keypair[63] & 0x80; 51 | 52 | /* Perform an Ed25519 signature with explicit private key */ 53 | crypto_sign_modified(sigbuf, &sigbuf_out_len, msg, msg_len, ed_keypair); 54 | memmove(signature_out, sigbuf, 64); 55 | 56 | /* Encode the sign bit into signature (in unused high bit of S) */ 57 | signature_out[63] |= sign_bit; 58 | } 59 | 60 | int curve25519_verify(unsigned char* signature, 61 | unsigned char* curve25519_pubkey, 62 | unsigned char* msg, unsigned long msg_len) 63 | { 64 | fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one; 65 | fe one; 66 | fe ed_y; 67 | unsigned char ed_pubkey[32]; 68 | unsigned long long some_retval; 69 | unsigned char verifybuf[msg_len + 64]; /* working buffer */ 70 | unsigned char verifybuf2[msg_len + 64]; /* working buffer #2 */ 71 | 72 | /* Convert the Curve25519 public key into an Ed25519 public key. In 73 | particular, convert Curve25519's "montgomery" x-coordinate into an 74 | Ed25519 "edwards" y-coordinate: 75 | 76 | ed_y = (mont_x - 1) / (mont_x + 1) 77 | 78 | Then move the sign bit into the pubkey from the signature. 79 | */ 80 | fe_frombytes(mont_x, curve25519_pubkey); 81 | fe_1(one); 82 | fe_sub(mont_x_minus_one, mont_x, one); 83 | fe_add(mont_x_plus_one, mont_x, one); 84 | fe_invert(inv_mont_x_plus_one, mont_x_plus_one); 85 | fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one); 86 | fe_tobytes(ed_pubkey, ed_y); 87 | 88 | /* Copy the sign bit, and remove it from signature */ 89 | ed_pubkey[31] |= (signature[63] & 0x80); 90 | signature[63] &= 0x7F; 91 | 92 | memmove(verifybuf, signature, 64); 93 | memmove(verifybuf+64, msg, msg_len); 94 | 95 | /* Then perform a normal Ed25519 verification, return 0 on success */ 96 | return crypto_sign_open(verifybuf2, &some_retval, verifybuf, 64 + msg_len, ed_pubkey); 97 | } 98 | -------------------------------------------------------------------------------- /native/ed25519/additions/curve_sigs.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __CURVE_SIGS_H__ 3 | #define __CURVE_SIGS_H__ 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | void curve25519_keygen(unsigned char* curve25519_pubkey_out, 10 | unsigned char* curve25519_privkey_in); 11 | 12 | void curve25519_sign(unsigned char* signature_out, 13 | unsigned char* curve25519_privkey, 14 | unsigned char* msg, unsigned long msg_len); 15 | 16 | /* returns 0 on success */ 17 | int curve25519_verify(unsigned char* signature, 18 | unsigned char* curve25519_pubkey, 19 | unsigned char* msg, unsigned long msg_len); 20 | 21 | /* helper function - modified version of crypto_sign() to use 22 | explicit private key */ 23 | int crypto_sign_modified( 24 | unsigned char *sm,unsigned long long *smlen, 25 | const unsigned char *m,unsigned long long mlen, 26 | const unsigned char *sk 27 | ); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /native/ed25519/additions/sha512.c: -------------------------------------------------------------------------------- 1 | #include "sha512.h" 2 | #include "sph_sha2.h" 3 | 4 | int crypto_hash_sha512_ref(unsigned char *output ,const unsigned char *input, 5 | unsigned long long len) 6 | { 7 | sph_sha512_context ctx; 8 | sph_sha512_init(&ctx); 9 | sph_sha512(&ctx, input, len); 10 | sph_sha512_close(&ctx, output); 11 | return 0; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /native/ed25519/additions/sha512.h: -------------------------------------------------------------------------------- 1 | #ifndef __SHA512_H__ 2 | #define __SHA512_H__ 3 | 4 | #include "sha512.h" 5 | #include "sph_sha2.h" 6 | 7 | int crypto_hash_sha512_ref(unsigned char *output ,const unsigned char *input, 8 | unsigned long long len); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /native/ed25519/additions/sign_modified.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "crypto_sign.h" 3 | #include "crypto_hash_sha512.h" 4 | #include "ge.h" 5 | #include "sc.h" 6 | 7 | /* NEW: Compare to pristine crypto_sign() 8 | Uses explicit private key for nonce derivation and as scalar, 9 | instead of deriving both from a master key. 10 | */ 11 | int crypto_sign_modified( 12 | unsigned char *sm,unsigned long long *smlen, 13 | const unsigned char *m,unsigned long long mlen, 14 | const unsigned char *sk 15 | ) 16 | { 17 | unsigned char pk[32]; 18 | //unsigned char az[64]; 19 | unsigned char nonce[64]; 20 | unsigned char hram[64]; 21 | ge_p3 R; 22 | 23 | memmove(pk,sk + 32,32); 24 | 25 | *smlen = mlen + 64; 26 | memmove(sm + 64,m,mlen); 27 | memmove(sm + 32,sk,32); /* NEW: Use privkey directly for nonce derivation */ 28 | crypto_hash_sha512(nonce,sm + 32,mlen + 32); 29 | memmove(sm + 32,pk,32); 30 | 31 | sc_reduce(nonce); 32 | ge_scalarmult_base(&R,nonce); 33 | ge_p3_tobytes(sm,&R); 34 | 35 | crypto_hash_sha512(hram,sm,mlen + 64); 36 | sc_reduce(hram); 37 | sc_muladd(sm + 32,hram,sk,nonce); /* NEW: Use privkey directly */ 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /native/ed25519/api.h: -------------------------------------------------------------------------------- 1 | #define CRYPTO_SECRETKEYBYTES 64 2 | #define CRYPTO_PUBLICKEYBYTES 32 3 | #define CRYPTO_BYTES 64 4 | #define CRYPTO_DETERMINISTIC 1 5 | -------------------------------------------------------------------------------- /native/ed25519/base2.h: -------------------------------------------------------------------------------- 1 | { 2 | { 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 }, 3 | { -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 }, 4 | { -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 }, 5 | }, 6 | { 7 | { 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 }, 8 | { 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 }, 9 | { 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 }, 10 | }, 11 | { 12 | { 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 }, 13 | { 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 }, 14 | { 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 }, 15 | }, 16 | { 17 | { 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 }, 18 | { -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 }, 19 | { 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 }, 20 | }, 21 | { 22 | { -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 }, 23 | { -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 }, 24 | { 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 }, 25 | }, 26 | { 27 | { -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 }, 28 | { 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 }, 29 | { 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 }, 30 | }, 31 | { 32 | { -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 }, 33 | { -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 }, 34 | { -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 }, 35 | }, 36 | { 37 | { -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 }, 38 | { -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 }, 39 | { -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 }, 40 | }, 41 | -------------------------------------------------------------------------------- /native/ed25519/d.h: -------------------------------------------------------------------------------- 1 | -10913610,13857413,-15372611,6949391,114729,-8787816,-6275908,-3247719,-18696448,-12055116 2 | -------------------------------------------------------------------------------- /native/ed25519/d2.h: -------------------------------------------------------------------------------- 1 | -21827239,-5839606,-30745221,13898782,229458,15978800,-12551817,-6495438,29715968,9444199 2 | -------------------------------------------------------------------------------- /native/ed25519/fe.h: -------------------------------------------------------------------------------- 1 | #ifndef FE_H 2 | #define FE_H 3 | 4 | #include "crypto_int32.h" 5 | 6 | typedef crypto_int32 fe[10]; 7 | 8 | /* 9 | fe means field element. 10 | Here the field is \Z/(2^255-19). 11 | An element t, entries t[0]...t[9], represents the integer 12 | t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. 13 | Bounds on each t[i] vary depending on context. 14 | */ 15 | 16 | #define fe_frombytes crypto_sign_ed25519_ref10_fe_frombytes 17 | #define fe_tobytes crypto_sign_ed25519_ref10_fe_tobytes 18 | #define fe_copy crypto_sign_ed25519_ref10_fe_copy 19 | #define fe_isnonzero crypto_sign_ed25519_ref10_fe_isnonzero 20 | #define fe_isnegative crypto_sign_ed25519_ref10_fe_isnegative 21 | #define fe_0 crypto_sign_ed25519_ref10_fe_0 22 | #define fe_1 crypto_sign_ed25519_ref10_fe_1 23 | #define fe_cswap crypto_sign_ed25519_ref10_fe_cswap 24 | #define fe_cmov crypto_sign_ed25519_ref10_fe_cmov 25 | #define fe_add crypto_sign_ed25519_ref10_fe_add 26 | #define fe_sub crypto_sign_ed25519_ref10_fe_sub 27 | #define fe_neg crypto_sign_ed25519_ref10_fe_neg 28 | #define fe_mul crypto_sign_ed25519_ref10_fe_mul 29 | #define fe_sq crypto_sign_ed25519_ref10_fe_sq 30 | #define fe_sq2 crypto_sign_ed25519_ref10_fe_sq2 31 | #define fe_mul121666 crypto_sign_ed25519_ref10_fe_mul121666 32 | #define fe_invert crypto_sign_ed25519_ref10_fe_invert 33 | #define fe_pow22523 crypto_sign_ed25519_ref10_fe_pow22523 34 | 35 | extern void fe_frombytes(fe,const unsigned char *); 36 | extern void fe_tobytes(unsigned char *,const fe); 37 | 38 | extern void fe_copy(fe,const fe); 39 | extern int fe_isnonzero(const fe); 40 | extern int fe_isnegative(const fe); 41 | extern void fe_0(fe); 42 | extern void fe_1(fe); 43 | extern void fe_cswap(fe,fe,unsigned int); 44 | extern void fe_cmov(fe,const fe,unsigned int); 45 | 46 | extern void fe_add(fe,const fe,const fe); 47 | extern void fe_sub(fe,const fe,const fe); 48 | extern void fe_neg(fe,const fe); 49 | extern void fe_mul(fe,const fe,const fe); 50 | extern void fe_sq(fe,const fe); 51 | extern void fe_sq2(fe,const fe); 52 | extern void fe_mul121666(fe,const fe); 53 | extern void fe_invert(fe,const fe); 54 | extern void fe_pow22523(fe,const fe); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /native/ed25519/fe_0.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | h = 0 5 | */ 6 | 7 | void fe_0(fe h) 8 | { 9 | h[0] = 0; 10 | h[1] = 0; 11 | h[2] = 0; 12 | h[3] = 0; 13 | h[4] = 0; 14 | h[5] = 0; 15 | h[6] = 0; 16 | h[7] = 0; 17 | h[8] = 0; 18 | h[9] = 0; 19 | } 20 | -------------------------------------------------------------------------------- /native/ed25519/fe_1.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | h = 1 5 | */ 6 | 7 | void fe_1(fe h) 8 | { 9 | h[0] = 1; 10 | h[1] = 0; 11 | h[2] = 0; 12 | h[3] = 0; 13 | h[4] = 0; 14 | h[5] = 0; 15 | h[6] = 0; 16 | h[7] = 0; 17 | h[8] = 0; 18 | h[9] = 0; 19 | } 20 | -------------------------------------------------------------------------------- /native/ed25519/fe_add.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | h = f + g 5 | Can overlap h with f or g. 6 | 7 | Preconditions: 8 | |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 9 | |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 10 | 11 | Postconditions: 12 | |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 13 | */ 14 | 15 | void fe_add(fe h,const fe f,const fe g) 16 | { 17 | crypto_int32 f0 = f[0]; 18 | crypto_int32 f1 = f[1]; 19 | crypto_int32 f2 = f[2]; 20 | crypto_int32 f3 = f[3]; 21 | crypto_int32 f4 = f[4]; 22 | crypto_int32 f5 = f[5]; 23 | crypto_int32 f6 = f[6]; 24 | crypto_int32 f7 = f[7]; 25 | crypto_int32 f8 = f[8]; 26 | crypto_int32 f9 = f[9]; 27 | crypto_int32 g0 = g[0]; 28 | crypto_int32 g1 = g[1]; 29 | crypto_int32 g2 = g[2]; 30 | crypto_int32 g3 = g[3]; 31 | crypto_int32 g4 = g[4]; 32 | crypto_int32 g5 = g[5]; 33 | crypto_int32 g6 = g[6]; 34 | crypto_int32 g7 = g[7]; 35 | crypto_int32 g8 = g[8]; 36 | crypto_int32 g9 = g[9]; 37 | crypto_int32 h0 = f0 + g0; 38 | crypto_int32 h1 = f1 + g1; 39 | crypto_int32 h2 = f2 + g2; 40 | crypto_int32 h3 = f3 + g3; 41 | crypto_int32 h4 = f4 + g4; 42 | crypto_int32 h5 = f5 + g5; 43 | crypto_int32 h6 = f6 + g6; 44 | crypto_int32 h7 = f7 + g7; 45 | crypto_int32 h8 = f8 + g8; 46 | crypto_int32 h9 = f9 + g9; 47 | h[0] = h0; 48 | h[1] = h1; 49 | h[2] = h2; 50 | h[3] = h3; 51 | h[4] = h4; 52 | h[5] = h5; 53 | h[6] = h6; 54 | h[7] = h7; 55 | h[8] = h8; 56 | h[9] = h9; 57 | } 58 | -------------------------------------------------------------------------------- /native/ed25519/fe_cmov.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | Replace (f,g) with (g,g) if b == 1; 5 | replace (f,g) with (f,g) if b == 0. 6 | 7 | Preconditions: b in {0,1}. 8 | */ 9 | 10 | void fe_cmov(fe f,const fe g,unsigned int b) 11 | { 12 | crypto_int32 f0 = f[0]; 13 | crypto_int32 f1 = f[1]; 14 | crypto_int32 f2 = f[2]; 15 | crypto_int32 f3 = f[3]; 16 | crypto_int32 f4 = f[4]; 17 | crypto_int32 f5 = f[5]; 18 | crypto_int32 f6 = f[6]; 19 | crypto_int32 f7 = f[7]; 20 | crypto_int32 f8 = f[8]; 21 | crypto_int32 f9 = f[9]; 22 | crypto_int32 g0 = g[0]; 23 | crypto_int32 g1 = g[1]; 24 | crypto_int32 g2 = g[2]; 25 | crypto_int32 g3 = g[3]; 26 | crypto_int32 g4 = g[4]; 27 | crypto_int32 g5 = g[5]; 28 | crypto_int32 g6 = g[6]; 29 | crypto_int32 g7 = g[7]; 30 | crypto_int32 g8 = g[8]; 31 | crypto_int32 g9 = g[9]; 32 | crypto_int32 x0 = f0 ^ g0; 33 | crypto_int32 x1 = f1 ^ g1; 34 | crypto_int32 x2 = f2 ^ g2; 35 | crypto_int32 x3 = f3 ^ g3; 36 | crypto_int32 x4 = f4 ^ g4; 37 | crypto_int32 x5 = f5 ^ g5; 38 | crypto_int32 x6 = f6 ^ g6; 39 | crypto_int32 x7 = f7 ^ g7; 40 | crypto_int32 x8 = f8 ^ g8; 41 | crypto_int32 x9 = f9 ^ g9; 42 | b = -b; 43 | x0 &= b; 44 | x1 &= b; 45 | x2 &= b; 46 | x3 &= b; 47 | x4 &= b; 48 | x5 &= b; 49 | x6 &= b; 50 | x7 &= b; 51 | x8 &= b; 52 | x9 &= b; 53 | f[0] = f0 ^ x0; 54 | f[1] = f1 ^ x1; 55 | f[2] = f2 ^ x2; 56 | f[3] = f3 ^ x3; 57 | f[4] = f4 ^ x4; 58 | f[5] = f5 ^ x5; 59 | f[6] = f6 ^ x6; 60 | f[7] = f7 ^ x7; 61 | f[8] = f8 ^ x8; 62 | f[9] = f9 ^ x9; 63 | } 64 | -------------------------------------------------------------------------------- /native/ed25519/fe_copy.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | h = f 5 | */ 6 | 7 | void fe_copy(fe h,const fe f) 8 | { 9 | crypto_int32 f0 = f[0]; 10 | crypto_int32 f1 = f[1]; 11 | crypto_int32 f2 = f[2]; 12 | crypto_int32 f3 = f[3]; 13 | crypto_int32 f4 = f[4]; 14 | crypto_int32 f5 = f[5]; 15 | crypto_int32 f6 = f[6]; 16 | crypto_int32 f7 = f[7]; 17 | crypto_int32 f8 = f[8]; 18 | crypto_int32 f9 = f[9]; 19 | h[0] = f0; 20 | h[1] = f1; 21 | h[2] = f2; 22 | h[3] = f3; 23 | h[4] = f4; 24 | h[5] = f5; 25 | h[6] = f6; 26 | h[7] = f7; 27 | h[8] = f8; 28 | h[9] = f9; 29 | } 30 | -------------------------------------------------------------------------------- /native/ed25519/fe_frombytes.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | #include "crypto_int64.h" 3 | #include "crypto_uint64.h" 4 | 5 | static crypto_uint64 load_3(const unsigned char *in) 6 | { 7 | crypto_uint64 result; 8 | result = (crypto_uint64) in[0]; 9 | result |= ((crypto_uint64) in[1]) << 8; 10 | result |= ((crypto_uint64) in[2]) << 16; 11 | return result; 12 | } 13 | 14 | static crypto_uint64 load_4(const unsigned char *in) 15 | { 16 | crypto_uint64 result; 17 | result = (crypto_uint64) in[0]; 18 | result |= ((crypto_uint64) in[1]) << 8; 19 | result |= ((crypto_uint64) in[2]) << 16; 20 | result |= ((crypto_uint64) in[3]) << 24; 21 | return result; 22 | } 23 | 24 | /* 25 | Ignores top bit of h. 26 | */ 27 | 28 | void fe_frombytes(fe h,const unsigned char *s) 29 | { 30 | crypto_int64 h0 = load_4(s); 31 | crypto_int64 h1 = load_3(s + 4) << 6; 32 | crypto_int64 h2 = load_3(s + 7) << 5; 33 | crypto_int64 h3 = load_3(s + 10) << 3; 34 | crypto_int64 h4 = load_3(s + 13) << 2; 35 | crypto_int64 h5 = load_4(s + 16); 36 | crypto_int64 h6 = load_3(s + 20) << 7; 37 | crypto_int64 h7 = load_3(s + 23) << 5; 38 | crypto_int64 h8 = load_3(s + 26) << 4; 39 | crypto_int64 h9 = (load_3(s + 29) & 8388607) << 2; 40 | crypto_int64 carry0; 41 | crypto_int64 carry1; 42 | crypto_int64 carry2; 43 | crypto_int64 carry3; 44 | crypto_int64 carry4; 45 | crypto_int64 carry5; 46 | crypto_int64 carry6; 47 | crypto_int64 carry7; 48 | crypto_int64 carry8; 49 | crypto_int64 carry9; 50 | 51 | carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; 52 | carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; 53 | carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; 54 | carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; 55 | carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; 56 | 57 | carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; 58 | carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; 59 | carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; 60 | carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; 61 | carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; 62 | 63 | h[0] = h0; 64 | h[1] = h1; 65 | h[2] = h2; 66 | h[3] = h3; 67 | h[4] = h4; 68 | h[5] = h5; 69 | h[6] = h6; 70 | h[7] = h7; 71 | h[8] = h8; 72 | h[9] = h9; 73 | } 74 | -------------------------------------------------------------------------------- /native/ed25519/fe_invert.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | void fe_invert(fe out,const fe z) 4 | { 5 | fe t0; 6 | fe t1; 7 | fe t2; 8 | fe t3; 9 | int i; 10 | 11 | #include "pow225521.h" 12 | 13 | return; 14 | } 15 | -------------------------------------------------------------------------------- /native/ed25519/fe_isnegative.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | return 1 if f is in {1,3,5,...,q-2} 5 | return 0 if f is in {0,2,4,...,q-1} 6 | 7 | Preconditions: 8 | |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 9 | */ 10 | 11 | int fe_isnegative(const fe f) 12 | { 13 | unsigned char s[32]; 14 | fe_tobytes(s,f); 15 | return s[0] & 1; 16 | } 17 | -------------------------------------------------------------------------------- /native/ed25519/fe_isnonzero.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | #include "crypto_verify_32.h" 3 | 4 | /* 5 | return 1 if f == 0 6 | return 0 if f != 0 7 | 8 | Preconditions: 9 | |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 10 | */ 11 | 12 | static const unsigned char zero[32]; 13 | 14 | int fe_isnonzero(const fe f) 15 | { 16 | unsigned char s[32]; 17 | fe_tobytes(s,f); 18 | return crypto_verify_32(s,zero); 19 | } 20 | -------------------------------------------------------------------------------- /native/ed25519/fe_neg.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | h = -f 5 | 6 | Preconditions: 7 | |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 8 | 9 | Postconditions: 10 | |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 11 | */ 12 | 13 | void fe_neg(fe h,const fe f) 14 | { 15 | crypto_int32 f0 = f[0]; 16 | crypto_int32 f1 = f[1]; 17 | crypto_int32 f2 = f[2]; 18 | crypto_int32 f3 = f[3]; 19 | crypto_int32 f4 = f[4]; 20 | crypto_int32 f5 = f[5]; 21 | crypto_int32 f6 = f[6]; 22 | crypto_int32 f7 = f[7]; 23 | crypto_int32 f8 = f[8]; 24 | crypto_int32 f9 = f[9]; 25 | crypto_int32 h0 = -f0; 26 | crypto_int32 h1 = -f1; 27 | crypto_int32 h2 = -f2; 28 | crypto_int32 h3 = -f3; 29 | crypto_int32 h4 = -f4; 30 | crypto_int32 h5 = -f5; 31 | crypto_int32 h6 = -f6; 32 | crypto_int32 h7 = -f7; 33 | crypto_int32 h8 = -f8; 34 | crypto_int32 h9 = -f9; 35 | h[0] = h0; 36 | h[1] = h1; 37 | h[2] = h2; 38 | h[3] = h3; 39 | h[4] = h4; 40 | h[5] = h5; 41 | h[6] = h6; 42 | h[7] = h7; 43 | h[8] = h8; 44 | h[9] = h9; 45 | } 46 | -------------------------------------------------------------------------------- /native/ed25519/fe_pow22523.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | void fe_pow22523(fe out,const fe z) 4 | { 5 | fe t0; 6 | fe t1; 7 | fe t2; 8 | int i; 9 | 10 | #include "pow22523.h" 11 | 12 | return; 13 | } 14 | -------------------------------------------------------------------------------- /native/ed25519/fe_sq.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | #include "crypto_int64.h" 3 | 4 | /* 5 | h = f * f 6 | Can overlap h with f. 7 | 8 | Preconditions: 9 | |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. 10 | 11 | Postconditions: 12 | |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. 13 | */ 14 | 15 | /* 16 | See fe_mul.c for discussion of implementation strategy. 17 | */ 18 | 19 | void fe_sq(fe h,const fe f) 20 | { 21 | crypto_int32 f0 = f[0]; 22 | crypto_int32 f1 = f[1]; 23 | crypto_int32 f2 = f[2]; 24 | crypto_int32 f3 = f[3]; 25 | crypto_int32 f4 = f[4]; 26 | crypto_int32 f5 = f[5]; 27 | crypto_int32 f6 = f[6]; 28 | crypto_int32 f7 = f[7]; 29 | crypto_int32 f8 = f[8]; 30 | crypto_int32 f9 = f[9]; 31 | crypto_int32 f0_2 = 2 * f0; 32 | crypto_int32 f1_2 = 2 * f1; 33 | crypto_int32 f2_2 = 2 * f2; 34 | crypto_int32 f3_2 = 2 * f3; 35 | crypto_int32 f4_2 = 2 * f4; 36 | crypto_int32 f5_2 = 2 * f5; 37 | crypto_int32 f6_2 = 2 * f6; 38 | crypto_int32 f7_2 = 2 * f7; 39 | crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ 40 | crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ 41 | crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ 42 | crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ 43 | crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ 44 | crypto_int64 f0f0 = f0 * (crypto_int64) f0; 45 | crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1; 46 | crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2; 47 | crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3; 48 | crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4; 49 | crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5; 50 | crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6; 51 | crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7; 52 | crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8; 53 | crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9; 54 | crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1; 55 | crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2; 56 | crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2; 57 | crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4; 58 | crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2; 59 | crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6; 60 | crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2; 61 | crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8; 62 | crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38; 63 | crypto_int64 f2f2 = f2 * (crypto_int64) f2; 64 | crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3; 65 | crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4; 66 | crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5; 67 | crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6; 68 | crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7; 69 | crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19; 70 | crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38; 71 | crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3; 72 | crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4; 73 | crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2; 74 | crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6; 75 | crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38; 76 | crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19; 77 | crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38; 78 | crypto_int64 f4f4 = f4 * (crypto_int64) f4; 79 | crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5; 80 | crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19; 81 | crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38; 82 | crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19; 83 | crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38; 84 | crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38; 85 | crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19; 86 | crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38; 87 | crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19; 88 | crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38; 89 | crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19; 90 | crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38; 91 | crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19; 92 | crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38; 93 | crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38; 94 | crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19; 95 | crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38; 96 | crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19; 97 | crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38; 98 | crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38; 99 | crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; 100 | crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; 101 | crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; 102 | crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; 103 | crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; 104 | crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; 105 | crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; 106 | crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; 107 | crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; 108 | crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; 109 | crypto_int64 carry0; 110 | crypto_int64 carry1; 111 | crypto_int64 carry2; 112 | crypto_int64 carry3; 113 | crypto_int64 carry4; 114 | crypto_int64 carry5; 115 | crypto_int64 carry6; 116 | crypto_int64 carry7; 117 | crypto_int64 carry8; 118 | crypto_int64 carry9; 119 | 120 | carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; 121 | carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; 122 | 123 | carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; 124 | carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; 125 | 126 | carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; 127 | carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; 128 | 129 | carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; 130 | carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; 131 | 132 | carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; 133 | carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; 134 | 135 | carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; 136 | 137 | carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; 138 | 139 | h[0] = h0; 140 | h[1] = h1; 141 | h[2] = h2; 142 | h[3] = h3; 143 | h[4] = h4; 144 | h[5] = h5; 145 | h[6] = h6; 146 | h[7] = h7; 147 | h[8] = h8; 148 | h[9] = h9; 149 | } 150 | -------------------------------------------------------------------------------- /native/ed25519/fe_sq2.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | #include "crypto_int64.h" 3 | 4 | /* 5 | h = 2 * f * f 6 | Can overlap h with f. 7 | 8 | Preconditions: 9 | |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. 10 | 11 | Postconditions: 12 | |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. 13 | */ 14 | 15 | /* 16 | See fe_mul.c for discussion of implementation strategy. 17 | */ 18 | 19 | void fe_sq2(fe h,const fe f) 20 | { 21 | crypto_int32 f0 = f[0]; 22 | crypto_int32 f1 = f[1]; 23 | crypto_int32 f2 = f[2]; 24 | crypto_int32 f3 = f[3]; 25 | crypto_int32 f4 = f[4]; 26 | crypto_int32 f5 = f[5]; 27 | crypto_int32 f6 = f[6]; 28 | crypto_int32 f7 = f[7]; 29 | crypto_int32 f8 = f[8]; 30 | crypto_int32 f9 = f[9]; 31 | crypto_int32 f0_2 = 2 * f0; 32 | crypto_int32 f1_2 = 2 * f1; 33 | crypto_int32 f2_2 = 2 * f2; 34 | crypto_int32 f3_2 = 2 * f3; 35 | crypto_int32 f4_2 = 2 * f4; 36 | crypto_int32 f5_2 = 2 * f5; 37 | crypto_int32 f6_2 = 2 * f6; 38 | crypto_int32 f7_2 = 2 * f7; 39 | crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ 40 | crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ 41 | crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ 42 | crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ 43 | crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ 44 | crypto_int64 f0f0 = f0 * (crypto_int64) f0; 45 | crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1; 46 | crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2; 47 | crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3; 48 | crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4; 49 | crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5; 50 | crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6; 51 | crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7; 52 | crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8; 53 | crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9; 54 | crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1; 55 | crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2; 56 | crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2; 57 | crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4; 58 | crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2; 59 | crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6; 60 | crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2; 61 | crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8; 62 | crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38; 63 | crypto_int64 f2f2 = f2 * (crypto_int64) f2; 64 | crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3; 65 | crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4; 66 | crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5; 67 | crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6; 68 | crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7; 69 | crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19; 70 | crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38; 71 | crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3; 72 | crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4; 73 | crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2; 74 | crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6; 75 | crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38; 76 | crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19; 77 | crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38; 78 | crypto_int64 f4f4 = f4 * (crypto_int64) f4; 79 | crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5; 80 | crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19; 81 | crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38; 82 | crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19; 83 | crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38; 84 | crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38; 85 | crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19; 86 | crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38; 87 | crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19; 88 | crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38; 89 | crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19; 90 | crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38; 91 | crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19; 92 | crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38; 93 | crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38; 94 | crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19; 95 | crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38; 96 | crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19; 97 | crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38; 98 | crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38; 99 | crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; 100 | crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; 101 | crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; 102 | crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; 103 | crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; 104 | crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; 105 | crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; 106 | crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; 107 | crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; 108 | crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; 109 | crypto_int64 carry0; 110 | crypto_int64 carry1; 111 | crypto_int64 carry2; 112 | crypto_int64 carry3; 113 | crypto_int64 carry4; 114 | crypto_int64 carry5; 115 | crypto_int64 carry6; 116 | crypto_int64 carry7; 117 | crypto_int64 carry8; 118 | crypto_int64 carry9; 119 | 120 | h0 += h0; 121 | h1 += h1; 122 | h2 += h2; 123 | h3 += h3; 124 | h4 += h4; 125 | h5 += h5; 126 | h6 += h6; 127 | h7 += h7; 128 | h8 += h8; 129 | h9 += h9; 130 | 131 | carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; 132 | carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; 133 | 134 | carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; 135 | carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; 136 | 137 | carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; 138 | carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; 139 | 140 | carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; 141 | carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; 142 | 143 | carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; 144 | carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; 145 | 146 | carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; 147 | 148 | carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; 149 | 150 | h[0] = h0; 151 | h[1] = h1; 152 | h[2] = h2; 153 | h[3] = h3; 154 | h[4] = h4; 155 | h[5] = h5; 156 | h[6] = h6; 157 | h[7] = h7; 158 | h[8] = h8; 159 | h[9] = h9; 160 | } 161 | -------------------------------------------------------------------------------- /native/ed25519/fe_sub.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | h = f - g 5 | Can overlap h with f or g. 6 | 7 | Preconditions: 8 | |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 9 | |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 10 | 11 | Postconditions: 12 | |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 13 | */ 14 | 15 | void fe_sub(fe h,const fe f,const fe g) 16 | { 17 | crypto_int32 f0 = f[0]; 18 | crypto_int32 f1 = f[1]; 19 | crypto_int32 f2 = f[2]; 20 | crypto_int32 f3 = f[3]; 21 | crypto_int32 f4 = f[4]; 22 | crypto_int32 f5 = f[5]; 23 | crypto_int32 f6 = f[6]; 24 | crypto_int32 f7 = f[7]; 25 | crypto_int32 f8 = f[8]; 26 | crypto_int32 f9 = f[9]; 27 | crypto_int32 g0 = g[0]; 28 | crypto_int32 g1 = g[1]; 29 | crypto_int32 g2 = g[2]; 30 | crypto_int32 g3 = g[3]; 31 | crypto_int32 g4 = g[4]; 32 | crypto_int32 g5 = g[5]; 33 | crypto_int32 g6 = g[6]; 34 | crypto_int32 g7 = g[7]; 35 | crypto_int32 g8 = g[8]; 36 | crypto_int32 g9 = g[9]; 37 | crypto_int32 h0 = f0 - g0; 38 | crypto_int32 h1 = f1 - g1; 39 | crypto_int32 h2 = f2 - g2; 40 | crypto_int32 h3 = f3 - g3; 41 | crypto_int32 h4 = f4 - g4; 42 | crypto_int32 h5 = f5 - g5; 43 | crypto_int32 h6 = f6 - g6; 44 | crypto_int32 h7 = f7 - g7; 45 | crypto_int32 h8 = f8 - g8; 46 | crypto_int32 h9 = f9 - g9; 47 | h[0] = h0; 48 | h[1] = h1; 49 | h[2] = h2; 50 | h[3] = h3; 51 | h[4] = h4; 52 | h[5] = h5; 53 | h[6] = h6; 54 | h[7] = h7; 55 | h[8] = h8; 56 | h[9] = h9; 57 | } 58 | -------------------------------------------------------------------------------- /native/ed25519/fe_tobytes.c: -------------------------------------------------------------------------------- 1 | #include "fe.h" 2 | 3 | /* 4 | Preconditions: 5 | |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 6 | 7 | Write p=2^255-19; q=floor(h/p). 8 | Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). 9 | 10 | Proof: 11 | Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. 12 | Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. 13 | 14 | Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). 15 | Then 0> 25; 53 | q = (h0 + q) >> 26; 54 | q = (h1 + q) >> 25; 55 | q = (h2 + q) >> 26; 56 | q = (h3 + q) >> 25; 57 | q = (h4 + q) >> 26; 58 | q = (h5 + q) >> 25; 59 | q = (h6 + q) >> 26; 60 | q = (h7 + q) >> 25; 61 | q = (h8 + q) >> 26; 62 | q = (h9 + q) >> 25; 63 | 64 | /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ 65 | h0 += 19 * q; 66 | /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ 67 | 68 | carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; 69 | carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; 70 | carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; 71 | carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; 72 | carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; 73 | carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; 74 | carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; 75 | carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; 76 | carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; 77 | carry9 = h9 >> 25; h9 -= carry9 << 25; 78 | /* h10 = carry9 */ 79 | 80 | /* 81 | Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. 82 | Have h0+...+2^230 h9 between 0 and 2^255-1; 83 | evidently 2^255 h10-2^255 q = 0. 84 | Goal: Output h0+...+2^230 h9. 85 | */ 86 | 87 | s[0] = h0 >> 0; 88 | s[1] = h0 >> 8; 89 | s[2] = h0 >> 16; 90 | s[3] = (h0 >> 24) | (h1 << 2); 91 | s[4] = h1 >> 6; 92 | s[5] = h1 >> 14; 93 | s[6] = (h1 >> 22) | (h2 << 3); 94 | s[7] = h2 >> 5; 95 | s[8] = h2 >> 13; 96 | s[9] = (h2 >> 21) | (h3 << 5); 97 | s[10] = h3 >> 3; 98 | s[11] = h3 >> 11; 99 | s[12] = (h3 >> 19) | (h4 << 6); 100 | s[13] = h4 >> 2; 101 | s[14] = h4 >> 10; 102 | s[15] = h4 >> 18; 103 | s[16] = h5 >> 0; 104 | s[17] = h5 >> 8; 105 | s[18] = h5 >> 16; 106 | s[19] = (h5 >> 24) | (h6 << 1); 107 | s[20] = h6 >> 7; 108 | s[21] = h6 >> 15; 109 | s[22] = (h6 >> 23) | (h7 << 3); 110 | s[23] = h7 >> 5; 111 | s[24] = h7 >> 13; 112 | s[25] = (h7 >> 21) | (h8 << 4); 113 | s[26] = h8 >> 4; 114 | s[27] = h8 >> 12; 115 | s[28] = (h8 >> 20) | (h9 << 6); 116 | s[29] = h9 >> 2; 117 | s[30] = h9 >> 10; 118 | s[31] = h9 >> 18; 119 | } 120 | -------------------------------------------------------------------------------- /native/ed25519/ge.h: -------------------------------------------------------------------------------- 1 | #ifndef GE_H 2 | #define GE_H 3 | 4 | /* 5 | ge means group element. 6 | 7 | Here the group is the set of pairs (x,y) of field elements (see fe.h) 8 | satisfying -x^2 + y^2 = 1 + d x^2y^2 9 | where d = -121665/121666. 10 | 11 | Representations: 12 | ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z 13 | ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT 14 | ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T 15 | ge_precomp (Duif): (y+x,y-x,2dxy) 16 | */ 17 | 18 | #include "fe.h" 19 | 20 | typedef struct { 21 | fe X; 22 | fe Y; 23 | fe Z; 24 | } ge_p2; 25 | 26 | typedef struct { 27 | fe X; 28 | fe Y; 29 | fe Z; 30 | fe T; 31 | } ge_p3; 32 | 33 | typedef struct { 34 | fe X; 35 | fe Y; 36 | fe Z; 37 | fe T; 38 | } ge_p1p1; 39 | 40 | typedef struct { 41 | fe yplusx; 42 | fe yminusx; 43 | fe xy2d; 44 | } ge_precomp; 45 | 46 | typedef struct { 47 | fe YplusX; 48 | fe YminusX; 49 | fe Z; 50 | fe T2d; 51 | } ge_cached; 52 | 53 | #define ge_frombytes_negate_vartime crypto_sign_ed25519_ref10_ge_frombytes_negate_vartime 54 | #define ge_tobytes crypto_sign_ed25519_ref10_ge_tobytes 55 | #define ge_p3_tobytes crypto_sign_ed25519_ref10_ge_p3_tobytes 56 | 57 | #define ge_p2_0 crypto_sign_ed25519_ref10_ge_p2_0 58 | #define ge_p3_0 crypto_sign_ed25519_ref10_ge_p3_0 59 | #define ge_precomp_0 crypto_sign_ed25519_ref10_ge_precomp_0 60 | #define ge_p3_to_p2 crypto_sign_ed25519_ref10_ge_p3_to_p2 61 | #define ge_p3_to_cached crypto_sign_ed25519_ref10_ge_p3_to_cached 62 | #define ge_p1p1_to_p2 crypto_sign_ed25519_ref10_ge_p1p1_to_p2 63 | #define ge_p1p1_to_p3 crypto_sign_ed25519_ref10_ge_p1p1_to_p3 64 | #define ge_p2_dbl crypto_sign_ed25519_ref10_ge_p2_dbl 65 | #define ge_p3_dbl crypto_sign_ed25519_ref10_ge_p3_dbl 66 | 67 | #define ge_madd crypto_sign_ed25519_ref10_ge_madd 68 | #define ge_msub crypto_sign_ed25519_ref10_ge_msub 69 | #define ge_add crypto_sign_ed25519_ref10_ge_add 70 | #define ge_sub crypto_sign_ed25519_ref10_ge_sub 71 | #define ge_scalarmult_base crypto_sign_ed25519_ref10_ge_scalarmult_base 72 | #define ge_double_scalarmult_vartime crypto_sign_ed25519_ref10_ge_double_scalarmult_vartime 73 | 74 | extern void ge_tobytes(unsigned char *,const ge_p2 *); 75 | extern void ge_p3_tobytes(unsigned char *,const ge_p3 *); 76 | extern int ge_frombytes_negate_vartime(ge_p3 *,const unsigned char *); 77 | 78 | extern void ge_p2_0(ge_p2 *); 79 | extern void ge_p3_0(ge_p3 *); 80 | extern void ge_precomp_0(ge_precomp *); 81 | extern void ge_p3_to_p2(ge_p2 *,const ge_p3 *); 82 | extern void ge_p3_to_cached(ge_cached *,const ge_p3 *); 83 | extern void ge_p1p1_to_p2(ge_p2 *,const ge_p1p1 *); 84 | extern void ge_p1p1_to_p3(ge_p3 *,const ge_p1p1 *); 85 | extern void ge_p2_dbl(ge_p1p1 *,const ge_p2 *); 86 | extern void ge_p3_dbl(ge_p1p1 *,const ge_p3 *); 87 | 88 | extern void ge_madd(ge_p1p1 *,const ge_p3 *,const ge_precomp *); 89 | extern void ge_msub(ge_p1p1 *,const ge_p3 *,const ge_precomp *); 90 | extern void ge_add(ge_p1p1 *,const ge_p3 *,const ge_cached *); 91 | extern void ge_sub(ge_p1p1 *,const ge_p3 *,const ge_cached *); 92 | extern void ge_scalarmult_base(ge_p3 *,const unsigned char *); 93 | extern void ge_double_scalarmult_vartime(ge_p2 *,const unsigned char *,const ge_p3 *,const unsigned char *); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /native/ed25519/ge_add.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p + q 5 | */ 6 | 7 | void ge_add(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q) 8 | { 9 | fe t0; 10 | #include "ge_add.h" 11 | } 12 | -------------------------------------------------------------------------------- /native/ed25519/ge_add.h: -------------------------------------------------------------------------------- 1 | 2 | /* qhasm: enter ge_add */ 3 | 4 | /* qhasm: fe X1 */ 5 | 6 | /* qhasm: fe Y1 */ 7 | 8 | /* qhasm: fe Z1 */ 9 | 10 | /* qhasm: fe Z2 */ 11 | 12 | /* qhasm: fe T1 */ 13 | 14 | /* qhasm: fe ZZ */ 15 | 16 | /* qhasm: fe YpX2 */ 17 | 18 | /* qhasm: fe YmX2 */ 19 | 20 | /* qhasm: fe T2d2 */ 21 | 22 | /* qhasm: fe X3 */ 23 | 24 | /* qhasm: fe Y3 */ 25 | 26 | /* qhasm: fe Z3 */ 27 | 28 | /* qhasm: fe T3 */ 29 | 30 | /* qhasm: fe YpX1 */ 31 | 32 | /* qhasm: fe YmX1 */ 33 | 34 | /* qhasm: fe A */ 35 | 36 | /* qhasm: fe B */ 37 | 38 | /* qhasm: fe C */ 39 | 40 | /* qhasm: fe D */ 41 | 42 | /* qhasm: YpX1 = Y1+X1 */ 43 | /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ 45 | fe_add(r->X,p->Y,p->X); 46 | 47 | /* qhasm: YmX1 = Y1-X1 */ 48 | /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ 50 | fe_sub(r->Y,p->Y,p->X); 51 | 52 | /* qhasm: A = YpX1*YpX2 */ 53 | /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,YplusX); */ 55 | fe_mul(r->Z,r->X,q->YplusX); 56 | 57 | /* qhasm: B = YmX1*YmX2 */ 58 | /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,YminusX); */ 60 | fe_mul(r->Y,r->Y,q->YminusX); 61 | 62 | /* qhasm: C = T2d2*T1 */ 63 | /* asm 1: fe_mul(>C=fe#4,C=r->T,T2d,T); */ 65 | fe_mul(r->T,q->T2d,p->T); 66 | 67 | /* qhasm: ZZ = Z1*Z2 */ 68 | /* asm 1: fe_mul(>ZZ=fe#1,ZZ=r->X,Z,Z); */ 70 | fe_mul(r->X,p->Z,q->Z); 71 | 72 | /* qhasm: D = 2*ZZ */ 73 | /* asm 1: fe_add(>D=fe#5,D=t0,X,X); */ 75 | fe_add(t0,r->X,r->X); 76 | 77 | /* qhasm: X3 = A-B */ 78 | /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ 80 | fe_sub(r->X,r->Z,r->Y); 81 | 82 | /* qhasm: Y3 = A+B */ 83 | /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ 85 | fe_add(r->Y,r->Z,r->Y); 86 | 87 | /* qhasm: Z3 = D+C */ 88 | /* asm 1: fe_add(>Z3=fe#3,Z3=r->Z,T); */ 90 | fe_add(r->Z,t0,r->T); 91 | 92 | /* qhasm: T3 = D-C */ 93 | /* asm 1: fe_sub(>T3=fe#4,T3=r->T,T); */ 95 | fe_sub(r->T,t0,r->T); 96 | 97 | /* qhasm: return */ 98 | -------------------------------------------------------------------------------- /native/ed25519/ge_double_scalarmult.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | static void slide(signed char *r,const unsigned char *a) 4 | { 5 | int i; 6 | int b; 7 | int k; 8 | 9 | for (i = 0;i < 256;++i) 10 | r[i] = 1 & (a[i >> 3] >> (i & 7)); 11 | 12 | for (i = 0;i < 256;++i) 13 | if (r[i]) { 14 | for (b = 1;b <= 6 && i + b < 256;++b) { 15 | if (r[i + b]) { 16 | if (r[i] + (r[i + b] << b) <= 15) { 17 | r[i] += r[i + b] << b; r[i + b] = 0; 18 | } else if (r[i] - (r[i + b] << b) >= -15) { 19 | r[i] -= r[i + b] << b; 20 | for (k = i + b;k < 256;++k) { 21 | if (!r[k]) { 22 | r[k] = 1; 23 | break; 24 | } 25 | r[k] = 0; 26 | } 27 | } else 28 | break; 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | static ge_precomp Bi[8] = { 36 | #include "base2.h" 37 | } ; 38 | 39 | /* 40 | r = a * A + b * B 41 | where a = a[0]+256*a[1]+...+256^31 a[31]. 42 | and b = b[0]+256*b[1]+...+256^31 b[31]. 43 | B is the Ed25519 base point (x,4/5) with x positive. 44 | */ 45 | 46 | void ge_double_scalarmult_vartime(ge_p2 *r,const unsigned char *a,const ge_p3 *A,const unsigned char *b) 47 | { 48 | signed char aslide[256]; 49 | signed char bslide[256]; 50 | ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ 51 | ge_p1p1 t; 52 | ge_p3 u; 53 | ge_p3 A2; 54 | int i; 55 | 56 | slide(aslide,a); 57 | slide(bslide,b); 58 | 59 | ge_p3_to_cached(&Ai[0],A); 60 | ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t); 61 | ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u); 62 | ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u); 63 | ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u); 64 | ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u); 65 | ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u); 66 | ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u); 67 | ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u); 68 | 69 | ge_p2_0(r); 70 | 71 | for (i = 255;i >= 0;--i) { 72 | if (aslide[i] || bslide[i]) break; 73 | } 74 | 75 | for (;i >= 0;--i) { 76 | ge_p2_dbl(&t,r); 77 | 78 | if (aslide[i] > 0) { 79 | ge_p1p1_to_p3(&u,&t); 80 | ge_add(&t,&u,&Ai[aslide[i]/2]); 81 | } else if (aslide[i] < 0) { 82 | ge_p1p1_to_p3(&u,&t); 83 | ge_sub(&t,&u,&Ai[(-aslide[i])/2]); 84 | } 85 | 86 | if (bslide[i] > 0) { 87 | ge_p1p1_to_p3(&u,&t); 88 | ge_madd(&t,&u,&Bi[bslide[i]/2]); 89 | } else if (bslide[i] < 0) { 90 | ge_p1p1_to_p3(&u,&t); 91 | ge_msub(&t,&u,&Bi[(-bslide[i])/2]); 92 | } 93 | 94 | ge_p1p1_to_p2(r,&t); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /native/ed25519/ge_frombytes.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | static const fe d = { 4 | #include "d.h" 5 | } ; 6 | 7 | static const fe sqrtm1 = { 8 | #include "sqrtm1.h" 9 | } ; 10 | 11 | int ge_frombytes_negate_vartime(ge_p3 *h,const unsigned char *s) 12 | { 13 | fe u; 14 | fe v; 15 | fe v3; 16 | fe vxx; 17 | fe check; 18 | 19 | fe_frombytes(h->Y,s); 20 | fe_1(h->Z); 21 | fe_sq(u,h->Y); 22 | fe_mul(v,u,d); 23 | fe_sub(u,u,h->Z); /* u = y^2-1 */ 24 | fe_add(v,v,h->Z); /* v = dy^2+1 */ 25 | 26 | fe_sq(v3,v); 27 | fe_mul(v3,v3,v); /* v3 = v^3 */ 28 | fe_sq(h->X,v3); 29 | fe_mul(h->X,h->X,v); 30 | fe_mul(h->X,h->X,u); /* x = uv^7 */ 31 | 32 | fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */ 33 | fe_mul(h->X,h->X,v3); 34 | fe_mul(h->X,h->X,u); /* x = uv^3(uv^7)^((q-5)/8) */ 35 | 36 | fe_sq(vxx,h->X); 37 | fe_mul(vxx,vxx,v); 38 | fe_sub(check,vxx,u); /* vx^2-u */ 39 | if (fe_isnonzero(check)) { 40 | fe_add(check,vxx,u); /* vx^2+u */ 41 | if (fe_isnonzero(check)) return -1; 42 | fe_mul(h->X,h->X,sqrtm1); 43 | } 44 | 45 | if (fe_isnegative(h->X) == (s[31] >> 7)) 46 | fe_neg(h->X,h->X); 47 | 48 | fe_mul(h->T,h->X,h->Y); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /native/ed25519/ge_madd.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p + q 5 | */ 6 | 7 | void ge_madd(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q) 8 | { 9 | fe t0; 10 | #include "ge_madd.h" 11 | } 12 | -------------------------------------------------------------------------------- /native/ed25519/ge_madd.h: -------------------------------------------------------------------------------- 1 | 2 | /* qhasm: enter ge_madd */ 3 | 4 | /* qhasm: fe X1 */ 5 | 6 | /* qhasm: fe Y1 */ 7 | 8 | /* qhasm: fe Z1 */ 9 | 10 | /* qhasm: fe T1 */ 11 | 12 | /* qhasm: fe ypx2 */ 13 | 14 | /* qhasm: fe ymx2 */ 15 | 16 | /* qhasm: fe xy2d2 */ 17 | 18 | /* qhasm: fe X3 */ 19 | 20 | /* qhasm: fe Y3 */ 21 | 22 | /* qhasm: fe Z3 */ 23 | 24 | /* qhasm: fe T3 */ 25 | 26 | /* qhasm: fe YpX1 */ 27 | 28 | /* qhasm: fe YmX1 */ 29 | 30 | /* qhasm: fe A */ 31 | 32 | /* qhasm: fe B */ 33 | 34 | /* qhasm: fe C */ 35 | 36 | /* qhasm: fe D */ 37 | 38 | /* qhasm: YpX1 = Y1+X1 */ 39 | /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ 41 | fe_add(r->X,p->Y,p->X); 42 | 43 | /* qhasm: YmX1 = Y1-X1 */ 44 | /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ 46 | fe_sub(r->Y,p->Y,p->X); 47 | 48 | /* qhasm: A = YpX1*ypx2 */ 49 | /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,yplusx); */ 51 | fe_mul(r->Z,r->X,q->yplusx); 52 | 53 | /* qhasm: B = YmX1*ymx2 */ 54 | /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,yminusx); */ 56 | fe_mul(r->Y,r->Y,q->yminusx); 57 | 58 | /* qhasm: C = xy2d2*T1 */ 59 | /* asm 1: fe_mul(>C=fe#4,C=r->T,xy2d,T); */ 61 | fe_mul(r->T,q->xy2d,p->T); 62 | 63 | /* qhasm: D = 2*Z1 */ 64 | /* asm 1: fe_add(>D=fe#5,D=t0,Z,Z); */ 66 | fe_add(t0,p->Z,p->Z); 67 | 68 | /* qhasm: X3 = A-B */ 69 | /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ 71 | fe_sub(r->X,r->Z,r->Y); 72 | 73 | /* qhasm: Y3 = A+B */ 74 | /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ 76 | fe_add(r->Y,r->Z,r->Y); 77 | 78 | /* qhasm: Z3 = D+C */ 79 | /* asm 1: fe_add(>Z3=fe#3,Z3=r->Z,T); */ 81 | fe_add(r->Z,t0,r->T); 82 | 83 | /* qhasm: T3 = D-C */ 84 | /* asm 1: fe_sub(>T3=fe#4,T3=r->T,T); */ 86 | fe_sub(r->T,t0,r->T); 87 | 88 | /* qhasm: return */ 89 | -------------------------------------------------------------------------------- /native/ed25519/ge_msub.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p - q 5 | */ 6 | 7 | void ge_msub(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q) 8 | { 9 | fe t0; 10 | #include "ge_msub.h" 11 | } 12 | -------------------------------------------------------------------------------- /native/ed25519/ge_msub.h: -------------------------------------------------------------------------------- 1 | 2 | /* qhasm: enter ge_msub */ 3 | 4 | /* qhasm: fe X1 */ 5 | 6 | /* qhasm: fe Y1 */ 7 | 8 | /* qhasm: fe Z1 */ 9 | 10 | /* qhasm: fe T1 */ 11 | 12 | /* qhasm: fe ypx2 */ 13 | 14 | /* qhasm: fe ymx2 */ 15 | 16 | /* qhasm: fe xy2d2 */ 17 | 18 | /* qhasm: fe X3 */ 19 | 20 | /* qhasm: fe Y3 */ 21 | 22 | /* qhasm: fe Z3 */ 23 | 24 | /* qhasm: fe T3 */ 25 | 26 | /* qhasm: fe YpX1 */ 27 | 28 | /* qhasm: fe YmX1 */ 29 | 30 | /* qhasm: fe A */ 31 | 32 | /* qhasm: fe B */ 33 | 34 | /* qhasm: fe C */ 35 | 36 | /* qhasm: fe D */ 37 | 38 | /* qhasm: YpX1 = Y1+X1 */ 39 | /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ 41 | fe_add(r->X,p->Y,p->X); 42 | 43 | /* qhasm: YmX1 = Y1-X1 */ 44 | /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ 46 | fe_sub(r->Y,p->Y,p->X); 47 | 48 | /* qhasm: A = YpX1*ymx2 */ 49 | /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,yminusx); */ 51 | fe_mul(r->Z,r->X,q->yminusx); 52 | 53 | /* qhasm: B = YmX1*ypx2 */ 54 | /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,yplusx); */ 56 | fe_mul(r->Y,r->Y,q->yplusx); 57 | 58 | /* qhasm: C = xy2d2*T1 */ 59 | /* asm 1: fe_mul(>C=fe#4,C=r->T,xy2d,T); */ 61 | fe_mul(r->T,q->xy2d,p->T); 62 | 63 | /* qhasm: D = 2*Z1 */ 64 | /* asm 1: fe_add(>D=fe#5,D=t0,Z,Z); */ 66 | fe_add(t0,p->Z,p->Z); 67 | 68 | /* qhasm: X3 = A-B */ 69 | /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ 71 | fe_sub(r->X,r->Z,r->Y); 72 | 73 | /* qhasm: Y3 = A+B */ 74 | /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ 76 | fe_add(r->Y,r->Z,r->Y); 77 | 78 | /* qhasm: Z3 = D-C */ 79 | /* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,T); */ 81 | fe_sub(r->Z,t0,r->T); 82 | 83 | /* qhasm: T3 = D+C */ 84 | /* asm 1: fe_add(>T3=fe#4,T3=r->T,T); */ 86 | fe_add(r->T,t0,r->T); 87 | 88 | /* qhasm: return */ 89 | -------------------------------------------------------------------------------- /native/ed25519/ge_p1p1_to_p2.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p 5 | */ 6 | 7 | extern void ge_p1p1_to_p2(ge_p2 *r,const ge_p1p1 *p) 8 | { 9 | fe_mul(r->X,p->X,p->T); 10 | fe_mul(r->Y,p->Y,p->Z); 11 | fe_mul(r->Z,p->Z,p->T); 12 | } 13 | -------------------------------------------------------------------------------- /native/ed25519/ge_p1p1_to_p3.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p 5 | */ 6 | 7 | extern void ge_p1p1_to_p3(ge_p3 *r,const ge_p1p1 *p) 8 | { 9 | fe_mul(r->X,p->X,p->T); 10 | fe_mul(r->Y,p->Y,p->Z); 11 | fe_mul(r->Z,p->Z,p->T); 12 | fe_mul(r->T,p->X,p->Y); 13 | } 14 | -------------------------------------------------------------------------------- /native/ed25519/ge_p2_0.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | void ge_p2_0(ge_p2 *h) 4 | { 5 | fe_0(h->X); 6 | fe_1(h->Y); 7 | fe_1(h->Z); 8 | } 9 | -------------------------------------------------------------------------------- /native/ed25519/ge_p2_dbl.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = 2 * p 5 | */ 6 | 7 | void ge_p2_dbl(ge_p1p1 *r,const ge_p2 *p) 8 | { 9 | fe t0; 10 | #include "ge_p2_dbl.h" 11 | } 12 | -------------------------------------------------------------------------------- /native/ed25519/ge_p2_dbl.h: -------------------------------------------------------------------------------- 1 | 2 | /* qhasm: enter ge_p2_dbl */ 3 | 4 | /* qhasm: fe X1 */ 5 | 6 | /* qhasm: fe Y1 */ 7 | 8 | /* qhasm: fe Z1 */ 9 | 10 | /* qhasm: fe A */ 11 | 12 | /* qhasm: fe AA */ 13 | 14 | /* qhasm: fe XX */ 15 | 16 | /* qhasm: fe YY */ 17 | 18 | /* qhasm: fe B */ 19 | 20 | /* qhasm: fe X3 */ 21 | 22 | /* qhasm: fe Y3 */ 23 | 24 | /* qhasm: fe Z3 */ 25 | 26 | /* qhasm: fe T3 */ 27 | 28 | /* qhasm: XX=X1^2 */ 29 | /* asm 1: fe_sq(>XX=fe#1,XX=r->X,X); */ 31 | fe_sq(r->X,p->X); 32 | 33 | /* qhasm: YY=Y1^2 */ 34 | /* asm 1: fe_sq(>YY=fe#3,YY=r->Z,Y); */ 36 | fe_sq(r->Z,p->Y); 37 | 38 | /* qhasm: B=2*Z1^2 */ 39 | /* asm 1: fe_sq2(>B=fe#4,B=r->T,Z); */ 41 | fe_sq2(r->T,p->Z); 42 | 43 | /* qhasm: A=X1+Y1 */ 44 | /* asm 1: fe_add(>A=fe#2,A=r->Y,X,Y); */ 46 | fe_add(r->Y,p->X,p->Y); 47 | 48 | /* qhasm: AA=A^2 */ 49 | /* asm 1: fe_sq(>AA=fe#5,AA=t0,Y); */ 51 | fe_sq(t0,r->Y); 52 | 53 | /* qhasm: Y3=YY+XX */ 54 | /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,X); */ 56 | fe_add(r->Y,r->Z,r->X); 57 | 58 | /* qhasm: Z3=YY-XX */ 59 | /* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,Z,X); */ 61 | fe_sub(r->Z,r->Z,r->X); 62 | 63 | /* qhasm: X3=AA-Y3 */ 64 | /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Y); */ 66 | fe_sub(r->X,t0,r->Y); 67 | 68 | /* qhasm: T3=B-Z3 */ 69 | /* asm 1: fe_sub(>T3=fe#4,T3=r->T,T,Z); */ 71 | fe_sub(r->T,r->T,r->Z); 72 | 73 | /* qhasm: return */ 74 | -------------------------------------------------------------------------------- /native/ed25519/ge_p3_0.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | void ge_p3_0(ge_p3 *h) 4 | { 5 | fe_0(h->X); 6 | fe_1(h->Y); 7 | fe_1(h->Z); 8 | fe_0(h->T); 9 | } 10 | -------------------------------------------------------------------------------- /native/ed25519/ge_p3_dbl.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = 2 * p 5 | */ 6 | 7 | void ge_p3_dbl(ge_p1p1 *r,const ge_p3 *p) 8 | { 9 | ge_p2 q; 10 | ge_p3_to_p2(&q,p); 11 | ge_p2_dbl(r,&q); 12 | } 13 | -------------------------------------------------------------------------------- /native/ed25519/ge_p3_to_cached.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p 5 | */ 6 | 7 | static const fe d2 = { 8 | #include "d2.h" 9 | } ; 10 | 11 | extern void ge_p3_to_cached(ge_cached *r,const ge_p3 *p) 12 | { 13 | fe_add(r->YplusX,p->Y,p->X); 14 | fe_sub(r->YminusX,p->Y,p->X); 15 | fe_copy(r->Z,p->Z); 16 | fe_mul(r->T2d,p->T,d2); 17 | } 18 | -------------------------------------------------------------------------------- /native/ed25519/ge_p3_to_p2.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p 5 | */ 6 | 7 | extern void ge_p3_to_p2(ge_p2 *r,const ge_p3 *p) 8 | { 9 | fe_copy(r->X,p->X); 10 | fe_copy(r->Y,p->Y); 11 | fe_copy(r->Z,p->Z); 12 | } 13 | -------------------------------------------------------------------------------- /native/ed25519/ge_p3_tobytes.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | void ge_p3_tobytes(unsigned char *s,const ge_p3 *h) 4 | { 5 | fe recip; 6 | fe x; 7 | fe y; 8 | 9 | fe_invert(recip,h->Z); 10 | fe_mul(x,h->X,recip); 11 | fe_mul(y,h->Y,recip); 12 | fe_tobytes(s,y); 13 | s[31] ^= fe_isnegative(x) << 7; 14 | } 15 | -------------------------------------------------------------------------------- /native/ed25519/ge_precomp_0.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | void ge_precomp_0(ge_precomp *h) 4 | { 5 | fe_1(h->yplusx); 6 | fe_1(h->yminusx); 7 | fe_0(h->xy2d); 8 | } 9 | -------------------------------------------------------------------------------- /native/ed25519/ge_scalarmult_base.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | #include "crypto_uint32.h" 3 | 4 | static unsigned char equal(signed char b,signed char c) 5 | { 6 | unsigned char ub = b; 7 | unsigned char uc = c; 8 | unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ 9 | crypto_uint32 y = x; /* 0: yes; 1..255: no */ 10 | y -= 1; /* 4294967295: yes; 0..254: no */ 11 | y >>= 31; /* 1: yes; 0: no */ 12 | return y; 13 | } 14 | 15 | static unsigned char negative(signed char b) 16 | { 17 | unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ 18 | x >>= 63; /* 1: yes; 0: no */ 19 | return x; 20 | } 21 | 22 | static void cmov(ge_precomp *t,ge_precomp *u,unsigned char b) 23 | { 24 | fe_cmov(t->yplusx,u->yplusx,b); 25 | fe_cmov(t->yminusx,u->yminusx,b); 26 | fe_cmov(t->xy2d,u->xy2d,b); 27 | } 28 | 29 | /* base[i][j] = (j+1)*256^i*B */ 30 | static ge_precomp base[32][8] = { 31 | #include "base.h" 32 | } ; 33 | 34 | static void select(ge_precomp *t,int pos,signed char b) 35 | { 36 | ge_precomp minust; 37 | unsigned char bnegative = negative(b); 38 | unsigned char babs = b - (((-bnegative) & b) << 1); 39 | 40 | ge_precomp_0(t); 41 | cmov(t,&base[pos][0],equal(babs,1)); 42 | cmov(t,&base[pos][1],equal(babs,2)); 43 | cmov(t,&base[pos][2],equal(babs,3)); 44 | cmov(t,&base[pos][3],equal(babs,4)); 45 | cmov(t,&base[pos][4],equal(babs,5)); 46 | cmov(t,&base[pos][5],equal(babs,6)); 47 | cmov(t,&base[pos][6],equal(babs,7)); 48 | cmov(t,&base[pos][7],equal(babs,8)); 49 | fe_copy(minust.yplusx,t->yminusx); 50 | fe_copy(minust.yminusx,t->yplusx); 51 | fe_neg(minust.xy2d,t->xy2d); 52 | cmov(t,&minust,bnegative); 53 | } 54 | 55 | /* 56 | h = a * B 57 | where a = a[0]+256*a[1]+...+256^31 a[31] 58 | B is the Ed25519 base point (x,4/5) with x positive. 59 | 60 | Preconditions: 61 | a[31] <= 127 62 | */ 63 | 64 | void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) 65 | { 66 | signed char e[64]; 67 | signed char carry; 68 | ge_p1p1 r; 69 | ge_p2 s; 70 | ge_precomp t; 71 | int i; 72 | 73 | for (i = 0;i < 32;++i) { 74 | e[2 * i + 0] = (a[i] >> 0) & 15; 75 | e[2 * i + 1] = (a[i] >> 4) & 15; 76 | } 77 | /* each e[i] is between 0 and 15 */ 78 | /* e[63] is between 0 and 7 */ 79 | 80 | carry = 0; 81 | for (i = 0;i < 63;++i) { 82 | e[i] += carry; 83 | carry = e[i] + 8; 84 | carry >>= 4; 85 | e[i] -= carry << 4; 86 | } 87 | e[63] += carry; 88 | /* each e[i] is between -8 and 8 */ 89 | 90 | ge_p3_0(h); 91 | for (i = 1;i < 64;i += 2) { 92 | select(&t,i / 2,e[i]); 93 | ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); 94 | } 95 | 96 | ge_p3_dbl(&r,h); ge_p1p1_to_p2(&s,&r); 97 | ge_p2_dbl(&r,&s); ge_p1p1_to_p2(&s,&r); 98 | ge_p2_dbl(&r,&s); ge_p1p1_to_p2(&s,&r); 99 | ge_p2_dbl(&r,&s); ge_p1p1_to_p3(h,&r); 100 | 101 | for (i = 0;i < 64;i += 2) { 102 | select(&t,i / 2,e[i]); 103 | ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /native/ed25519/ge_sub.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | /* 4 | r = p - q 5 | */ 6 | 7 | void ge_sub(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q) 8 | { 9 | fe t0; 10 | #include "ge_sub.h" 11 | } 12 | -------------------------------------------------------------------------------- /native/ed25519/ge_sub.h: -------------------------------------------------------------------------------- 1 | 2 | /* qhasm: enter ge_sub */ 3 | 4 | /* qhasm: fe X1 */ 5 | 6 | /* qhasm: fe Y1 */ 7 | 8 | /* qhasm: fe Z1 */ 9 | 10 | /* qhasm: fe Z2 */ 11 | 12 | /* qhasm: fe T1 */ 13 | 14 | /* qhasm: fe ZZ */ 15 | 16 | /* qhasm: fe YpX2 */ 17 | 18 | /* qhasm: fe YmX2 */ 19 | 20 | /* qhasm: fe T2d2 */ 21 | 22 | /* qhasm: fe X3 */ 23 | 24 | /* qhasm: fe Y3 */ 25 | 26 | /* qhasm: fe Z3 */ 27 | 28 | /* qhasm: fe T3 */ 29 | 30 | /* qhasm: fe YpX1 */ 31 | 32 | /* qhasm: fe YmX1 */ 33 | 34 | /* qhasm: fe A */ 35 | 36 | /* qhasm: fe B */ 37 | 38 | /* qhasm: fe C */ 39 | 40 | /* qhasm: fe D */ 41 | 42 | /* qhasm: YpX1 = Y1+X1 */ 43 | /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ 45 | fe_add(r->X,p->Y,p->X); 46 | 47 | /* qhasm: YmX1 = Y1-X1 */ 48 | /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ 50 | fe_sub(r->Y,p->Y,p->X); 51 | 52 | /* qhasm: A = YpX1*YmX2 */ 53 | /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,YminusX); */ 55 | fe_mul(r->Z,r->X,q->YminusX); 56 | 57 | /* qhasm: B = YmX1*YpX2 */ 58 | /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,YplusX); */ 60 | fe_mul(r->Y,r->Y,q->YplusX); 61 | 62 | /* qhasm: C = T2d2*T1 */ 63 | /* asm 1: fe_mul(>C=fe#4,C=r->T,T2d,T); */ 65 | fe_mul(r->T,q->T2d,p->T); 66 | 67 | /* qhasm: ZZ = Z1*Z2 */ 68 | /* asm 1: fe_mul(>ZZ=fe#1,ZZ=r->X,Z,Z); */ 70 | fe_mul(r->X,p->Z,q->Z); 71 | 72 | /* qhasm: D = 2*ZZ */ 73 | /* asm 1: fe_add(>D=fe#5,D=t0,X,X); */ 75 | fe_add(t0,r->X,r->X); 76 | 77 | /* qhasm: X3 = A-B */ 78 | /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ 80 | fe_sub(r->X,r->Z,r->Y); 81 | 82 | /* qhasm: Y3 = A+B */ 83 | /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ 85 | fe_add(r->Y,r->Z,r->Y); 86 | 87 | /* qhasm: Z3 = D-C */ 88 | /* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,T); */ 90 | fe_sub(r->Z,t0,r->T); 91 | 92 | /* qhasm: T3 = D+C */ 93 | /* asm 1: fe_add(>T3=fe#4,T3=r->T,T); */ 95 | fe_add(r->T,t0,r->T); 96 | 97 | /* qhasm: return */ 98 | -------------------------------------------------------------------------------- /native/ed25519/ge_tobytes.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | 3 | void ge_tobytes(unsigned char *s,const ge_p2 *h) 4 | { 5 | fe recip; 6 | fe x; 7 | fe y; 8 | 9 | fe_invert(recip,h->Z); 10 | fe_mul(x,h->X,recip); 11 | fe_mul(y,h->Y,recip); 12 | fe_tobytes(s,y); 13 | s[31] ^= fe_isnegative(x) << 7; 14 | } 15 | -------------------------------------------------------------------------------- /native/ed25519/main/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sha512.h" 4 | #include "curve_sigs.h" 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | unsigned char privkey[32]; 9 | unsigned char pubkey[32]; 10 | unsigned char signature[64]; 11 | unsigned char msg[100]; 12 | unsigned long long msg_len = 100; 13 | 14 | /* Initialize pubkey, privkey, msg */ 15 | memset(msg, 0, 100); 16 | memset(privkey, 0, 32); 17 | memset(pubkey, 0, 32); 18 | privkey[0] &= 248; 19 | privkey[31] &= 63; 20 | privkey[31] |= 64; 21 | 22 | privkey[8] = 189; /* just so there's some bits set */ 23 | 24 | curve25519_keygen(pubkey, privkey); 25 | 26 | curve25519_sign(signature, privkey, msg, msg_len); 27 | 28 | if (curve25519_verify(signature, pubkey, msg, msg_len) == 0) 29 | printf("success #1\n"); 30 | else 31 | printf("failure #1\n"); 32 | 33 | signature[0] ^= 1; 34 | 35 | if (curve25519_verify(signature, pubkey, msg, msg_len) == 0) 36 | printf("failure #2\n"); 37 | else 38 | printf("success #2\n"); 39 | 40 | return 1; 41 | } 42 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_hash_sha512.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_hash_sha512_H 2 | #define crypto_hash_sha512_H 3 | 4 | #define crypto_hash_sha512_ref_BYTES 64 5 | #ifdef __cplusplus 6 | #include 7 | extern std::string crypto_hash_sha512_ref(const std::string &); 8 | extern "C" { 9 | #endif 10 | extern int crypto_hash_sha512_ref(unsigned char *,const unsigned char *,unsigned long long); 11 | #ifdef __cplusplus 12 | } 13 | #endif 14 | 15 | #define crypto_hash_sha512 crypto_hash_sha512_ref 16 | #define crypto_hash_sha512_BYTES crypto_hash_sha512_ref_BYTES 17 | #define crypto_hash_sha512_IMPLEMENTATION "crypto_hash/sha512/ref" 18 | #ifndef crypto_hash_sha512_ref_VERSION 19 | #define crypto_hash_sha512_ref_VERSION "-" 20 | #endif 21 | #define crypto_hash_sha512_VERSION crypto_hash_sha512_ref_VERSION 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_int32.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_int32_h 2 | #define crypto_int32_h 3 | 4 | typedef int crypto_int32; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_int64.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_int64_h 2 | #define crypto_int64_h 3 | 4 | typedef long long crypto_int64; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_sign.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_sign_H 2 | #define crypto_sign_H 3 | 4 | #include "crypto_sign_edwards25519sha512batch.h" 5 | 6 | #define crypto_sign crypto_sign_edwards25519sha512batch 7 | #define crypto_sign_open crypto_sign_edwards25519sha512batch_open 8 | #define crypto_sign_keypair crypto_sign_edwards25519sha512batch_keypair 9 | #define crypto_sign_BYTES crypto_sign_edwards25519sha512batch_BYTES 10 | #define crypto_sign_PUBLICKEYBYTES crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES 11 | #define crypto_sign_SECRETKEYBYTES crypto_sign_edwards25519sha512batch_SECRETKEYBYTES 12 | #define crypto_sign_PRIMITIVE "edwards25519sha512batch" 13 | #define crypto_sign_IMPLEMENTATION crypto_sign_edwards25519sha512batch_IMPLEMENTATION 14 | #define crypto_sign_VERSION crypto_sign_edwards25519sha512batch_VERSION 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_sign_edwards25519sha512batch_H 2 | #define crypto_sign_edwards25519sha512batch_H 3 | 4 | #define crypto_sign_edwards25519sha512batch_ref10_SECRETKEYBYTES 64 5 | #define crypto_sign_edwards25519sha512batch_ref10_PUBLICKEYBYTES 32 6 | #define crypto_sign_edwards25519sha512batch_ref10_BYTES 64 7 | #ifdef __cplusplus 8 | #include 9 | extern std::string crypto_sign_edwards25519sha512batch_ref10(const std::string &,const std::string &); 10 | extern std::string crypto_sign_edwards25519sha512batch_ref10_open(const std::string &,const std::string &); 11 | extern std::string crypto_sign_edwards25519sha512batch_ref10_keypair(std::string *); 12 | extern "C" { 13 | #endif 14 | extern int crypto_sign_edwards25519sha512batch_ref10(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); 15 | extern int crypto_sign_edwards25519sha512batch_ref10_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); 16 | extern int crypto_sign_edwards25519sha512batch_ref10_keypair(unsigned char *,unsigned char *); 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #define crypto_sign_edwards25519sha512batch crypto_sign_edwards25519sha512batch_ref10 22 | #define crypto_sign_edwards25519sha512batch_open crypto_sign_edwards25519sha512batch_ref10_open 23 | #define crypto_sign_edwards25519sha512batch_keypair crypto_sign_edwards25519sha512batch_ref10_keypair 24 | #define crypto_sign_edwards25519sha512batch_BYTES crypto_sign_edwards25519sha512batch_ref10_BYTES 25 | #define crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES crypto_sign_edwards25519sha512batch_ref10_PUBLICKEYBYTES 26 | #define crypto_sign_edwards25519sha512batch_SECRETKEYBYTES crypto_sign_edwards25519sha512batch_ref10_SECRETKEYBYTES 27 | #define crypto_sign_edwards25519sha512batch_IMPLEMENTATION "crypto_sign/edwards25519sha512batch/ref10" 28 | #ifndef crypto_sign_edwards25519sha512batch_ref10_VERSION 29 | #define crypto_sign_edwards25519sha512batch_ref10_VERSION "-" 30 | #endif 31 | #define crypto_sign_edwards25519sha512batch_VERSION crypto_sign_edwards25519sha512batch_ref10_VERSION 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_uint32.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_uint32_h 2 | #define crypto_uint32_h 3 | 4 | typedef unsigned int crypto_uint32; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_uint64.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_uint64_h 2 | #define crypto_uint64_h 3 | 4 | typedef unsigned long long crypto_uint64; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /native/ed25519/nacl_includes/crypto_verify_32.h: -------------------------------------------------------------------------------- 1 | #ifndef crypto_verify_32_H 2 | #define crypto_verify_32_H 3 | 4 | #define crypto_verify_32_ref_BYTES 32 5 | #ifdef __cplusplus 6 | #include 7 | extern "C" { 8 | #endif 9 | extern int crypto_verify_32_ref(const unsigned char *,const unsigned char *); 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #define crypto_verify_32 crypto_verify_32_ref 15 | #define crypto_verify_32_BYTES crypto_verify_32_ref_BYTES 16 | #define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/ref" 17 | #ifndef crypto_verify_32_ref_VERSION 18 | #define crypto_verify_32_ref_VERSION "-" 19 | #endif 20 | #define crypto_verify_32_VERSION crypto_verify_32_ref_VERSION 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /native/ed25519/open.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "crypto_sign.h" 3 | #include "crypto_hash_sha512.h" 4 | #include "crypto_verify_32.h" 5 | #include "ge.h" 6 | #include "sc.h" 7 | 8 | int crypto_sign_open( 9 | unsigned char *m,unsigned long long *mlen, 10 | const unsigned char *sm,unsigned long long smlen, 11 | const unsigned char *pk 12 | ) 13 | { 14 | unsigned char pkcopy[32]; 15 | unsigned char rcopy[32]; 16 | unsigned char scopy[32]; 17 | unsigned char h[64]; 18 | unsigned char rcheck[32]; 19 | ge_p3 A; 20 | ge_p2 R; 21 | 22 | if (smlen < 64) goto badsig; 23 | if (sm[63] & 224) goto badsig; 24 | if (ge_frombytes_negate_vartime(&A,pk) != 0) goto badsig; 25 | 26 | memmove(pkcopy,pk,32); 27 | memmove(rcopy,sm,32); 28 | memmove(scopy,sm + 32,32); 29 | 30 | memmove(m,sm,smlen); 31 | memmove(m + 32,pkcopy,32); 32 | crypto_hash_sha512(h,m,smlen); 33 | sc_reduce(h); 34 | 35 | ge_double_scalarmult_vartime(&R,h,&A,scopy); 36 | ge_tobytes(rcheck,&R); 37 | if (crypto_verify_32(rcheck,rcopy) == 0) { 38 | memmove(m,m + 64,smlen - 64); 39 | memset(m + smlen - 64,0,64); 40 | *mlen = smlen - 64; 41 | return 0; 42 | } 43 | 44 | badsig: 45 | *mlen = -1; 46 | memset(m,0,smlen); 47 | return -1; 48 | } 49 | -------------------------------------------------------------------------------- /native/ed25519/pow22523.h: -------------------------------------------------------------------------------- 1 | 2 | /* qhasm: fe z1 */ 3 | 4 | /* qhasm: fe z2 */ 5 | 6 | /* qhasm: fe z8 */ 7 | 8 | /* qhasm: fe z9 */ 9 | 10 | /* qhasm: fe z11 */ 11 | 12 | /* qhasm: fe z22 */ 13 | 14 | /* qhasm: fe z_5_0 */ 15 | 16 | /* qhasm: fe z_10_5 */ 17 | 18 | /* qhasm: fe z_10_0 */ 19 | 20 | /* qhasm: fe z_20_10 */ 21 | 22 | /* qhasm: fe z_20_0 */ 23 | 24 | /* qhasm: fe z_40_20 */ 25 | 26 | /* qhasm: fe z_40_0 */ 27 | 28 | /* qhasm: fe z_50_10 */ 29 | 30 | /* qhasm: fe z_50_0 */ 31 | 32 | /* qhasm: fe z_100_50 */ 33 | 34 | /* qhasm: fe z_100_0 */ 35 | 36 | /* qhasm: fe z_200_100 */ 37 | 38 | /* qhasm: fe z_200_0 */ 39 | 40 | /* qhasm: fe z_250_50 */ 41 | 42 | /* qhasm: fe z_250_0 */ 43 | 44 | /* qhasm: fe z_252_2 */ 45 | 46 | /* qhasm: fe z_252_3 */ 47 | 48 | /* qhasm: enter pow22523 */ 49 | 50 | /* qhasm: z2 = z1^2^1 */ 51 | /* asm 1: fe_sq(>z2=fe#1,z2=fe#1,>z2=fe#1); */ 52 | /* asm 2: fe_sq(>z2=t0,z2=t0,>z2=t0); */ 53 | fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); 54 | 55 | /* qhasm: z8 = z2^2^2 */ 56 | /* asm 1: fe_sq(>z8=fe#2,z8=fe#2,>z8=fe#2); */ 57 | /* asm 2: fe_sq(>z8=t1,z8=t1,>z8=t1); */ 58 | fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); 59 | 60 | /* qhasm: z9 = z1*z8 */ 61 | /* asm 1: fe_mul(>z9=fe#2,z9=t1,z11=fe#1,z11=t0,z22=fe#1,z22=fe#1,>z22=fe#1); */ 72 | /* asm 2: fe_sq(>z22=t0,z22=t0,>z22=t0); */ 73 | fe_sq(t0,t0); for (i = 1;i < 1;++i) fe_sq(t0,t0); 74 | 75 | /* qhasm: z_5_0 = z9*z22 */ 76 | /* asm 1: fe_mul(>z_5_0=fe#1,z_5_0=t0,z_10_5=fe#2,z_10_5=fe#2,>z_10_5=fe#2); */ 82 | /* asm 2: fe_sq(>z_10_5=t1,z_10_5=t1,>z_10_5=t1); */ 83 | fe_sq(t1,t0); for (i = 1;i < 5;++i) fe_sq(t1,t1); 84 | 85 | /* qhasm: z_10_0 = z_10_5*z_5_0 */ 86 | /* asm 1: fe_mul(>z_10_0=fe#1,z_10_0=t0,z_20_10=fe#2,z_20_10=fe#2,>z_20_10=fe#2); */ 92 | /* asm 2: fe_sq(>z_20_10=t1,z_20_10=t1,>z_20_10=t1); */ 93 | fe_sq(t1,t0); for (i = 1;i < 10;++i) fe_sq(t1,t1); 94 | 95 | /* qhasm: z_20_0 = z_20_10*z_10_0 */ 96 | /* asm 1: fe_mul(>z_20_0=fe#2,z_20_0=t1,z_40_20=fe#3,z_40_20=fe#3,>z_40_20=fe#3); */ 102 | /* asm 2: fe_sq(>z_40_20=t2,z_40_20=t2,>z_40_20=t2); */ 103 | fe_sq(t2,t1); for (i = 1;i < 20;++i) fe_sq(t2,t2); 104 | 105 | /* qhasm: z_40_0 = z_40_20*z_20_0 */ 106 | /* asm 1: fe_mul(>z_40_0=fe#2,z_40_0=t1,z_50_10=fe#2,z_50_10=fe#2,>z_50_10=fe#2); */ 112 | /* asm 2: fe_sq(>z_50_10=t1,z_50_10=t1,>z_50_10=t1); */ 113 | fe_sq(t1,t1); for (i = 1;i < 10;++i) fe_sq(t1,t1); 114 | 115 | /* qhasm: z_50_0 = z_50_10*z_10_0 */ 116 | /* asm 1: fe_mul(>z_50_0=fe#1,z_50_0=t0,z_100_50=fe#2,z_100_50=fe#2,>z_100_50=fe#2); */ 122 | /* asm 2: fe_sq(>z_100_50=t1,z_100_50=t1,>z_100_50=t1); */ 123 | fe_sq(t1,t0); for (i = 1;i < 50;++i) fe_sq(t1,t1); 124 | 125 | /* qhasm: z_100_0 = z_100_50*z_50_0 */ 126 | /* asm 1: fe_mul(>z_100_0=fe#2,z_100_0=t1,z_200_100=fe#3,z_200_100=fe#3,>z_200_100=fe#3); */ 132 | /* asm 2: fe_sq(>z_200_100=t2,z_200_100=t2,>z_200_100=t2); */ 133 | fe_sq(t2,t1); for (i = 1;i < 100;++i) fe_sq(t2,t2); 134 | 135 | /* qhasm: z_200_0 = z_200_100*z_100_0 */ 136 | /* asm 1: fe_mul(>z_200_0=fe#2,z_200_0=t1,z_250_50=fe#2,z_250_50=fe#2,>z_250_50=fe#2); */ 142 | /* asm 2: fe_sq(>z_250_50=t1,z_250_50=t1,>z_250_50=t1); */ 143 | fe_sq(t1,t1); for (i = 1;i < 50;++i) fe_sq(t1,t1); 144 | 145 | /* qhasm: z_250_0 = z_250_50*z_50_0 */ 146 | /* asm 1: fe_mul(>z_250_0=fe#1,z_250_0=t0,z_252_2=fe#1,z_252_2=fe#1,>z_252_2=fe#1); */ 152 | /* asm 2: fe_sq(>z_252_2=t0,z_252_2=t0,>z_252_2=t0); */ 153 | fe_sq(t0,t0); for (i = 1;i < 2;++i) fe_sq(t0,t0); 154 | 155 | /* qhasm: z_252_3 = z_252_2*z1 */ 156 | /* asm 1: fe_mul(>z_252_3=fe#12,z_252_3=out,z2=fe#1,z2=fe#1,>z2=fe#1); */ 52 | /* asm 2: fe_sq(>z2=t0,z2=t0,>z2=t0); */ 53 | fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); 54 | 55 | /* qhasm: z8 = z2^2^2 */ 56 | /* asm 1: fe_sq(>z8=fe#2,z8=fe#2,>z8=fe#2); */ 57 | /* asm 2: fe_sq(>z8=t1,z8=t1,>z8=t1); */ 58 | fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); 59 | 60 | /* qhasm: z9 = z1*z8 */ 61 | /* asm 1: fe_mul(>z9=fe#2,z9=t1,z11=fe#1,z11=t0,z22=fe#3,z22=fe#3,>z22=fe#3); */ 72 | /* asm 2: fe_sq(>z22=t2,z22=t2,>z22=t2); */ 73 | fe_sq(t2,t0); for (i = 1;i < 1;++i) fe_sq(t2,t2); 74 | 75 | /* qhasm: z_5_0 = z9*z22 */ 76 | /* asm 1: fe_mul(>z_5_0=fe#2,z_5_0=t1,z_10_5=fe#3,z_10_5=fe#3,>z_10_5=fe#3); */ 82 | /* asm 2: fe_sq(>z_10_5=t2,z_10_5=t2,>z_10_5=t2); */ 83 | fe_sq(t2,t1); for (i = 1;i < 5;++i) fe_sq(t2,t2); 84 | 85 | /* qhasm: z_10_0 = z_10_5*z_5_0 */ 86 | /* asm 1: fe_mul(>z_10_0=fe#2,z_10_0=t1,z_20_10=fe#3,z_20_10=fe#3,>z_20_10=fe#3); */ 92 | /* asm 2: fe_sq(>z_20_10=t2,z_20_10=t2,>z_20_10=t2); */ 93 | fe_sq(t2,t1); for (i = 1;i < 10;++i) fe_sq(t2,t2); 94 | 95 | /* qhasm: z_20_0 = z_20_10*z_10_0 */ 96 | /* asm 1: fe_mul(>z_20_0=fe#3,z_20_0=t2,z_40_20=fe#4,z_40_20=fe#4,>z_40_20=fe#4); */ 102 | /* asm 2: fe_sq(>z_40_20=t3,z_40_20=t3,>z_40_20=t3); */ 103 | fe_sq(t3,t2); for (i = 1;i < 20;++i) fe_sq(t3,t3); 104 | 105 | /* qhasm: z_40_0 = z_40_20*z_20_0 */ 106 | /* asm 1: fe_mul(>z_40_0=fe#3,z_40_0=t2,z_50_10=fe#3,z_50_10=fe#3,>z_50_10=fe#3); */ 112 | /* asm 2: fe_sq(>z_50_10=t2,z_50_10=t2,>z_50_10=t2); */ 113 | fe_sq(t2,t2); for (i = 1;i < 10;++i) fe_sq(t2,t2); 114 | 115 | /* qhasm: z_50_0 = z_50_10*z_10_0 */ 116 | /* asm 1: fe_mul(>z_50_0=fe#2,z_50_0=t1,z_100_50=fe#3,z_100_50=fe#3,>z_100_50=fe#3); */ 122 | /* asm 2: fe_sq(>z_100_50=t2,z_100_50=t2,>z_100_50=t2); */ 123 | fe_sq(t2,t1); for (i = 1;i < 50;++i) fe_sq(t2,t2); 124 | 125 | /* qhasm: z_100_0 = z_100_50*z_50_0 */ 126 | /* asm 1: fe_mul(>z_100_0=fe#3,z_100_0=t2,z_200_100=fe#4,z_200_100=fe#4,>z_200_100=fe#4); */ 132 | /* asm 2: fe_sq(>z_200_100=t3,z_200_100=t3,>z_200_100=t3); */ 133 | fe_sq(t3,t2); for (i = 1;i < 100;++i) fe_sq(t3,t3); 134 | 135 | /* qhasm: z_200_0 = z_200_100*z_100_0 */ 136 | /* asm 1: fe_mul(>z_200_0=fe#3,z_200_0=t2,z_250_50=fe#3,z_250_50=fe#3,>z_250_50=fe#3); */ 142 | /* asm 2: fe_sq(>z_250_50=t2,z_250_50=t2,>z_250_50=t2); */ 143 | fe_sq(t2,t2); for (i = 1;i < 50;++i) fe_sq(t2,t2); 144 | 145 | /* qhasm: z_250_0 = z_250_50*z_50_0 */ 146 | /* asm 1: fe_mul(>z_250_0=fe#2,z_250_0=t1,z_255_5=fe#2,z_255_5=fe#2,>z_255_5=fe#2); */ 152 | /* asm 2: fe_sq(>z_255_5=t1,z_255_5=t1,>z_255_5=t1); */ 153 | fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); 154 | 155 | /* qhasm: z_255_21 = z_255_5*z11 */ 156 | /* asm 1: fe_mul(>z_255_21=fe#12,z_255_21=out,> 5); 39 | crypto_int64 s2 = 2097151 & (load_3(s + 5) >> 2); 40 | crypto_int64 s3 = 2097151 & (load_4(s + 7) >> 7); 41 | crypto_int64 s4 = 2097151 & (load_4(s + 10) >> 4); 42 | crypto_int64 s5 = 2097151 & (load_3(s + 13) >> 1); 43 | crypto_int64 s6 = 2097151 & (load_4(s + 15) >> 6); 44 | crypto_int64 s7 = 2097151 & (load_3(s + 18) >> 3); 45 | crypto_int64 s8 = 2097151 & load_3(s + 21); 46 | crypto_int64 s9 = 2097151 & (load_4(s + 23) >> 5); 47 | crypto_int64 s10 = 2097151 & (load_3(s + 26) >> 2); 48 | crypto_int64 s11 = 2097151 & (load_4(s + 28) >> 7); 49 | crypto_int64 s12 = 2097151 & (load_4(s + 31) >> 4); 50 | crypto_int64 s13 = 2097151 & (load_3(s + 34) >> 1); 51 | crypto_int64 s14 = 2097151 & (load_4(s + 36) >> 6); 52 | crypto_int64 s15 = 2097151 & (load_3(s + 39) >> 3); 53 | crypto_int64 s16 = 2097151 & load_3(s + 42); 54 | crypto_int64 s17 = 2097151 & (load_4(s + 44) >> 5); 55 | crypto_int64 s18 = 2097151 & (load_3(s + 47) >> 2); 56 | crypto_int64 s19 = 2097151 & (load_4(s + 49) >> 7); 57 | crypto_int64 s20 = 2097151 & (load_4(s + 52) >> 4); 58 | crypto_int64 s21 = 2097151 & (load_3(s + 55) >> 1); 59 | crypto_int64 s22 = 2097151 & (load_4(s + 57) >> 6); 60 | crypto_int64 s23 = (load_4(s + 60) >> 3); 61 | crypto_int64 carry0; 62 | crypto_int64 carry1; 63 | crypto_int64 carry2; 64 | crypto_int64 carry3; 65 | crypto_int64 carry4; 66 | crypto_int64 carry5; 67 | crypto_int64 carry6; 68 | crypto_int64 carry7; 69 | crypto_int64 carry8; 70 | crypto_int64 carry9; 71 | crypto_int64 carry10; 72 | crypto_int64 carry11; 73 | crypto_int64 carry12; 74 | crypto_int64 carry13; 75 | crypto_int64 carry14; 76 | crypto_int64 carry15; 77 | crypto_int64 carry16; 78 | 79 | s11 += s23 * 666643; 80 | s12 += s23 * 470296; 81 | s13 += s23 * 654183; 82 | s14 -= s23 * 997805; 83 | s15 += s23 * 136657; 84 | s16 -= s23 * 683901; 85 | s23 = 0; 86 | 87 | s10 += s22 * 666643; 88 | s11 += s22 * 470296; 89 | s12 += s22 * 654183; 90 | s13 -= s22 * 997805; 91 | s14 += s22 * 136657; 92 | s15 -= s22 * 683901; 93 | s22 = 0; 94 | 95 | s9 += s21 * 666643; 96 | s10 += s21 * 470296; 97 | s11 += s21 * 654183; 98 | s12 -= s21 * 997805; 99 | s13 += s21 * 136657; 100 | s14 -= s21 * 683901; 101 | s21 = 0; 102 | 103 | s8 += s20 * 666643; 104 | s9 += s20 * 470296; 105 | s10 += s20 * 654183; 106 | s11 -= s20 * 997805; 107 | s12 += s20 * 136657; 108 | s13 -= s20 * 683901; 109 | s20 = 0; 110 | 111 | s7 += s19 * 666643; 112 | s8 += s19 * 470296; 113 | s9 += s19 * 654183; 114 | s10 -= s19 * 997805; 115 | s11 += s19 * 136657; 116 | s12 -= s19 * 683901; 117 | s19 = 0; 118 | 119 | s6 += s18 * 666643; 120 | s7 += s18 * 470296; 121 | s8 += s18 * 654183; 122 | s9 -= s18 * 997805; 123 | s10 += s18 * 136657; 124 | s11 -= s18 * 683901; 125 | s18 = 0; 126 | 127 | carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; 128 | carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; 129 | carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; 130 | carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; 131 | carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; 132 | carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; 133 | 134 | carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; 135 | carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; 136 | carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; 137 | carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; 138 | carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; 139 | 140 | s5 += s17 * 666643; 141 | s6 += s17 * 470296; 142 | s7 += s17 * 654183; 143 | s8 -= s17 * 997805; 144 | s9 += s17 * 136657; 145 | s10 -= s17 * 683901; 146 | s17 = 0; 147 | 148 | s4 += s16 * 666643; 149 | s5 += s16 * 470296; 150 | s6 += s16 * 654183; 151 | s7 -= s16 * 997805; 152 | s8 += s16 * 136657; 153 | s9 -= s16 * 683901; 154 | s16 = 0; 155 | 156 | s3 += s15 * 666643; 157 | s4 += s15 * 470296; 158 | s5 += s15 * 654183; 159 | s6 -= s15 * 997805; 160 | s7 += s15 * 136657; 161 | s8 -= s15 * 683901; 162 | s15 = 0; 163 | 164 | s2 += s14 * 666643; 165 | s3 += s14 * 470296; 166 | s4 += s14 * 654183; 167 | s5 -= s14 * 997805; 168 | s6 += s14 * 136657; 169 | s7 -= s14 * 683901; 170 | s14 = 0; 171 | 172 | s1 += s13 * 666643; 173 | s2 += s13 * 470296; 174 | s3 += s13 * 654183; 175 | s4 -= s13 * 997805; 176 | s5 += s13 * 136657; 177 | s6 -= s13 * 683901; 178 | s13 = 0; 179 | 180 | s0 += s12 * 666643; 181 | s1 += s12 * 470296; 182 | s2 += s12 * 654183; 183 | s3 -= s12 * 997805; 184 | s4 += s12 * 136657; 185 | s5 -= s12 * 683901; 186 | s12 = 0; 187 | 188 | carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; 189 | carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; 190 | carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; 191 | carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; 192 | carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; 193 | carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; 194 | 195 | carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; 196 | carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; 197 | carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; 198 | carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; 199 | carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; 200 | carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; 201 | 202 | s0 += s12 * 666643; 203 | s1 += s12 * 470296; 204 | s2 += s12 * 654183; 205 | s3 -= s12 * 997805; 206 | s4 += s12 * 136657; 207 | s5 -= s12 * 683901; 208 | s12 = 0; 209 | 210 | carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; 211 | carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; 212 | carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; 213 | carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; 214 | carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; 215 | carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; 216 | carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; 217 | carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; 218 | carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; 219 | carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; 220 | carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; 221 | carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; 222 | 223 | s0 += s12 * 666643; 224 | s1 += s12 * 470296; 225 | s2 += s12 * 654183; 226 | s3 -= s12 * 997805; 227 | s4 += s12 * 136657; 228 | s5 -= s12 * 683901; 229 | s12 = 0; 230 | 231 | carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; 232 | carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; 233 | carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; 234 | carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; 235 | carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; 236 | carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; 237 | carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; 238 | carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; 239 | carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; 240 | carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; 241 | carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; 242 | 243 | s[0] = s0 >> 0; 244 | s[1] = s0 >> 8; 245 | s[2] = (s0 >> 16) | (s1 << 5); 246 | s[3] = s1 >> 3; 247 | s[4] = s1 >> 11; 248 | s[5] = (s1 >> 19) | (s2 << 2); 249 | s[6] = s2 >> 6; 250 | s[7] = (s2 >> 14) | (s3 << 7); 251 | s[8] = s3 >> 1; 252 | s[9] = s3 >> 9; 253 | s[10] = (s3 >> 17) | (s4 << 4); 254 | s[11] = s4 >> 4; 255 | s[12] = s4 >> 12; 256 | s[13] = (s4 >> 20) | (s5 << 1); 257 | s[14] = s5 >> 7; 258 | s[15] = (s5 >> 15) | (s6 << 6); 259 | s[16] = s6 >> 2; 260 | s[17] = s6 >> 10; 261 | s[18] = (s6 >> 18) | (s7 << 3); 262 | s[19] = s7 >> 5; 263 | s[20] = s7 >> 13; 264 | s[21] = s8 >> 0; 265 | s[22] = s8 >> 8; 266 | s[23] = (s8 >> 16) | (s9 << 5); 267 | s[24] = s9 >> 3; 268 | s[25] = s9 >> 11; 269 | s[26] = (s9 >> 19) | (s10 << 2); 270 | s[27] = s10 >> 6; 271 | s[28] = (s10 >> 14) | (s11 << 7); 272 | s[29] = s11 >> 1; 273 | s[30] = s11 >> 9; 274 | s[31] = s11 >> 17; 275 | } 276 | -------------------------------------------------------------------------------- /native/ed25519/sha512/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2011 Projet RNRT SAPHIR 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /native/ed25519/sha512/sha2big.c: -------------------------------------------------------------------------------- 1 | /* $Id: sha2big.c 216 2010-06-08 09:46:57Z tp $ */ 2 | /* 3 | * SHA-384 / SHA-512 implementation. 4 | * 5 | * ==========================(LICENSE BEGIN)============================ 6 | * 7 | * Copyright (c) 2007-2010 Projet RNRT SAPHIR 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining 10 | * a copy of this software and associated documentation files (the 11 | * "Software"), to deal in the Software without restriction, including 12 | * without limitation the rights to use, copy, modify, merge, publish, 13 | * distribute, sublicense, and/or sell copies of the Software, and to 14 | * permit persons to whom the Software is furnished to do so, subject to 15 | * the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | * 28 | * ===========================(LICENSE END)============================= 29 | * 30 | * @author Thomas Pornin 31 | */ 32 | 33 | #include 34 | #include 35 | 36 | #include "sph_sha2.h" 37 | 38 | #if SPH_64 39 | 40 | #define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) 41 | #define MAJ(X, Y, Z) (((X) & (Y)) | (((X) | (Y)) & (Z))) 42 | 43 | #define ROTR64 SPH_ROTR64 44 | 45 | #define BSG5_0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) 46 | #define BSG5_1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) 47 | #define SSG5_0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ SPH_T64((x) >> 7)) 48 | #define SSG5_1(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ SPH_T64((x) >> 6)) 49 | 50 | static const sph_u64 K512[80] = { 51 | SPH_C64(0x428A2F98D728AE22), SPH_C64(0x7137449123EF65CD), 52 | SPH_C64(0xB5C0FBCFEC4D3B2F), SPH_C64(0xE9B5DBA58189DBBC), 53 | SPH_C64(0x3956C25BF348B538), SPH_C64(0x59F111F1B605D019), 54 | SPH_C64(0x923F82A4AF194F9B), SPH_C64(0xAB1C5ED5DA6D8118), 55 | SPH_C64(0xD807AA98A3030242), SPH_C64(0x12835B0145706FBE), 56 | SPH_C64(0x243185BE4EE4B28C), SPH_C64(0x550C7DC3D5FFB4E2), 57 | SPH_C64(0x72BE5D74F27B896F), SPH_C64(0x80DEB1FE3B1696B1), 58 | SPH_C64(0x9BDC06A725C71235), SPH_C64(0xC19BF174CF692694), 59 | SPH_C64(0xE49B69C19EF14AD2), SPH_C64(0xEFBE4786384F25E3), 60 | SPH_C64(0x0FC19DC68B8CD5B5), SPH_C64(0x240CA1CC77AC9C65), 61 | SPH_C64(0x2DE92C6F592B0275), SPH_C64(0x4A7484AA6EA6E483), 62 | SPH_C64(0x5CB0A9DCBD41FBD4), SPH_C64(0x76F988DA831153B5), 63 | SPH_C64(0x983E5152EE66DFAB), SPH_C64(0xA831C66D2DB43210), 64 | SPH_C64(0xB00327C898FB213F), SPH_C64(0xBF597FC7BEEF0EE4), 65 | SPH_C64(0xC6E00BF33DA88FC2), SPH_C64(0xD5A79147930AA725), 66 | SPH_C64(0x06CA6351E003826F), SPH_C64(0x142929670A0E6E70), 67 | SPH_C64(0x27B70A8546D22FFC), SPH_C64(0x2E1B21385C26C926), 68 | SPH_C64(0x4D2C6DFC5AC42AED), SPH_C64(0x53380D139D95B3DF), 69 | SPH_C64(0x650A73548BAF63DE), SPH_C64(0x766A0ABB3C77B2A8), 70 | SPH_C64(0x81C2C92E47EDAEE6), SPH_C64(0x92722C851482353B), 71 | SPH_C64(0xA2BFE8A14CF10364), SPH_C64(0xA81A664BBC423001), 72 | SPH_C64(0xC24B8B70D0F89791), SPH_C64(0xC76C51A30654BE30), 73 | SPH_C64(0xD192E819D6EF5218), SPH_C64(0xD69906245565A910), 74 | SPH_C64(0xF40E35855771202A), SPH_C64(0x106AA07032BBD1B8), 75 | SPH_C64(0x19A4C116B8D2D0C8), SPH_C64(0x1E376C085141AB53), 76 | SPH_C64(0x2748774CDF8EEB99), SPH_C64(0x34B0BCB5E19B48A8), 77 | SPH_C64(0x391C0CB3C5C95A63), SPH_C64(0x4ED8AA4AE3418ACB), 78 | SPH_C64(0x5B9CCA4F7763E373), SPH_C64(0x682E6FF3D6B2B8A3), 79 | SPH_C64(0x748F82EE5DEFB2FC), SPH_C64(0x78A5636F43172F60), 80 | SPH_C64(0x84C87814A1F0AB72), SPH_C64(0x8CC702081A6439EC), 81 | SPH_C64(0x90BEFFFA23631E28), SPH_C64(0xA4506CEBDE82BDE9), 82 | SPH_C64(0xBEF9A3F7B2C67915), SPH_C64(0xC67178F2E372532B), 83 | SPH_C64(0xCA273ECEEA26619C), SPH_C64(0xD186B8C721C0C207), 84 | SPH_C64(0xEADA7DD6CDE0EB1E), SPH_C64(0xF57D4F7FEE6ED178), 85 | SPH_C64(0x06F067AA72176FBA), SPH_C64(0x0A637DC5A2C898A6), 86 | SPH_C64(0x113F9804BEF90DAE), SPH_C64(0x1B710B35131C471B), 87 | SPH_C64(0x28DB77F523047D84), SPH_C64(0x32CAAB7B40C72493), 88 | SPH_C64(0x3C9EBE0A15C9BEBC), SPH_C64(0x431D67C49C100D4C), 89 | SPH_C64(0x4CC5D4BECB3E42B6), SPH_C64(0x597F299CFC657E2A), 90 | SPH_C64(0x5FCB6FAB3AD6FAEC), SPH_C64(0x6C44198C4A475817) 91 | }; 92 | 93 | static const sph_u64 H384[8] = { 94 | SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507), 95 | SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939), 96 | SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511), 97 | SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4) 98 | }; 99 | 100 | static const sph_u64 H512[8] = { 101 | SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B), 102 | SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1), 103 | SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F), 104 | SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179) 105 | }; 106 | 107 | /* 108 | * This macro defines the body for a SHA-384 / SHA-512 compression function 109 | * implementation. The "in" parameter should evaluate, when applied to a 110 | * numerical input parameter from 0 to 15, to an expression which yields 111 | * the corresponding input block. The "r" parameter should evaluate to 112 | * an array or pointer expression designating the array of 8 words which 113 | * contains the input and output of the compression function. 114 | * 115 | * SHA-512 is hard for the compiler. If the loop is completely unrolled, 116 | * then the code will be quite huge (possibly more than 100 kB), and the 117 | * performance will be degraded due to cache misses on the code. We 118 | * unroll only eight steps, which avoids all needless copies when 119 | * 64-bit registers are swapped. 120 | */ 121 | 122 | #define SHA3_STEP(A, B, C, D, E, F, G, H, i) do { \ 123 | sph_u64 T1, T2; \ 124 | T1 = SPH_T64(H + BSG5_1(E) + CH(E, F, G) + K512[i] + W[i]); \ 125 | T2 = SPH_T64(BSG5_0(A) + MAJ(A, B, C)); \ 126 | D = SPH_T64(D + T1); \ 127 | H = SPH_T64(T1 + T2); \ 128 | } while (0) 129 | 130 | #define SHA3_ROUND_BODY(in, r) do { \ 131 | int i; \ 132 | sph_u64 A, B, C, D, E, F, G, H; \ 133 | sph_u64 W[80]; \ 134 | \ 135 | for (i = 0; i < 16; i ++) \ 136 | W[i] = in(i); \ 137 | for (i = 16; i < 80; i ++) \ 138 | W[i] = SPH_T64(SSG5_1(W[i - 2]) + W[i - 7] \ 139 | + SSG5_0(W[i - 15]) + W[i - 16]); \ 140 | A = (r)[0]; \ 141 | B = (r)[1]; \ 142 | C = (r)[2]; \ 143 | D = (r)[3]; \ 144 | E = (r)[4]; \ 145 | F = (r)[5]; \ 146 | G = (r)[6]; \ 147 | H = (r)[7]; \ 148 | for (i = 0; i < 80; i += 8) { \ 149 | SHA3_STEP(A, B, C, D, E, F, G, H, i + 0); \ 150 | SHA3_STEP(H, A, B, C, D, E, F, G, i + 1); \ 151 | SHA3_STEP(G, H, A, B, C, D, E, F, i + 2); \ 152 | SHA3_STEP(F, G, H, A, B, C, D, E, i + 3); \ 153 | SHA3_STEP(E, F, G, H, A, B, C, D, i + 4); \ 154 | SHA3_STEP(D, E, F, G, H, A, B, C, i + 5); \ 155 | SHA3_STEP(C, D, E, F, G, H, A, B, i + 6); \ 156 | SHA3_STEP(B, C, D, E, F, G, H, A, i + 7); \ 157 | } \ 158 | (r)[0] = SPH_T64((r)[0] + A); \ 159 | (r)[1] = SPH_T64((r)[1] + B); \ 160 | (r)[2] = SPH_T64((r)[2] + C); \ 161 | (r)[3] = SPH_T64((r)[3] + D); \ 162 | (r)[4] = SPH_T64((r)[4] + E); \ 163 | (r)[5] = SPH_T64((r)[5] + F); \ 164 | (r)[6] = SPH_T64((r)[6] + G); \ 165 | (r)[7] = SPH_T64((r)[7] + H); \ 166 | } while (0) 167 | 168 | /* 169 | * One round of SHA-384 / SHA-512. The data must be aligned for 64-bit access. 170 | */ 171 | static void 172 | sha3_round(const unsigned char *data, sph_u64 r[8]) 173 | { 174 | #define SHA3_IN(x) sph_dec64be_aligned(data + (8 * (x))) 175 | SHA3_ROUND_BODY(SHA3_IN, r); 176 | #undef SHA3_IN 177 | } 178 | 179 | /* see sph_sha3.h */ 180 | void 181 | sph_sha384_init(void *cc) 182 | { 183 | sph_sha384_context *sc; 184 | 185 | sc = cc; 186 | memcpy(sc->val, H384, sizeof H384); 187 | sc->count = 0; 188 | } 189 | 190 | /* see sph_sha3.h */ 191 | void 192 | sph_sha512_init(void *cc) 193 | { 194 | sph_sha512_context *sc; 195 | 196 | sc = cc; 197 | memcpy(sc->val, H512, sizeof H512); 198 | sc->count = 0; 199 | } 200 | 201 | #define RFUN sha3_round 202 | #define HASH sha384 203 | #define BE64 1 204 | #include "md_helper.c" 205 | 206 | /* see sph_sha3.h */ 207 | void 208 | sph_sha384_close(void *cc, void *dst) 209 | { 210 | sha384_close(cc, dst, 6); 211 | sph_sha384_init(cc); 212 | } 213 | 214 | /* see sph_sha3.h */ 215 | void 216 | sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) 217 | { 218 | sha384_addbits_and_close(cc, ub, n, dst, 6); 219 | sph_sha384_init(cc); 220 | } 221 | 222 | /* see sph_sha3.h */ 223 | void 224 | sph_sha512_close(void *cc, void *dst) 225 | { 226 | sha384_close(cc, dst, 8); 227 | sph_sha512_init(cc); 228 | } 229 | 230 | /* see sph_sha3.h */ 231 | void 232 | sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) 233 | { 234 | sha384_addbits_and_close(cc, ub, n, dst, 8); 235 | sph_sha512_init(cc); 236 | } 237 | 238 | /* see sph_sha3.h */ 239 | void 240 | sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]) 241 | { 242 | #define SHA3_IN(x) msg[x] 243 | SHA3_ROUND_BODY(SHA3_IN, val); 244 | #undef SHA3_IN 245 | } 246 | 247 | #endif 248 | -------------------------------------------------------------------------------- /native/ed25519/sign.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "crypto_sign.h" 3 | #include "crypto_hash_sha512.h" 4 | #include "ge.h" 5 | #include "sc.h" 6 | 7 | int crypto_sign( 8 | unsigned char *sm,unsigned long long *smlen, 9 | const unsigned char *m,unsigned long long mlen, 10 | const unsigned char *sk 11 | ) 12 | { 13 | unsigned char pk[32]; 14 | unsigned char az[64]; 15 | unsigned char nonce[64]; 16 | unsigned char hram[64]; 17 | ge_p3 R; 18 | 19 | memmove(pk,sk + 32,32); 20 | 21 | crypto_hash_sha512(az,sk,32); 22 | az[0] &= 248; 23 | az[31] &= 63; 24 | az[31] |= 64; 25 | 26 | *smlen = mlen + 64; 27 | memmove(sm + 64,m,mlen); 28 | memmove(sm + 32,az + 32,32); 29 | crypto_hash_sha512(nonce,sm + 32,mlen + 32); 30 | memmove(sm + 32,pk,32); 31 | 32 | sc_reduce(nonce); 33 | ge_scalarmult_base(&R,nonce); 34 | ge_p3_tobytes(sm,&R); 35 | 36 | crypto_hash_sha512(hram,sm,mlen + 64); 37 | sc_reduce(hram); 38 | sc_muladd(sm + 32,hram,az,nonce); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /native/ed25519/sqrtm1.h: -------------------------------------------------------------------------------- 1 | -32595792,-7943725,9377950,3500415,12389472,-272473,-25146209,-2005654,326686,11406482 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libsignal-protocol", 3 | "repository": "https://github.com/WhisperSystems/libsignal-protocol-javascript.git", 4 | "version": "1.3.0", 5 | "license": "GPL-3.0", 6 | "dependencies": { 7 | "long": "^3.1.0", 8 | "bytebuffer": "^3.5.5", 9 | "protobufjs": "5.0.1" 10 | }, 11 | "devDependencies": { 12 | "blanket": "1.1.7", 13 | "chai": "^3.5.0", 14 | "grunt": "^0.4.5", 15 | "grunt-cli": "^0.1.13", 16 | "grunt-contrib-concat": "^0.5.0", 17 | "grunt-contrib-connect": "^0.9.0", 18 | "grunt-contrib-jshint": "^1.1.0", 19 | "grunt-contrib-sass": "^0.8.1", 20 | "grunt-contrib-watch": "^0.6.1", 21 | "grunt-jscs": "^1.1.0", 22 | "grunt-preen": "^1.0.0", 23 | "grunt-saucelabs": "^8.3.3", 24 | "jquery": "^2.2.3", 25 | "mocha": "^2.4.5" 26 | }, 27 | "scripts": { 28 | "test": "grunt test", 29 | "lint": "grunt jshint" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /protos/WhisperTextProtocol.proto: -------------------------------------------------------------------------------- 1 | package textsecure; 2 | 3 | option java_package = "org.whispersystems.libsignal.protocol"; 4 | option java_outer_classname = "WhisperProtos"; 5 | 6 | message WhisperMessage { 7 | optional bytes ephemeralKey = 1; 8 | optional uint32 counter = 2; 9 | optional uint32 previousCounter = 3; 10 | optional bytes ciphertext = 4; // PushMessageContent 11 | } 12 | 13 | message PreKeyWhisperMessage { 14 | optional uint32 registrationId = 5; 15 | optional uint32 preKeyId = 1; 16 | optional uint32 signedPreKeyId = 6; 17 | optional bytes baseKey = 2; 18 | optional bytes identityKey = 3; 19 | optional bytes message = 4; // WhisperMessage 20 | } 21 | 22 | message KeyExchangeMessage { 23 | optional uint32 id = 1; 24 | optional bytes baseKey = 2; 25 | optional bytes ephemeralKey = 3; 26 | optional bytes identityKey = 4; 27 | optional bytes baseKeySignature = 5; 28 | } 29 | -------------------------------------------------------------------------------- /src/Curve.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | function validatePrivKey(privKey) { 5 | if (privKey === undefined || !(privKey instanceof ArrayBuffer) || privKey.byteLength != 32) { 6 | throw new Error("Invalid private key"); 7 | } 8 | } 9 | function validatePubKeyFormat(pubKey) { 10 | if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32)) { 11 | throw new Error("Invalid public key"); 12 | } 13 | if (pubKey.byteLength == 33) { 14 | return pubKey.slice(1); 15 | } else { 16 | console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey"); 17 | return pubKey; 18 | } 19 | } 20 | 21 | function processKeys(raw_keys) { 22 | // prepend version byte 23 | var origPub = new Uint8Array(raw_keys.pubKey); 24 | var pub = new Uint8Array(33); 25 | pub.set(origPub, 1); 26 | pub[0] = 5; 27 | 28 | return { pubKey: pub.buffer, privKey: raw_keys.privKey }; 29 | } 30 | 31 | function wrapCurve25519(curve25519) { 32 | return { 33 | // Curve 25519 crypto 34 | createKeyPair: function(privKey) { 35 | validatePrivKey(privKey); 36 | var raw_keys = curve25519.keyPair(privKey); 37 | if (raw_keys instanceof Promise) { 38 | return raw_keys.then(processKeys); 39 | } else { 40 | return processKeys(raw_keys); 41 | } 42 | }, 43 | ECDHE: function(pubKey, privKey) { 44 | pubKey = validatePubKeyFormat(pubKey); 45 | validatePrivKey(privKey); 46 | 47 | if (pubKey === undefined || pubKey.byteLength != 32) { 48 | throw new Error("Invalid public key"); 49 | } 50 | 51 | return curve25519.sharedSecret(pubKey, privKey); 52 | }, 53 | Ed25519Sign: function(privKey, message) { 54 | validatePrivKey(privKey); 55 | 56 | if (message === undefined) { 57 | throw new Error("Invalid message"); 58 | } 59 | 60 | return curve25519.sign(privKey, message); 61 | }, 62 | Ed25519Verify: function(pubKey, msg, sig) { 63 | pubKey = validatePubKeyFormat(pubKey); 64 | 65 | if (pubKey === undefined || pubKey.byteLength != 32) { 66 | throw new Error("Invalid public key"); 67 | } 68 | 69 | if (msg === undefined) { 70 | throw new Error("Invalid message"); 71 | } 72 | 73 | if (sig === undefined || sig.byteLength != 64) { 74 | throw new Error("Invalid signature"); 75 | } 76 | 77 | return curve25519.verify(pubKey, msg, sig); 78 | } 79 | }; 80 | } 81 | 82 | Internal.Curve = wrapCurve25519(Internal.curve25519); 83 | Internal.Curve.async = wrapCurve25519(Internal.curve25519_async); 84 | 85 | function wrapCurve(curve) { 86 | return { 87 | generateKeyPair: function() { 88 | var privKey = Internal.crypto.getRandomBytes(32); 89 | return curve.createKeyPair(privKey); 90 | }, 91 | createKeyPair: function(privKey) { 92 | return curve.createKeyPair(privKey); 93 | }, 94 | calculateAgreement: function(pubKey, privKey) { 95 | return curve.ECDHE(pubKey, privKey); 96 | }, 97 | verifySignature: function(pubKey, msg, sig) { 98 | return curve.Ed25519Verify(pubKey, msg, sig); 99 | }, 100 | calculateSignature: function(privKey, message) { 101 | return curve.Ed25519Sign(privKey, message); 102 | } 103 | }; 104 | } 105 | 106 | libsignal.Curve = wrapCurve(Internal.Curve); 107 | libsignal.Curve.async = wrapCurve(Internal.Curve.async); 108 | 109 | })(); 110 | -------------------------------------------------------------------------------- /src/KeyHelper.js: -------------------------------------------------------------------------------- 1 | function isNonNegativeInteger(n) { 2 | return (typeof n === 'number' && (n % 1) === 0 && n >= 0); 3 | } 4 | 5 | var KeyHelper = { 6 | generateIdentityKeyPair: function() { 7 | return Internal.crypto.createKeyPair(); 8 | }, 9 | 10 | generateRegistrationId: function() { 11 | var registrationId = new Uint16Array(Internal.crypto.getRandomBytes(2))[0]; 12 | return registrationId & 0x3fff; 13 | }, 14 | 15 | generateSignedPreKey: function (identityKeyPair, signedKeyId) { 16 | if (!(identityKeyPair.privKey instanceof ArrayBuffer) || 17 | identityKeyPair.privKey.byteLength != 32 || 18 | !(identityKeyPair.pubKey instanceof ArrayBuffer) || 19 | identityKeyPair.pubKey.byteLength != 33) { 20 | throw new TypeError('Invalid argument for identityKeyPair'); 21 | } 22 | if (!isNonNegativeInteger(signedKeyId)) { 23 | throw new TypeError( 24 | 'Invalid argument for signedKeyId: ' + signedKeyId 25 | ); 26 | } 27 | 28 | return Internal.crypto.createKeyPair().then(function(keyPair) { 29 | return Internal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) { 30 | return { 31 | keyId : signedKeyId, 32 | keyPair : keyPair, 33 | signature : sig 34 | }; 35 | }); 36 | }); 37 | }, 38 | 39 | generatePreKey: function(keyId) { 40 | if (!isNonNegativeInteger(keyId)) { 41 | throw new TypeError('Invalid argument for keyId: ' + keyId); 42 | } 43 | 44 | return Internal.crypto.createKeyPair().then(function(keyPair) { 45 | return { keyId: keyId, keyPair: keyPair }; 46 | }); 47 | } 48 | }; 49 | 50 | libsignal.KeyHelper = KeyHelper; 51 | -------------------------------------------------------------------------------- /src/NumericFingerprint.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var VERSION = 0; 3 | 4 | function iterateHash(data, key, count) { 5 | data = dcodeIO.ByteBuffer.concat([data, key]).toArrayBuffer(); 6 | return Internal.crypto.hash(data).then(function(result) { 7 | if (--count === 0) { 8 | return result; 9 | } else { 10 | return iterateHash(result, key, count); 11 | } 12 | }); 13 | } 14 | 15 | function shortToArrayBuffer(number) { 16 | return new Uint16Array([number]).buffer; 17 | } 18 | 19 | function getEncodedChunk(hash, offset) { 20 | var chunk = ( hash[offset] * Math.pow(2,32) + 21 | hash[offset+1] * Math.pow(2,24) + 22 | hash[offset+2] * Math.pow(2,16) + 23 | hash[offset+3] * Math.pow(2,8) + 24 | hash[offset+4] ) % 100000; 25 | var s = chunk.toString(); 26 | while (s.length < 5) { 27 | s = '0' + s; 28 | } 29 | return s; 30 | } 31 | 32 | function getDisplayStringFor(identifier, key, iterations) { 33 | var bytes = dcodeIO.ByteBuffer.concat([ 34 | shortToArrayBuffer(VERSION), key, identifier 35 | ]).toArrayBuffer(); 36 | return iterateHash(bytes, key, iterations).then(function(output) { 37 | output = new Uint8Array(output); 38 | return getEncodedChunk(output, 0) + 39 | getEncodedChunk(output, 5) + 40 | getEncodedChunk(output, 10) + 41 | getEncodedChunk(output, 15) + 42 | getEncodedChunk(output, 20) + 43 | getEncodedChunk(output, 25); 44 | }); 45 | } 46 | 47 | libsignal.FingerprintGenerator = function(iterations) { 48 | this.iterations = iterations; 49 | }; 50 | libsignal.FingerprintGenerator.prototype = { 51 | createFor: function(localIdentifier, localIdentityKey, 52 | remoteIdentifier, remoteIdentityKey) { 53 | if (typeof localIdentifier !== 'string' || 54 | typeof remoteIdentifier !== 'string' || 55 | !(localIdentityKey instanceof ArrayBuffer) || 56 | !(remoteIdentityKey instanceof ArrayBuffer)) { 57 | 58 | throw new Error('Invalid arguments'); 59 | } 60 | 61 | return Promise.all([ 62 | getDisplayStringFor(localIdentifier, localIdentityKey, this.iterations), 63 | getDisplayStringFor(remoteIdentifier, remoteIdentityKey, this.iterations) 64 | ]).then(function(fingerprints) { 65 | return fingerprints.sort().join(''); 66 | }); 67 | } 68 | }; 69 | 70 | })(); 71 | 72 | -------------------------------------------------------------------------------- /src/SessionBuilder.js: -------------------------------------------------------------------------------- 1 | function SessionBuilder(storage, remoteAddress) { 2 | this.remoteAddress = remoteAddress; 3 | this.storage = storage; 4 | } 5 | 6 | SessionBuilder.prototype = { 7 | processPreKey: function(device) { 8 | return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() { 9 | return this.storage.isTrustedIdentity( 10 | this.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING 11 | ).then(function(trusted) { 12 | if (!trusted) { 13 | throw new Error('Identity key changed'); 14 | } 15 | 16 | return Internal.crypto.Ed25519Verify( 17 | device.identityKey, 18 | device.signedPreKey.publicKey, 19 | device.signedPreKey.signature 20 | ); 21 | }).then(function() { 22 | return Internal.crypto.createKeyPair(); 23 | }).then(function(baseKey) { 24 | var devicePreKey; 25 | if (device.preKey) { 26 | devicePreKey = device.preKey.publicKey; 27 | } 28 | return this.initSession(true, baseKey, undefined, device.identityKey, 29 | devicePreKey, device.signedPreKey.publicKey, device.registrationId 30 | ).then(function(session) { 31 | session.pendingPreKey = { 32 | signedKeyId : device.signedPreKey.keyId, 33 | baseKey : baseKey.pubKey 34 | }; 35 | if (device.preKey) { 36 | session.pendingPreKey.preKeyId = device.preKey.keyId; 37 | } 38 | return session; 39 | }); 40 | }.bind(this)).then(function(session) { 41 | var address = this.remoteAddress.toString(); 42 | return this.storage.loadSession(address).then(function(serialized) { 43 | var record; 44 | if (serialized !== undefined) { 45 | record = Internal.SessionRecord.deserialize(serialized); 46 | } else { 47 | record = new Internal.SessionRecord(); 48 | } 49 | 50 | record.archiveCurrentState(); 51 | record.updateSessionState(session); 52 | return Promise.all([ 53 | this.storage.storeSession(address, record.serialize()), 54 | this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey) 55 | ]); 56 | }.bind(this)); 57 | }.bind(this)); 58 | }.bind(this)); 59 | }, 60 | processV3: function(record, message) { 61 | var preKeyPair, signedPreKeyPair, session; 62 | return this.storage.isTrustedIdentity( 63 | this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING 64 | ).then(function(trusted) { 65 | if (!trusted) { 66 | var e = new Error('Unknown identity key'); 67 | e.identityKey = message.identityKey.toArrayBuffer(); 68 | throw e; 69 | } 70 | return Promise.all([ 71 | this.storage.loadPreKey(message.preKeyId), 72 | this.storage.loadSignedPreKey(message.signedPreKeyId), 73 | ]).then(function(results) { 74 | preKeyPair = results[0]; 75 | signedPreKeyPair = results[1]; 76 | }); 77 | }.bind(this)).then(function() { 78 | session = record.getSessionByBaseKey(message.baseKey); 79 | if (session) { 80 | console.log("Duplicate PreKeyMessage for session"); 81 | return; 82 | } 83 | 84 | session = record.getOpenSession(); 85 | 86 | if (signedPreKeyPair === undefined) { 87 | // Session may or may not be the right one, but if its not, we 88 | // can't do anything about it ...fall through and let 89 | // decryptWhisperMessage handle that case 90 | if (session !== undefined && session.currentRatchet !== undefined) { 91 | return; 92 | } else { 93 | throw new Error("Missing Signed PreKey for PreKeyWhisperMessage"); 94 | } 95 | } 96 | 97 | if (session !== undefined) { 98 | record.archiveCurrentState(); 99 | } 100 | if (message.preKeyId && !preKeyPair) { 101 | console.log('Invalid prekey id', message.preKeyId); 102 | } 103 | return this.initSession(false, preKeyPair, signedPreKeyPair, 104 | message.identityKey.toArrayBuffer(), 105 | message.baseKey.toArrayBuffer(), undefined, message.registrationId 106 | ).then(function(new_session) { 107 | // Note that the session is not actually saved until the very 108 | // end of decryptWhisperMessage ... to ensure that the sender 109 | // actually holds the private keys for all reported pubkeys 110 | record.updateSessionState(new_session); 111 | return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() { 112 | return message.preKeyId; 113 | }); 114 | }.bind(this)); 115 | }.bind(this)); 116 | }, 117 | initSession: function(isInitiator, ourEphemeralKey, ourSignedKey, 118 | theirIdentityPubKey, theirEphemeralPubKey, 119 | theirSignedPubKey, registrationId) { 120 | return this.storage.getIdentityKeyPair().then(function(ourIdentityKey) { 121 | if (isInitiator) { 122 | if (ourSignedKey !== undefined) { 123 | throw new Error("Invalid call to initSession"); 124 | } 125 | ourSignedKey = ourEphemeralKey; 126 | } else { 127 | if (theirSignedPubKey !== undefined) { 128 | throw new Error("Invalid call to initSession"); 129 | } 130 | theirSignedPubKey = theirEphemeralPubKey; 131 | } 132 | 133 | var sharedSecret; 134 | if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) { 135 | sharedSecret = new Uint8Array(32 * 4); 136 | } else { 137 | sharedSecret = new Uint8Array(32 * 5); 138 | } 139 | 140 | for (var i = 0; i < 32; i++) { 141 | sharedSecret[i] = 0xff; 142 | } 143 | 144 | return Promise.all([ 145 | Internal.crypto.ECDHE(theirSignedPubKey, ourIdentityKey.privKey), 146 | Internal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey), 147 | Internal.crypto.ECDHE(theirSignedPubKey, ourSignedKey.privKey) 148 | ]).then(function(ecRes) { 149 | if (isInitiator) { 150 | sharedSecret.set(new Uint8Array(ecRes[0]), 32); 151 | sharedSecret.set(new Uint8Array(ecRes[1]), 32 * 2); 152 | } else { 153 | sharedSecret.set(new Uint8Array(ecRes[0]), 32 * 2); 154 | sharedSecret.set(new Uint8Array(ecRes[1]), 32); 155 | } 156 | sharedSecret.set(new Uint8Array(ecRes[2]), 32 * 3); 157 | 158 | if (ourEphemeralKey !== undefined && theirEphemeralPubKey !== undefined) { 159 | return Internal.crypto.ECDHE( 160 | theirEphemeralPubKey, ourEphemeralKey.privKey 161 | ).then(function(ecRes4) { 162 | sharedSecret.set(new Uint8Array(ecRes4), 32 * 4); 163 | }); 164 | } 165 | }).then(function() { 166 | return Internal.HKDF(sharedSecret.buffer, new ArrayBuffer(32), "WhisperText"); 167 | }).then(function(masterKey) { 168 | var session = { 169 | registrationId: registrationId, 170 | currentRatchet: { 171 | rootKey : masterKey[0], 172 | lastRemoteEphemeralKey : theirSignedPubKey, 173 | previousCounter : 0 174 | }, 175 | indexInfo: { 176 | remoteIdentityKey : theirIdentityPubKey, 177 | closed : -1 178 | }, 179 | oldRatchetList: [] 180 | }; 181 | 182 | // If we're initiating we go ahead and set our first sending ephemeral key now, 183 | // otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key 184 | if (isInitiator) { 185 | session.indexInfo.baseKey = ourEphemeralKey.pubKey; 186 | session.indexInfo.baseKeyType = Internal.BaseKeyType.OURS; 187 | return Internal.crypto.createKeyPair().then(function(ourSendingEphemeralKey) { 188 | session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey; 189 | return this.calculateSendingRatchet(session, theirSignedPubKey).then(function() { 190 | return session; 191 | }); 192 | }.bind(this)); 193 | } else { 194 | session.indexInfo.baseKey = theirEphemeralPubKey; 195 | session.indexInfo.baseKeyType = Internal.BaseKeyType.THEIRS; 196 | session.currentRatchet.ephemeralKeyPair = ourSignedKey; 197 | return session; 198 | } 199 | }.bind(this)); 200 | }.bind(this)); 201 | }, 202 | calculateSendingRatchet: function(session, remoteKey) { 203 | var ratchet = session.currentRatchet; 204 | 205 | return Internal.crypto.ECDHE( 206 | remoteKey, util.toArrayBuffer(ratchet.ephemeralKeyPair.privKey) 207 | ).then(function(sharedSecret) { 208 | return Internal.HKDF( 209 | sharedSecret, util.toArrayBuffer(ratchet.rootKey), "WhisperRatchet" 210 | ); 211 | }).then(function(masterKey) { 212 | session[util.toString(ratchet.ephemeralKeyPair.pubKey)] = { 213 | messageKeys : {}, 214 | chainKey : { counter : -1, key : masterKey[1] }, 215 | chainType : Internal.ChainType.SENDING 216 | }; 217 | ratchet.rootKey = masterKey[0]; 218 | }); 219 | } 220 | 221 | }; 222 | 223 | libsignal.SessionBuilder = function (storage, remoteAddress) { 224 | var builder = new SessionBuilder(storage, remoteAddress); 225 | this.processPreKey = builder.processPreKey.bind(builder); 226 | this.processV3 = builder.processV3.bind(builder); 227 | }; 228 | -------------------------------------------------------------------------------- /src/SessionLock.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jobQueue manages multiple queues indexed by device to serialize 3 | * session io ops on the database. 4 | */ 5 | ;(function() { 6 | 'use strict'; 7 | 8 | Internal.SessionLock = {}; 9 | 10 | var jobQueue = {}; 11 | 12 | Internal.SessionLock.queueJobForNumber = function queueJobForNumber(number, runJob) { 13 | var runPrevious = jobQueue[number] || Promise.resolve(); 14 | var runCurrent = jobQueue[number] = runPrevious.then(runJob, runJob); 15 | runCurrent.then(function() { 16 | if (jobQueue[number] === runCurrent) { 17 | delete jobQueue[number]; 18 | } 19 | }); 20 | return runCurrent; 21 | }; 22 | 23 | })(); 24 | -------------------------------------------------------------------------------- /src/SignalProtocolAddress.js: -------------------------------------------------------------------------------- 1 | function SignalProtocolAddress(name, deviceId) { 2 | this.name = name; 3 | this.deviceId = deviceId; 4 | } 5 | 6 | SignalProtocolAddress.prototype = { 7 | getName: function() { 8 | return this.name; 9 | }, 10 | getDeviceId: function() { 11 | return this.deviceId; 12 | }, 13 | toString: function() { 14 | return this.name + '.' + this.deviceId; 15 | }, 16 | equals: function(other) { 17 | if (!(other instanceof SignalProtocolAddress)) { return false; } 18 | return other.name === this.name && other.deviceId === this.deviceId; 19 | } 20 | }; 21 | 22 | libsignal.SignalProtocolAddress = function(name, deviceId) { 23 | var address = new SignalProtocolAddress(name, deviceId); 24 | 25 | ['getName', 'getDeviceId', 'toString', 'equals'].forEach(function(method) { 26 | this[method] = address[method].bind(address); 27 | }.bind(this)); 28 | }; 29 | 30 | libsignal.SignalProtocolAddress.fromString = function(encodedAddress) { 31 | if (typeof encodedAddress !== 'string' || !encodedAddress.match(/.*\.\d+/)) { 32 | throw new Error('Invalid SignalProtocolAddress string'); 33 | } 34 | var parts = encodedAddress.split('.'); 35 | return new libsignal.SignalProtocolAddress(parts[0], parseInt(parts[1])); 36 | }; 37 | -------------------------------------------------------------------------------- /src/crypto.js: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=4:sw=4 3 | */ 4 | 5 | var Internal = Internal || {}; 6 | 7 | (function() { 8 | 'use strict'; 9 | 10 | var crypto = window.crypto; 11 | 12 | if (!crypto || !crypto.subtle || typeof crypto.getRandomValues !== 'function') { 13 | throw new Error('WebCrypto not found'); 14 | } 15 | 16 | Internal.crypto = { 17 | getRandomBytes: function(size) { 18 | var array = new Uint8Array(size); 19 | crypto.getRandomValues(array); 20 | return array.buffer; 21 | }, 22 | encrypt: function(key, data, iv) { 23 | return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['encrypt']).then(function(key) { 24 | return crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data); 25 | }); 26 | }, 27 | decrypt: function(key, data, iv) { 28 | return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['decrypt']).then(function(key) { 29 | return crypto.subtle.decrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data); 30 | }); 31 | }, 32 | sign: function(key, data) { 33 | return crypto.subtle.importKey('raw', key, {name: 'HMAC', hash: {name: 'SHA-256'}}, false, ['sign']).then(function(key) { 34 | return crypto.subtle.sign( {name: 'HMAC', hash: 'SHA-256'}, key, data); 35 | }); 36 | }, 37 | 38 | hash: function(data) { 39 | return crypto.subtle.digest({name: 'SHA-512'}, data); 40 | }, 41 | 42 | HKDF: function(input, salt, info) { 43 | // Specific implementation of RFC 5869 that only returns the first 3 32-byte chunks 44 | // TODO: We dont always need the third chunk, we might skip it 45 | return Internal.crypto.sign(salt, input).then(function(PRK) { 46 | var infoBuffer = new ArrayBuffer(info.byteLength + 1 + 32); 47 | var infoArray = new Uint8Array(infoBuffer); 48 | infoArray.set(new Uint8Array(info), 32); 49 | infoArray[infoArray.length - 1] = 1; 50 | return Internal.crypto.sign(PRK, infoBuffer.slice(32)).then(function(T1) { 51 | infoArray.set(new Uint8Array(T1)); 52 | infoArray[infoArray.length - 1] = 2; 53 | return Internal.crypto.sign(PRK, infoBuffer).then(function(T2) { 54 | infoArray.set(new Uint8Array(T2)); 55 | infoArray[infoArray.length - 1] = 3; 56 | return Internal.crypto.sign(PRK, infoBuffer).then(function(T3) { 57 | return [ T1, T2, T3 ]; 58 | }); 59 | }); 60 | }); 61 | }); 62 | }, 63 | 64 | // Curve 25519 crypto 65 | createKeyPair: function(privKey) { 66 | if (privKey === undefined) { 67 | privKey = Internal.crypto.getRandomBytes(32); 68 | } 69 | return Internal.Curve.async.createKeyPair(privKey); 70 | }, 71 | ECDHE: function(pubKey, privKey) { 72 | return Internal.Curve.async.ECDHE(pubKey, privKey); 73 | }, 74 | Ed25519Sign: function(privKey, message) { 75 | return Internal.Curve.async.Ed25519Sign(privKey, message); 76 | }, 77 | Ed25519Verify: function(pubKey, msg, sig) { 78 | return Internal.Curve.async.Ed25519Verify(pubKey, msg, sig); 79 | } 80 | }; 81 | 82 | 83 | // HKDF for TextSecure has a bit of additional handling - salts always end up being 32 bytes 84 | Internal.HKDF = function(input, salt, info) { 85 | if (salt.byteLength != 32) { 86 | throw new Error("Got salt of incorrect length"); 87 | } 88 | 89 | return Internal.crypto.HKDF(input, salt, util.toArrayBuffer(info)); 90 | }; 91 | 92 | Internal.verifyMAC = function(data, key, mac, length) { 93 | return Internal.crypto.sign(key, data).then(function(calculated_mac) { 94 | if (mac.byteLength != length || calculated_mac.byteLength < length) { 95 | throw new Error("Bad MAC length"); 96 | } 97 | var a = new Uint8Array(calculated_mac); 98 | var b = new Uint8Array(mac); 99 | var result = 0; 100 | for (var i=0; i < mac.byteLength; ++i) { 101 | result = result | (a[i] ^ b[i]); 102 | } 103 | if (result !== 0) { 104 | throw new Error("Bad MAC"); 105 | } 106 | }); 107 | }; 108 | 109 | libsignal.HKDF = { 110 | deriveSecrets: function(input, salt, info) { 111 | return Internal.HKDF(input, salt, info); 112 | } 113 | }; 114 | 115 | libsignal.crypto = { 116 | encrypt: function(key, data, iv) { 117 | return Internal.crypto.encrypt(key, data, iv); 118 | }, 119 | decrypt: function(key, data, iv) { 120 | return Internal.crypto.decrypt(key, data, iv); 121 | }, 122 | calculateMAC: function(key, data) { 123 | return Internal.crypto.sign(key, data); 124 | }, 125 | verifyMAC: function(data, key, mac, length) { 126 | return Internal.verifyMAC(data, key, mac, length); 127 | }, 128 | getRandomBytes: function(size) { 129 | return Internal.crypto.getRandomBytes(size); 130 | } 131 | }; 132 | 133 | })(); 134 | -------------------------------------------------------------------------------- /src/curve25519_worker.js: -------------------------------------------------------------------------------- 1 | var Internal = Internal || {}; 2 | // I am the worker 3 | this.onmessage = function(e) { 4 | Internal.curve25519_async[e.data.methodName].apply(null, e.data.args).then(function(result) { 5 | postMessage({ id: e.data.id, result: result }); 6 | }).catch(function(error) { 7 | postMessage({ id: e.data.id, error: error.message }); 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /src/curve25519_worker_manager.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 3 | 'use strict'; 4 | 5 | // I am the...workee? 6 | var origCurve25519 = Internal.curve25519_async; 7 | 8 | Internal.startWorker = function(url) { 9 | Internal.stopWorker(); // there can be only one 10 | Internal.curve25519_async = new Curve25519Worker(url); 11 | }; 12 | 13 | Internal.stopWorker = function() { 14 | if (Internal.curve25519_async instanceof Curve25519Worker) { 15 | var worker = Internal.curve25519_async.worker; 16 | Internal.curve25519_async = origCurve25519; 17 | worker.terminate(); 18 | } 19 | }; 20 | 21 | libsignal.worker = { 22 | startWorker: Internal.startWorker, 23 | stopWorker: Internal.stopWorker, 24 | }; 25 | 26 | function Curve25519Worker(url) { 27 | this.jobs = {}; 28 | this.jobId = 0; 29 | this.worker = new Worker(url); 30 | this.worker.onmessage = function(e) { 31 | var job = this.jobs[e.data.id]; 32 | if (e.data.error && typeof job.onerror === 'function') { 33 | job.onerror(new Error(e.data.error)); 34 | } else if (typeof job.onsuccess === 'function') { 35 | job.onsuccess(e.data.result); 36 | } 37 | delete this.jobs[e.data.id]; 38 | }.bind(this); 39 | } 40 | 41 | Curve25519Worker.prototype = { 42 | constructor: Curve25519Worker, 43 | postMessage: function(methodName, args, onsuccess, onerror) { 44 | return new Promise(function(resolve, reject) { 45 | this.jobs[this.jobId] = { onsuccess: resolve, onerror: reject }; 46 | this.worker.postMessage({ id: this.jobId, methodName: methodName, args: args }); 47 | this.jobId++; 48 | }.bind(this)); 49 | }, 50 | keyPair: function(privKey) { 51 | return this.postMessage('keyPair', [privKey]); 52 | }, 53 | sharedSecret: function(pubKey, privKey) { 54 | return this.postMessage('sharedSecret', [pubKey, privKey]); 55 | }, 56 | sign: function(privKey, message) { 57 | return this.postMessage('sign', [privKey, message]); 58 | }, 59 | verify: function(pubKey, message, sig) { 60 | return this.postMessage('verify', [pubKey, message, sig]); 61 | } 62 | }; 63 | 64 | })(); 65 | -------------------------------------------------------------------------------- /src/curve25519_wrapper.js: -------------------------------------------------------------------------------- 1 | /* vim: ts=4:sw=4:expandtab */ 2 | var Internal = Internal || {}; 3 | 4 | (function() { 5 | 'use strict'; 6 | 7 | // Insert some bytes into the emscripten memory and return a pointer 8 | function _allocate(bytes) { 9 | var address = Module._malloc(bytes.length); 10 | Module.HEAPU8.set(bytes, address); 11 | 12 | return address; 13 | } 14 | 15 | function _readBytes(address, length, array) { 16 | array.set(Module.HEAPU8.subarray(address, address + length)); 17 | } 18 | 19 | var basepoint = new Uint8Array(32); 20 | basepoint[0] = 9; 21 | 22 | Internal.curve25519 = { 23 | keyPair: function(privKey) { 24 | var priv = new Uint8Array(privKey); 25 | priv[0] &= 248; 26 | priv[31] &= 127; 27 | priv[31] |= 64; 28 | 29 | // Where to store the result 30 | var publicKey_ptr = Module._malloc(32); 31 | 32 | // Get a pointer to the private key 33 | var privateKey_ptr = _allocate(priv); 34 | 35 | // The basepoint for generating public keys 36 | var basepoint_ptr = _allocate(basepoint); 37 | 38 | // The return value is just 0, the operation is done in place 39 | var err = Module._curve25519_donna(publicKey_ptr, 40 | privateKey_ptr, 41 | basepoint_ptr); 42 | 43 | var res = new Uint8Array(32); 44 | _readBytes(publicKey_ptr, 32, res); 45 | 46 | Module._free(publicKey_ptr); 47 | Module._free(privateKey_ptr); 48 | Module._free(basepoint_ptr); 49 | 50 | return { pubKey: res.buffer, privKey: priv.buffer }; 51 | }, 52 | sharedSecret: function(pubKey, privKey) { 53 | // Where to store the result 54 | var sharedKey_ptr = Module._malloc(32); 55 | 56 | // Get a pointer to our private key 57 | var privateKey_ptr = _allocate(new Uint8Array(privKey)); 58 | 59 | // Get a pointer to their public key, the basepoint when you're 60 | // generating a shared secret 61 | var basepoint_ptr = _allocate(new Uint8Array(pubKey)); 62 | 63 | // Return value is 0 here too of course 64 | var err = Module._curve25519_donna(sharedKey_ptr, 65 | privateKey_ptr, 66 | basepoint_ptr); 67 | 68 | var res = new Uint8Array(32); 69 | _readBytes(sharedKey_ptr, 32, res); 70 | 71 | Module._free(sharedKey_ptr); 72 | Module._free(privateKey_ptr); 73 | Module._free(basepoint_ptr); 74 | 75 | return res.buffer; 76 | }, 77 | sign: function(privKey, message) { 78 | // Where to store the result 79 | var signature_ptr = Module._malloc(64); 80 | 81 | // Get a pointer to our private key 82 | var privateKey_ptr = _allocate(new Uint8Array(privKey)); 83 | 84 | // Get a pointer to the message 85 | var message_ptr = _allocate(new Uint8Array(message)); 86 | 87 | var err = Module._curve25519_sign(signature_ptr, 88 | privateKey_ptr, 89 | message_ptr, 90 | message.byteLength); 91 | 92 | var res = new Uint8Array(64); 93 | _readBytes(signature_ptr, 64, res); 94 | 95 | Module._free(signature_ptr); 96 | Module._free(privateKey_ptr); 97 | Module._free(message_ptr); 98 | 99 | return res.buffer; 100 | }, 101 | verify: function(pubKey, message, sig) { 102 | // Get a pointer to their public key 103 | var publicKey_ptr = _allocate(new Uint8Array(pubKey)); 104 | 105 | // Get a pointer to the signature 106 | var signature_ptr = _allocate(new Uint8Array(sig)); 107 | 108 | // Get a pointer to the message 109 | var message_ptr = _allocate(new Uint8Array(message)); 110 | 111 | var res = Module._curve25519_verify(signature_ptr, 112 | publicKey_ptr, 113 | message_ptr, 114 | message.byteLength); 115 | 116 | Module._free(publicKey_ptr); 117 | Module._free(signature_ptr); 118 | Module._free(message_ptr); 119 | 120 | return res !== 0; 121 | } 122 | }; 123 | 124 | Internal.curve25519_async = { 125 | keyPair: function(privKey) { 126 | return new Promise(function(resolve) { 127 | resolve(Internal.curve25519.keyPair(privKey)); 128 | }); 129 | }, 130 | sharedSecret: function(pubKey, privKey) { 131 | return new Promise(function(resolve) { 132 | resolve(Internal.curve25519.sharedSecret(pubKey, privKey)); 133 | }); 134 | }, 135 | sign: function(privKey, message) { 136 | return new Promise(function(resolve) { 137 | resolve(Internal.curve25519.sign(privKey, message)); 138 | }); 139 | }, 140 | verify: function(pubKey, message, sig) { 141 | return new Promise(function(resolve, reject) { 142 | if (Internal.curve25519.verify(pubKey, message, sig)) { 143 | reject(new Error("Invalid signature")); 144 | } else { 145 | resolve(); 146 | } 147 | }); 148 | }, 149 | }; 150 | 151 | })(); 152 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=4:sw=4 3 | */ 4 | 5 | var util = (function() { 6 | 'use strict'; 7 | 8 | var StaticArrayBufferProto = new ArrayBuffer().__proto__; 9 | 10 | return { 11 | toString: function(thing) { 12 | if (typeof thing == 'string') { 13 | return thing; 14 | } 15 | return new dcodeIO.ByteBuffer.wrap(thing).toString('binary'); 16 | }, 17 | toArrayBuffer: function(thing) { 18 | if (thing === undefined) { 19 | return undefined; 20 | } 21 | if (thing === Object(thing)) { 22 | if (thing.__proto__ == StaticArrayBufferProto) { 23 | return thing; 24 | } 25 | } 26 | 27 | var str; 28 | if (typeof thing == "string") { 29 | str = thing; 30 | } else { 31 | throw new Error("Tried to convert a non-string of type " + typeof thing + " to an array buffer"); 32 | } 33 | return new dcodeIO.ByteBuffer.wrap(thing, 'binary').toArrayBuffer(); 34 | }, 35 | isEqual: function(a, b) { 36 | // TODO: Special-case arraybuffers, etc 37 | if (a === undefined || b === undefined) { 38 | return false; 39 | } 40 | a = util.toString(a); 41 | b = util.toString(b); 42 | var maxLength = Math.max(a.length, b.length); 43 | if (maxLength < 5) { 44 | throw new Error("a/b compare too short"); 45 | } 46 | return a.substring(0, Math.min(maxLength, a.length)) == b.substring(0, Math.min(maxLength, b.length)); 47 | } 48 | }; 49 | })(); 50 | -------------------------------------------------------------------------------- /src/protobufs.js: -------------------------------------------------------------------------------- 1 | /* vim: ts=4:sw=4 */ 2 | var Internal = Internal || {}; 3 | 4 | Internal.protobuf = function() { 5 | 'use strict'; 6 | 7 | function loadProtoBufs(filename) { 8 | return dcodeIO.ProtoBuf.loadProto(Internal.protoText['protos/' + filename]).build('textsecure'); 9 | } 10 | 11 | var protocolMessages = loadProtoBufs('WhisperTextProtocol.proto'); 12 | 13 | return { 14 | WhisperMessage : protocolMessages.WhisperMessage, 15 | PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage 16 | }; 17 | }(); 18 | -------------------------------------------------------------------------------- /test/IdentityKeyStore_test.js: -------------------------------------------------------------------------------- 1 | function testIdentityKeyStore(store, registrationId, identityKey) { 2 | describe('IdentityKeyStore', function() { 3 | var number = '+5558675309'; 4 | var address = new libsignal.SignalProtocolAddress('+5558675309', 1); 5 | var testKey; 6 | before(function(done) { 7 | Internal.crypto.createKeyPair().then(function(keyPair) { 8 | testKey = keyPair; 9 | }).then(done,done); 10 | }); 11 | 12 | describe('getLocalRegistrationId', function() { 13 | it('retrieves my registration id', function(done) { 14 | store.getLocalRegistrationId().then(function(reg) { 15 | assert.strictEqual(reg, registrationId); 16 | }).then(done, done); 17 | }); 18 | }); 19 | describe('getIdentityKeyPair', function() { 20 | it('retrieves my identity key', function(done) { 21 | store.getIdentityKeyPair().then(function(key) { 22 | assertEqualArrayBuffers(key.pubKey, identityKey.pubKey); 23 | assertEqualArrayBuffers(key.privKey, identityKey.privKey); 24 | }).then(done,done); 25 | }); 26 | }); 27 | describe('saveIdentity', function() { 28 | it('stores identity keys', function(done) { 29 | store.saveIdentity(address.toString(), testKey.pubKey).then(function() { 30 | return store.loadIdentityKey(number).then(function(key) { 31 | assertEqualArrayBuffers(key, testKey.pubKey); 32 | }); 33 | }).then(done,done); 34 | }); 35 | }); 36 | describe('isTrustedIdentity', function() { 37 | it('returns true if a key is trusted', function(done) { 38 | store.saveIdentity(address.toString(), testKey.pubKey).then(function() { 39 | store.isTrustedIdentity(number, testKey.pubKey).then(function(trusted) { 40 | if (trusted) { 41 | done(); 42 | } else { 43 | done(new Error('Wrong value for trusted key')); 44 | } 45 | }).catch(done); 46 | }); 47 | }); 48 | it('returns false if a key is untrusted', function(done) { 49 | var newIdentity = libsignal.crypto.getRandomBytes(33); 50 | store.saveIdentity(address.toString(), testKey.pubKey).then(function() { 51 | store.isTrustedIdentity(number, newIdentity).then(function(trusted) { 52 | if (trusted) { 53 | done(new Error('Wrong value for untrusted key')); 54 | } else { 55 | done(); 56 | } 57 | }).catch(done); 58 | }); 59 | }); 60 | }); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /test/InMemorySignalProtocolStore.js: -------------------------------------------------------------------------------- 1 | function SignalProtocolStore() { 2 | this.store = {}; 3 | } 4 | 5 | SignalProtocolStore.prototype = { 6 | Direction: { 7 | SENDING: 1, 8 | RECEIVING: 2, 9 | }, 10 | 11 | getIdentityKeyPair: function() { 12 | return Promise.resolve(this.get('identityKey')); 13 | }, 14 | getLocalRegistrationId: function() { 15 | return Promise.resolve(this.get('registrationId')); 16 | }, 17 | put: function(key, value) { 18 | if (key === undefined || value === undefined || key === null || value === null) 19 | throw new Error("Tried to store undefined/null"); 20 | this.store[key] = value; 21 | }, 22 | get: function(key, defaultValue) { 23 | if (key === null || key === undefined) 24 | throw new Error("Tried to get value for undefined/null key"); 25 | if (key in this.store) { 26 | return this.store[key]; 27 | } else { 28 | return defaultValue; 29 | } 30 | }, 31 | remove: function(key) { 32 | if (key === null || key === undefined) 33 | throw new Error("Tried to remove value for undefined/null key"); 34 | delete this.store[key]; 35 | }, 36 | 37 | isTrustedIdentity: function(identifier, identityKey, direction) { 38 | if (identifier === null || identifier === undefined) { 39 | throw new Error("tried to check identity key for undefined/null key"); 40 | } 41 | if (!(identityKey instanceof ArrayBuffer)) { 42 | throw new Error("Expected identityKey to be an ArrayBuffer"); 43 | } 44 | var trusted = this.get('identityKey' + identifier); 45 | if (trusted === undefined) { 46 | return Promise.resolve(true); 47 | } 48 | return Promise.resolve(util.toString(identityKey) === util.toString(trusted)); 49 | }, 50 | loadIdentityKey: function(identifier) { 51 | if (identifier === null || identifier === undefined) 52 | throw new Error("Tried to get identity key for undefined/null key"); 53 | return Promise.resolve(this.get('identityKey' + identifier)); 54 | }, 55 | saveIdentity: function(identifier, identityKey) { 56 | if (identifier === null || identifier === undefined) 57 | throw new Error("Tried to put identity key for undefined/null key"); 58 | 59 | var address = new libsignal.SignalProtocolAddress.fromString(identifier); 60 | 61 | var existing = this.get('identityKey' + address.getName()); 62 | this.put('identityKey' + address.getName(), identityKey) 63 | 64 | if (existing && util.toString(identityKey) !== util.toString(existing)) { 65 | return Promise.resolve(true); 66 | } else { 67 | return Promise.resolve(false); 68 | } 69 | 70 | }, 71 | 72 | /* Returns a prekeypair object or undefined */ 73 | loadPreKey: function(keyId) { 74 | var res = this.get('25519KeypreKey' + keyId); 75 | if (res !== undefined) { 76 | res = { pubKey: res.pubKey, privKey: res.privKey }; 77 | } 78 | return Promise.resolve(res); 79 | }, 80 | storePreKey: function(keyId, keyPair) { 81 | return Promise.resolve(this.put('25519KeypreKey' + keyId, keyPair)); 82 | }, 83 | removePreKey: function(keyId) { 84 | return Promise.resolve(this.remove('25519KeypreKey' + keyId)); 85 | }, 86 | 87 | /* Returns a signed keypair object or undefined */ 88 | loadSignedPreKey: function(keyId) { 89 | var res = this.get('25519KeysignedKey' + keyId); 90 | if (res !== undefined) { 91 | res = { pubKey: res.pubKey, privKey: res.privKey }; 92 | } 93 | return Promise.resolve(res); 94 | }, 95 | storeSignedPreKey: function(keyId, keyPair) { 96 | return Promise.resolve(this.put('25519KeysignedKey' + keyId, keyPair)); 97 | }, 98 | removeSignedPreKey: function(keyId) { 99 | return Promise.resolve(this.remove('25519KeysignedKey' + keyId)); 100 | }, 101 | 102 | loadSession: function(identifier) { 103 | return Promise.resolve(this.get('session' + identifier)); 104 | }, 105 | storeSession: function(identifier, record) { 106 | return Promise.resolve(this.put('session' + identifier, record)); 107 | }, 108 | removeSession: function(identifier) { 109 | return Promise.resolve(this.remove('session' + identifier)); 110 | }, 111 | removeAllSessions: function(identifier) { 112 | for (var id in this.store) { 113 | if (id.startsWith('session' + identifier)) { 114 | delete this.store[id]; 115 | } 116 | } 117 | return Promise.resolve(); 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /test/KeyHelperTest.js: -------------------------------------------------------------------------------- 1 | describe('KeyHelper', function() { 2 | function validateKeyPair(keyPair) { 3 | assert.isDefined(keyPair.pubKey); 4 | assert.isDefined(keyPair.privKey); 5 | assert.strictEqual(keyPair.privKey.byteLength, 32); 6 | assert.strictEqual(keyPair.pubKey.byteLength, 33); 7 | assert.strictEqual(new Uint8Array(keyPair.pubKey)[0], 5); 8 | } 9 | 10 | describe('generateIdentityKeyPair', function() { 11 | it ('works', function() { 12 | libsignal.KeyHelper.generateIdentityKeyPair().then(function(keyPair) { 13 | validateKeyPair(keyPair); 14 | }); 15 | }); 16 | }); 17 | 18 | describe('generateRegistrationId', function() { 19 | it('generates a 14-bit integer', function() { 20 | var registrationId = libsignal.KeyHelper.generateRegistrationId(); 21 | assert.isNumber(registrationId); 22 | assert(registrationId >= 0); 23 | assert(registrationId < 16384); 24 | assert.strictEqual(registrationId, Math.round(registrationId)); // integer 25 | }); 26 | }); 27 | 28 | describe("generatePreKey", function() { 29 | it('generates a preKey', function(done) { 30 | libsignal.KeyHelper.generatePreKey(1337).then(function(result) { 31 | validateKeyPair(result.keyPair); 32 | assert.strictEqual(result.keyId, 1337); 33 | }).then(done, done); 34 | }); 35 | it('throws on bad keyId', function() { 36 | assert.throws(function(done) { 37 | libsignal.KeyHelper.generatePreKey('bad'); 38 | }, TypeError); 39 | }); 40 | }); 41 | 42 | describe("generateSignedPreKey", function() { 43 | it('generates a preKey', function(done) { 44 | libsignal.KeyHelper.generateIdentityKeyPair().then(function(identityKey) { 45 | libsignal.KeyHelper.generateSignedPreKey(identityKey, 1337).then(function(result) { 46 | validateKeyPair(result.keyPair); 47 | assert.strictEqual(result.keyId, 1337); 48 | //todo: validate result.signature 49 | }); 50 | }).then(done, done); 51 | }); 52 | it('throws on bad keyId', function() { 53 | assert.throws(function(done) { 54 | libsignal.KeyHelper.generateSignedPreKey('bad'); 55 | }, TypeError); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/NumericFingerprintTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=4:sw=4 3 | */ 4 | 5 | 'use strict'; 6 | describe('NumericFingerprint', function() { 7 | this.timeout(5000); 8 | var ALICE_IDENTITY = [ 9 | 0x05, 0x06, 0x86, 0x3b, 0xc6, 0x6d, 0x02, 0xb4, 0x0d, 0x27, 0xb8, 0xd4, 10 | 0x9c, 0xa7, 0xc0, 0x9e, 0x92, 0x39, 0x23, 0x6f, 0x9d, 0x7d, 0x25, 0xd6, 11 | 0xfc, 0xca, 0x5c, 0xe1, 0x3c, 0x70, 0x64, 0xd8, 0x68 12 | ]; 13 | var BOB_IDENTITY = [ 14 | 0x05, 0xf7, 0x81, 0xb6, 0xfb, 0x32, 0xfe, 0xd9, 0xba, 0x1c, 0xf2, 0xde, 15 | 0x97, 0x8d, 0x4d, 0x5d, 0xa2, 0x8d, 0xc3, 0x40, 0x46, 0xae, 0x81, 0x44, 16 | 0x02, 0xb5, 0xc0, 0xdb, 0xd9, 0x6f, 0xda, 0x90, 0x7b 17 | ]; 18 | var FINGERPRINT = "300354477692869396892869876765458257569162576843440918079131"; 19 | 20 | var alice = { 21 | identifier: '+14152222222', 22 | key: new Uint8Array(ALICE_IDENTITY).buffer 23 | }; 24 | var bob = { 25 | identifier: '+14153333333', 26 | key: new Uint8Array(BOB_IDENTITY).buffer 27 | }; 28 | 29 | it('returns the correct fingerprint', function(done) { 30 | var generator = new libsignal.FingerprintGenerator(5200); 31 | generator.createFor( 32 | alice.identifier, alice.key, bob.identifier, bob.key 33 | ).then(function(fingerprint) { 34 | assert.strictEqual(fingerprint, FINGERPRINT); 35 | }).then(done,done); 36 | }); 37 | 38 | it ('alice and bob results match', function(done) { 39 | var generator = new libsignal.FingerprintGenerator(1024); 40 | Promise.all([ 41 | generator.createFor( 42 | alice.identifier, alice.key, bob.identifier, bob.key 43 | ), 44 | generator.createFor( 45 | bob.identifier, bob.key, alice.identifier, alice.key 46 | ) 47 | ]).then(function(fingerprints) { 48 | assert.strictEqual(fingerprints[0], fingerprints[1]); 49 | }).then(done,done); 50 | }); 51 | 52 | it ('alice and !bob results mismatch', function(done) { 53 | var generator = new libsignal.FingerprintGenerator(1024); 54 | Promise.all([ 55 | generator.createFor( 56 | alice.identifier, alice.key, '+15558675309', bob.key 57 | ), 58 | generator.createFor( 59 | bob.identifier, bob.key, alice.identifier, alice.key 60 | ) 61 | ]).then(function(fingerprints) { 62 | assert.notStrictEqual(fingerprints[0], fingerprints[1]); 63 | }).then(done,done); 64 | }); 65 | 66 | it ('alice and mitm results mismatch', function(done) { 67 | var mitm = libsignal.crypto.getRandomBytes(33); 68 | var generator = new libsignal.FingerprintGenerator(1024); 69 | Promise.all([ 70 | generator.createFor( 71 | alice.identifier, alice.key, bob.identifier, mitm 72 | ), 73 | generator.createFor( 74 | bob.identifier, bob.key, alice.identifier, alice.key 75 | ) 76 | ]).then(function(fingerprints) { 77 | assert.notStrictEqual(fingerprints[0], fingerprints[1]); 78 | }).then(done,done); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/PreKeyStore_test.js: -------------------------------------------------------------------------------- 1 | function testPreKeyStore(store) { 2 | var number = '+5558675309'; 3 | var testKey; 4 | describe('PreKeyStore', function() { 5 | before(function(done) { 6 | Internal.crypto.createKeyPair().then(function(keyPair) { 7 | testKey = keyPair; 8 | }).then(done,done); 9 | }); 10 | describe('storePreKey', function() { 11 | it('stores prekeys', function(done) { 12 | var address = new SignalProtocolAddress(number, 1); 13 | store.storePreKey(address.toString(), testKey).then(function() { 14 | return store.loadPreKey(address.toString()).then(function(key) { 15 | assertEqualArrayBuffers(key.pubKey, testKey.pubKey); 16 | assertEqualArrayBuffers(key.privKey, testKey.privKey); 17 | }); 18 | }).then(done,done); 19 | }); 20 | }); 21 | describe('loadPreKey', function() { 22 | it('returns prekeys that exist', function(done) { 23 | var address = new SignalProtocolAddress(number, 1); 24 | store.storePreKey(address.toString(), testKey).then(function() { 25 | return store.loadPreKey(address.toString()).then(function(key) { 26 | assertEqualArrayBuffers(key.pubKey, testKey.pubKey); 27 | assertEqualArrayBuffers(key.privKey, testKey.privKey); 28 | }); 29 | }).then(done,done); 30 | }); 31 | it('returns undefined for prekeys that do not exist', function(done) { 32 | var address = new SignalProtocolAddress(number, 2); 33 | return store.loadPreKey(2).then(function(key) { 34 | assert.isUndefined(key); 35 | }).then(done,done); 36 | }); 37 | }); 38 | describe('removePreKey', function() { 39 | it('deletes prekeys', function(done) { 40 | var address = new SignalProtocolAddress(number, 2); 41 | before(function(done) { 42 | store.storePreKey(address.toString(), testKey).then(done); 43 | }); 44 | store.removePreKey(address.toString()).then(function() { 45 | return store.loadPreKey(address.toString()).then(function(key) { 46 | assert.isUndefined(key); 47 | }); 48 | }).then(done,done); 49 | }); 50 | }); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /test/SessionBuilderTest.js: -------------------------------------------------------------------------------- 1 | describe('SessionBuilder', function() { 2 | this.timeout(5000); 3 | 4 | var ALICE_ADDRESS = new SignalProtocolAddress("+14151111111", 1); 5 | var BOB_ADDRESS = new SignalProtocolAddress("+14152222222", 1); 6 | 7 | describe("basic prekey v3", function() { 8 | var aliceStore = new SignalProtocolStore(); 9 | 10 | var bobStore = new SignalProtocolStore(); 11 | var bobPreKeyId = 1337; 12 | var bobSignedKeyId = 1; 13 | 14 | var Curve = libsignal.Curve; 15 | 16 | before(function(done) { 17 | Promise.all([ 18 | generateIdentity(aliceStore), 19 | generateIdentity(bobStore), 20 | ]).then(function() { 21 | return generatePreKeyBundle(bobStore, bobPreKeyId, bobSignedKeyId); 22 | }).then(function(preKeyBundle) { 23 | var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS); 24 | return builder.processPreKey(preKeyBundle).then(function() { 25 | done(); 26 | }); 27 | }).catch(done); 28 | }); 29 | 30 | var originalMessage = util.toArrayBuffer("L'homme est condamné à être libre"); 31 | var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS); 32 | var bobSessionCipher = new libsignal.SessionCipher(bobStore, ALICE_ADDRESS); 33 | 34 | it('creates a session', function(done) { 35 | return aliceStore.loadSession(BOB_ADDRESS.toString()).then(function(record) { 36 | assert.isDefined(record); 37 | var sessionRecord = Internal.SessionRecord.deserialize(record); 38 | assert.isTrue(sessionRecord.haveOpenSession()); 39 | assert.isDefined(sessionRecord.getOpenSession()); 40 | }).then(done, done); 41 | }); 42 | 43 | it('the session can encrypt', function(done) { 44 | aliceSessionCipher.encrypt(originalMessage).then(function(ciphertext) { 45 | 46 | assert.strictEqual(ciphertext.type, 3); // PREKEY_BUNDLE 47 | 48 | return bobSessionCipher.decryptPreKeyWhisperMessage(ciphertext.body, 'binary'); 49 | 50 | }).then(function(plaintext) { 51 | 52 | assertEqualArrayBuffers(plaintext, originalMessage); 53 | 54 | }).then(done, done); 55 | }); 56 | 57 | it('the session can decrypt', function(done) { 58 | bobSessionCipher.encrypt(originalMessage).then(function(ciphertext) { 59 | 60 | return aliceSessionCipher.decryptWhisperMessage(ciphertext.body, 'binary'); 61 | 62 | }).then(function(plaintext) { 63 | 64 | assertEqualArrayBuffers(plaintext, originalMessage); 65 | 66 | }).then(done, done); 67 | }); 68 | 69 | it('accepts a new preKey with the same identity', function(done) { 70 | generatePreKeyBundle(bobStore, bobPreKeyId + 1, bobSignedKeyId + 1).then(function(preKeyBundle) { 71 | var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS); 72 | return builder.processPreKey(preKeyBundle).then(function() { 73 | return aliceStore.loadSession(BOB_ADDRESS.toString()).then(function(record) { 74 | assert.isDefined(record); 75 | var sessionRecord = Internal.SessionRecord.deserialize(record); 76 | assert.isTrue(sessionRecord.haveOpenSession()); 77 | assert.isDefined(sessionRecord.getOpenSession()); 78 | done(); 79 | }); 80 | }); 81 | }).catch(done); 82 | }); 83 | 84 | it('rejects untrusted identity keys', function(done) { 85 | KeyHelper.generateIdentityKeyPair().then(function(newIdentity) { 86 | var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS); 87 | return builder.processPreKey({ 88 | identityKey: newIdentity.pubKey, 89 | registrationId : 12356 90 | }).then(function(e) { 91 | assert.fail('should not be trusted'); 92 | }).catch(function(e) { 93 | assert.strictEqual(e.message, 'Identity key changed'); 94 | done(); 95 | }).catch(done); 96 | }); 97 | }); 98 | }); 99 | 100 | describe("basic v3 NO PREKEY", function() { 101 | var aliceStore = new SignalProtocolStore(); 102 | 103 | var bobStore = new SignalProtocolStore(); 104 | var bobPreKeyId = 1337; 105 | var bobSignedKeyId = 1; 106 | 107 | var Curve = libsignal.Curve; 108 | 109 | before(function(done) { 110 | Promise.all([ 111 | generateIdentity(aliceStore), 112 | generateIdentity(bobStore), 113 | ]).then(function() { 114 | return generatePreKeyBundle(bobStore, bobPreKeyId, bobSignedKeyId); 115 | }).then(function(preKeyBundle) { 116 | delete preKeyBundle.preKey; 117 | var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS); 118 | return builder.processPreKey(preKeyBundle).then(function() { 119 | done(); 120 | }); 121 | }).catch(done); 122 | }); 123 | 124 | var originalMessage = util.toArrayBuffer("L'homme est condamné à être libre"); 125 | var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS); 126 | var bobSessionCipher = new libsignal.SessionCipher(bobStore, ALICE_ADDRESS); 127 | 128 | it('creates a session', function(done) { 129 | return aliceStore.loadSession(BOB_ADDRESS.toString()).then(function(record) { 130 | assert.isDefined(record); 131 | var sessionRecord = Internal.SessionRecord.deserialize(record); 132 | assert.isTrue(sessionRecord.haveOpenSession()); 133 | assert.isDefined(sessionRecord.getOpenSession()); 134 | }).then(done, done); 135 | }); 136 | 137 | it('the session can encrypt', function(done) { 138 | aliceSessionCipher.encrypt(originalMessage).then(function(ciphertext) { 139 | 140 | assert.strictEqual(ciphertext.type, 3); // PREKEY_BUNDLE 141 | 142 | return bobSessionCipher.decryptPreKeyWhisperMessage(ciphertext.body, 'binary'); 143 | 144 | }).then(function(plaintext) { 145 | 146 | assertEqualArrayBuffers(plaintext, originalMessage); 147 | 148 | }).then(done, done); 149 | }); 150 | 151 | it('the session can decrypt', function(done) { 152 | bobSessionCipher.encrypt(originalMessage).then(function(ciphertext) { 153 | 154 | return aliceSessionCipher.decryptWhisperMessage(ciphertext.body, 'binary'); 155 | 156 | }).then(function(plaintext) { 157 | 158 | assertEqualArrayBuffers(plaintext, originalMessage); 159 | 160 | }).then(done, done); 161 | }); 162 | 163 | it('accepts a new preKey with the same identity', function(done) { 164 | generatePreKeyBundle(bobStore, bobPreKeyId + 1, bobSignedKeyId + 1).then(function(preKeyBundle) { 165 | delete preKeyBundle.preKey; 166 | var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS); 167 | return builder.processPreKey(preKeyBundle).then(function() { 168 | return aliceStore.loadSession(BOB_ADDRESS.toString()).then(function(record) { 169 | assert.isDefined(record); 170 | var sessionRecord = Internal.SessionRecord.deserialize(record); 171 | assert.isTrue(sessionRecord.haveOpenSession()); 172 | assert.isDefined(sessionRecord.getOpenSession()); 173 | done(); 174 | }); 175 | }); 176 | }).catch(done); 177 | }); 178 | 179 | it('rejects untrusted identity keys', function(done) { 180 | KeyHelper.generateIdentityKeyPair().then(function(newIdentity) { 181 | var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS); 182 | return builder.processPreKey({ 183 | identityKey: newIdentity.pubKey, 184 | registrationId : 12356 185 | }).then(function(e) { 186 | assert.fail('should not be trusted'); 187 | }).catch(function(e) { 188 | assert.strictEqual(e.message, 'Identity key changed'); 189 | done(); 190 | }).catch(done); 191 | }); 192 | }); 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /test/SessionStore_test.js: -------------------------------------------------------------------------------- 1 | function testSessionStore(store) { 2 | describe('SessionStore', function() { 3 | var number = '+5558675309'; 4 | var testRecord = 'an opaque string'; 5 | describe('storeSession', function() { 6 | var address = new SignalProtocolAddress(number, 1); 7 | it('stores sessions encoded as strings', function(done) { 8 | store.storeSession(address.toString(), testRecord).then(function() { 9 | return store.loadSession(address.toString()).then(function(record) { 10 | assert.strictEqual(record, testRecord); 11 | }); 12 | }).then(done,done); 13 | }); 14 | it('stores sessions encoded as array buffers', function(done) { 15 | var testRecord = new Uint8Array([1,2,3]).buffer; 16 | store.storeSession(address.toString(), testRecord).then(function() { 17 | return store.loadSession(address.toString()).then(function(record) { 18 | assertEqualArrayBuffers(testRecord, record); 19 | }); 20 | }).then(done,done); 21 | }); 22 | }); 23 | describe('loadSession', function() { 24 | it('returns sessions that exist', function(done) { 25 | var address = new SignalProtocolAddress(number, 1); 26 | var testRecord = 'an opaque string'; 27 | store.storeSession(address.toString(), testRecord).then(function() { 28 | return store.loadSession(address.toString()).then(function(record) { 29 | assert.strictEqual(record, testRecord); 30 | }); 31 | }).then(done,done); 32 | }); 33 | it('returns undefined for sessions that do not exist', function(done) { 34 | var address = new SignalProtocolAddress(number, 2); 35 | return store.loadSession(address.toString()).then(function(record) { 36 | assert.isUndefined(record); 37 | }).then(done,done); 38 | }); 39 | }); 40 | describe('removeSession', function() { 41 | it('deletes sessions', function(done) { 42 | var address = new SignalProtocolAddress(number, 1); 43 | before(function(done) { 44 | store.storeSession(address.toString(), testRecord).then(done); 45 | }); 46 | store.removeSession(address.toString()).then(function() { 47 | return store.loadSession(address.toString()).then(function(record) { 48 | assert.isUndefined(record); 49 | }); 50 | }).then(done,done); 51 | }); 52 | }); 53 | describe('removeAllSessions', function() { 54 | it('removes all sessions for a number', function(done) { 55 | var devices = [1, 2, 3].map(function(deviceId) { 56 | var address = new SignalProtocolAddress(number, deviceId); 57 | return address.toString(); 58 | }); 59 | var promise = Promise.resolve(); 60 | devices.forEach(function(encodedNumber) { 61 | promise = promise.then(function() { 62 | return store.storeSession(encodedNumber, testRecord + encodedNumber); 63 | }); 64 | }); 65 | promise.then(function() { 66 | return store.removeAllSessions(number).then(function(record) { 67 | return Promise.all(devices.map(store.loadSession.bind(store))).then(function(records) { 68 | for (var i in records) { 69 | assert.isUndefined(records[i]); 70 | }; 71 | }); 72 | }); 73 | }).then(done,done); 74 | }); 75 | }); 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /test/SignalProtocolAddressTest.js: -------------------------------------------------------------------------------- 1 | describe('SignalProtocolAddress', function() { 2 | var name = 'name'; 3 | var deviceId = 42; 4 | var string = 'name.42'; 5 | describe('getName', function() { 6 | it('returns the name', function() { 7 | var address = new SignalProtocolAddress(name, 1); 8 | assert.strictEqual(name, address.getName()); 9 | }); 10 | }); 11 | describe('getDeviceId', function() { 12 | it('returns the deviceId', function() { 13 | var address = new SignalProtocolAddress(name, deviceId); 14 | assert.strictEqual(deviceId, address.getDeviceId()); 15 | }); 16 | }); 17 | describe('toString', function() { 18 | it('returns the address', function() { 19 | var address = new SignalProtocolAddress(name, deviceId); 20 | assert.strictEqual(string, address.toString()); 21 | }); 22 | }); 23 | describe('fromString', function() { 24 | it('throws on a bad inputs', function() { 25 | [ '', null, {} ].forEach(function(input) { 26 | assert.throws(function() { 27 | var address = libsignal.SignalProtocolAddress.fromString(input); 28 | }); 29 | }); 30 | }); 31 | it('constructs the address', function() { 32 | var address = libsignal.SignalProtocolAddress.fromString(string); 33 | assert.strictEqual(deviceId, address.getDeviceId()); 34 | assert.strictEqual(name, address.getName()); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/SignalProtocolStore_test.js: -------------------------------------------------------------------------------- 1 | /* vim: ts=4:sw=4 */ 2 | 3 | 'use strict'; 4 | 5 | describe("SignalProtocolStore", function() { 6 | var store = new SignalProtocolStore(); 7 | var registrationId = 1337; 8 | var identityKey = { 9 | pubKey: Internal.crypto.getRandomBytes(33), 10 | privKey: Internal.crypto.getRandomBytes(32), 11 | }; 12 | before(function() { 13 | store.put('registrationId', registrationId); 14 | store.put('identityKey', identityKey); 15 | }); 16 | testIdentityKeyStore(store, registrationId, identityKey); 17 | testPreKeyStore(store); 18 | testSignedPreKeyStore(store); 19 | testSessionStore(store); 20 | }); 21 | -------------------------------------------------------------------------------- /test/SignedPreKeyStore_test.js: -------------------------------------------------------------------------------- 1 | function testSignedPreKeyStore(store) { 2 | describe('SignedPreKeyStore', function() { 3 | var testKey; 4 | before(function(done) { 5 | Internal.crypto.createKeyPair().then(function(keyPair) { 6 | testKey = keyPair; 7 | }).then(done,done); 8 | }); 9 | describe('storeSignedPreKey', function() { 10 | it('stores signed prekeys', function(done) { 11 | store.storeSignedPreKey(3, testKey).then(function() { 12 | return store.loadSignedPreKey(3).then(function(key) { 13 | assertEqualArrayBuffers(key.pubKey, testKey.pubKey); 14 | assertEqualArrayBuffers(key.privKey, testKey.privKey); 15 | }); 16 | }).then(done,done); 17 | }); 18 | }); 19 | describe('loadSignedPreKey', function() { 20 | it('returns prekeys that exist', function(done) { 21 | store.storeSignedPreKey(1, testKey).then(function() { 22 | return store.loadSignedPreKey(1).then(function(key) { 23 | assertEqualArrayBuffers(key.pubKey, testKey.pubKey); 24 | assertEqualArrayBuffers(key.privKey, testKey.privKey); 25 | }); 26 | }).then(done,done); 27 | }); 28 | it('returns undefined for prekeys that do not exist', function(done) { 29 | store.storeSignedPreKey(1, testKey).then(function() { 30 | return store.loadSignedPreKey(2).then(function(key) { 31 | assert.isUndefined(key); 32 | }); 33 | }).then(done,done); 34 | }); 35 | }); 36 | describe('removeSignedPreKey', function() { 37 | it('deletes signed prekeys', function(done) { 38 | before(function(done) { 39 | store.storeSignedPreKey(4, testKey).then(done); 40 | }); 41 | store.removeSignedPreKey(4, testKey).then(function() { 42 | return store.loadSignedPreKey(4).then(function(key) { 43 | assert.isUndefined(key); 44 | }); 45 | }).then(done,done); 46 | }); 47 | }); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /test/_test.js: -------------------------------------------------------------------------------- 1 | mocha.setup("bdd"); 2 | window.assert = chai.assert; 3 | 4 | (function() { 5 | var OriginalReporter = mocha._reporter; 6 | 7 | var SauceReporter = function(runner) { 8 | var failedTests = []; 9 | 10 | runner.on('end', function() { 11 | window.mochaResults = runner.stats; 12 | window.mochaResults.reports = failedTests; 13 | }); 14 | 15 | runner.on('fail', function(test, err) { 16 | var flattenTitles = function(test) { 17 | var titles = []; 18 | while (test.parent.title) { 19 | titles.push(test.parent.title); 20 | test = test.parent; 21 | } 22 | return titles.reverse(); 23 | }; 24 | failedTests.push({ 25 | name: test.title, 26 | result: false, 27 | message: err.message, 28 | stack: err.stack, 29 | titles: flattenTitles(test) 30 | }); 31 | }); 32 | 33 | new OriginalReporter(runner); 34 | }; 35 | 36 | SauceReporter.prototype = OriginalReporter.prototype; 37 | 38 | mocha.reporter(SauceReporter); 39 | }()); 40 | 41 | /* 42 | * global helpers for tests 43 | */ 44 | function assertEqualArrayBuffers(ab1, ab2) { 45 | assert.deepEqual(new Uint8Array(ab1), new Uint8Array(ab2)); 46 | }; 47 | 48 | function hexToArrayBuffer(str) { 49 | var ret = new ArrayBuffer(str.length / 2); 50 | var array = new Uint8Array(ret); 51 | for (var i = 0; i < str.length/2; i++) 52 | array[i] = parseInt(str.substr(i*2, 2), 16); 53 | return ret; 54 | }; 55 | 56 | var KeyHelper = libsignal.KeyHelper; 57 | 58 | function generateIdentity(store) { 59 | return Promise.all([ 60 | KeyHelper.generateIdentityKeyPair(), 61 | KeyHelper.generateRegistrationId(), 62 | ]).then(function(result) { 63 | store.put('identityKey', result[0]); 64 | store.put('registrationId', result[1]); 65 | }); 66 | } 67 | 68 | function generatePreKeyBundle(store, preKeyId, signedPreKeyId) { 69 | return Promise.all([ 70 | store.getIdentityKeyPair(), 71 | store.getLocalRegistrationId() 72 | ]).then(function(result) { 73 | var identity = result[0]; 74 | var registrationId = result[1]; 75 | 76 | return Promise.all([ 77 | KeyHelper.generatePreKey(preKeyId), 78 | KeyHelper.generateSignedPreKey(identity, signedPreKeyId), 79 | ]).then(function(keys) { 80 | var preKey = keys[0] 81 | var signedPreKey = keys[1]; 82 | 83 | store.storePreKey(preKeyId, preKey.keyPair); 84 | store.storeSignedPreKey(signedPreKeyId, signedPreKey.keyPair); 85 | 86 | return { 87 | identityKey: identity.pubKey, 88 | registrationId : registrationId, 89 | preKey: { 90 | keyId : preKeyId, 91 | publicKey : preKey.keyPair.pubKey 92 | }, 93 | signedPreKey: { 94 | keyId : signedPreKeyId, 95 | publicKey : signedPreKey.keyPair.pubKey, 96 | signature : signedPreKey.signature 97 | } 98 | }; 99 | }); 100 | }); 101 | } 102 | -------------------------------------------------------------------------------- /test/crypto_test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=4:sw=4 3 | */ 4 | 5 | 'use strict'; 6 | window.assert = chai.assert; 7 | 8 | describe("Crypto", function() { 9 | describe("Encrypt AES-CBC", function() { 10 | it('works', function(done) { 11 | var key = hexToArrayBuffer('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'); 12 | var iv = hexToArrayBuffer('000102030405060708090a0b0c0d0e0f'); 13 | var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710'); 14 | var ciphertext = hexToArrayBuffer('f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644'); 15 | Internal.crypto.encrypt(key, plaintext, iv).then(function(result) { 16 | assertEqualArrayBuffers(result, ciphertext); 17 | }).then(done).catch(done); 18 | }); 19 | }); 20 | 21 | describe("Decrypt AES-CBC", function() { 22 | it('works', function(done) { 23 | var key = hexToArrayBuffer('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'); 24 | var iv = hexToArrayBuffer('000102030405060708090a0b0c0d0e0f'); 25 | var plaintext = hexToArrayBuffer('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710'); 26 | var ciphertext = hexToArrayBuffer('f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644'); 27 | Internal.crypto.decrypt(key, ciphertext, iv).then(function(result) { 28 | assertEqualArrayBuffers(result, plaintext); 29 | }).then(done).catch(done); 30 | }); 31 | }); 32 | 33 | describe("HMAC SHA-256", function() { 34 | it("works", function(done) { 35 | var key = hexToArrayBuffer('6f35628d65813435534b5d67fbdb54cb33403d04e843103e6399f806cb5df95febbdd61236f33245'); 36 | var input = hexToArrayBuffer('752cff52e4b90768558e5369e75d97c69643509a5e5904e0a386cbe4d0970ef73f918f675945a9aefe26daea27587e8dc909dd56fd0468805f834039b345f855cfe19c44b55af241fff3ffcd8045cd5c288e6c4e284c3720570b58e4d47b8feeedc52fd1401f698a209fccfa3b4c0d9a797b046a2759f82a54c41ccd7b5f592b'); 37 | var mac = hexToArrayBuffer('05d1243e6465ed9620c9aec1c351a186'); 38 | Internal.crypto.sign(key, input).then(function(result) { 39 | assertEqualArrayBuffers(result.slice(0, mac.byteLength), mac); 40 | }).then(done).catch(done); 41 | }); 42 | }); 43 | 44 | 45 | describe("HKDF", function() { 46 | it('works', function(done) { 47 | // HMAC RFC5869 Test vectors 48 | var T1 = hexToArrayBuffer("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf"); 49 | var T2 = hexToArrayBuffer("34007208d5b887185865"); 50 | var IKM = new Uint8Array(new ArrayBuffer(22)); 51 | for (var i = 0; i < 22; i++) 52 | IKM[i] = 11; 53 | 54 | var salt = new Uint8Array(new ArrayBuffer(13)); 55 | for (var i = 0; i < 13; i++) 56 | salt[i] = i; 57 | 58 | var info = new Uint8Array(new ArrayBuffer(10)); 59 | for (var i = 0; i < 10; i++) 60 | info[i] = 240 + i; 61 | 62 | return Internal.crypto.HKDF(IKM.buffer, salt.buffer, info.buffer).then(function(OKM){ 63 | assertEqualArrayBuffers(OKM[0], T1); 64 | assertEqualArrayBuffers(OKM[1].slice(0, 10), T2); 65 | }).then(done).catch(done); 66 | }); 67 | }); 68 | 69 | function testCurve25519() { 70 | var alice_bytes = hexToArrayBuffer("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); 71 | var alice_priv = hexToArrayBuffer("70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a"); 72 | var alice_pub = hexToArrayBuffer("058520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); 73 | var bob_bytes = hexToArrayBuffer("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"); 74 | var bob_priv = hexToArrayBuffer("58ab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e06b"); 75 | var bob_pub = hexToArrayBuffer("05de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); 76 | var shared_sec = hexToArrayBuffer("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); 77 | 78 | describe("createKeyPair", function() { 79 | it ('converts alice private keys to a keypair', function(done) { 80 | Internal.crypto.createKeyPair(alice_bytes).then(function(keypair) { 81 | assertEqualArrayBuffers(keypair.privKey, alice_priv); 82 | assertEqualArrayBuffers(keypair.pubKey, alice_pub); 83 | done(); 84 | }).catch(done); 85 | }); 86 | it ('converts bob private keys to a keypair', function(done) { 87 | Internal.crypto.createKeyPair(bob_bytes).then(function(keypair) { 88 | assertEqualArrayBuffers(keypair.privKey, bob_priv); 89 | assertEqualArrayBuffers(keypair.pubKey, bob_pub); 90 | done(); 91 | }).catch(done); 92 | }); 93 | it ('generates a key if one is not provided', function(done) { 94 | Internal.crypto.createKeyPair().then(function(keypair) { 95 | assert.strictEqual(keypair.privKey.byteLength, 32); 96 | assert.strictEqual(keypair.pubKey.byteLength, 33); 97 | assert.strictEqual(new Uint8Array(keypair.pubKey)[0], 5); 98 | done(); 99 | }).catch(done); 100 | }); 101 | }); 102 | 103 | describe("ECDHE", function() { 104 | it("computes the shared secret for alice", function(done) { 105 | Internal.crypto.ECDHE(bob_pub, alice_priv).then(function(secret) { 106 | assertEqualArrayBuffers(shared_sec, secret); 107 | done(); 108 | }).catch(done); 109 | }); 110 | it("computes the shared secret for bob", function(done) { 111 | Internal.crypto.ECDHE(alice_pub, bob_priv).then(function(secret) { 112 | assertEqualArrayBuffers(shared_sec, secret); 113 | done(); 114 | }).catch(done); 115 | }); 116 | }); 117 | 118 | var priv = hexToArrayBuffer("48a8892cc4e49124b7b57d94fa15becfce071830d6449004685e387c62409973"); 119 | var pub = hexToArrayBuffer("0555f1bfede27b6a03e0dd389478ffb01462e5c52dbbac32cf870f00af1ed9af3a"); 120 | var msg = hexToArrayBuffer("617364666173646661736466"); 121 | var sig = hexToArrayBuffer("2bc06c745acb8bae10fbc607ee306084d0c28e2b3bb819133392473431291fd0dfa9c7f11479996cf520730d2901267387e08d85bbf2af941590e3035a545285"); 122 | describe("Ed25519Sign", function() { 123 | // Some self-generated test vectors 124 | it('works', function(done) { 125 | return Internal.crypto.Ed25519Sign(priv, msg).then(function(sigCalc) { 126 | assertEqualArrayBuffers(sig, sigCalc); 127 | }).then(done).catch(done); 128 | }); 129 | }); 130 | 131 | describe("Ed25519Verify", function() { 132 | it("throws on bad signature", function(done) { 133 | var badsig = sig.slice(0); 134 | new Uint8Array(badsig).set([0], 0); 135 | 136 | Internal.crypto.Ed25519Verify(pub, msg, badsig).catch(function(e) { 137 | if (e.message === 'Invalid signature') { 138 | done(); 139 | } else { throw e; } 140 | }).catch(done); 141 | }); 142 | 143 | it("does not throw on good signature", function(done) { 144 | return Internal.crypto.Ed25519Verify(pub, msg, sig).then(done).catch(done); 145 | }); 146 | }); 147 | } 148 | 149 | describe('curve25519', function() { 150 | this.timeout(5000); 151 | testCurve25519(); 152 | }); 153 | describe('curve25519 in a worker', function() { 154 | before(function() { 155 | libsignal.worker.startWorker('../../dist/libsignal-protocol-worker.js'); 156 | }); 157 | after(function() { 158 | libsignal.worker.stopWorker(); 159 | }); 160 | this.timeout(5000); 161 | testCurve25519(); 162 | }); 163 | }); 164 | -------------------------------------------------------------------------------- /test/helpers_test.js: -------------------------------------------------------------------------------- 1 | /* vim: ts=4:sw=4 */ 2 | 3 | 'use strict'; 4 | describe('util', function() { 5 | describe("isEqual", function(){ 6 | it('returns false when a or b is undefined', function(){ 7 | assert.isFalse(util.isEqual("defined value", undefined)); 8 | assert.isFalse(util.isEqual(undefined, "defined value")); 9 | }); 10 | it('returns true when a and b are equal', function(){ 11 | var a = "same value"; 12 | var b = "same value"; 13 | assert.isTrue(util.isEqual(a, b)); 14 | }); 15 | it('returns false when a and b are not equal', function(){ 16 | var a = "same value"; 17 | var b = "diferent value"; 18 | assert.isFalse(util.isEqual(a, b)); 19 | }); 20 | it('throws an error when a/b compare is too short', function(){ 21 | var a = "1234"; 22 | var b = "1234"; 23 | assert.throw(function() { util.isEqual(a, b) }, 24 | Error, /a\/b compare too short/); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | libsignal-protocol tests 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/protos: -------------------------------------------------------------------------------- 1 | ../../protos/ -------------------------------------------------------------------------------- /test/temp_helpers.js: -------------------------------------------------------------------------------- 1 | var pushMessages = dcodeIO.ProtoBuf.loadProto('package textsecure;\n' + 2 | '\n' + 3 | 'option java_package = "org.whispersystems.textsecure.push";\n' + 4 | 'option java_outer_classname = "PushMessageProtos";\n' + 5 | '\n' + 6 | 'message IncomingPushMessageSignal {\n' + 7 | ' enum Type {\n' + 8 | ' UNKNOWN = 0;\n' + 9 | ' CIPHERTEXT = 1;\n' + 10 | ' KEY_EXCHANGE = 2;\n' + 11 | ' PREKEY_BUNDLE = 3;\n' + 12 | ' PLAINTEXT = 4;\n' + 13 | ' RECEIPT = 5;\n' + 14 | ' PREKEY_BUNDLE_DEVICE_CONTROL = 6;\n' + 15 | ' DEVICE_CONTROL = 7;\n' + 16 | ' }\n' + 17 | ' optional Type type = 1;\n' + 18 | ' optional string source = 2;\n' + 19 | ' optional uint32 sourceDevice = 7;\n' + 20 | ' optional string relay = 3;\n' + 21 | ' optional uint64 timestamp = 5;\n' + 22 | ' optional bytes message = 6; // Contains an encrypted PushMessageContent\n' + 23 | '// repeated string destinations = 4; // No longer supported\n' + 24 | '}\n' + 25 | '\n' + 26 | 'message PushMessageContent {\n' + 27 | ' message AttachmentPointer {\n' + 28 | ' optional fixed64 id = 1;\n' + 29 | ' optional string contentType = 2;\n' + 30 | ' optional bytes key = 3;\n' + 31 | ' }\n' + 32 | '\n' + 33 | ' message GroupContext {\n' + 34 | ' enum Type {\n' + 35 | ' UNKNOWN = 0;\n' + 36 | ' UPDATE = 1;\n' + 37 | ' DELIVER = 2;\n' + 38 | ' QUIT = 3;\n' + 39 | ' }\n' + 40 | ' optional bytes id = 1;\n' + 41 | ' optional Type type = 2;\n' + 42 | ' optional string name = 3;\n' + 43 | ' repeated string members = 4;\n' + 44 | ' optional AttachmentPointer avatar = 5;\n' + 45 | ' }\n' + 46 | '\n' + 47 | ' enum Flags {\n' + 48 | ' END_SESSION = 1;\n' + 49 | ' }\n' + 50 | '\n' + 51 | ' optional string body = 1;\n' + 52 | ' repeated AttachmentPointer attachments = 2;\n' + 53 | ' optional GroupContext group = 3;\n' + 54 | ' optional uint32 flags = 4;\n' + 55 | '}').build('textsecure'); 56 | 57 | window.textsecure = { 58 | protobuf: { 59 | IncomingPushMessageSignal : pushMessages.IncomingPushMessageSignal, 60 | PushMessageContent : pushMessages.PushMessageContent, 61 | } 62 | 63 | }; 64 | --------------------------------------------------------------------------------