├── .gitignore ├── .jscsrc ├── .jshintrc ├── .travis.yml ├── README.md ├── RNOpenPGP.podspec ├── android ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── github │ │ └── orhan │ │ └── openpgp │ │ ├── RNOpenPGPModule.java │ │ └── RNOpenPGPPackage.java ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle ├── iOS ├── RNOpenPGP.h ├── RNOpenPGP.m └── RNOpenPGP.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── orhan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── tenaciousmv.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ ├── orhan.xcuserdatad │ └── xcschemes │ │ ├── RNOpenPGP.xcscheme │ │ └── xcschememanagement.plist │ └── tenaciousmv.xcuserdatad │ └── xcschemes │ ├── RNRandomBytes.xcscheme │ └── xcschememanagement.plist ├── package.json └── src ├── cleartext.js ├── compression ├── huffman.js ├── rawdeflate.js ├── rawinflate.js └── zlib.min.js ├── config ├── config.js ├── index.js └── localStorage.js ├── crypto ├── cfb.js ├── cipher │ ├── aes.js │ ├── blowfish.js │ ├── cast5.js │ ├── des.js │ ├── index.js │ └── twofish.js ├── crypto.js ├── hash │ ├── index.js │ ├── md5.js │ ├── ripe-md.js │ └── sha.js ├── index.js ├── isaac.js ├── pkcs1.js ├── public_key │ ├── dsa.js │ ├── elgamal.js │ ├── index.js │ ├── jsbn.js │ └── rsa.js ├── random.js ├── randomBytes.js └── signature.js ├── encoding ├── armor.js └── base64.js ├── enums.js ├── hkp.js ├── index.js ├── key.js ├── keyring ├── index.js ├── keyring.js └── localstore.js ├── message.js ├── openpgp.js ├── packet ├── all_packets.js ├── clone.js ├── compressed.js ├── index.js ├── literal.js ├── marker.js ├── one_pass_signature.js ├── packet.js ├── packetlist.js ├── public_key.js ├── public_key_encrypted_session_key.js ├── public_subkey.js ├── secret_key.js ├── secret_subkey.js ├── signature.js ├── sym_encrypted_integrity_protected.js ├── sym_encrypted_session_key.js ├── symmetrically_encrypted.js ├── trust.js ├── user_attribute.js └── userid.js ├── type ├── keyid.js ├── mpi.js └── s2k.js ├── util.js └── worker ├── async_proxy.js └── worker.js /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .DS_Store 3 | node_modules/ 4 | npm* 5 | test/lib/ 6 | dist/ 7 | dist/*.tgz 8 | dist/*_debug.js 9 | openpgp.store/ 10 | doc/ 11 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowTrailingWhitespace": true, 3 | "validateIndentation": 2 4 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "nonew": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "immed": true, 8 | "regexp": true, 9 | "evil": true, 10 | "eqnull": true, 11 | "expr": true, 12 | "undef": true, 13 | "unused": true, 14 | "esnext": true, 15 | 16 | "globals": { 17 | "console": true, 18 | "Promise": true, 19 | "importScripts": true, 20 | "process": true, 21 | "Event": true, 22 | "describe": true, 23 | "it": true, 24 | "sinon": true, 25 | "mocha": true, 26 | "before": true, 27 | "beforeEach": true, 28 | "after": true, 29 | "afterEach": true, 30 | "escape": true, 31 | "unescape": true, 32 | "postMessage": true, 33 | "resolves": true, 34 | "rejects": true 35 | } 36 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | addons: 7 | code_climate: 8 | repo_token: $CODECLIMATE_REPO_TOKEN 9 | matrix: 10 | fast_finish: true 11 | include: 12 | - node_js: "0.12" 13 | env: OPENPGPJSTEST='unit' OPENPGP_NODE_JS='0.12' 14 | - node_js: "4" 15 | env: OPENPGPJSTEST='unit' OPENPGP_NODE_JS='4.2' 16 | - node_js: "5" 17 | env: OPENPGPJSTEST='unit' OPENPGP_NODE_JS='5' 18 | - node_js: "4" 19 | env: OPENPGPJSTEST='end2end-4' BROWSER='chrome 46' 20 | - node_js: "4" 21 | env: OPENPGPJSTEST='end2end-1' BROWSER='firefox 42' 22 | - node_js: "4" 23 | env: OPENPGPJSTEST='end2end-6' BROWSER='internet explorer 11' 24 | - node_js: "4" 25 | env: OPENPGPJSTEST='end2end-9' BROWSER='safari 9' 26 | - node_js: "4" 27 | env: OPENPGPJSTEST='end2end-0' BROWSER='firefox 38' 28 | - node_js: "4" 29 | env: OPENPGPJSTEST='end2end-2' BROWSER='firefox beta' 30 | - node_js: "4" 31 | env: OPENPGPJSTEST='end2end-3' BROWSER='chrome 38' 32 | - node_js: "4" 33 | env: OPENPGPJSTEST='end2end-5' BROWSER='chrome beta' 34 | - node_js: "4" 35 | env: OPENPGPJSTEST='end2end-7' BROWSER='microsoft edge 20.10240' 36 | - node_js: "4" 37 | env: OPENPGPJSTEST='end2end-8' BROWSER='safari 8' 38 | - node_js: "4" 39 | env: OPENPGPJSTEST='end2end-10' BROWSER='android 4.4' 40 | - node_js: "4" 41 | env: OPENPGPJSTEST='end2end-11' BROWSER='android 5.1' 42 | - node_js: "4" 43 | env: OPENPGPJSTEST='end2end-12' BROWSER='iphone 7.0' 44 | - node_js: "4" 45 | env: OPENPGPJSTEST='end2end-13' BROWSER='iphone 9.1' 46 | allow_failures: 47 | - env: OPENPGPJSTEST='end2end-0' BROWSER='firefox 38' 48 | - env: OPENPGPJSTEST='end2end-2' BROWSER='firefox beta' 49 | - env: OPENPGPJSTEST='end2end-3' BROWSER='chrome 38' 50 | - env: OPENPGPJSTEST='end2end-5' BROWSER='chrome beta' 51 | - env: OPENPGPJSTEST='end2end-7' BROWSER='microsoft edge 20.10240' 52 | - env: OPENPGPJSTEST='end2end-8' BROWSER='safari 8' 53 | - env: OPENPGPJSTEST='end2end-10' BROWSER='android 4.4' 54 | - env: OPENPGPJSTEST='end2end-11' BROWSER='android 5.1' 55 | - env: OPENPGPJSTEST='end2end-12' BROWSER='iphone 7.0' 56 | - env: OPENPGPJSTEST='end2end-13' BROWSER='iphone 9.1' 57 | before_script: 58 | - npm install -g grunt-cli codeclimate-test-reporter 59 | script: 60 | - $TRAVIS_BUILD_DIR/travis.sh 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React-Native-OpenPGP 2 | ========== 3 | 4 | [React-Native-OpenPGP](http://openpgpjs.org/) is a Javascript implementation of the OpenPGP protocol based on [OpenPGP.js](https://github.com/openpgpjs/openpgpjs). 5 | 6 | 7 | ## Getting started 8 | 9 | #### Installation 10 | 11 | npm install --save react-native-openpgp 12 | 13 | // not needed if using auto-linking RN > 0.060 14 | react-native link react-native-openpgp 15 | 16 | Alternatively you can add the Android and iOS modules library by following the official guide. 17 | 18 | ## Usage 19 | 20 | ```js 21 | import * as openpgp from 'react-native-openpgp'; 22 | ``` 23 | 24 | #### Encrypt and decrypt *String* data with a password 25 | 26 | ```js 27 | var options, encrypted; 28 | 29 | options = { 30 | data: 'Hello, World!', // input as String 31 | passwords: ['secret stuff'] // multiple passwords possible 32 | }; 33 | 34 | openpgp.encrypt(options) 35 | .then((ciphertext) => { 36 | encrypted = ciphertext.data; // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----' 37 | }) 38 | .catch((error) => { 39 | console.log("Something went wrong: " + error); 40 | }); 41 | ``` 42 | 43 | ```js 44 | options = { 45 | message: openpgp.readMessage(encrypted), // parse armored message 46 | password: 'secret stuff' // decrypt with password 47 | }; 48 | 49 | openpgp.decrypt(options) 50 | .then((plaintext) => { 51 | return plaintext.data; // 'Hello, World!' 52 | }) 53 | .catch((error) => { 54 | console.log("Something went wrong: " + error); 55 | }); 56 | ``` 57 | 58 | #### Encrypt and decrypt *Uint8Array* data with PGP keys 59 | 60 | ```js 61 | var options, encrypted; 62 | 63 | var pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; 64 | var privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; 65 | 66 | options = { 67 | data: new Uint8Array([0x01, 0x01, 0x01]), // input as Uint8Array 68 | publicKeys: openpgp.readArmoredKey(pubkey).keys, // for encryption 69 | privateKeys: openpgp.readArmoredKey(privkey).keys, // for signing (optional) 70 | armor: false // don't ASCII armor 71 | }; 72 | 73 | openpgp.encrypt(options) 74 | .then((ciphertext) => { 75 | encrypted = ciphertext.message.packets.write(); // get raw encrypted packets as Uint8Array 76 | }) 77 | .catch((error) => { 78 | console.log("Something went wrong: " + error); 79 | }); 80 | ``` 81 | 82 | ```js 83 | options = { 84 | message: openpgp.readBinaryMessage(encrypted), // parse encrypted bytes 85 | publicKeys: openpgp.readArmoredKey(pubkey).keys, // for verification (optional) 86 | privateKey: openpgp.readArmoredKey(privkey).keys[0], // for decryption 87 | format: 'binary' // output as Uint8Array 88 | }; 89 | 90 | openpgp.decrypt(options) 91 | .then((plaintext) => { 92 | return plaintext.data // Uint8Array([0x01, 0x01, 0x01]) 93 | }) 94 | .catch((error) => { 95 | console.log("Something went wrong: " + error); 96 | }); 97 | ``` 98 | 99 | #### Generate new key pair 100 | 101 | ```js 102 | var options = { 103 | userIds: [{ name:'Jon Smith', email:'jon@example.com' }], // multiple user IDs 104 | numBits: 2048, // RSA key size 105 | passphrase: 'super long and hard to guess secret' // protects the private key 106 | }; 107 | 108 | openpgp.generateKey(options) 109 | .then((key) => { 110 | var privkey = key.privateKeyArmored; // '-----BEGIN PGP PRIVATE KEY BLOCK ... ' 111 | var pubkey = key.publicKeyArmored; // '-----BEGIN PGP PUBLIC KEY BLOCK ... ' 112 | }) 113 | .catch((error) => { 114 | console.log("Something went wrong: " + error); 115 | }); 116 | 117 | ``` 118 | -------------------------------------------------------------------------------- /RNOpenPGP.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "RNOpenPGP" 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | s.homepage = "https://github.com/orhan/react-native-openpgp" 10 | s.license = "MIT" 11 | s.authors = { "Place Holder" => "email@email.com" } 12 | s.platforms = { :ios => "9.0", :tvos => "9.0" } 13 | s.source = { :git => "https://github.com/orhan/react-native-openpgp.git", :tag => "v#{s.version}" } 14 | 15 | s.source_files = "ios/**/*.{h,m,swift}" 16 | s.requires_arc = true 17 | 18 | s.dependency "React" 19 | end 20 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 22 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | } 14 | 15 | dependencies { 16 | compile 'com.facebook.react:react-native:0.13.+' 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/github/orhan/openpgp/RNOpenPGPModule.java: -------------------------------------------------------------------------------- 1 | package com.github.orhan.openpgp; 2 | 3 | import com.facebook.react.bridge.ReactMethod; 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.Callback; 6 | 7 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 8 | 9 | import java.security.SecureRandom; 10 | 11 | import android.util.Base64; 12 | 13 | public class RNOpenPGPModule extends ReactContextBaseJavaModule { 14 | 15 | public RNOpenPGPModule(ReactApplicationContext reactContext) { 16 | super(reactContext); 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return "RNOpenPGP"; 22 | } 23 | @ReactMethod 24 | public void randomBytes(int size, Callback success) { 25 | SecureRandom sr = new SecureRandom(); 26 | byte[] output = new byte[size]; 27 | sr.nextBytes(output); 28 | String string = Base64.encodeToString(output, Base64.DEFAULT); 29 | success.invoke(null, string); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/github/orhan/openpgp/RNOpenPGPPackage.java: -------------------------------------------------------------------------------- 1 | package com.github.orhan.openpgp; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class RNOpenPGPPackage implements ReactPackage { 14 | 15 | @Override 16 | public List createNativeModules( 17 | ReactApplicationContext reactContext) { 18 | List modules = new ArrayList<>(); 19 | 20 | modules.add(new RNOpenPGPModule(reactContext)); 21 | 22 | return modules; 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Collections.emptyList(); 28 | } 29 | 30 | @Override 31 | public List> createJSModules() { 32 | return Collections.emptyList(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:1.3.0' 7 | 8 | // NOTE: Do not place your application dependencies here; they belong 9 | // in the individual module build.gradle files 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | mavenLocal() 16 | jcenter() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orhan/react-native-openpgp/922da5126c888f8f31e7ca889a6d018a1536fd9a/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Apr 10 02:20:41 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | #Sun Apr 10 02:20:08 CEST 2016 11 | sdk.dir=/Users/orhan/Library/Android/sdk 12 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'openpgp' 2 | include ':app' 3 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.h: -------------------------------------------------------------------------------- 1 | // RNOpenPGP.h 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | @interface RNOpenPGP : NSObject 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.m: -------------------------------------------------------------------------------- 1 | #import "RNOpenPGP.h" 2 | #import 3 | #import 4 | 5 | @implementation RNOpenPGP 6 | 7 | + (BOOL)requiresMainQueueSetup 8 | { 9 | return YES; 10 | } 11 | 12 | RCT_EXPORT_MODULE() 13 | 14 | @synthesize bridge = _bridge; 15 | 16 | RCT_EXPORT_METHOD(randomBytes:(NSUInteger)length 17 | callback:(RCTResponseSenderBlock)callback) 18 | { 19 | callback(@[[NSNull null], [self randomBytes:length]]); 20 | } 21 | 22 | - (NSString *) randomBytes:(NSUInteger)length 23 | { 24 | NSMutableData* bytes = [NSMutableData dataWithLength:length]; 25 | SecRandomCopyBytes(kSecRandomDefault, length, [bytes mutableBytes]); 26 | return [bytes base64EncodedStringWithOptions:0]; 27 | } 28 | 29 | - (NSDictionary *)constantsToExport 30 | { 31 | return @{ 32 | @"seed": [self randomBytes:4096] 33 | }; 34 | }; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/project.xcworkspace/xcuserdata/orhan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orhan/react-native-openpgp/922da5126c888f8f31e7ca889a6d018a1536fd9a/iOS/RNOpenPGP.xcodeproj/project.xcworkspace/xcuserdata/orhan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/project.xcworkspace/xcuserdata/tenaciousmv.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orhan/react-native-openpgp/922da5126c888f8f31e7ca889a6d018a1536fd9a/iOS/RNOpenPGP.xcodeproj/project.xcworkspace/xcuserdata/tenaciousmv.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/xcuserdata/orhan.xcuserdatad/xcschemes/RNOpenPGP.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/xcuserdata/orhan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RNOpenPGP.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 73EEC9381BFE4B1D00D468EB 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/xcuserdata/tenaciousmv.xcuserdatad/xcschemes/RNRandomBytes.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /iOS/RNOpenPGP.xcodeproj/xcuserdata/tenaciousmv.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RNRandomBytes.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 73EEC9381BFE4B1D00D468EB 16 | 17 | primary 18 | 19 | 20 | 73EEC9431BFE4B1D00D468EB 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-openpgp", 3 | "description": "React-Native OpenPGP is an OpenPGP.js implementation made to work under React-Native.", 4 | "version": "1.0.4", 5 | "license": "MIT", 6 | "engines": { 7 | "node": ">=0.8" 8 | }, 9 | "keywords": [ 10 | "crypto", 11 | "pgp", 12 | "gpg", 13 | "openpgp", 14 | "react-component", 15 | "react-native", 16 | "randomBytes", 17 | "rng", 18 | "ios", 19 | "android" 20 | ], 21 | "main": "src/openpgp.js", 22 | "directories": { 23 | "lib": "src" 24 | }, 25 | "dependencies": { 26 | "asmcrypto-lite": "^1.0.1", 27 | "buffer": "^4.5.0", 28 | "es6-promise": "3.1.2", 29 | "node-fetch": "^1.3.3", 30 | "node-localstorage": "~1.1.2", 31 | "rusha": "^0.8.3" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/orhan/react-native-openpgp" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/cleartext.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * @requires config 20 | * @requires encoding/armor 21 | * @requires enums 22 | * @requires packet 23 | * @module cleartext 24 | */ 25 | 26 | 'use strict'; 27 | 28 | import config from './config'; 29 | import packet from './packet'; 30 | import enums from './enums.js'; 31 | import armor from './encoding/armor.js'; 32 | 33 | /** 34 | * @class 35 | * @classdesc Class that represents an OpenPGP cleartext signed message. 36 | * See {@link http://tools.ietf.org/html/rfc4880#section-7} 37 | * @param {String} text The cleartext of the signed message 38 | * @param {module:packet/packetlist} packetlist The packetlist with signature packets or undefined 39 | * if message not yet signed 40 | */ 41 | 42 | export function CleartextMessage(text, packetlist) { 43 | if (!(this instanceof CleartextMessage)) { 44 | return new CleartextMessage(text, packetlist); 45 | } 46 | // normalize EOL to canonical form 47 | this.text = text.replace(/\r/g, '').replace(/[\t ]+\n/g, "\n").replace(/\n/g,"\r\n"); 48 | this.packets = packetlist || new packet.List(); 49 | } 50 | 51 | /** 52 | * Returns the key IDs of the keys that signed the cleartext message 53 | * @return {Array} array of keyid objects 54 | */ 55 | CleartextMessage.prototype.getSigningKeyIds = function() { 56 | var keyIds = []; 57 | var signatureList = this.packets.filterByTag(enums.packet.signature); 58 | signatureList.forEach(function(packet) { 59 | keyIds.push(packet.issuerKeyId); 60 | }); 61 | return keyIds; 62 | }; 63 | 64 | /** 65 | * Sign the cleartext message 66 | * @param {Array} privateKeys private keys with decrypted secret key data for signing 67 | */ 68 | CleartextMessage.prototype.sign = function(privateKeys) { 69 | var packetlist = new packet.List(); 70 | var literalDataPacket = new packet.Literal(); 71 | literalDataPacket.setText(this.text); 72 | for (var i = 0; i < privateKeys.length; i++) { 73 | if (privateKeys[i].isPublic()) { 74 | throw new Error('Need private key for signing'); 75 | } 76 | var signaturePacket = new packet.Signature(); 77 | signaturePacket.signatureType = enums.signature.text; 78 | signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; 79 | var signingKeyPacket = privateKeys[i].getSigningKeyPacket(); 80 | signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; 81 | if (!signingKeyPacket.isDecrypted) { 82 | throw new Error('Private key is not decrypted.'); 83 | } 84 | signaturePacket.sign(signingKeyPacket, literalDataPacket); 85 | packetlist.push(signaturePacket); 86 | } 87 | this.packets = packetlist; 88 | }; 89 | 90 | /** 91 | * Verify signatures of cleartext signed message 92 | * @param {Array} keys array of keys to verify signatures 93 | * @return {Array<{keyid: module:type/keyid, valid: Boolean}>} list of signer's keyid and validity of signature 94 | */ 95 | CleartextMessage.prototype.verify = function(keys) { 96 | var result = []; 97 | var signatureList = this.packets.filterByTag(enums.packet.signature); 98 | var literalDataPacket = new packet.Literal(); 99 | // we assume that cleartext signature is generated based on UTF8 cleartext 100 | literalDataPacket.setText(this.text); 101 | for (var i = 0; i < signatureList.length; i++) { 102 | var keyPacket = null; 103 | for (var j = 0; j < keys.length; j++) { 104 | keyPacket = keys[j].getSigningKeyPacket(signatureList[i].issuerKeyId); 105 | if (keyPacket) { 106 | break; 107 | } 108 | } 109 | 110 | var verifiedSig = {}; 111 | if (keyPacket) { 112 | verifiedSig.keyid = signatureList[i].issuerKeyId; 113 | verifiedSig.valid = signatureList[i].verify(keyPacket, literalDataPacket); 114 | } else { 115 | verifiedSig.keyid = signatureList[i].issuerKeyId; 116 | verifiedSig.valid = null; 117 | } 118 | result.push(verifiedSig); 119 | } 120 | return result; 121 | }; 122 | 123 | /** 124 | * Get cleartext 125 | * @return {String} cleartext of message 126 | */ 127 | CleartextMessage.prototype.getText = function() { 128 | // normalize end of line to \n 129 | return this.text.replace(/\r\n/g,"\n"); 130 | }; 131 | 132 | /** 133 | * Returns ASCII armored text of cleartext signed message 134 | * @return {String} ASCII armor 135 | */ 136 | CleartextMessage.prototype.armor = function() { 137 | var body = { 138 | hash: enums.read(enums.hash, config.prefer_hash_algorithm).toUpperCase(), 139 | text: this.text, 140 | data: this.packets.write() 141 | }; 142 | return armor.encode(enums.armor.signed, body); 143 | }; 144 | 145 | 146 | /** 147 | * reads an OpenPGP cleartext signed message and returns a CleartextMessage object 148 | * @param {String} armoredText text to be parsed 149 | * @return {module:cleartext~CleartextMessage} new cleartext message object 150 | * @static 151 | */ 152 | export function readArmored(armoredText) { 153 | var input = armor.decode(armoredText); 154 | if (input.type !== enums.armor.signed) { 155 | throw new Error('No cleartext signed message.'); 156 | } 157 | var packetlist = new packet.List(); 158 | packetlist.read(input.data); 159 | verifyHeaders(input.headers, packetlist); 160 | var newMessage = new CleartextMessage(input.text, packetlist); 161 | return newMessage; 162 | } 163 | 164 | /** 165 | * Compare hash algorithm specified in the armor header with signatures 166 | * @private 167 | * @param {Array} headers Armor headers 168 | * @param {module:packet/packetlist} packetlist The packetlist with signature packets 169 | */ 170 | function verifyHeaders(headers, packetlist) { 171 | var checkHashAlgos = function(hashAlgos) { 172 | function check(algo) { 173 | return packetlist[i].hashAlgorithm === algo; 174 | } 175 | for (var i = 0; i < packetlist.length; i++) { 176 | if (packetlist[i].tag === enums.packet.signature && !hashAlgos.some(check)) { 177 | return false; 178 | } 179 | } 180 | return true; 181 | }; 182 | var oneHeader = null; 183 | var hashAlgos = []; 184 | headers.forEach(function(header) { 185 | oneHeader = header.match(/Hash: (.+)/); // get header value 186 | if (oneHeader) { 187 | oneHeader = oneHeader[1].replace(/\s/g, ''); // remove whitespace 188 | oneHeader = oneHeader.split(','); 189 | oneHeader = oneHeader.map(function(hash) { 190 | hash = hash.toLowerCase(); 191 | try { 192 | return enums.write(enums.hash, hash); 193 | } catch (e) { 194 | throw new Error('Unknown hash algorithm in armor header: ' + hash); 195 | } 196 | }); 197 | hashAlgos = hashAlgos.concat(oneHeader); 198 | } else { 199 | throw new Error('Only "Hash" header allowed in cleartext signed message'); 200 | } 201 | }); 202 | if (!hashAlgos.length && !checkHashAlgos([enums.hash.md5])) { 203 | throw new Error('If no "Hash" header in cleartext signed message, then only MD5 signatures allowed'); 204 | } else if (!checkHashAlgos(hashAlgos)) { 205 | throw new Error('Hash algorithm mismatch in armor header and signature'); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/compression/huffman.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * build huffman table from length list. 4 | * @param {!(Array.|Uint8Array)} lengths length list. 5 | * @return {!Array} huffman table. 6 | */ 7 | export function buildHuffmanTable(lengths) { 8 | /** @type {number} length list size. */ 9 | var listSize = lengths.length; 10 | /** @type {number} max code length for table size. */ 11 | var maxCodeLength = 0; 12 | /** @type {number} min code length for table size. */ 13 | var minCodeLength = Number.POSITIVE_INFINITY; 14 | /** @type {number} table size. */ 15 | var size; 16 | /** @type {!(Array|Uint8Array)} huffman code table. */ 17 | var table; 18 | /** @type {number} bit length. */ 19 | var bitLength; 20 | /** @type {number} huffman code. */ 21 | var code; 22 | /** 23 | * サイズが 2^maxlength 個のテーブルを埋めるためのスキップ長. 24 | * @type {number} skip length for table filling. 25 | */ 26 | var skip; 27 | /** @type {number} reversed code. */ 28 | var reversed; 29 | /** @type {number} reverse temp. */ 30 | var rtemp; 31 | /** @type {number} loop counter. */ 32 | var i; 33 | /** @type {number} loop limit. */ 34 | var il; 35 | /** @type {number} loop counter. */ 36 | var j; 37 | /** @type {number} table value. */ 38 | var value; 39 | 40 | // Math.max は遅いので最長の値は for-loop で取得する 41 | for (i = 0, il = listSize; i < il; ++i) { 42 | if (lengths[i] > maxCodeLength) { 43 | maxCodeLength = lengths[i]; 44 | } 45 | if (lengths[i] < minCodeLength) { 46 | minCodeLength = lengths[i]; 47 | } 48 | } 49 | 50 | size = 1 << maxCodeLength; 51 | table = new (Uint32Array)(size); 52 | 53 | // ビット長の短い順からハフマン符号を割り当てる 54 | for (bitLength = 1, code = 0, skip = 2; bitLength <= maxCodeLength;) { 55 | for (i = 0; i < listSize; ++i) { 56 | if (lengths[i] === bitLength) { 57 | // ビットオーダーが逆になるためビット長分並びを反転する 58 | for (reversed = 0, rtemp = code, j = 0; j < bitLength; ++j) { 59 | reversed = (reversed << 1) | (rtemp & 1); 60 | rtemp >>= 1; 61 | } 62 | 63 | // 最大ビット長をもとにテーブルを作るため、 64 | // 最大ビット長以外では 0 / 1 どちらでも良い箇所ができる 65 | // そのどちらでも良い場所は同じ値で埋めることで 66 | // 本来のビット長以上のビット数取得しても問題が起こらないようにする 67 | value = (bitLength << 16) | i; 68 | for (j = reversed; j < size; j += skip) { 69 | table[j] = value; 70 | } 71 | 72 | ++code; 73 | } 74 | } 75 | 76 | // 次のビット長へ 77 | ++bitLength; 78 | code <<= 1; 79 | skip <<= 1; 80 | } 81 | 82 | return [table, maxCodeLength, minCodeLength]; 83 | } 84 | 85 | /* vim:set expandtab ts=2 sw=2 tw=80: */ 86 | -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * This object contains configuration values. 20 | * @requires enums 21 | * @property {Integer} prefer_hash_algorithm 22 | * @property {Integer} encryption_cipher 23 | * @property {Integer} compression 24 | * @property {Boolean} show_version 25 | * @property {Boolean} show_comment 26 | * @property {Boolean} integrity_protect 27 | * @property {String} keyserver 28 | * @property {Boolean} debug If enabled, debug messages will be printed 29 | * @module config/config 30 | */ 31 | 32 | 'use strict'; 33 | 34 | import enums from '../enums.js'; 35 | 36 | export default { 37 | prefer_hash_algorithm: enums.hash.sha256, 38 | encryption_cipher: enums.symmetric.aes256, 39 | compression: enums.compression.zip, 40 | integrity_protect: true, // use integrity protection for symmetric encryption 41 | ignore_mdc_error: false, // fail on decrypt if message is not integrity protected 42 | rsa_blinding: true, 43 | useNative: true, // use native node.js crypto and Web Crypto apis (if available) 44 | zeroCopy: false, // use transferable objects between the Web Worker and main thread 45 | debug: false, 46 | show_version: true, 47 | show_comment: true, 48 | versionstring: "React-Native-OpenPGP.js 0.1", 49 | commentstring: "http://openpgpjs.org", 50 | keyserver: "https://keyserver.ubuntu.com", 51 | node_store: './openpgp.store' 52 | }; 53 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @see module:config/config 3 | * @module config 4 | */ 5 | 6 | 'use strict'; 7 | 8 | export { default } from './config.js'; -------------------------------------------------------------------------------- /src/config/localStorage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This object storing and retrieving configuration from HTML5 local storage. 3 | * @module config/localStorage 4 | */ 5 | 6 | 'use strict'; 7 | 8 | /** 9 | * @constructor 10 | */ 11 | export default function LocalStorage() {} 12 | 13 | /** 14 | * Reads the config out of the HTML5 local storage 15 | * and initializes the object config. 16 | * if config is null the default config will be used 17 | */ 18 | LocalStorage.prototype.read = function () { 19 | var raw = window.localStorage.getItem("config"); 20 | var cf = (raw === null ? null : JSON.parse(raw)); 21 | if (cf === null) { 22 | this.config = this.default_config; 23 | this.write(); 24 | } else { 25 | this.config = cf; 26 | } 27 | }; 28 | 29 | /** 30 | * Writes the config to HTML5 local storage 31 | */ 32 | LocalStorage.prototype.write = function () { 33 | window.localStorage.setItem("config", JSON.stringify(this.config)); 34 | }; 35 | -------------------------------------------------------------------------------- /src/crypto/cipher/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @requires crypto/cipher/aes 3 | * @requires crypto/cipher/blowfish 4 | * @requires crypto/cipher/cast5 5 | * @requires crypto/cipher/twofish 6 | * @module crypto/cipher 7 | */ 8 | 9 | 'use strict'; 10 | 11 | import desModule from './des.js'; 12 | import aes from './aes.js'; 13 | import cast5 from './cast5.js'; 14 | import twofish from './twofish.js'; 15 | import blowfish from './blowfish.js'; 16 | 17 | export default { 18 | /** @see module:crypto/cipher/aes */ 19 | aes128: aes[128], 20 | aes192: aes[192], 21 | aes256: aes[256], 22 | /** @see module:crypto/cipher/des.originalDes */ 23 | des: desModule.originalDes, 24 | /** @see module:crypto/cipher/des.des */ 25 | tripledes: desModule.des, 26 | /** @see module:crypto/cipher/cast5 */ 27 | cast5: cast5, 28 | /** @see module:crypto/cipher/twofish */ 29 | twofish: twofish, 30 | /** @see module:crypto/cipher/blowfish */ 31 | blowfish: blowfish, 32 | /** Not implemented */ 33 | idea: function() { 34 | throw new Error('IDEA symmetric-key algorithm not implemented'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/crypto/hash/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @requires crypto/hash/sha 3 | * @requires crypto/hash/md5 4 | * @requires crypto/hash/ripe-md 5 | * @requires util 6 | * @module crypto/hash 7 | */ 8 | 9 | 'use strict'; 10 | 11 | import sha from './sha.js'; 12 | import asmCrypto from 'asmcrypto-lite'; 13 | import Rusha from 'rusha'; 14 | import md5 from './md5.js'; 15 | import ripemd from './ripe-md.js'; 16 | import util from '../../util.js'; 17 | 18 | const rusha = new Rusha(), 19 | nodeCrypto = util.getNodeCrypto(), 20 | Buffer = util.getNodeBuffer(); 21 | 22 | function node_hash(type) { 23 | return function (data) { 24 | var shasum = nodeCrypto.createHash(type); 25 | shasum.update(new Buffer(data)); 26 | return new Uint8Array(shasum.digest()); 27 | }; 28 | } 29 | 30 | var hash_fns; 31 | if(nodeCrypto) { // Use Node native crypto for all hash functions 32 | 33 | hash_fns = { 34 | md5: node_hash('md5'), 35 | sha1: node_hash('sha1'), 36 | sha224: node_hash('sha224'), 37 | sha256: node_hash('sha256'), 38 | sha384: node_hash('sha384'), 39 | sha512: node_hash('sha512'), 40 | ripemd: node_hash('ripemd160') 41 | }; 42 | 43 | } else { // Use JS fallbacks 44 | 45 | hash_fns = { 46 | /** @see module:crypto/hash/md5 */ 47 | md5: md5, 48 | /** @see module:rusha */ 49 | sha1: function(data) { 50 | return util.str2Uint8Array(util.hex2bin(rusha.digest(data))); 51 | }, 52 | /** @see module:crypto/hash/sha.sha224 */ 53 | sha224: sha.sha224, 54 | /** @see module:asmcrypto */ 55 | sha256: asmCrypto.SHA256.bytes, 56 | /** @see module:crypto/hash/sha.sha384 */ 57 | sha384: sha.sha384, 58 | /** @see module:crypto/hash/sha.sha512 */ 59 | sha512: sha.sha512, 60 | /** @see module:crypto/hash/ripe-md */ 61 | ripemd: ripemd 62 | }; 63 | } 64 | 65 | export default { 66 | 67 | md5: hash_fns.md5, 68 | sha1: hash_fns.sha1, 69 | sha224: hash_fns.sha224, 70 | sha256: hash_fns.sha256, 71 | sha384: hash_fns.sha384, 72 | sha512: hash_fns.sha512, 73 | ripemd: hash_fns.ripemd, 74 | 75 | /** 76 | * Create a hash on the specified data using the specified algorithm 77 | * @param {module:enums.hash} algo Hash algorithm type (see {@link http://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}) 78 | * @param {Uint8Array} data Data to be hashed 79 | * @return {Uint8Array} hash value 80 | */ 81 | digest: function(algo, data) { 82 | switch (algo) { 83 | case 1: 84 | // - MD5 [HAC] 85 | return this.md5(data); 86 | case 2: 87 | // - SHA-1 [FIPS180] 88 | return this.sha1(data); 89 | case 3: 90 | // - RIPE-MD/160 [HAC] 91 | return this.ripemd(data); 92 | case 8: 93 | // - SHA256 [FIPS180] 94 | return this.sha256(data); 95 | case 9: 96 | // - SHA384 [FIPS180] 97 | return this.sha384(data); 98 | case 10: 99 | // - SHA512 [FIPS180] 100 | return this.sha512(data); 101 | case 11: 102 | // - SHA224 [FIPS180] 103 | return this.sha224(data); 104 | default: 105 | throw new Error('Invalid hash function.'); 106 | } 107 | }, 108 | 109 | /** 110 | * Returns the hash size in bytes of the specified hash algorithm type 111 | * @param {module:enums.hash} algo Hash algorithm type (See {@link http://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}) 112 | * @return {Integer} Size in bytes of the resulting hash 113 | */ 114 | getHashByteLength: function(algo) { 115 | switch (algo) { 116 | case 1: 117 | // - MD5 [HAC] 118 | return 16; 119 | case 2: 120 | // - SHA-1 [FIPS180] 121 | case 3: 122 | // - RIPE-MD/160 [HAC] 123 | return 20; 124 | case 8: 125 | // - SHA256 [FIPS180] 126 | return 32; 127 | case 9: 128 | // - SHA384 [FIPS180] 129 | return 48; 130 | case 10: 131 | // - SHA512 [FIPS180] 132 | return 64; 133 | case 11: 134 | // - SHA224 [FIPS180] 135 | return 28; 136 | default: 137 | throw new Error('Invalid hash algorithm.'); 138 | } 139 | } 140 | }; 141 | -------------------------------------------------------------------------------- /src/crypto/hash/md5.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A fast MD5 JavaScript implementation 3 | * Copyright (c) 2012 Joseph Myers 4 | * http://www.myersdaily.org/joseph/javascript/md5-text.html 5 | * 6 | * Permission to use, copy, modify, and distribute this software 7 | * and its documentation for any purposes and without 8 | * fee is hereby granted provided that this copyright notice 9 | * appears in all copies. 10 | * 11 | * Of course, this soft is provided "as is" without express or implied 12 | * warranty of any kind. 13 | */ 14 | 15 | /** 16 | * @requires util 17 | * @module crypto/hash/md5 18 | */ 19 | 20 | 'use strict'; 21 | 22 | import util from '../../util.js'; 23 | 24 | /** 25 | * MD5 hash 26 | * @param {String} entree string to hash 27 | */ 28 | export default function(entree) { 29 | var hex = md5(util.Uint8Array2str(entree)); 30 | var bin = util.str2Uint8Array(util.hex2bin(hex)); 31 | return bin; 32 | } 33 | 34 | function md5cycle(x, k) { 35 | var a = x[0], 36 | b = x[1], 37 | c = x[2], 38 | d = x[3]; 39 | 40 | a = ff(a, b, c, d, k[0], 7, -680876936); 41 | d = ff(d, a, b, c, k[1], 12, -389564586); 42 | c = ff(c, d, a, b, k[2], 17, 606105819); 43 | b = ff(b, c, d, a, k[3], 22, -1044525330); 44 | a = ff(a, b, c, d, k[4], 7, -176418897); 45 | d = ff(d, a, b, c, k[5], 12, 1200080426); 46 | c = ff(c, d, a, b, k[6], 17, -1473231341); 47 | b = ff(b, c, d, a, k[7], 22, -45705983); 48 | a = ff(a, b, c, d, k[8], 7, 1770035416); 49 | d = ff(d, a, b, c, k[9], 12, -1958414417); 50 | c = ff(c, d, a, b, k[10], 17, -42063); 51 | b = ff(b, c, d, a, k[11], 22, -1990404162); 52 | a = ff(a, b, c, d, k[12], 7, 1804603682); 53 | d = ff(d, a, b, c, k[13], 12, -40341101); 54 | c = ff(c, d, a, b, k[14], 17, -1502002290); 55 | b = ff(b, c, d, a, k[15], 22, 1236535329); 56 | 57 | a = gg(a, b, c, d, k[1], 5, -165796510); 58 | d = gg(d, a, b, c, k[6], 9, -1069501632); 59 | c = gg(c, d, a, b, k[11], 14, 643717713); 60 | b = gg(b, c, d, a, k[0], 20, -373897302); 61 | a = gg(a, b, c, d, k[5], 5, -701558691); 62 | d = gg(d, a, b, c, k[10], 9, 38016083); 63 | c = gg(c, d, a, b, k[15], 14, -660478335); 64 | b = gg(b, c, d, a, k[4], 20, -405537848); 65 | a = gg(a, b, c, d, k[9], 5, 568446438); 66 | d = gg(d, a, b, c, k[14], 9, -1019803690); 67 | c = gg(c, d, a, b, k[3], 14, -187363961); 68 | b = gg(b, c, d, a, k[8], 20, 1163531501); 69 | a = gg(a, b, c, d, k[13], 5, -1444681467); 70 | d = gg(d, a, b, c, k[2], 9, -51403784); 71 | c = gg(c, d, a, b, k[7], 14, 1735328473); 72 | b = gg(b, c, d, a, k[12], 20, -1926607734); 73 | 74 | a = hh(a, b, c, d, k[5], 4, -378558); 75 | d = hh(d, a, b, c, k[8], 11, -2022574463); 76 | c = hh(c, d, a, b, k[11], 16, 1839030562); 77 | b = hh(b, c, d, a, k[14], 23, -35309556); 78 | a = hh(a, b, c, d, k[1], 4, -1530992060); 79 | d = hh(d, a, b, c, k[4], 11, 1272893353); 80 | c = hh(c, d, a, b, k[7], 16, -155497632); 81 | b = hh(b, c, d, a, k[10], 23, -1094730640); 82 | a = hh(a, b, c, d, k[13], 4, 681279174); 83 | d = hh(d, a, b, c, k[0], 11, -358537222); 84 | c = hh(c, d, a, b, k[3], 16, -722521979); 85 | b = hh(b, c, d, a, k[6], 23, 76029189); 86 | a = hh(a, b, c, d, k[9], 4, -640364487); 87 | d = hh(d, a, b, c, k[12], 11, -421815835); 88 | c = hh(c, d, a, b, k[15], 16, 530742520); 89 | b = hh(b, c, d, a, k[2], 23, -995338651); 90 | 91 | a = ii(a, b, c, d, k[0], 6, -198630844); 92 | d = ii(d, a, b, c, k[7], 10, 1126891415); 93 | c = ii(c, d, a, b, k[14], 15, -1416354905); 94 | b = ii(b, c, d, a, k[5], 21, -57434055); 95 | a = ii(a, b, c, d, k[12], 6, 1700485571); 96 | d = ii(d, a, b, c, k[3], 10, -1894986606); 97 | c = ii(c, d, a, b, k[10], 15, -1051523); 98 | b = ii(b, c, d, a, k[1], 21, -2054922799); 99 | a = ii(a, b, c, d, k[8], 6, 1873313359); 100 | d = ii(d, a, b, c, k[15], 10, -30611744); 101 | c = ii(c, d, a, b, k[6], 15, -1560198380); 102 | b = ii(b, c, d, a, k[13], 21, 1309151649); 103 | a = ii(a, b, c, d, k[4], 6, -145523070); 104 | d = ii(d, a, b, c, k[11], 10, -1120210379); 105 | c = ii(c, d, a, b, k[2], 15, 718787259); 106 | b = ii(b, c, d, a, k[9], 21, -343485551); 107 | 108 | x[0] = add32(a, x[0]); 109 | x[1] = add32(b, x[1]); 110 | x[2] = add32(c, x[2]); 111 | x[3] = add32(d, x[3]); 112 | 113 | } 114 | 115 | function cmn(q, a, b, x, s, t) { 116 | a = add32(add32(a, q), add32(x, t)); 117 | return add32((a << s) | (a >>> (32 - s)), b); 118 | } 119 | 120 | function ff(a, b, c, d, x, s, t) { 121 | return cmn((b & c) | ((~b) & d), a, b, x, s, t); 122 | } 123 | 124 | function gg(a, b, c, d, x, s, t) { 125 | return cmn((b & d) | (c & (~d)), a, b, x, s, t); 126 | } 127 | 128 | function hh(a, b, c, d, x, s, t) { 129 | return cmn(b ^ c ^ d, a, b, x, s, t); 130 | } 131 | 132 | function ii(a, b, c, d, x, s, t) { 133 | return cmn(c ^ (b | (~d)), a, b, x, s, t); 134 | } 135 | 136 | function md51(s) { 137 | var n = s.length, 138 | state = [1732584193, -271733879, -1732584194, 271733878], 139 | i; 140 | for (i = 64; i <= s.length; i += 64) { 141 | md5cycle(state, md5blk(s.substring(i - 64, i))); 142 | } 143 | s = s.substring(i - 64); 144 | var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 145 | for (i = 0; i < s.length; i++) { 146 | tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); 147 | } 148 | tail[i >> 2] |= 0x80 << ((i % 4) << 3); 149 | if (i > 55) { 150 | md5cycle(state, tail); 151 | for (i = 0; i < 16; i++) { 152 | tail[i] = 0; 153 | } 154 | } 155 | tail[14] = n * 8; 156 | md5cycle(state, tail); 157 | return state; 158 | } 159 | 160 | /* there needs to be support for Unicode here, 161 | * unless we pretend that we can redefine the MD-5 162 | * algorithm for multi-byte characters (perhaps 163 | * by adding every four 16-bit characters and 164 | * shortening the sum to 32 bits). Otherwise 165 | * I suggest performing MD-5 as if every character 166 | * was two bytes--e.g., 0040 0025 = @%--but then 167 | * how will an ordinary MD-5 sum be matched? 168 | * There is no way to standardize text to something 169 | * like UTF-8 before transformation; speed cost is 170 | * utterly prohibitive. The JavaScript standard 171 | * itself needs to look at this: it should start 172 | * providing access to strings as preformed UTF-8 173 | * 8-bit unsigned value arrays. 174 | */ 175 | function md5blk(s) { /* I figured global was faster. */ 176 | var md5blks = [], 177 | i; /* Andy King said do it this way. */ 178 | for (i = 0; i < 64; i += 4) { 179 | md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 180 | 24); 181 | } 182 | return md5blks; 183 | } 184 | 185 | var hex_chr = '0123456789abcdef'.split(''); 186 | 187 | function rhex(n) { 188 | var s = '', 189 | j = 0; 190 | for (; j < 4; j++) { 191 | s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; 192 | } 193 | return s; 194 | } 195 | 196 | function hex(x) { 197 | for (var i = 0; i < x.length; i++) { 198 | x[i] = rhex(x[i]); 199 | } 200 | return x.join(''); 201 | } 202 | 203 | function md5(s) { 204 | return hex(md51(s)); 205 | } 206 | 207 | /* this function is much faster, 208 | so if possible we use it. Some IEs 209 | are the only ones I know of that 210 | need the idiotic second function, 211 | generated by an if clause. */ 212 | 213 | function add32(a, b) { 214 | return (a + b) & 0xFFFFFFFF; 215 | } 216 | -------------------------------------------------------------------------------- /src/crypto/hash/ripe-md.js: -------------------------------------------------------------------------------- 1 | /* 2 | * CryptoMX Tools 3 | * Copyright (C) 2004 - 2006 Derek Buitenhuis 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | /* Modified by Recurity Labs GmbH 21 | */ 22 | 23 | /* Modified by ProtonTech AG 24 | */ 25 | 26 | /** 27 | * @requires util 28 | * @module crypto/hash/ripe-md 29 | */ 30 | 31 | import util from '../../util.js'; 32 | 33 | var RMDsize = 160; 34 | var X = []; 35 | 36 | function ROL(x, n) { 37 | return new Number((x << n) | (x >>> (32 - n))); 38 | } 39 | 40 | function F(x, y, z) { 41 | return new Number(x ^ y ^ z); 42 | } 43 | 44 | function G(x, y, z) { 45 | return new Number((x & y) | (~x & z)); 46 | } 47 | 48 | function H(x, y, z) { 49 | return new Number((x | ~y) ^ z); 50 | } 51 | 52 | function I(x, y, z) { 53 | return new Number((x & z) | (y & ~z)); 54 | } 55 | 56 | function J(x, y, z) { 57 | return new Number(x ^ (y | ~z)); 58 | } 59 | 60 | function mixOneRound(a, b, c, d, e, x, s, roundNumber) { 61 | switch (roundNumber) { 62 | case 0: 63 | a += F(b, c, d) + x + 0x00000000; 64 | break; 65 | case 1: 66 | a += G(b, c, d) + x + 0x5a827999; 67 | break; 68 | case 2: 69 | a += H(b, c, d) + x + 0x6ed9eba1; 70 | break; 71 | case 3: 72 | a += I(b, c, d) + x + 0x8f1bbcdc; 73 | break; 74 | case 4: 75 | a += J(b, c, d) + x + 0xa953fd4e; 76 | break; 77 | case 5: 78 | a += J(b, c, d) + x + 0x50a28be6; 79 | break; 80 | case 6: 81 | a += I(b, c, d) + x + 0x5c4dd124; 82 | break; 83 | case 7: 84 | a += H(b, c, d) + x + 0x6d703ef3; 85 | break; 86 | case 8: 87 | a += G(b, c, d) + x + 0x7a6d76e9; 88 | break; 89 | case 9: 90 | a += F(b, c, d) + x + 0x00000000; 91 | break; 92 | 93 | default: 94 | throw new Error("Bogus round number"); 95 | break; 96 | } 97 | 98 | a = ROL(a, s) + e; 99 | c = ROL(c, 10); 100 | 101 | a &= 0xffffffff; 102 | b &= 0xffffffff; 103 | c &= 0xffffffff; 104 | d &= 0xffffffff; 105 | e &= 0xffffffff; 106 | 107 | var retBlock = []; 108 | retBlock[0] = a; 109 | retBlock[1] = b; 110 | retBlock[2] = c; 111 | retBlock[3] = d; 112 | retBlock[4] = e; 113 | retBlock[5] = x; 114 | retBlock[6] = s; 115 | 116 | return retBlock; 117 | } 118 | 119 | function MDinit(MDbuf) { 120 | MDbuf[0] = 0x67452301; 121 | MDbuf[1] = 0xefcdab89; 122 | MDbuf[2] = 0x98badcfe; 123 | MDbuf[3] = 0x10325476; 124 | MDbuf[4] = 0xc3d2e1f0; 125 | } 126 | 127 | var ROLs = [ 128 | [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], 129 | [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12], 130 | [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5], 131 | [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12], 132 | [9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6], 133 | [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6], 134 | [9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11], 135 | [9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5], 136 | [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8], 137 | [8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11] 138 | ]; 139 | 140 | var indexes = [ 141 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 142 | [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8], 143 | [3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12], 144 | [1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2], 145 | [4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13], 146 | [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12], 147 | [6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2], 148 | [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13], 149 | [8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14], 150 | [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11] 151 | ]; 152 | 153 | function compress(MDbuf, X) { 154 | var blockA = []; 155 | var blockB = []; 156 | 157 | var retBlock; 158 | 159 | var i, j; 160 | 161 | for (i = 0; i < 5; i++) { 162 | blockA[i] = new Number(MDbuf[i]); 163 | blockB[i] = new Number(MDbuf[i]); 164 | } 165 | 166 | var step = 0; 167 | for (j = 0; j < 5; j++) { 168 | for (i = 0; i < 16; i++) { 169 | retBlock = mixOneRound( 170 | blockA[(step + 0) % 5], 171 | blockA[(step + 1) % 5], 172 | blockA[(step + 2) % 5], 173 | blockA[(step + 3) % 5], 174 | blockA[(step + 4) % 5], 175 | X[indexes[j][i]], 176 | ROLs[j][i], 177 | j); 178 | 179 | blockA[(step + 0) % 5] = retBlock[0]; 180 | blockA[(step + 1) % 5] = retBlock[1]; 181 | blockA[(step + 2) % 5] = retBlock[2]; 182 | blockA[(step + 3) % 5] = retBlock[3]; 183 | blockA[(step + 4) % 5] = retBlock[4]; 184 | 185 | step += 4; 186 | } 187 | } 188 | 189 | step = 0; 190 | for (j = 5; j < 10; j++) { 191 | for (i = 0; i < 16; i++) { 192 | retBlock = mixOneRound( 193 | blockB[(step + 0) % 5], 194 | blockB[(step + 1) % 5], 195 | blockB[(step + 2) % 5], 196 | blockB[(step + 3) % 5], 197 | blockB[(step + 4) % 5], 198 | X[indexes[j][i]], 199 | ROLs[j][i], 200 | j); 201 | 202 | blockB[(step + 0) % 5] = retBlock[0]; 203 | blockB[(step + 1) % 5] = retBlock[1]; 204 | blockB[(step + 2) % 5] = retBlock[2]; 205 | blockB[(step + 3) % 5] = retBlock[3]; 206 | blockB[(step + 4) % 5] = retBlock[4]; 207 | 208 | step += 4; 209 | } 210 | } 211 | 212 | blockB[3] += blockA[2] + MDbuf[1]; 213 | MDbuf[1] = MDbuf[2] + blockA[3] + blockB[4]; 214 | MDbuf[2] = MDbuf[3] + blockA[4] + blockB[0]; 215 | MDbuf[3] = MDbuf[4] + blockA[0] + blockB[1]; 216 | MDbuf[4] = MDbuf[0] + blockA[1] + blockB[2]; 217 | MDbuf[0] = blockB[3]; 218 | } 219 | 220 | function zeroX(X) { 221 | for (var i = 0; i < 16; i++) { 222 | X[i] = 0; 223 | } 224 | } 225 | 226 | function MDfinish(MDbuf, strptr, lswlen, mswlen) { 227 | var X = new Array(16); 228 | zeroX(X); 229 | 230 | var j = 0; 231 | for (var i = 0; i < (lswlen & 63); i++) { 232 | X[i >>> 2] ^= (strptr.charCodeAt(j++) & 255) << (8 * (i & 3)); 233 | } 234 | 235 | X[(lswlen >>> 2) & 15] ^= 1 << (8 * (lswlen & 3) + 7); 236 | 237 | if ((lswlen & 63) > 55) { 238 | compress(MDbuf, X); 239 | X = new Array(16); 240 | zeroX(X); 241 | } 242 | 243 | X[14] = lswlen << 3; 244 | X[15] = (lswlen >>> 29) | (mswlen << 3); 245 | 246 | compress(MDbuf, X); 247 | } 248 | 249 | function BYTES_TO_DWORD(fourChars) { 250 | var tmp = (fourChars.charCodeAt(3) & 255) << 24; 251 | tmp |= (fourChars.charCodeAt(2) & 255) << 16; 252 | tmp |= (fourChars.charCodeAt(1) & 255) << 8; 253 | tmp |= (fourChars.charCodeAt(0) & 255); 254 | 255 | return tmp; 256 | } 257 | 258 | function RMD(message) { 259 | var MDbuf = new Array(RMDsize / 32); 260 | var hashcode = new Array(RMDsize / 8); 261 | var length; 262 | var nbytes; 263 | 264 | MDinit(MDbuf); 265 | length = message.length; 266 | 267 | var X = new Array(16); 268 | zeroX(X); 269 | 270 | var i, j = 0; 271 | for (nbytes = length; nbytes > 63; nbytes -= 64) { 272 | for (i = 0; i < 16; i++) { 273 | X[i] = BYTES_TO_DWORD(message.substr(j, 4)); 274 | j += 4; 275 | } 276 | compress(MDbuf, X); 277 | } 278 | 279 | MDfinish(MDbuf, message.substr(j), length, 0); 280 | 281 | for (i = 0; i < RMDsize / 8; i += 4) { 282 | hashcode[i] = MDbuf[i >>> 2] & 255; 283 | hashcode[i + 1] = (MDbuf[i >>> 2] >>> 8) & 255; 284 | hashcode[i + 2] = (MDbuf[i >>> 2] >>> 16) & 255; 285 | hashcode[i + 3] = (MDbuf[i >>> 2] >>> 24) & 255; 286 | } 287 | 288 | return hashcode; 289 | } 290 | 291 | 292 | export default function RMDstring(message) { 293 | var hashcode = RMD(util.Uint8Array2str(message)); 294 | var retString = ""; 295 | 296 | for (var i = 0; i < RMDsize / 8; i++) { 297 | retString += String.fromCharCode(hashcode[i]); 298 | } 299 | 300 | return util.str2Uint8Array(retString); 301 | } 302 | -------------------------------------------------------------------------------- /src/crypto/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @see module:crypto/crypto 3 | * @module crypto 4 | */ 5 | 6 | 'use strict'; 7 | 8 | import cipher from './cipher'; 9 | import hash from './hash'; 10 | import cfb from './cfb'; 11 | import publicKey from './public_key'; 12 | import signature from './signature'; 13 | import random from './random'; 14 | import pkcs1 from './pkcs1'; 15 | import crypto from './crypto.js'; 16 | 17 | const mod = { 18 | /** @see module:crypto/cipher */ 19 | cipher: cipher, 20 | /** @see module:crypto/hash */ 21 | hash: hash, 22 | /** @see module:crypto/cfb */ 23 | cfb: cfb, 24 | /** @see module:crypto/public_key */ 25 | publicKey: publicKey, 26 | /** @see module:crypto/signature */ 27 | signature: signature, 28 | /** @see module:crypto/random */ 29 | random: random, 30 | /** @see module:crypto/pkcs1 */ 31 | pkcs1: pkcs1, 32 | }; 33 | 34 | for (var i in crypto) { 35 | mod[i] = crypto[i]; 36 | } 37 | 38 | export default mod; 39 | -------------------------------------------------------------------------------- /src/crypto/isaac.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------- 2 | * Copyright (c) 2012 Yves-Marie K. Rinquin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | * ---------------------------------------------------------------------- 24 | * 25 | * ISAAC is a cryptographically secure pseudo-random number generator 26 | * (or CSPRNG for short) designed by Robert J. Jenkins Jr. in 1996 and 27 | * based on RC4. It is designed for speed and security. 28 | * 29 | * ISAAC's informations & analysis: 30 | * http://burtleburtle.net/bob/rand/isaac.html 31 | * ISAAC's implementation details: 32 | * http://burtleburtle.net/bob/rand/isaacafa.html 33 | * 34 | * ISAAC succesfully passed TestU01 35 | * 36 | * ---------------------------------------------------------------------- 37 | * 38 | * Usage: 39 | * 40 | * var random_number = isaac.random(); 41 | * 42 | * Output: [ 0x00000000; 0xffffffff] 43 | * [-2147483648; 2147483647] 44 | * 45 | */ 46 | 47 | /* private: internal states */ 48 | var m = Array(256), // internal memory 49 | acc = 0, // accumulator 50 | brs = 0, // last result 51 | cnt = 0, // counter 52 | r = Array(256), // result array 53 | gnt = 0; // generation counter 54 | 55 | 56 | /* isaac module pattern */ 57 | export function Isaac() { 58 | this.seed(Math.random() * 0xffffffff); 59 | } 60 | 61 | /* private: 32-bit integer safe adder */ 62 | function add(x, y) { 63 | var lsb = (x & 0xffff) + (y & 0xffff); 64 | var msb = (x >>> 16) + (y >>> 16) + (lsb >>> 16); 65 | return (msb << 16) | (lsb & 0xffff); 66 | } 67 | 68 | /* public: initialisation */ 69 | Isaac.prototype.reset = function() { 70 | acc = brs = cnt = 0; 71 | for(var i = 0; i < 256; ++i) { 72 | m[i] = r[i] = 0; 73 | } 74 | gnt = 0; 75 | }; 76 | 77 | /* public: seeding function */ 78 | Isaac.prototype.seed = function(s) { 79 | var a, b, c, d, e, f, g, h, i; 80 | 81 | /* seeding the seeds of love */ 82 | a = b = c = d = 83 | e = f = g = h = 0x9e3779b9; /* the golden ratio */ 84 | 85 | if(s && typeof(s) === 'string') { 86 | s = s.toIntArray(); 87 | } 88 | 89 | if(s && typeof(s) === 'number') { 90 | s = [s]; 91 | } 92 | 93 | if(s instanceof Array) { 94 | this.reset(); 95 | for(i = 0; i < s.length; i++) { 96 | r[i & 0xff] += (typeof(s[i]) === 'number') ? s[i] : 0; 97 | } 98 | } 99 | 100 | /* private: seed mixer */ 101 | function seed_mix() { 102 | a ^= b << 11; d = add(d, a); b = add(b, c); 103 | b ^= c >>> 2; e = add(e, b); c = add(c, d); 104 | c ^= d << 8; f = add(f, c); d = add(d, e); 105 | d ^= e >>> 16; g = add(g, d); e = add(e, f); 106 | e ^= f << 10; h = add(h, e); f = add(f, g); 107 | f ^= g >>> 4; a = add(a, f); g = add(g, h); 108 | g ^= h << 8; b = add(b, g); h = add(h, a); 109 | h ^= a >>> 9; c = add(c, h); a = add(a, b); 110 | } 111 | 112 | for(i = 0; i < 4; i++) { /* scramble it */ 113 | seed_mix(); 114 | } 115 | 116 | for(i = 0; i < 256; i += 8) { 117 | if(s) { /* use all the information in the seed */ 118 | a = add(a, r[i + 0]); b = add(b, r[i + 1]); 119 | c = add(c, r[i + 2]); d = add(d, r[i + 3]); 120 | e = add(e, r[i + 4]); f = add(f, r[i + 5]); 121 | g = add(g, r[i + 6]); h = add(h, r[i + 7]); 122 | } 123 | seed_mix(); 124 | /* fill in m[] with messy stuff */ 125 | m[i + 0] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d; 126 | m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h; 127 | } 128 | if(s) { 129 | /* do a second pass to make all of the seed affect all of m[] */ 130 | for(i = 0; i < 256; i += 8) { 131 | a = add(a, m[i + 0]); b = add(b, m[i + 1]); 132 | c = add(c, m[i + 2]); d = add(d, m[i + 3]); 133 | e = add(e, m[i + 4]); f = add(f, m[i + 5]); 134 | g = add(g, m[i + 6]); h = add(h, m[i + 7]); 135 | seed_mix(); 136 | /* fill in m[] with messy stuff (again) */ 137 | m[i + 0] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d; 138 | m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h; 139 | } 140 | } 141 | 142 | this.prng(); /* fill in the first set of results */ 143 | gnt = 256 /* prepare to use the first set of results */; 144 | }; 145 | 146 | /* public: isaac generator, n = number of run */ 147 | Isaac.prototype.prng = function(n) { 148 | var i, x, y; 149 | 150 | n = (n && typeof(n) === 'number') ? Math.abs(Math.floor(n)) : 1; 151 | 152 | while(n--) { 153 | cnt = add(cnt, 1); 154 | brs = add(brs, cnt); 155 | 156 | for(i = 0; i < 256; i++) { 157 | switch(i & 3) { 158 | case 0: acc ^= acc << 13; break; 159 | case 1: acc ^= acc >>> 6; break; 160 | case 2: acc ^= acc << 2; break; 161 | case 3: acc ^= acc >>> 16; break; 162 | } 163 | acc = add(m[(i + 128) & 0xff], acc); x = m[i]; 164 | m[i] = y = add(m[(x >>> 2) & 0xff], add(acc, brs)); 165 | r[i] = brs = add(m[(y >>> 10) & 0xff], x); 166 | } 167 | } 168 | }; 169 | 170 | /* public: return a random number between */ 171 | Isaac.prototype.rand = function() { 172 | if(!gnt--) { 173 | this.prng(); gnt = 255; 174 | } 175 | return r[gnt]; 176 | }; 177 | 178 | /* public: return internals in an object*/ 179 | Isaac.prototype.internals = function() { 180 | return {a: acc, b: brs, c: cnt, m: m, r: r}; 181 | }; 182 | 183 | /* public: output*/ 184 | Isaac.prototype.random = function() { 185 | return 0.5 + this.rand() * 2.3283064365386963e-10; // 2^-32 186 | }; 187 | 188 | 189 | /* js string (ucs-2/utf16) to a 32-bit integer (utf-8 chars, little-endian) array */ 190 | String.prototype.toIntArray = function() { 191 | var w1, w2, u, r4 = [], r = [], i = 0; 192 | var s = this + '\0\0\0'; // pad string to avoid discarding last chars 193 | var l = s.length - 1; 194 | 195 | while(i < l) { 196 | w1 = s.charCodeAt(i++); 197 | w2 = s.charCodeAt(i+1); 198 | if (w1 < 0x0080) { 199 | // 0x0000 - 0x007f code point: basic ascii 200 | r4.push(w1); 201 | } else if(w1 < 0x0800) { 202 | // 0x0080 - 0x07ff code point 203 | r4.push(((w1 >>> 6) & 0x1f) | 0xc0); 204 | r4.push(((w1 >>> 0) & 0x3f) | 0x80); 205 | } else if((w1 & 0xf800) !== 0xd800) { 206 | // 0x0800 - 0xd7ff / 0xe000 - 0xffff code point 207 | r4.push(((w1 >>> 12) & 0x0f) | 0xe0); 208 | r4.push(((w1 >>> 6) & 0x3f) | 0x80); 209 | r4.push(((w1 >>> 0) & 0x3f) | 0x80); 210 | } else if(((w1 & 0xfc00) === 0xd800) && 211 | ((w2 & 0xfc00) === 0xdc00)) { 212 | // 0xd800 - 0xdfff surrogate / 0x10ffff - 0x10000 code point 213 | u = ((w2 & 0x3f) | ((w1 & 0x3f) << 10)) + 0x10000; 214 | r4.push(((u >>> 18) & 0x07) | 0xf0); 215 | r4.push(((u >>> 12) & 0x3f) | 0x80); 216 | r4.push(((u >>> 6) & 0x3f) | 0x80); 217 | r4.push(((u >>> 0) & 0x3f) | 0x80); 218 | i++; 219 | } else { 220 | // invalid char 221 | } 222 | /* add integer (four utf-8 value) to array */ 223 | if(r4.length > 3) { 224 | // little endian 225 | r.push((r4.shift() << 0) | (r4.shift() << 8) | 226 | (r4.shift() << 16) | (r4.shift() << 24)); 227 | } 228 | } 229 | 230 | return r; 231 | }; 232 | -------------------------------------------------------------------------------- /src/crypto/pkcs1.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * PKCS1 encoding 20 | * @requires crypto/crypto 21 | * @requires crypto/hash 22 | * @requires crypto/public_key/jsbn 23 | * @requires crypto/random 24 | * @requires util 25 | * @module crypto/pkcs1 26 | */ 27 | 28 | 'use strict'; 29 | 30 | import random from './random.js'; 31 | import util from '../util.js'; 32 | import BigInteger from './public_key/jsbn.js'; 33 | import hash from './hash'; 34 | 35 | /** 36 | * ASN1 object identifiers for hashes (See {@link http://tools.ietf.org/html/rfc4880#section-5.2.2}) 37 | */ 38 | var hash_headers = []; 39 | hash_headers[1] = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 40 | 0x10 41 | ]; 42 | hash_headers[2] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14]; 43 | hash_headers[3] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14]; 44 | hash_headers[8] = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 45 | 0x04, 0x20 46 | ]; 47 | hash_headers[9] = [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 48 | 0x04, 0x30 49 | ]; 50 | hash_headers[10] = [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 51 | 0x00, 0x04, 0x40 52 | ]; 53 | hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 54 | 0x00, 0x04, 0x1C 55 | ]; 56 | 57 | /** 58 | * Create padding with secure random data 59 | * @private 60 | * @param {Integer} length Length of the padding in bytes 61 | * @return {String} Padding as string 62 | */ 63 | function getPkcs1Padding(length) { 64 | var result = ''; 65 | var randomByte; 66 | while (result.length < length) { 67 | randomByte = random.getSecureRandomOctet(); 68 | if (randomByte !== 0) { 69 | result += String.fromCharCode(randomByte); 70 | } 71 | } 72 | return result; 73 | } 74 | 75 | 76 | export default { 77 | eme: { 78 | /** 79 | * create a EME-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1}) 80 | * @param {String} M message to be encoded 81 | * @param {Integer} k the length in octets of the key modulus 82 | * @return {String} EME-PKCS1 padded message 83 | */ 84 | encode: function(M, k) { 85 | var mLen = M.length; 86 | // length checking 87 | if (mLen > k - 11) { 88 | throw new Error('Message too long'); 89 | } 90 | // Generate an octet string PS of length k - mLen - 3 consisting of 91 | // pseudo-randomly generated nonzero octets 92 | var PS = getPkcs1Padding(k - mLen - 3); 93 | // Concatenate PS, the message M, and other padding to form an 94 | // encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M. 95 | var EM = String.fromCharCode(0) + 96 | String.fromCharCode(2) + 97 | PS + 98 | String.fromCharCode(0) + 99 | M; 100 | return EM; 101 | }, 102 | /** 103 | * decodes a EME-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2}) 104 | * @param {String} EM encoded message, an octet string 105 | * @return {String} message, an octet string 106 | */ 107 | decode: function(EM) { 108 | // leading zeros truncated by jsbn 109 | if (EM.charCodeAt(0) !== 0) { 110 | EM = String.fromCharCode(0) + EM; 111 | } 112 | var firstOct = EM.charCodeAt(0); 113 | var secondOct = EM.charCodeAt(1); 114 | var i = 2; 115 | while (EM.charCodeAt(i) !== 0 && i < EM.length) { 116 | i++; 117 | } 118 | var psLen = i - 2; 119 | var separator = EM.charCodeAt(i++); 120 | if (firstOct === 0 && secondOct === 2 && psLen >= 8 && separator === 0) { 121 | return EM.substr(i); 122 | } else { 123 | throw new Error('Decryption error'); 124 | } 125 | } 126 | }, 127 | 128 | emsa: { 129 | /** 130 | * create a EMSA-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3}) 131 | * @param {Integer} algo Hash algorithm type used 132 | * @param {String} M message to be encoded 133 | * @param {Integer} emLen intended length in octets of the encoded message 134 | * @returns {String} encoded message 135 | */ 136 | encode: function(algo, M, emLen) { 137 | var i; 138 | // Apply the hash function to the message M to produce a hash value H 139 | var H = util.Uint8Array2str(hash.digest(algo, util.str2Uint8Array(M))); 140 | if (H.length !== hash.getHashByteLength(algo)) { 141 | throw new Error('Invalid hash length'); 142 | } 143 | // produce an ASN.1 DER value for the hash function used. 144 | // Let T be the full hash prefix 145 | var T = ''; 146 | for (i = 0; i < hash_headers[algo].length; i++) { 147 | T += String.fromCharCode(hash_headers[algo][i]); 148 | } 149 | // add hash value to prefix 150 | T += H; 151 | // and let tLen be the length in octets of T 152 | var tLen = T.length; 153 | if (emLen < tLen + 11) { 154 | throw new Error('Intended encoded message length too short'); 155 | } 156 | // an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xFF 157 | // The length of PS will be at least 8 octets 158 | var PS = ''; 159 | for (i = 0; i < (emLen - tLen - 3); i++) { 160 | PS += String.fromCharCode(0xff); 161 | } 162 | // Concatenate PS, the hash prefix T, and other padding to form the 163 | // encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T. 164 | var EM = String.fromCharCode(0x00) + 165 | String.fromCharCode(0x01) + 166 | PS + 167 | String.fromCharCode(0x00) + 168 | T; 169 | return new BigInteger(util.hexstrdump(EM), 16); 170 | } 171 | } 172 | }; 173 | -------------------------------------------------------------------------------- /src/crypto/public_key/dsa.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | // 18 | // A Digital signature algorithm implementation 19 | 20 | /** 21 | * @requires crypto/hash 22 | * @requires crypto/public_key/jsbn 23 | * @requires crypto/random 24 | * @requires util 25 | * @module crypto/public_key/dsa 26 | */ 27 | 28 | 'use strict'; 29 | 30 | import BigInteger from './jsbn.js'; 31 | import random from '../random.js'; 32 | import hashModule from '../hash'; 33 | import util from '../../util.js'; 34 | import config from '../../config'; 35 | 36 | export default function DSA() { 37 | // s1 = ((g**s) mod p) mod q 38 | // s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q) 39 | function sign(hashalgo, m, g, p, q, x) { 40 | // If the output size of the chosen hash is larger than the number of 41 | // bits of q, the hash result is truncated to fit by taking the number 42 | // of leftmost bits equal to the number of bits of q. This (possibly 43 | // truncated) hash function result is treated as a number and used 44 | // directly in the DSA signature algorithm. 45 | var hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength()); 46 | var hash = new BigInteger(util.hexstrdump(hashed_data), 16); 47 | // FIPS-186-4, section 4.6: 48 | // The values of r and s shall be checked to determine if r = 0 or s = 0. 49 | // If either r = 0 or s = 0, a new value of k shall be generated, and the 50 | // signature shall be recalculated. It is extremely unlikely that r = 0 51 | // or s = 0 if signatures are generated properly. 52 | var k, s1, s2; 53 | while (true) { 54 | k = random.getRandomBigIntegerInRange(BigInteger.ONE, q.subtract(BigInteger.ONE)); 55 | s1 = (g.modPow(k, p)).mod(q); 56 | s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q); 57 | if (s1 !== 0 && s2 !== 0) { 58 | break; 59 | } 60 | } 61 | var result = []; 62 | result[0] = s1.toMPI(); 63 | result[1] = s2.toMPI(); 64 | return result; 65 | } 66 | 67 | function select_hash_algorithm(q) { 68 | var usersetting = config.prefer_hash_algorithm; 69 | /* 70 | * 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash 71 | * 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash 72 | * 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash 73 | * 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash 74 | */ 75 | switch (Math.round(q.bitLength() / 8)) { 76 | case 20: 77 | // 1024 bit 78 | if (usersetting !== 2 && 79 | usersetting > 11 && 80 | usersetting !== 10 && 81 | usersetting < 8) { 82 | return 2; // prefer sha1 83 | } 84 | return usersetting; 85 | case 28: 86 | // 2048 bit 87 | if (usersetting > 11 && 88 | usersetting < 8) { 89 | return 11; 90 | } 91 | return usersetting; 92 | case 32: 93 | // 4096 bit // prefer sha224 94 | if (usersetting > 10 && 95 | usersetting < 8) { 96 | return 8; // prefer sha256 97 | } 98 | return usersetting; 99 | default: 100 | util.print_debug("DSA select hash algorithm: returning null for an unknown length of q"); 101 | return null; 102 | } 103 | } 104 | this.select_hash_algorithm = select_hash_algorithm; 105 | 106 | function verify(hashalgo, s1, s2, m, p, q, g, y) { 107 | var hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength()); 108 | var hash = new BigInteger(util.hexstrdump(hashed_data), 16); 109 | if (BigInteger.ZERO.compareTo(s1) >= 0 || 110 | s1.compareTo(q) >= 0 || 111 | BigInteger.ZERO.compareTo(s2) >= 0 || 112 | s2.compareTo(q) >= 0) { 113 | util.print_debug("invalid DSA Signature"); 114 | return null; 115 | } 116 | var w = s2.modInverse(q); 117 | if (BigInteger.ZERO.compareTo(w) === 0) { 118 | util.print_debug("invalid DSA Signature"); 119 | return null; 120 | } 121 | var u1 = hash.multiply(w).mod(q); 122 | var u2 = s1.multiply(w).mod(q); 123 | return g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q); 124 | } 125 | 126 | this.sign = sign; 127 | this.verify = verify; 128 | } 129 | -------------------------------------------------------------------------------- /src/crypto/public_key/elgamal.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | // 18 | // ElGamal implementation 19 | 20 | /** 21 | * @requires crypto/public_key/jsbn 22 | * @requires crypto/random 23 | * @requires util 24 | * @module crypto/public_key/elgamal 25 | */ 26 | 27 | 'use strict'; 28 | 29 | import BigInteger from './jsbn.js'; 30 | import random from '../random.js'; 31 | import util from '../../util.js'; 32 | 33 | export default function Elgamal() { 34 | 35 | function encrypt(m, g, p, y) { 36 | // choose k in {2,...,p-2} 37 | var pMinus2 = p.subtract(BigInteger.TWO); 38 | var k = random.getRandomBigIntegerInRange(BigInteger.ONE, pMinus2); 39 | k = k.mod(pMinus2).add(BigInteger.ONE); 40 | var c = []; 41 | c[0] = g.modPow(k, p); 42 | c[1] = y.modPow(k, p).multiply(m).mod(p); 43 | return c; 44 | } 45 | 46 | function decrypt(c1, c2, p, x) { 47 | util.print_debug("Elgamal Decrypt:\nc1:" + util.hexstrdump(c1.toMPI()) + "\n" + 48 | "c2:" + util.hexstrdump(c2.toMPI()) + "\n" + 49 | "p:" + util.hexstrdump(p.toMPI()) + "\n" + 50 | "x:" + util.hexstrdump(x.toMPI())); 51 | return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p); 52 | //var c = c1.pow(x).modInverse(p); // c0^-a mod p 53 | //return c.multiply(c2).mod(p); 54 | } 55 | 56 | // signing and signature verification using Elgamal is not required by OpenPGP. 57 | this.encrypt = encrypt; 58 | this.decrypt = decrypt; 59 | } 60 | -------------------------------------------------------------------------------- /src/crypto/public_key/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @requires crypto/public_key/dsa 3 | * @requires crypto/public_key/elgamal 4 | * @requires crypto/public_key/rsa 5 | * @module crypto/public_key 6 | */ 7 | 8 | 'use strict'; 9 | 10 | /** @see module:crypto/public_key/rsa */ 11 | import rsa from './rsa.js'; 12 | /** @see module:crypto/public_key/elgamal */ 13 | import elgamal from './elgamal.js'; 14 | /** @see module:crypto/public_key/dsa */ 15 | import dsa from './dsa.js'; 16 | 17 | export default { 18 | rsa: rsa, 19 | elgamal: elgamal, 20 | dsa: dsa 21 | }; 22 | -------------------------------------------------------------------------------- /src/crypto/random.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | // The GPG4Browsers crypto interface 19 | 20 | /** 21 | * @requires type/mpi 22 | * @requires util 23 | * @module crypto/random 24 | */ 25 | 26 | 'use strict'; 27 | 28 | import type_mpi from '../type/mpi.js'; 29 | import util from '../util.js'; 30 | import * as Isaac from './isaac.js'; 31 | const nodeCrypto = null; 32 | 33 | var randomizer = require('./randomBytes'); 34 | var isaacson = new Isaac.Isaac(); 35 | 36 | export default { 37 | /** 38 | * Generates random values 39 | */ 40 | generateRandomValues: function () { 41 | var promise = new Promise(function(success, failed) { 42 | try { 43 | randomizer.randomBytes(4096, (err, bytes) => { 44 | if (bytes) { 45 | isaacson.seed(bytes); 46 | success(); 47 | } else { 48 | failed(); 49 | } 50 | }); 51 | } catch(error) { 52 | failed(); 53 | } 54 | }); 55 | 56 | return promise; 57 | }, 58 | 59 | /** 60 | * Retrieve secure random byte array of the specified length 61 | * @param {Integer} length Length in bytes to generate 62 | * @return {Uint8Array} Random byte array 63 | */ 64 | getRandomBytes: function(length) { 65 | var result = new Uint8Array(length); 66 | for (var i = 0; i < length; i++) { 67 | result[i] = this.getSecureRandomOctet(); 68 | } 69 | return result; 70 | }, 71 | 72 | /** 73 | * Return a secure random number in the specified range 74 | * @param {Integer} from Min of the random number 75 | * @param {Integer} to Max of the random number (max 32bit) 76 | * @return {Integer} A secure random number 77 | */ 78 | getSecureRandom: function(from, to) { 79 | var randUint = this.getSecureRandomUint(); 80 | var bits = ((to - from)).toString(2).length; 81 | while ((randUint & (Math.pow(2, bits) - 1)) > (to - from)) { 82 | randUint = this.getSecureRandomUint(); 83 | } 84 | return from + (Math.abs(randUint & (Math.pow(2, bits) - 1))); 85 | }, 86 | 87 | getSecureRandomOctet: function() { 88 | var buf = new Uint8Array(1); 89 | this.getRandomValues(buf); 90 | return buf[0]; 91 | }, 92 | 93 | getSecureRandomUint: function() { 94 | var buf = new Uint8Array(4); 95 | var dv = new DataView(buf.buffer); 96 | this.getRandomValues(buf); 97 | return dv.getUint32(0); 98 | }, 99 | 100 | /** 101 | * Helper routine which calls platform specific crypto random generator 102 | * @param {Uint8Array} buf 103 | */ 104 | getRandomValues: function(buf) { 105 | if (!(buf instanceof Uint8Array)) { 106 | throw new Error('Invalid type: buf not an Uint8Array'); 107 | } 108 | if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) { 109 | window.crypto.getRandomValues(buf); 110 | } else if (typeof window !== 'undefined' && typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') { 111 | window.msCrypto.getRandomValues(buf); 112 | } else if (nodeCrypto) { 113 | var bytes = nodeCrypto.randomBytes(buf.length); 114 | buf.set(bytes); 115 | } else if (this.randomBuffer.buffer) { 116 | this.randomBuffer.get(buf); 117 | } else { 118 | for (var i = 0; i < buf.length; i++) { 119 | buf[i] = Math.floor((isaacson.random() * 255)); 120 | } 121 | } 122 | }, 123 | 124 | /** 125 | * Create a secure random big integer of bits length 126 | * @param {Integer} bits Bit length of the MPI to create 127 | * @return {BigInteger} Resulting big integer 128 | */ 129 | getRandomBigInteger: function(bits) { 130 | if (bits < 1) { 131 | throw new Error('Illegal parameter value: bits < 1'); 132 | } 133 | var numBytes = Math.floor((bits + 7) / 8); 134 | 135 | var randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes)); 136 | if (bits % 8 > 0) { 137 | 138 | randomBits = String.fromCharCode( 139 | (Math.pow(2, bits % 8) - 1) & 140 | randomBits.charCodeAt(0)) + 141 | randomBits.substring(1); 142 | } 143 | var mpi = new type_mpi(); 144 | mpi.fromBytes(randomBits); 145 | return mpi.toBigInteger(); 146 | }, 147 | 148 | getRandomBigIntegerInRange: function(min, max) { 149 | if (max.compareTo(min) <= 0) { 150 | throw new Error('Illegal parameter value: max <= min'); 151 | } 152 | 153 | var range = max.subtract(min); 154 | var r = this.getRandomBigInteger(range.bitLength()); 155 | while (r.compareTo(range) > 0) { 156 | r = this.getRandomBigInteger(range.bitLength()); 157 | } 158 | return min.add(r); 159 | }, 160 | 161 | randomBuffer: new RandomBuffer() 162 | 163 | }; 164 | 165 | /** 166 | * Buffer for secure random numbers 167 | */ 168 | function RandomBuffer() { 169 | this.buffer = null; 170 | this.size = null; 171 | } 172 | 173 | /** 174 | * Initialize buffer 175 | * @param {Integer} size size of buffer 176 | */ 177 | RandomBuffer.prototype.init = function(size) { 178 | this.buffer = new Uint8Array(size); 179 | this.size = 0; 180 | }; 181 | 182 | /** 183 | * Concat array of secure random numbers to buffer 184 | * @param {Uint8Array} buf 185 | */ 186 | RandomBuffer.prototype.set = function(buf) { 187 | if (!this.buffer) { 188 | throw new Error('RandomBuffer is not initialized'); 189 | } 190 | if (!(buf instanceof Uint8Array)) { 191 | throw new Error('Invalid type: buf not an Uint8Array'); 192 | } 193 | var freeSpace = this.buffer.length - this.size; 194 | if (buf.length > freeSpace) { 195 | buf = buf.subarray(0, freeSpace); 196 | } 197 | // set buf with offset old size of buffer 198 | this.buffer.set(buf, this.size); 199 | this.size += buf.length; 200 | }; 201 | 202 | /** 203 | * Take numbers out of buffer and copy to array 204 | * @param {Uint8Array} buf the destination array 205 | */ 206 | RandomBuffer.prototype.get = function(buf) { 207 | if (!this.buffer) { 208 | throw new Error('RandomBuffer is not initialized'); 209 | } 210 | if (!(buf instanceof Uint8Array)) { 211 | throw new Error('Invalid type: buf not an Uint8Array'); 212 | } 213 | if (this.size < buf.length) { 214 | throw new Error('Random number buffer depleted'); 215 | } 216 | for (var i = 0; i < buf.length; i++) { 217 | buf[i] = this.buffer[--this.size]; 218 | // clear buffer value 219 | this.buffer[this.size] = 0; 220 | } 221 | }; 222 | -------------------------------------------------------------------------------- /src/crypto/randomBytes.js: -------------------------------------------------------------------------------- 1 | if (typeof Buffer === 'undefined') { 2 | global.Buffer = require('buffer').Buffer 3 | } 4 | 5 | let RNOpenPGP = require('react-native').NativeModules.RNOpenPGP 6 | 7 | function noop () {} 8 | 9 | function toBuffer (nativeStr) { 10 | return new Buffer(nativeStr, 'base64') 11 | } 12 | 13 | export function randomBytes (length, cb) { 14 | if (!cb) { 15 | cb("Error: No callback function defined"); 16 | } 17 | 18 | RNOpenPGP.randomBytes(length, function(err, base64String) { 19 | if (err) { 20 | cb(err) 21 | } else { 22 | cb(null, toBuffer(base64String)) 23 | } 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/crypto/signature.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @requires util 3 | * @requires crypto/hash 4 | * @requires crypto/pkcs1 5 | * @requires crypto/public_key 6 | * @module crypto/signature */ 7 | 8 | 'use strict'; 9 | 10 | import util from '../util'; 11 | import publicKey from './public_key'; 12 | import pkcs1 from './pkcs1.js'; 13 | 14 | export default { 15 | /** 16 | * 17 | * @param {module:enums.publicKey} algo public Key algorithm 18 | * @param {module:enums.hash} hash_algo Hash algorithm 19 | * @param {Array} msg_MPIs Signature multiprecision integers 20 | * @param {Array} publickey_MPIs Public key multiprecision integers 21 | * @param {Uint8Array} data Data on where the signature was computed on. 22 | * @return {Boolean} true if signature (sig_data was equal to data over hash) 23 | */ 24 | verify: function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { 25 | var m; 26 | 27 | data = util.Uint8Array2str(data); 28 | 29 | switch (algo) { 30 | case 1: 31 | // RSA (Encrypt or Sign) [HAC] 32 | case 2: 33 | // RSA Encrypt-Only [HAC] 34 | case 3: 35 | // RSA Sign-Only [HAC] 36 | var rsa = new publicKey.rsa(); 37 | var n = publickey_MPIs[0].toBigInteger(); 38 | var k = publickey_MPIs[0].byteLength(); 39 | var e = publickey_MPIs[1].toBigInteger(); 40 | m = msg_MPIs[0].toBigInteger(); 41 | var EM = rsa.verify(m, e, n); 42 | var EM2 = pkcs1.emsa.encode(hash_algo, data, k); 43 | return EM.compareTo(EM2) === 0; 44 | case 16: 45 | // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] 46 | throw new Error("signing with Elgamal is not defined in the OpenPGP standard."); 47 | case 17: 48 | // DSA (Digital Signature Algorithm) [FIPS186] [HAC] 49 | var dsa = new publicKey.dsa(); 50 | var s1 = msg_MPIs[0].toBigInteger(); 51 | var s2 = msg_MPIs[1].toBigInteger(); 52 | var p = publickey_MPIs[0].toBigInteger(); 53 | var q = publickey_MPIs[1].toBigInteger(); 54 | var g = publickey_MPIs[2].toBigInteger(); 55 | var y = publickey_MPIs[3].toBigInteger(); 56 | m = data; 57 | var dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y); 58 | return dopublic.compareTo(s1) === 0; 59 | default: 60 | throw new Error('Invalid signature algorithm.'); 61 | } 62 | }, 63 | 64 | /** 65 | * Create a signature on data using the specified algorithm 66 | * @param {module:enums.hash} hash_algo hash Algorithm to use (See {@link http://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}) 67 | * @param {module:enums.publicKey} algo Asymmetric cipher algorithm to use (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) 68 | * @param {Array} publicMPIs Public key multiprecision integers 69 | * of the private key 70 | * @param {Array} secretMPIs Private key multiprecision 71 | * integers which is used to sign the data 72 | * @param {Uint8Array} data Data to be signed 73 | * @return {Array} 74 | */ 75 | sign: function(hash_algo, algo, keyIntegers, data) { 76 | 77 | data = util.Uint8Array2str(data); 78 | 79 | var m; 80 | 81 | switch (algo) { 82 | case 1: 83 | // RSA (Encrypt or Sign) [HAC] 84 | case 2: 85 | // RSA Encrypt-Only [HAC] 86 | case 3: 87 | // RSA Sign-Only [HAC] 88 | var rsa = new publicKey.rsa(); 89 | var d = keyIntegers[2].toBigInteger(); 90 | var n = keyIntegers[0].toBigInteger(); 91 | m = pkcs1.emsa.encode(hash_algo, 92 | data, keyIntegers[0].byteLength()); 93 | return util.str2Uint8Array(rsa.sign(m, d, n).toMPI()); 94 | 95 | case 17: 96 | // DSA (Digital Signature Algorithm) [FIPS186] [HAC] 97 | var dsa = new publicKey.dsa(); 98 | 99 | var p = keyIntegers[0].toBigInteger(); 100 | var q = keyIntegers[1].toBigInteger(); 101 | var g = keyIntegers[2].toBigInteger(); 102 | var x = keyIntegers[4].toBigInteger(); 103 | m = data; 104 | var result = dsa.sign(hash_algo, m, g, p, q, x); 105 | 106 | return util.str2Uint8Array(result[0].toString() + result[1].toString()); 107 | case 16: 108 | // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] 109 | throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.'); 110 | default: 111 | throw new Error('Invalid signature algorithm.'); 112 | } 113 | } 114 | }; 115 | -------------------------------------------------------------------------------- /src/encoding/base64.js: -------------------------------------------------------------------------------- 1 | /* OpenPGP radix-64/base64 string encoding/decoding 2 | * Copyright 2005 Herbert Hanewinkel, www.haneWIN.de 3 | * version 1.0, check www.haneWIN.de for the latest version 4 | * 5 | * This software is provided as-is, without express or implied warranty. 6 | * Permission to use, copy, modify, distribute or sell this software, with or 7 | * without fee, for any purpose and by any individual or organization, is hereby 8 | * granted, provided that the above copyright notice and this paragraph appear 9 | * in all copies. Distribution as a part of an application or binary must 10 | * include the above copyright notice in the documentation and/or other materials 11 | * provided with the application or distribution. 12 | */ 13 | 14 | /** 15 | * @module encoding/base64 16 | */ 17 | 18 | 'use strict'; 19 | 20 | var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 21 | 22 | /** 23 | * Convert binary array to radix-64 24 | * @param {Uint8Array} t Uint8Array to convert 25 | * @returns {string} radix-64 version of input string 26 | * @static 27 | */ 28 | function s2r(t, o) { 29 | // TODO check btoa alternative 30 | var a, c, n; 31 | var r = o ? o : [], 32 | l = 0, 33 | s = 0; 34 | var tl = t.length; 35 | 36 | for (n = 0; n < tl; n++) { 37 | c = t[n]; 38 | if (s === 0) { 39 | r.push(b64s.charAt((c >> 2) & 63)); 40 | a = (c & 3) << 4; 41 | } else if (s === 1) { 42 | r.push(b64s.charAt((a | (c >> 4) & 15))); 43 | a = (c & 15) << 2; 44 | } else if (s === 2) { 45 | r.push(b64s.charAt(a | ((c >> 6) & 3))); 46 | l += 1; 47 | if ((l % 60) === 0) { 48 | r.push("\n"); 49 | } 50 | r.push(b64s.charAt(c & 63)); 51 | } 52 | l += 1; 53 | if ((l % 60) === 0) { 54 | r.push("\n"); 55 | } 56 | 57 | s += 1; 58 | if (s === 3) { 59 | s = 0; 60 | } 61 | } 62 | if (s > 0) { 63 | r.push(b64s.charAt(a)); 64 | l += 1; 65 | if ((l % 60) === 0) { 66 | r.push("\n"); 67 | } 68 | r.push('='); 69 | l += 1; 70 | } 71 | if (s === 1) { 72 | if ((l % 60) === 0) { 73 | r.push("\n"); 74 | } 75 | r.push('='); 76 | } 77 | if (o) 78 | { 79 | return; 80 | } 81 | return r.join(''); 82 | } 83 | 84 | /** 85 | * Convert radix-64 to binary array 86 | * @param {String} t radix-64 string to convert 87 | * @returns {Uint8Array} binary array version of input string 88 | * @static 89 | */ 90 | function r2s(t) { 91 | // TODO check atob alternative 92 | var c, n; 93 | var r = [], 94 | s = 0, 95 | a = 0; 96 | var tl = t.length; 97 | 98 | for (n = 0; n < tl; n++) { 99 | c = b64s.indexOf(t.charAt(n)); 100 | if (c >= 0) { 101 | if (s) { 102 | r.push(a | (c >> (6 - s)) & 255); 103 | } 104 | s = (s + 2) & 7; 105 | a = (c << s) & 255; 106 | } 107 | } 108 | return new Uint8Array(r); 109 | } 110 | 111 | export default { 112 | encode: s2r, 113 | decode: r2s 114 | }; 115 | -------------------------------------------------------------------------------- /src/hkp.js: -------------------------------------------------------------------------------- 1 | // OpenPGP.js - An OpenPGP implementation in javascript 2 | // Copyright (C) 2015 Tankred Hase 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * @fileoverview This class implements a client for the OpenPGP HTTP Keyserver Protocol (HKP) 20 | * in order to lookup and upload keys on standard public key servers. 21 | */ 22 | 23 | 'use strict'; 24 | 25 | import config from './config'; 26 | 27 | /** 28 | * Initialize the HKP client and configure it with the key server url and fetch function. 29 | * @constructor 30 | * @param {String} keyServerBaseUrl (optional) The HKP key server base url including 31 | * the protocol to use e.g. https://pgp.mit.edu 32 | */ 33 | export default function HKP(keyServerBaseUrl) { 34 | this._baseUrl = keyServerBaseUrl ? keyServerBaseUrl : config.keyserver; 35 | this._fetch = typeof window !== 'undefined' ? window.fetch : require('node-fetch'); 36 | } 37 | 38 | /** 39 | * Search for a public key on the key server either by key ID or part of the user ID. 40 | * @param {String} options.keyID The long public key ID. 41 | * @param {String} options.query This can be any part of the key user ID such as name 42 | * or email address. 43 | * @return {Promise} The ascii armored public key. 44 | */ 45 | HKP.prototype.lookup = function(options) { 46 | var uri = this._baseUrl + '/pks/lookup?op=get&options=mr&search=', 47 | fetch = this._fetch; 48 | 49 | if (options.keyId) { 50 | uri += '0x' + encodeURIComponent(options.keyId); 51 | } else if (options.query) { 52 | uri += encodeURIComponent(options.query); 53 | } else { 54 | throw new Error('You must provide a query parameter!'); 55 | } 56 | 57 | return fetch(uri).then(function(response) { 58 | if (response.status === 200) { 59 | return response.text(); 60 | } 61 | 62 | }).then(function(publicKeyArmored) { 63 | if (!publicKeyArmored || publicKeyArmored.indexOf('-----END PGP PUBLIC KEY BLOCK-----') < 0) { 64 | return; 65 | } 66 | return publicKeyArmored.trim(); 67 | }); 68 | }; 69 | 70 | /** 71 | * Upload a public key to the server. 72 | * @param {String} publicKeyArmored An ascii armored public key to be uploaded. 73 | * @return {Promise} 74 | */ 75 | HKP.prototype.upload = function(publicKeyArmored) { 76 | var uri = this._baseUrl + '/pks/add', 77 | fetch = this._fetch; 78 | 79 | return fetch(uri, { 80 | method: 'post', 81 | headers: { 82 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 83 | }, 84 | body: 'keytext=' + encodeURIComponent(publicKeyArmored) 85 | }); 86 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Export high level api as default. 5 | * Usage: 6 | * 7 | * import openpgp from 'openpgp.js' 8 | * openpgp.encryptMessage(keys, text) 9 | */ 10 | import * as openpgp from './openpgp'; 11 | export default openpgp; 12 | 13 | /** 14 | * Export each high level api function seperately. 15 | * Usage: 16 | * 17 | * import { encryptMessage } from 'openpgp.js' 18 | * encryptMessage(keys, text) 19 | */ 20 | export * from './openpgp'; 21 | 22 | /** 23 | * @see module:key 24 | * @name module:openpgp.key 25 | */ 26 | import * as keyMod from './key'; 27 | export const key = keyMod; 28 | 29 | /** 30 | * @see module:message 31 | * @name module:openpgp.message 32 | */ 33 | import * as messageMod from './message'; 34 | export const message = messageMod; 35 | 36 | /** 37 | * @see module:cleartext 38 | * @name module:openpgp.cleartext 39 | */ 40 | import * as cleartextMod from './cleartext'; 41 | export const cleartext = cleartextMod; 42 | 43 | /** 44 | * @see module:util 45 | * @name module:openpgp.util 46 | */ 47 | export { default as util } from './util'; 48 | 49 | /** 50 | * @see module:packet 51 | * @name module:openpgp.packet 52 | */ 53 | export { default as packet } from './packet'; 54 | 55 | /** 56 | * @see module:type/mpi 57 | * @name module:openpgp.MPI 58 | */ 59 | export { default as MPI } from './type/mpi'; 60 | 61 | /** 62 | * @see module:type/s2k 63 | * @name module:openpgp.S2K 64 | */ 65 | export { default as S2K } from './type/s2k'; 66 | 67 | /** 68 | * @see module:type/keyid 69 | * @name module:openpgp.Keyid 70 | */ 71 | export { default as Keyid } from './type/keyid'; 72 | 73 | /** 74 | * @see module:encoding/armor 75 | * @name module:openpgp.armor 76 | */ 77 | export { default as armor } from './encoding/armor'; 78 | 79 | /** 80 | * @see module:enums 81 | * @name module:openpgp.enums 82 | */ 83 | export { default as enums } from './enums'; 84 | 85 | /** 86 | * @see module:config/config 87 | * @name module:openpgp.config 88 | */ 89 | export { default as config } from './config/config'; 90 | 91 | /** 92 | * @see module:crypto 93 | * @name module:openpgp.crypto 94 | */ 95 | export { default as crypto } from './crypto'; 96 | 97 | /** 98 | * @see module:keyring 99 | * @name module:openpgp.Keyring 100 | */ 101 | export { default as Keyring } from './keyring'; 102 | 103 | /** 104 | * @see module:worker/async_proxy 105 | * @name module:openpgp.AsyncProxy 106 | */ 107 | export { default as AsyncProxy } from './worker/async_proxy'; 108 | 109 | /** 110 | * @see module:hkp 111 | * @name module:openpgp.HKP 112 | */ 113 | export { default as HKP } from './hkp'; -------------------------------------------------------------------------------- /src/keyring/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @see module:keyring/keyring 5 | * @module keyring 6 | */ 7 | import Keyring from './keyring.js'; 8 | 9 | import localstore from './localstore.js'; 10 | Keyring.localstore = localstore; 11 | 12 | export default Keyring; 13 | -------------------------------------------------------------------------------- /src/keyring/keyring.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage. 20 | * @requires enums 21 | * @requires key 22 | * @requires util 23 | * @module keyring/keyring 24 | */ 25 | 26 | 'use strict'; 27 | 28 | import * as keyModule from '../key.js'; 29 | import LocalStore from './localstore.js'; 30 | 31 | /** 32 | * Initialization routine for the keyring. This method reads the 33 | * keyring from HTML5 local storage and initializes this instance. 34 | * @constructor 35 | * @param {class} [storeHandler] class implementing load() and store() methods 36 | */ 37 | export default function Keyring(storeHandler) { 38 | this.storeHandler = storeHandler || new LocalStore(); 39 | this.publicKeys = new KeyArray(this.storeHandler.loadPublic()); 40 | this.privateKeys = new KeyArray(this.storeHandler.loadPrivate()); 41 | } 42 | 43 | /** 44 | * Calls the storeHandler to save the keys 45 | */ 46 | Keyring.prototype.store = function () { 47 | this.storeHandler.storePublic(this.publicKeys.keys); 48 | this.storeHandler.storePrivate(this.privateKeys.keys); 49 | }; 50 | 51 | /** 52 | * Clear the keyring - erase all the keys 53 | */ 54 | Keyring.prototype.clear = function() { 55 | this.publicKeys.keys = []; 56 | this.privateKeys.keys = []; 57 | }; 58 | 59 | /** 60 | * Searches the keyring for keys having the specified key id 61 | * @param {String} keyId provided as string of lowercase hex number 62 | * withouth 0x prefix (can be 16-character key ID or fingerprint) 63 | * @param {Boolean} deep if true search also in subkeys 64 | * @return {Array|null} keys found or null 65 | */ 66 | Keyring.prototype.getKeysForId = function (keyId, deep) { 67 | var result = []; 68 | result = result.concat(this.publicKeys.getForId(keyId, deep) || []); 69 | result = result.concat(this.privateKeys.getForId(keyId, deep) || []); 70 | return result.length ? result : null; 71 | }; 72 | 73 | /** 74 | * Removes keys having the specified key id from the keyring 75 | * @param {String} keyId provided as string of lowercase hex number 76 | * withouth 0x prefix (can be 16-character key ID or fingerprint) 77 | * @return {Array|null} keys found or null 78 | */ 79 | Keyring.prototype.removeKeysForId = function (keyId) { 80 | var result = []; 81 | result = result.concat(this.publicKeys.removeForId(keyId) || []); 82 | result = result.concat(this.privateKeys.removeForId(keyId) || []); 83 | return result.length ? result : null; 84 | }; 85 | 86 | /** 87 | * Get all public and private keys 88 | * @return {Array} all keys 89 | */ 90 | Keyring.prototype.getAllKeys = function () { 91 | return this.publicKeys.keys.concat(this.privateKeys.keys); 92 | }; 93 | 94 | /** 95 | * Array of keys 96 | * @param {Array} keys The keys to store in this array 97 | */ 98 | function KeyArray(keys) { 99 | this.keys = keys; 100 | } 101 | 102 | /** 103 | * Searches all keys in the KeyArray matching the address or address part of the user ids 104 | * @param {String} email email address to search for 105 | * @return {Array} The public keys associated with provided email address. 106 | */ 107 | KeyArray.prototype.getForAddress = function(email) { 108 | var results = []; 109 | for (var i = 0; i < this.keys.length; i++) { 110 | if (emailCheck(email, this.keys[i])) { 111 | results.push(this.keys[i]); 112 | } 113 | } 114 | return results; 115 | }; 116 | 117 | /** 118 | * Checks a key to see if it matches the specified email address 119 | * @private 120 | * @param {String} email email address to search for 121 | * @param {module:key~Key} key The key to be checked. 122 | * @return {Boolean} True if the email address is defined in the specified key 123 | */ 124 | function emailCheck(email, key) { 125 | email = email.toLowerCase(); 126 | // escape email before using in regular expression 127 | var emailEsc = email.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); 128 | var emailRegex = new RegExp('<' + emailEsc + '>'); 129 | var userIds = key.getUserIds(); 130 | for (var i = 0; i < userIds.length; i++) { 131 | var userId = userIds[i].toLowerCase(); 132 | if (email === userId || emailRegex.test(userId)) { 133 | return true; 134 | } 135 | } 136 | return false; 137 | } 138 | 139 | /** 140 | * Checks a key to see if it matches the specified keyid 141 | * @private 142 | * @param {String} keyId provided as string of lowercase hex number 143 | * withouth 0x prefix (can be 16-character key ID or fingerprint) 144 | * @param {module:packet/secret_key|public_key|public_subkey|secret_subkey} keypacket The keypacket to be checked 145 | * @return {Boolean} True if keypacket has the specified keyid 146 | */ 147 | function keyIdCheck(keyId, keypacket) { 148 | if (keyId.length === 16) { 149 | return keyId === keypacket.getKeyId().toHex(); 150 | } else { 151 | return keyId === keypacket.getFingerprint(); 152 | } 153 | } 154 | 155 | /** 156 | * Searches the KeyArray for a key having the specified key id 157 | * @param {String} keyId provided as string of lowercase hex number 158 | * withouth 0x prefix (can be 16-character key ID or fingerprint) 159 | * @param {Boolean} deep if true search also in subkeys 160 | * @return {module:key~Key|null} key found or null 161 | */ 162 | KeyArray.prototype.getForId = function (keyId, deep) { 163 | for (var i = 0; i < this.keys.length; i++) { 164 | if (keyIdCheck(keyId, this.keys[i].primaryKey)) { 165 | return this.keys[i]; 166 | } 167 | if (deep && this.keys[i].subKeys) { 168 | for (var j = 0; j < this.keys[i].subKeys.length; j++) { 169 | if (keyIdCheck(keyId, this.keys[i].subKeys[j].subKey)) { 170 | return this.keys[i]; 171 | } 172 | } 173 | } 174 | } 175 | return null; 176 | }; 177 | 178 | /** 179 | * Imports a key from an ascii armored message 180 | * @param {String} armored message to read the keys/key from 181 | * @return {Array|null} array of error objects or null 182 | */ 183 | KeyArray.prototype.importKey = function (armored) { 184 | var imported = keyModule.readArmored(armored); 185 | var that = this; 186 | imported.keys.forEach(function(key) { 187 | // check if key already in key array 188 | var keyidHex = key.primaryKey.getKeyId().toHex(); 189 | var keyFound = that.getForId(keyidHex); 190 | if (keyFound) { 191 | keyFound.update(key); 192 | } else { 193 | that.push(key); 194 | } 195 | }); 196 | return imported.err ? imported.err : null; 197 | }; 198 | 199 | /** 200 | * Add key to KeyArray 201 | * @param {module:key~Key} key The key that will be added to the keyring 202 | * @return {Number} The new length of the KeyArray 203 | */ 204 | KeyArray.prototype.push = function (key) { 205 | return this.keys.push(key); 206 | }; 207 | 208 | /** 209 | * Removes a key with the specified keyid from the keyring 210 | * @param {String} keyId provided as string of lowercase hex number 211 | * withouth 0x prefix (can be 16-character key ID or fingerprint) 212 | * @return {module:key~Key|null} The key object which has been removed or null 213 | */ 214 | KeyArray.prototype.removeForId = function (keyId) { 215 | for (var i = 0; i < this.keys.length; i++) { 216 | if (keyIdCheck(keyId, this.keys[i].primaryKey)) { 217 | return this.keys.splice(i, 1)[0]; 218 | } 219 | } 220 | return null; 221 | }; 222 | -------------------------------------------------------------------------------- /src/keyring/localstore.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage. 20 | * @requires config 21 | * @module keyring/localstore 22 | * @param {String} prefix prefix for itemnames in localstore 23 | */ 24 | 25 | 'use strict'; 26 | 27 | import config from '../config'; 28 | import * as keyModule from '../key.js'; 29 | import util from '../util.js'; 30 | 31 | export default function LocalStore(prefix) { 32 | prefix = prefix || 'openpgp-'; 33 | this.publicKeysItem = prefix + this.publicKeysItem; 34 | this.privateKeysItem = prefix + this.privateKeysItem; 35 | if (typeof window !== 'undefined' && window.localStorage) { 36 | this.storage = window.localStorage; 37 | } else { 38 | this.storage = new (require('node-localstorage').LocalStorage)(config.node_store); 39 | } 40 | } 41 | 42 | /* 43 | * Declare the localstore itemnames 44 | */ 45 | LocalStore.prototype.publicKeysItem = 'public-keys'; 46 | LocalStore.prototype.privateKeysItem = 'private-keys'; 47 | 48 | /** 49 | * Load the public keys from HTML5 local storage. 50 | * @return {Array} array of keys retrieved from localstore 51 | */ 52 | LocalStore.prototype.loadPublic = function () { 53 | return loadKeys(this.storage, this.publicKeysItem); 54 | }; 55 | 56 | /** 57 | * Load the private keys from HTML5 local storage. 58 | * @return {Array} array of keys retrieved from localstore 59 | */ 60 | LocalStore.prototype.loadPrivate = function () { 61 | return loadKeys(this.storage, this.privateKeysItem); 62 | }; 63 | 64 | function loadKeys(storage, itemname) { 65 | var armoredKeys = JSON.parse(storage.getItem(itemname)); 66 | var keys = []; 67 | if (armoredKeys !== null && armoredKeys.length !== 0) { 68 | var key; 69 | for (var i = 0; i < armoredKeys.length; i++) { 70 | key = keyModule.readArmored(armoredKeys[i]); 71 | if (!key.err) { 72 | keys.push(key.keys[0]); 73 | } else { 74 | util.print_debug("Error reading armored key from keyring index: " + i); 75 | } 76 | } 77 | } 78 | return keys; 79 | } 80 | 81 | /** 82 | * Saves the current state of the public keys to HTML5 local storage. 83 | * The key array gets stringified using JSON 84 | * @param {Array} keys array of keys to save in localstore 85 | */ 86 | LocalStore.prototype.storePublic = function (keys) { 87 | storeKeys(this.storage, this.publicKeysItem, keys); 88 | }; 89 | 90 | /** 91 | * Saves the current state of the private keys to HTML5 local storage. 92 | * The key array gets stringified using JSON 93 | * @param {Array} keys array of keys to save in localstore 94 | */ 95 | LocalStore.prototype.storePrivate = function (keys) { 96 | storeKeys(this.storage, this.privateKeysItem, keys); 97 | }; 98 | 99 | function storeKeys(storage, itemname, keys) { 100 | var armoredKeys = []; 101 | if (keys.length) { 102 | for (var i = 0; i < keys.length; i++) { 103 | armoredKeys.push(keys[i].armor()); 104 | } 105 | storage.setItem(itemname, JSON.stringify(armoredKeys)); 106 | } else { 107 | storage.removeItem(itemname); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/packet/all_packets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @requires enums 3 | * @module packet 4 | */ 5 | 6 | 'use strict'; 7 | 8 | import enums from '../enums.js'; 9 | import * as packets from './all_packets.js'; // re-import module to parse packets from tag 10 | 11 | /** @see module:packet/compressed */ 12 | export { default as Compressed } from './compressed.js'; 13 | /** @see module:packet/sym_encrypted_integrity_protected */ 14 | export { default as SymEncryptedIntegrityProtected } from './sym_encrypted_integrity_protected.js'; 15 | /** @see module:packet/public_key_encrypted_session_key */ 16 | export { default as PublicKeyEncryptedSessionKey } from './public_key_encrypted_session_key.js'; 17 | /** @see module:packet/sym_encrypted_session_key */ 18 | export { default as SymEncryptedSessionKey } from './sym_encrypted_session_key.js'; 19 | /** @see module:packet/literal */ 20 | export { default as Literal } from './literal.js'; 21 | /** @see module:packet/public_key */ 22 | export { default as PublicKey } from './public_key.js'; 23 | /** @see module:packet/symmetrically_encrypted */ 24 | export { default as SymmetricallyEncrypted } from './symmetrically_encrypted.js'; 25 | /** @see module:packet/marker */ 26 | export { default as Marker } from './marker.js'; 27 | /** @see module:packet/public_subkey */ 28 | export { default as PublicSubkey } from './public_subkey.js'; 29 | /** @see module:packet/user_attribute */ 30 | export { default as UserAttribute } from './user_attribute.js'; 31 | /** @see module:packet/one_pass_signature */ 32 | export { default as OnePassSignature } from './one_pass_signature.js'; 33 | /** @see module:packet/secret_key */ 34 | export { default as SecretKey } from './secret_key.js'; 35 | /** @see module:packet/userid */ 36 | export { default as Userid } from './userid.js'; 37 | /** @see module:packet/secret_subkey */ 38 | export { default as SecretSubkey } from './secret_subkey.js'; 39 | /** @see module:packet/signature */ 40 | export { default as Signature } from './signature.js'; 41 | /** @see module:packet/trust */ 42 | export { default as Trust } from './trust.js'; 43 | 44 | /** 45 | * Allocate a new packet 46 | * @param {String} tag property name from {@link module:enums.packet} 47 | * @returns {Object} new packet object with type based on tag 48 | */ 49 | export function newPacketFromTag(tag) { 50 | return new packets[packetClassFromTagName(tag)](); 51 | } 52 | 53 | /** 54 | * Allocate a new packet from structured packet clone 55 | * See {@link http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data} 56 | * @param {Object} packetClone packet clone 57 | * @returns {Object} new packet object with data from packet clone 58 | */ 59 | export function fromStructuredClone(packetClone) { 60 | var tagName = enums.read(enums.packet, packetClone.tag); 61 | var packet = newPacketFromTag(tagName); 62 | for (var attr in packetClone) { 63 | if (packetClone.hasOwnProperty(attr)) { 64 | packet[attr] = packetClone[attr]; 65 | } 66 | } 67 | if (packet.postCloneTypeFix) { 68 | packet.postCloneTypeFix(); 69 | } 70 | return packet; 71 | } 72 | 73 | /** 74 | * Convert tag name to class name 75 | * @param {String} tag property name from {@link module:enums.packet} 76 | * @returns {String} 77 | */ 78 | function packetClassFromTagName(tag) { 79 | return tag.substr(0, 1).toUpperCase() + tag.substr(1); 80 | } 81 | -------------------------------------------------------------------------------- /src/packet/clone.js: -------------------------------------------------------------------------------- 1 | // OpenPGP.js - An OpenPGP implementation in javascript 2 | // Copyright (C) 2015 Tankred Hase 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * @fileoverview This module implements packet list cloning required to 20 | * pass certain object types beteen the web worker and main thread using 21 | * the structured cloning algorithm. 22 | */ 23 | 24 | 'use strict'; 25 | 26 | import * as key from '../key.js'; 27 | import * as message from '../message.js'; 28 | import * as cleartext from '../cleartext.js'; 29 | import Packetlist from './packetlist.js'; 30 | import type_keyid from '../type/keyid.js'; 31 | 32 | 33 | ////////////////////////////// 34 | // // 35 | // Packetlist --> Clone // 36 | // // 37 | ////////////////////////////// 38 | 39 | 40 | /** 41 | * Create a packetlist from the correspoding object types. 42 | * @param {Object} options the object passed to and from the web worker 43 | * @return {Object} a mutated version of the options optject 44 | */ 45 | export function clonePackets(options) { 46 | if(options.publicKeys) { 47 | options.publicKeys = options.publicKeys.map(key => key.toPacketlist()); 48 | } 49 | if(options.privateKeys) { 50 | options.privateKeys = options.privateKeys.map(key => key.toPacketlist()); 51 | } 52 | if(options.privateKey) { 53 | options.privateKey = options.privateKey.toPacketlist(); 54 | } 55 | if (options.key) { 56 | options.key = options.key.toPacketlist(); 57 | } 58 | return options; 59 | } 60 | 61 | 62 | ////////////////////////////// 63 | // // 64 | // Clone --> Packetlist // 65 | // // 66 | ////////////////////////////// 67 | 68 | 69 | /** 70 | * Creates an object with the correct prototype from a corresponding packetlist. 71 | * @param {Object} options the object passed to and from the web worker 72 | * @param {String} method the public api function name to be delegated to the worker 73 | * @return {Object} a mutated version of the options optject 74 | */ 75 | export function parseClonedPackets(options, method) { 76 | if(options.publicKeys) { 77 | options.publicKeys = options.publicKeys.map(packetlistCloneToKey); 78 | } 79 | if(options.privateKeys) { 80 | options.privateKeys = options.privateKeys.map(packetlistCloneToKey); 81 | } 82 | if(options.privateKey) { 83 | options.privateKey = packetlistCloneToKey(options.privateKey); 84 | } 85 | if (options.key) { 86 | options.key = packetlistCloneToKey(options.key); 87 | } 88 | if (options.message && (method === 'sign' || method === 'verify')) { // sign and verify support only CleartextMessage 89 | options.message = packetlistCloneToCleartextMessage(options.message); 90 | } else if (options.message) { 91 | options.message = packetlistCloneToMessage(options.message); 92 | } 93 | if (options.signatures) { 94 | options.signatures = options.signatures.map(packetlistCloneToSignature); 95 | } 96 | return options; 97 | } 98 | 99 | function packetlistCloneToKey(clone) { 100 | const packetlist = Packetlist.fromStructuredClone(clone); 101 | return new key.Key(packetlist); 102 | } 103 | 104 | function packetlistCloneToMessage(clone) { 105 | const packetlist = Packetlist.fromStructuredClone(clone.packets); 106 | return new message.Message(packetlist); 107 | } 108 | 109 | function packetlistCloneToCleartextMessage(clone) { 110 | var packetlist = Packetlist.fromStructuredClone(clone.packets); 111 | return new cleartext.CleartextMessage(clone.text, packetlist); 112 | } 113 | 114 | function packetlistCloneToSignature(clone) { 115 | clone.keyid = type_keyid.fromClone(clone.keyid); 116 | return clone; 117 | } 118 | -------------------------------------------------------------------------------- /src/packet/compressed.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the Compressed Data Packet (Tag 8)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.6|RFC4880 5.6}: The Compressed Data packet contains compressed data. Typically, 22 | * this packet is found as the contents of an encrypted packet, or following 23 | * a Signature or One-Pass Signature packet, and contains a literal data packet. 24 | * @requires compression/zlib 25 | * @requires compression/rawinflate 26 | * @requires compression/rawdeflate 27 | * @requires enums 28 | * @requires util 29 | * @module packet/compressed 30 | */ 31 | 32 | 'use strict'; 33 | 34 | import enums from '../enums.js'; 35 | import util from '../util.js'; 36 | import Zlib from '../compression/zlib.min.js'; 37 | import * as Inflater from '../compression/rawinflate.js'; 38 | import * as Deflater from '../compression/rawdeflate.js'; 39 | 40 | /** 41 | * @constructor 42 | */ 43 | export default function Compressed() { 44 | /** 45 | * Packet type 46 | * @type {module:enums.packet} 47 | */ 48 | this.tag = enums.packet.compressed; 49 | /** 50 | * List of packets 51 | * @type {module:packet/packetlist} 52 | */ 53 | this.packets = null; 54 | /** 55 | * Compression algorithm 56 | * @type {compression} 57 | */ 58 | this.algorithm = 'zip'; 59 | 60 | /** 61 | * Compressed packet data 62 | * @type {String} 63 | */ 64 | this.compressed = null; 65 | } 66 | 67 | /** 68 | * Parsing function for the packet. 69 | * @param {String} bytes Payload of a tag 8 packet 70 | */ 71 | Compressed.prototype.read = function (bytes) { 72 | // One octet that gives the algorithm used to compress the packet. 73 | this.algorithm = enums.read(enums.compression, bytes[0]); 74 | 75 | // Compressed data, which makes up the remainder of the packet. 76 | this.compressed = bytes.subarray(1, bytes.length); 77 | 78 | this.decompress(); 79 | }; 80 | 81 | 82 | 83 | /** 84 | * Return the compressed packet. 85 | * @return {String} binary compressed packet 86 | */ 87 | Compressed.prototype.write = function () { 88 | if (this.compressed === null) { 89 | this.compress(); 90 | } 91 | 92 | return util.concatUint8Array(new Uint8Array([enums.write(enums.compression, this.algorithm)]), this.compressed); 93 | }; 94 | 95 | 96 | /** 97 | * Decompression method for decompressing the compressed data 98 | * read by read_packet 99 | */ 100 | Compressed.prototype.decompress = function () { 101 | var decompressed, inflate; 102 | 103 | switch (this.algorithm) { 104 | case 'uncompressed': 105 | decompressed = this.compressed; 106 | break; 107 | 108 | case 'zip': 109 | inflate = new Inflater.RawInflate(this.compressed); 110 | decompressed = inflate.decompress(); 111 | break; 112 | 113 | case 'zlib': 114 | inflate = new Zlib.Zlib.Inflate(this.compressed); 115 | decompressed = inflate.decompress(); 116 | break; 117 | 118 | case 'bzip2': 119 | // TODO: need to implement this 120 | throw new Error('Compression algorithm BZip2 [BZ2] is not implemented.'); 121 | 122 | default: 123 | throw new Error("Compression algorithm unknown :" + this.alogrithm); 124 | } 125 | 126 | this.packets.read(decompressed); 127 | }; 128 | 129 | /** 130 | * Compress the packet data (member decompressedData) 131 | */ 132 | Compressed.prototype.compress = function () { 133 | var uncompressed, deflate; 134 | uncompressed = this.packets.write(); 135 | 136 | switch (this.algorithm) { 137 | 138 | case 'uncompressed': 139 | // - Uncompressed 140 | this.compressed = uncompressed; 141 | break; 142 | 143 | case 'zip': 144 | // - ZIP [RFC1951] 145 | deflate = new Deflater.RawDeflate(uncompressed); 146 | this.compressed = deflate.compress(); 147 | break; 148 | 149 | case 'zlib': 150 | // - ZLIB [RFC1950] 151 | deflate = new Zlib.Zlib.Deflate(uncompressed); 152 | this.compressed = deflate.compress(); 153 | break; 154 | 155 | case 'bzip2': 156 | // - BZip2 [BZ2] 157 | // TODO: need to implement this 158 | throw new Error("Compression algorithm BZip2 [BZ2] is not implemented."); 159 | 160 | default: 161 | throw new Error("Compression algorithm unknown :" + this.type); 162 | } 163 | }; 164 | -------------------------------------------------------------------------------- /src/packet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as packets from './all_packets.js'; 4 | import * as clone from './clone.js'; 5 | import List from './packetlist.js'; 6 | 7 | const mod = { 8 | /** @see module:packet/packetlist */ 9 | List: List, 10 | /** @see module:packet/clone */ 11 | clone: clone 12 | }; 13 | 14 | for (let i in packets) { 15 | mod[i] = packets[i]; 16 | } 17 | 18 | export default mod; 19 | -------------------------------------------------------------------------------- /src/packet/literal.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the Literal Data Packet (Tag 11)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.9|RFC4880 5.9}: A Literal Data packet contains the body of a message; data that 22 | * is not to be further interpreted. 23 | * @requires enums 24 | * @requires util 25 | * @module packet/literal 26 | */ 27 | 28 | 'use strict'; 29 | 30 | import util from '../util.js'; 31 | import enums from '../enums.js'; 32 | 33 | /** 34 | * @constructor 35 | */ 36 | export default function Literal() { 37 | this.tag = enums.packet.literal; 38 | this.format = 'utf8'; // default format for literal data packets 39 | this.date = new Date(); 40 | this.data = new Uint8Array(0); // literal data representation 41 | this.filename = 'msg.txt'; 42 | } 43 | 44 | /** 45 | * Set the packet data to a javascript native string, end of line 46 | * will be normalized to \r\n and by default text is converted to UTF8 47 | * @param {String} text Any native javascript string 48 | */ 49 | Literal.prototype.setText = function(text) { 50 | // normalize EOL to \r\n 51 | text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n'); 52 | // encode UTF8 53 | this.data = this.format === 'utf8' ? util.str2Uint8Array(util.encode_utf8(text)) : util.str2Uint8Array(text); 54 | }; 55 | 56 | /** 57 | * Returns literal data packets as native JavaScript string 58 | * with normalized end of line to \n 59 | * @return {String} literal data as text 60 | */ 61 | Literal.prototype.getText = function() { 62 | // decode UTF8 63 | var text = util.decode_utf8(util.Uint8Array2str(this.data)); 64 | // normalize EOL to \n 65 | return text.replace(/\r\n/g, '\n'); 66 | }; 67 | 68 | /** 69 | * Set the packet data to value represented by the provided string of bytes. 70 | * @param {Uint8Array} bytes The string of bytes 71 | * @param {utf8|binary|text} format The format of the string of bytes 72 | */ 73 | Literal.prototype.setBytes = function(bytes, format) { 74 | this.format = format; 75 | this.data = bytes; 76 | }; 77 | 78 | 79 | /** 80 | * Get the byte sequence representing the literal packet data 81 | * @returns {Uint8Array} A sequence of bytes 82 | */ 83 | Literal.prototype.getBytes = function() { 84 | return this.data; 85 | }; 86 | 87 | 88 | /** 89 | * Sets the filename of the literal packet data 90 | * @param {String} filename Any native javascript string 91 | */ 92 | Literal.prototype.setFilename = function(filename) { 93 | this.filename = filename; 94 | }; 95 | 96 | 97 | /** 98 | * Get the filename of the literal packet data 99 | * @returns {String} filename 100 | */ 101 | Literal.prototype.getFilename = function() { 102 | return this.filename; 103 | }; 104 | 105 | 106 | /** 107 | * Parsing function for a literal data packet (tag 11). 108 | * 109 | * @param {Uint8Array} input Payload of a tag 11 packet 110 | * @return {module:packet/literal} object representation 111 | */ 112 | Literal.prototype.read = function(bytes) { 113 | // - A one-octet field that describes how the data is formatted. 114 | var format = enums.read(enums.literal, bytes[0]); 115 | 116 | var filename_len = bytes[1]; 117 | this.filename = util.decode_utf8(util.Uint8Array2str(bytes.subarray(2, 2 + filename_len))); 118 | 119 | this.date = util.readDate(bytes.subarray(2 + filename_len, 2 + filename_len + 4)); 120 | 121 | var data = bytes.subarray(6 + filename_len, bytes.length); 122 | 123 | this.setBytes(data, format); 124 | }; 125 | 126 | /** 127 | * Creates a string representation of the packet 128 | * 129 | * @return {Uint8Array} Uint8Array representation of the packet 130 | */ 131 | Literal.prototype.write = function() { 132 | var filename = util.str2Uint8Array(util.encode_utf8(this.filename)); 133 | var filename_length = new Uint8Array([filename.length]); 134 | 135 | var format = new Uint8Array([enums.write(enums.literal, this.format)]); 136 | var date = util.writeDate(this.date); 137 | var data = this.getBytes(); 138 | 139 | return util.concatUint8Array([format, filename_length, filename, date, data]); 140 | }; 141 | -------------------------------------------------------------------------------- /src/packet/marker.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | 19 | /** 20 | * Implementation of the strange "Marker packet" (Tag 10)
21 | *
22 | * {@link http://tools.ietf.org/html/rfc4880#section-5.8|RFC4880 5.8}: An experimental version of PGP used this packet as the Literal 23 | * packet, but no released version of PGP generated Literal packets with this 24 | * tag. With PGP 5.x, this packet has been reassigned and is reserved for use as 25 | * the Marker packet.
26 | *
27 | * Such a packet MUST be ignored when received. 28 | * @requires enums 29 | * @module packet/marker 30 | */ 31 | 32 | 'use strict'; 33 | 34 | import enums from '../enums.js'; 35 | 36 | /** 37 | * @constructor 38 | */ 39 | export default function Marker() { 40 | this.tag = enums.packet.marker; 41 | } 42 | 43 | /** 44 | * Parsing function for a literal data packet (tag 10). 45 | * 46 | * @param {String} input Payload of a tag 10 packet 47 | * @param {Integer} position 48 | * Position to start reading from the input string 49 | * @param {Integer} len 50 | * Length of the packet or the remaining length of 51 | * input at position 52 | * @return {module:packet/marker} Object representation 53 | */ 54 | Marker.prototype.read = function (bytes) { 55 | if (bytes[0] === 0x50 && // P 56 | bytes[1] === 0x47 && // G 57 | bytes[2] === 0x50) { // P 58 | return true; 59 | } 60 | // marker packet does not contain "PGP" 61 | return false; 62 | }; 63 | -------------------------------------------------------------------------------- /src/packet/one_pass_signature.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the One-Pass Signature Packets (Tag 4)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.4|RFC4880 5.4}: The One-Pass Signature packet precedes the signed data and contains 22 | * enough information to allow the receiver to begin calculating any 23 | * hashes needed to verify the signature. It allows the Signature 24 | * packet to be placed at the end of the message, so that the signer 25 | * can compute the entire signed message in one pass. 26 | * @requires util 27 | * @requires enums 28 | * @requires type/keyid 29 | * @module packet/one_pass_signature 30 | */ 31 | 32 | 'use strict'; 33 | 34 | import util from '../util.js'; 35 | import enums from '../enums.js'; 36 | import type_keyid from '../type/keyid.js'; 37 | 38 | /** 39 | * @constructor 40 | */ 41 | export default function OnePassSignature() { 42 | this.tag = enums.packet.onePassSignature; // The packet type 43 | this.version = null; // A one-octet version number. The current version is 3. 44 | this.type = null; // A one-octet signature type. Signature types are described in {@link http://tools.ietf.org/html/rfc4880#section-5.2.1|RFC4880 Section 5.2.1}. 45 | this.hashAlgorithm = null; // A one-octet number describing the hash algorithm used. (See {@link http://tools.ietf.org/html/rfc4880#section-9.4|RFC4880 9.4}) 46 | this.publicKeyAlgorithm = null; // A one-octet number describing the public-key algorithm used. (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC4880 9.1}) 47 | this.signingKeyId = null; // An eight-octet number holding the Key ID of the signing key. 48 | this.flags = null; // A one-octet number holding a flag showing whether the signature is nested. A zero value indicates that the next packet is another One-Pass Signature packet that describes another signature to be applied to the same message data. 49 | } 50 | 51 | /** 52 | * parsing function for a one-pass signature packet (tag 4). 53 | * @param {Uint8Array} bytes payload of a tag 4 packet 54 | * @return {module:packet/one_pass_signature} object representation 55 | */ 56 | OnePassSignature.prototype.read = function (bytes) { 57 | var mypos = 0; 58 | // A one-octet version number. The current version is 3. 59 | this.version = bytes[mypos++]; 60 | 61 | // A one-octet signature type. Signature types are described in 62 | // Section 5.2.1. 63 | this.type = enums.read(enums.signature, bytes[mypos++]); 64 | 65 | // A one-octet number describing the hash algorithm used. 66 | this.hashAlgorithm = enums.read(enums.hash, bytes[mypos++]); 67 | 68 | // A one-octet number describing the public-key algorithm used. 69 | this.publicKeyAlgorithm = enums.read(enums.publicKey, bytes[mypos++]); 70 | 71 | // An eight-octet number holding the Key ID of the signing key. 72 | this.signingKeyId = new type_keyid(); 73 | this.signingKeyId.read(bytes.subarray(mypos, mypos + 8)); 74 | mypos += 8; 75 | 76 | // A one-octet number holding a flag showing whether the signature 77 | // is nested. A zero value indicates that the next packet is 78 | // another One-Pass Signature packet that describes another 79 | // signature to be applied to the same message data. 80 | this.flags = bytes[mypos++]; 81 | return this; 82 | }; 83 | 84 | /** 85 | * creates a string representation of a one-pass signature packet 86 | * @return {Uint8Array} a Uint8Array representation of a one-pass signature packet 87 | */ 88 | OnePassSignature.prototype.write = function () { 89 | 90 | var start = new Uint8Array([3, enums.write(enums.signature, this.type), 91 | enums.write(enums.hash, this.hashAlgorithm), 92 | enums.write(enums.publicKey, this.publicKeyAlgorithm)]); 93 | 94 | var end = new Uint8Array([this.flags]); 95 | 96 | return util.concatUint8Array([start, this.signingKeyId.write(), end]); 97 | }; 98 | 99 | /** 100 | * Fix custom types after cloning 101 | */ 102 | OnePassSignature.prototype.postCloneTypeFix = function() { 103 | this.signingKeyId = type_keyid.fromClone(this.signingKeyId); 104 | }; 105 | -------------------------------------------------------------------------------- /src/packet/packetlist.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This class represents a list of openpgp packets. 3 | * Take care when iterating over it - the packets themselves 4 | * are stored as numerical indices. 5 | * @requires util 6 | * @requires enums 7 | * @requires packet 8 | * @requires packet/packet 9 | * @module packet/packetlist 10 | */ 11 | 12 | 'use strict'; 13 | 14 | import util from '../util'; 15 | import packetParser from './packet.js'; 16 | import * as packets from './all_packets.js'; 17 | import enums from '../enums.js'; 18 | 19 | /** 20 | * @constructor 21 | */ 22 | export default function Packetlist() { 23 | /** The number of packets contained within the list. 24 | * @readonly 25 | * @type {Integer} */ 26 | this.length = 0; 27 | } 28 | /** 29 | * Reads a stream of binary data and interprents it as a list of packets. 30 | * @param {Uint8Array} A Uint8Array of bytes. 31 | */ 32 | Packetlist.prototype.read = function (bytes) { 33 | var i = 0; 34 | 35 | while (i < bytes.length) { 36 | var parsed = packetParser.read(bytes, i, bytes.length - i); 37 | i = parsed.offset; 38 | 39 | var tag = enums.read(enums.packet, parsed.tag); 40 | var packet = packets.newPacketFromTag(tag); 41 | 42 | this.push(packet); 43 | packet.read(parsed.packet); 44 | } 45 | }; 46 | 47 | /** 48 | * Creates a binary representation of openpgp objects contained within the 49 | * class instance. 50 | * @returns {Uint8Array} A Uint8Array containing valid openpgp packets. 51 | */ 52 | Packetlist.prototype.write = function () { 53 | var arr = []; 54 | 55 | for (var i = 0; i < this.length; i++) { 56 | var packetbytes = this[i].write(); 57 | arr.push(packetParser.writeHeader(this[i].tag, packetbytes.length)); 58 | arr.push(packetbytes); 59 | } 60 | 61 | return util.concatUint8Array(arr); 62 | }; 63 | 64 | /** 65 | * Adds a packet to the list. This is the only supported method of doing so; 66 | * writing to packetlist[i] directly will result in an error. 67 | */ 68 | Packetlist.prototype.push = function (packet) { 69 | if (!packet) { 70 | return; 71 | } 72 | 73 | packet.packets = packet.packets || new Packetlist(); 74 | 75 | this[this.length] = packet; 76 | this.length++; 77 | }; 78 | 79 | /** 80 | * Creates a new PacketList with all packets that pass the test implemented by the provided function. 81 | */ 82 | Packetlist.prototype.filter = function (callback) { 83 | 84 | var filtered = new Packetlist(); 85 | 86 | for (var i = 0; i < this.length; i++) { 87 | if (callback(this[i], i, this)) { 88 | filtered.push(this[i]); 89 | } 90 | } 91 | 92 | return filtered; 93 | }; 94 | 95 | /** 96 | * Creates a new PacketList with all packets from the given types 97 | */ 98 | Packetlist.prototype.filterByTag = function () { 99 | var args = Array.prototype.slice.call(arguments); 100 | var filtered = new Packetlist(); 101 | var that = this; 102 | 103 | function handle(packetType) {return that[i].tag === packetType;} 104 | for (var i = 0; i < this.length; i++) { 105 | if (args.some(handle)) { 106 | filtered.push(this[i]); 107 | } 108 | } 109 | return filtered; 110 | }; 111 | 112 | /** 113 | * Executes the provided callback once for each element 114 | */ 115 | Packetlist.prototype.forEach = function (callback) { 116 | for (var i = 0; i < this.length; i++) { 117 | callback(this[i]); 118 | } 119 | }; 120 | 121 | /** 122 | * Traverses packet tree and returns first matching packet 123 | * @param {module:enums.packet} type The packet type 124 | * @return {module:packet/packet|null} 125 | */ 126 | Packetlist.prototype.findPacket = function (type) { 127 | var packetlist = this.filterByTag(type); 128 | if (packetlist.length) { 129 | return packetlist[0]; 130 | } else { 131 | var found = null; 132 | for (var i = 0; i < this.length; i++) { 133 | if (this[i].packets.length) { 134 | found = this[i].packets.findPacket(type); 135 | if (found) { 136 | return found; 137 | } 138 | } 139 | } 140 | } 141 | return null; 142 | }; 143 | 144 | /** 145 | * Returns array of found indices by tag 146 | */ 147 | Packetlist.prototype.indexOfTag = function () { 148 | var args = Array.prototype.slice.call(arguments); 149 | var tagIndex = []; 150 | var that = this; 151 | 152 | function handle(packetType) {return that[i].tag === packetType;} 153 | for (var i = 0; i < this.length; i++) { 154 | if (args.some(handle)) { 155 | tagIndex.push(i); 156 | } 157 | } 158 | return tagIndex; 159 | }; 160 | 161 | /** 162 | * Returns slice of packetlist 163 | */ 164 | Packetlist.prototype.slice = function (begin, end) { 165 | if (!end) { 166 | end = this.length; 167 | } 168 | var part = new Packetlist(); 169 | for (var i = begin; i < end; i++) { 170 | part.push(this[i]); 171 | } 172 | return part; 173 | }; 174 | 175 | /** 176 | * Concatenates packetlist or array of packets 177 | */ 178 | Packetlist.prototype.concat = function (packetlist) { 179 | if (packetlist) { 180 | for (var i = 0; i < packetlist.length; i++) { 181 | this.push(packetlist[i]); 182 | } 183 | } 184 | }; 185 | 186 | /** 187 | * Allocate a new packetlist from structured packetlist clone 188 | * See {@link http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data} 189 | * @param {Object} packetClone packetlist clone 190 | * @returns {Object} new packetlist object with data from packetlist clone 191 | */ 192 | Packetlist.fromStructuredClone = function(packetlistClone) { 193 | var packetlist = new Packetlist(); 194 | for (var i = 0; i < packetlistClone.length; i++) { 195 | packetlist.push(packets.fromStructuredClone(packetlistClone[i])); 196 | if (packetlist[i].packets.length !== 0) { 197 | packetlist[i].packets = this.fromStructuredClone(packetlist[i].packets); 198 | } else { 199 | packetlist[i].packets = new Packetlist(); 200 | } 201 | } 202 | return packetlist; 203 | }; 204 | -------------------------------------------------------------------------------- /src/packet/public_key.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the Key Material Packet (Tag 5,6,7,14)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.5|RFC4480 5.5}: 22 | * A key material packet contains all the information about a public or 23 | * private key. There are four variants of this packet type, and two 24 | * major versions. Consequently, this section is complex. 25 | * @requires crypto 26 | * @requires enums 27 | * @requires type/keyid 28 | * @requires type/mpi 29 | * @requires util 30 | * @module packet/public_key 31 | */ 32 | 33 | 'use strict'; 34 | 35 | import util from '../util.js'; 36 | import type_mpi from '../type/mpi.js'; 37 | import type_keyid from '../type/keyid.js'; 38 | import enums from '../enums.js'; 39 | import crypto from '../crypto'; 40 | 41 | /** 42 | * @constructor 43 | */ 44 | export default function PublicKey() { 45 | this.tag = enums.packet.publicKey; 46 | this.version = 4; 47 | /** Key creation date. 48 | * @type {Date} */ 49 | this.created = new Date(); 50 | /** A list of multiprecision integers 51 | * @type {module:type/mpi} */ 52 | this.mpi = []; 53 | /** Public key algorithm 54 | * @type {module:enums.publicKey} */ 55 | this.algorithm = 'rsa_sign'; 56 | // time in days (V3 only) 57 | this.expirationTimeV3 = 0; 58 | /** 59 | * Fingerprint in lowercase hex 60 | * @type {String} 61 | */ 62 | this.fingerprint = null; 63 | /** 64 | * Keyid 65 | * @type {module:type/keyid} 66 | */ 67 | this.keyid = null; 68 | } 69 | 70 | /** 71 | * Internal Parser for public keys as specified in {@link http://tools.ietf.org/html/rfc4880#section-5.5.2|RFC 4880 section 5.5.2 Public-Key Packet Formats} 72 | * called by read_tag<num> 73 | * @param {Uint8Array} bytes Input array to read the packet from 74 | * @return {Object} This object with attributes set by the parser 75 | */ 76 | PublicKey.prototype.read = function (bytes) { 77 | var pos = 0; 78 | // A one-octet version number (3 or 4). 79 | this.version = bytes[pos++]; 80 | 81 | if (this.version === 3 || this.version === 4) { 82 | // - A four-octet number denoting the time that the key was created. 83 | this.created = util.readDate(bytes.subarray(pos, pos + 4)); 84 | pos += 4; 85 | 86 | if (this.version === 3) { 87 | // - A two-octet number denoting the time in days that this key is 88 | // valid. If this number is zero, then it does not expire. 89 | this.expirationTimeV3 = util.readNumber(bytes.subarray(pos, pos + 2)); 90 | pos += 2; 91 | } 92 | 93 | // - A one-octet number denoting the public-key algorithm of this key. 94 | this.algorithm = enums.read(enums.publicKey, bytes[pos++]); 95 | 96 | var mpicount = crypto.getPublicMpiCount(this.algorithm); 97 | this.mpi = []; 98 | 99 | var bmpi = bytes.subarray(pos, bytes.length); 100 | var p = 0; 101 | 102 | for (var i = 0; i < mpicount && p < bmpi.length; i++) { 103 | 104 | this.mpi[i] = new type_mpi(); 105 | 106 | p += this.mpi[i].read(bmpi.subarray(p, bmpi.length)); 107 | 108 | if (p > bmpi.length) { 109 | throw new Error('Error reading MPI @:' + p); 110 | } 111 | } 112 | 113 | return p + 6; 114 | } else { 115 | throw new Error('Version ' + this.version + ' of the key packet is unsupported.'); 116 | } 117 | }; 118 | 119 | /** 120 | * Alias of read() 121 | * @see module:packet/public_key~PublicKey#read 122 | */ 123 | PublicKey.prototype.readPublicKey = PublicKey.prototype.read; 124 | 125 | /** 126 | * Same as write_private_key, but has less information because of 127 | * public key. 128 | * @return {Uint8Array} OpenPGP packet body contents, 129 | */ 130 | PublicKey.prototype.write = function () { 131 | 132 | var arr = []; 133 | // Version 134 | arr.push(new Uint8Array([this.version])); 135 | arr.push(util.writeDate(this.created)); 136 | if (this.version === 3) { 137 | arr.push(util.writeNumber(this.expirationTimeV3, 2)); 138 | } 139 | arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); 140 | 141 | var mpicount = crypto.getPublicMpiCount(this.algorithm); 142 | 143 | for (var i = 0; i < mpicount; i++) { 144 | arr.push(this.mpi[i].write()); 145 | } 146 | 147 | return util.concatUint8Array(arr); 148 | }; 149 | 150 | /** 151 | * Alias of write() 152 | * @see module:packet/public_key~PublicKey#write 153 | */ 154 | PublicKey.prototype.writePublicKey = PublicKey.prototype.write; 155 | 156 | /** 157 | * Write an old version packet - it's used by some of the internal routines. 158 | */ 159 | PublicKey.prototype.writeOld = function () { 160 | var bytes = this.writePublicKey(); 161 | 162 | return util.concatUint8Array([new Uint8Array([0x99]), util.writeNumber(bytes.length, 2), bytes]); 163 | }; 164 | 165 | /** 166 | * Calculates the key id of the key 167 | * @return {String} A 8 byte key id 168 | */ 169 | PublicKey.prototype.getKeyId = function () { 170 | if (this.keyid) { 171 | return this.keyid; 172 | } 173 | this.keyid = new type_keyid(); 174 | if (this.version === 4) { 175 | this.keyid.read(util.str2Uint8Array(util.hex2bin(this.getFingerprint()).substr(12, 8))); 176 | } else if (this.version === 3) { 177 | var arr = this.mpi[0].write(); 178 | this.keyid.read(arr.subarray(arr.length - 8, arr.length)); 179 | } 180 | return this.keyid; 181 | }; 182 | 183 | /** 184 | * Calculates the fingerprint of the key 185 | * @return {String} A string containing the fingerprint in lowercase hex 186 | */ 187 | PublicKey.prototype.getFingerprint = function () { 188 | if (this.fingerprint) { 189 | return this.fingerprint; 190 | } 191 | var toHash = ''; 192 | if (this.version === 4) { 193 | toHash = this.writeOld(); 194 | this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); 195 | } else if (this.version === 3) { 196 | var mpicount = crypto.getPublicMpiCount(this.algorithm); 197 | for (var i = 0; i < mpicount; i++) { 198 | toHash += this.mpi[i].toBytes(); 199 | } 200 | this.fingerprint = util.Uint8Array2str(crypto.hash.md5(util.str2Uint8Array(toHash))); 201 | } 202 | this.fingerprint = util.hexstrdump(this.fingerprint); 203 | return this.fingerprint; 204 | }; 205 | 206 | /** 207 | * Returns bit size of key 208 | * @return {int} Number of bits 209 | */ 210 | PublicKey.prototype.getBitSize = function () { 211 | return this.mpi[0].byteLength() * 8; 212 | }; 213 | 214 | /** 215 | * Fix custom types after cloning 216 | */ 217 | PublicKey.prototype.postCloneTypeFix = function() { 218 | for (var i = 0; i < this.mpi.length; i++) { 219 | this.mpi[i] = type_mpi.fromClone(this.mpi[i]); 220 | } 221 | if (this.keyid) { 222 | this.keyid = type_keyid.fromClone(this.keyid); 223 | } 224 | }; 225 | -------------------------------------------------------------------------------- /src/packet/public_key_encrypted_session_key.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Public-Key Encrypted Session Key Packets (Tag 1)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: A Public-Key Encrypted Session Key packet holds the session key 22 | * used to encrypt a message. Zero or more Public-Key Encrypted Session Key 23 | * packets and/or Symmetric-Key Encrypted Session Key packets may precede a 24 | * Symmetrically Encrypted Data Packet, which holds an encrypted message. The 25 | * message is encrypted with the session key, and the session key is itself 26 | * encrypted and stored in the Encrypted Session Key packet(s). The 27 | * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted 28 | * Session Key packet for each OpenPGP key to which the message is encrypted. 29 | * The recipient of the message finds a session key that is encrypted to their 30 | * public key, decrypts the session key, and then uses the session key to 31 | * decrypt the message. 32 | * @requires crypto 33 | * @requires enums 34 | * @requires type/keyid 35 | * @requires type/mpi 36 | * @requires util 37 | * @module packet/public_key_encrypted_session_key 38 | */ 39 | 40 | 'use strict'; 41 | 42 | import type_keyid from '../type/keyid.js'; 43 | import util from '../util.js'; 44 | import type_mpi from '../type/mpi.js'; 45 | import enums from '../enums.js'; 46 | import crypto from '../crypto'; 47 | 48 | /** 49 | * @constructor 50 | */ 51 | export default function PublicKeyEncryptedSessionKey() { 52 | this.tag = enums.packet.publicKeyEncryptedSessionKey; 53 | this.version = 3; 54 | 55 | this.publicKeyId = new type_keyid(); 56 | this.publicKeyAlgorithm = 'rsa_encrypt'; 57 | 58 | this.sessionKey = null; 59 | this.sessionKeyAlgorithm = 'aes256'; 60 | 61 | /** @type {Array} */ 62 | this.encrypted = []; 63 | } 64 | 65 | /** 66 | * Parsing function for a publickey encrypted session key packet (tag 1). 67 | * 68 | * @param {Uint8Array} input Payload of a tag 1 packet 69 | * @param {Integer} position Position to start reading from the input string 70 | * @param {Integer} len Length of the packet or the remaining length of 71 | * input at position 72 | * @return {module:packet/public_key_encrypted_session_key} Object representation 73 | */ 74 | PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { 75 | 76 | this.version = bytes[0]; 77 | this.publicKeyId.read(bytes.subarray(1,bytes.length)); 78 | this.publicKeyAlgorithm = enums.read(enums.publicKey, bytes[9]); 79 | 80 | var i = 10; 81 | 82 | var integerCount = (function(algo) { 83 | switch (algo) { 84 | case 'rsa_encrypt': 85 | case 'rsa_encrypt_sign': 86 | return 1; 87 | 88 | case 'elgamal': 89 | return 2; 90 | 91 | default: 92 | throw new Error("Invalid algorithm."); 93 | } 94 | })(this.publicKeyAlgorithm); 95 | 96 | this.encrypted = []; 97 | 98 | for (var j = 0; j < integerCount; j++) { 99 | var mpi = new type_mpi(); 100 | i += mpi.read(bytes.subarray(i, bytes.length)); 101 | this.encrypted.push(mpi); 102 | } 103 | }; 104 | 105 | /** 106 | * Create a string representation of a tag 1 packet 107 | * 108 | * @return {Uint8Array} The Uint8Array representation 109 | */ 110 | PublicKeyEncryptedSessionKey.prototype.write = function () { 111 | 112 | var arr = [new Uint8Array([this.version]), this.publicKeyId.write(), new Uint8Array([enums.write(enums.publicKey, this.publicKeyAlgorithm)])]; 113 | 114 | for (var i = 0; i < this.encrypted.length; i++) { 115 | arr.push(this.encrypted[i].write()); 116 | } 117 | 118 | return util.concatUint8Array(arr); 119 | }; 120 | 121 | PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { 122 | var data = String.fromCharCode( 123 | enums.write(enums.symmetric, this.sessionKeyAlgorithm)); 124 | 125 | data += util.Uint8Array2str(this.sessionKey); 126 | var checksum = util.calc_checksum(this.sessionKey); 127 | data += util.Uint8Array2str(util.writeNumber(checksum, 2)); 128 | 129 | var mpi = new type_mpi(); 130 | mpi.fromBytes(crypto.pkcs1.eme.encode( 131 | data, 132 | key.mpi[0].byteLength())); 133 | 134 | this.encrypted = crypto.publicKeyEncrypt( 135 | this.publicKeyAlgorithm, 136 | key.mpi, 137 | mpi); 138 | }; 139 | 140 | /** 141 | * Decrypts the session key (only for public key encrypted session key 142 | * packets (tag 1) 143 | * 144 | * @param {module:packet/secret_key} key 145 | * Private key with secMPIs unlocked 146 | * @return {String} The unencrypted session key 147 | */ 148 | PublicKeyEncryptedSessionKey.prototype.decrypt = function (key) { 149 | var result = crypto.publicKeyDecrypt( 150 | this.publicKeyAlgorithm, 151 | key.mpi, 152 | this.encrypted).toBytes(); 153 | 154 | var checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); 155 | var decoded = crypto.pkcs1.eme.decode(result); 156 | 157 | key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2)); 158 | 159 | if (checksum !== util.calc_checksum(key)) { 160 | throw new Error('Checksum mismatch'); 161 | } else { 162 | this.sessionKey = key; 163 | this.sessionKeyAlgorithm = 164 | enums.read(enums.symmetric, decoded.charCodeAt(0)); 165 | } 166 | }; 167 | 168 | /** 169 | * Fix custom types after cloning 170 | */ 171 | PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() { 172 | this.publicKeyId = type_keyid.fromClone(this.publicKeyId); 173 | for (var i = 0; i < this.encrypted.length; i++) { 174 | this.encrypted[i] = type_mpi.fromClone(this.encrypted[i]); 175 | } 176 | }; 177 | -------------------------------------------------------------------------------- /src/packet/public_subkey.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * @requires packet/public_key 20 | * @requires enums 21 | * @module packet/public_subkey 22 | */ 23 | 24 | 'use strict'; 25 | 26 | import publicKey from './public_key.js'; 27 | import enums from '../enums.js'; 28 | 29 | /** 30 | * @constructor 31 | * @extends module:packet/public_key 32 | */ 33 | export default function PublicSubkey() { 34 | publicKey.call(this); 35 | this.tag = enums.packet.publicSubkey; 36 | } 37 | 38 | PublicSubkey.prototype = new publicKey(); 39 | PublicSubkey.prototype.constructor = PublicSubkey; 40 | -------------------------------------------------------------------------------- /src/packet/secret_subkey.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * @requires packet/secret_key 20 | * @requires enums 21 | * @module packet/secret_subkey 22 | */ 23 | 24 | 'use strict'; 25 | 26 | import secretKey from './secret_key.js'; 27 | import enums from '../enums.js'; 28 | 29 | /** 30 | * @constructor 31 | * @extends module:packet/secret_key 32 | */ 33 | export default function SecretSubkey() { 34 | secretKey.call(this); 35 | this.tag = enums.packet.secretSubkey; 36 | } 37 | 38 | SecretSubkey.prototype = new secretKey(); 39 | SecretSubkey.prototype.constructor = SecretSubkey; 40 | -------------------------------------------------------------------------------- /src/packet/sym_encrypted_integrity_protected.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the Sym. Encrypted Integrity Protected Data 20 | * Packet (Tag 18)
21 | *
22 | * {@link http://tools.ietf.org/html/rfc4880#section-5.13|RFC4880 5.13}: 23 | * The Symmetrically Encrypted Integrity Protected Data packet is 24 | * a variant of the Symmetrically Encrypted Data packet. It is a new feature 25 | * created for OpenPGP that addresses the problem of detecting a modification to 26 | * encrypted data. It is used in combination with a Modification Detection Code 27 | * packet. 28 | * @requires crypto 29 | * @requires util 30 | * @requires enums 31 | * @requires config 32 | * @module packet/sym_encrypted_integrity_protected 33 | */ 34 | 35 | 'use strict'; 36 | 37 | import util from '../util.js'; 38 | import crypto from '../crypto'; 39 | import enums from '../enums.js'; 40 | import asmCrypto from 'asmcrypto-lite'; 41 | const nodeCrypto = util.getNodeCrypto(); 42 | const Buffer = util.getNodeBuffer(); 43 | 44 | /** 45 | * @constructor 46 | */ 47 | export default function SymEncryptedIntegrityProtected() { 48 | this.tag = enums.packet.symEncryptedIntegrityProtected; 49 | /** The encrypted payload. */ 50 | this.encrypted = null; // string 51 | /** 52 | * If after decrypting the packet this is set to true, 53 | * a modification has been detected and thus the contents 54 | * should be discarded. 55 | * @type {Boolean} 56 | */ 57 | this.modification = false; 58 | this.packets = null; 59 | } 60 | 61 | SymEncryptedIntegrityProtected.prototype.read = function (bytes) { 62 | // - A one-octet version number. The only currently defined value is 1. 63 | var version = bytes[0]; 64 | 65 | if (version !== 1) { 66 | throw new Error('Invalid packet version.'); 67 | } 68 | 69 | // - Encrypted data, the output of the selected symmetric-key cipher 70 | // operating in Cipher Feedback mode with shift amount equal to the 71 | // block size of the cipher (CFB-n where n is the block size). 72 | this.encrypted = bytes.subarray(1, bytes.length); 73 | }; 74 | 75 | SymEncryptedIntegrityProtected.prototype.write = function () { 76 | // 1 = Version 77 | return util.concatUint8Array([new Uint8Array([1]), this.encrypted]); 78 | }; 79 | 80 | SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) { 81 | var bytes = this.packets.write(); 82 | 83 | var prefixrandom = crypto.getPrefixRandom(sessionKeyAlgorithm); 84 | var repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]); 85 | var prefix = util.concatUint8Array([prefixrandom, repeat]); 86 | 87 | // Modification detection code packet. 88 | var mdc = new Uint8Array([0xD3, 0x14]); 89 | 90 | // This could probably be cleaned up to use less memory 91 | var tohash = util.concatUint8Array([bytes, mdc]); 92 | var hash = crypto.hash.sha1(util.concatUint8Array([prefix, tohash])); 93 | tohash = util.concatUint8Array([tohash, hash]); 94 | 95 | if(sessionKeyAlgorithm.substr(0,3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser. 96 | var blockSize = crypto.cipher[sessionKeyAlgorithm].blockSize; 97 | 98 | if(nodeCrypto) { // Node crypto library. Only loaded if config.useNative === true 99 | var cipherObj = new nodeCrypto.createCipheriv('aes-' + sessionKeyAlgorithm.substr(3,3) + '-cfb', 100 | new Buffer(key), new Buffer(new Uint8Array(blockSize))); 101 | this.encrypted = new Uint8Array(cipherObj.update(new Buffer(util.concatUint8Array([prefix, tohash])))); 102 | 103 | } else { // asm.js fallback 104 | this.encrypted = asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, tohash]), key); 105 | } 106 | 107 | } else { 108 | this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false) 109 | .subarray(0, prefix.length + tohash.length); 110 | } 111 | }; 112 | 113 | /** 114 | * Decrypts the encrypted data contained in this object read_packet must 115 | * have been called before 116 | * 117 | * @param {module:enums.symmetric} sessionKeyAlgorithm 118 | * The selected symmetric encryption algorithm to be used 119 | * @param {Uint8Array} key The key of cipher blocksize length to be used 120 | * @return {String} The decrypted data of this packet 121 | */ 122 | SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm, key) { 123 | var decrypted; 124 | 125 | if(sessionKeyAlgorithm.substr(0,3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser. 126 | var blockSize = crypto.cipher[sessionKeyAlgorithm].blockSize; 127 | 128 | if(nodeCrypto) { // Node crypto library. Only loaded if config.useNative === true 129 | var decipherObj = new nodeCrypto.createDecipheriv('aes-' + sessionKeyAlgorithm.substr(3,3) + '-cfb', 130 | new Buffer(key), new Buffer(new Uint8Array(blockSize))); 131 | decrypted = new Uint8Array(decipherObj.update(new Buffer(this.encrypted))); 132 | 133 | } else { // asm.js fallback 134 | decrypted = asmCrypto.AES_CFB.decrypt(this.encrypted, key); 135 | } 136 | 137 | // Remove random prefix 138 | decrypted = decrypted.subarray(blockSize + 2, decrypted.length); 139 | 140 | } else { 141 | decrypted = crypto.cfb.decrypt(sessionKeyAlgorithm, key, this.encrypted, false); 142 | } 143 | 144 | // there must be a modification detection code packet as the 145 | // last packet and everything gets hashed except the hash itself 146 | this.hash = util.Uint8Array2str(crypto.hash.sha1(util.concatUint8Array([crypto.cfb.mdc(sessionKeyAlgorithm, key, this.encrypted), 147 | decrypted.subarray(0, decrypted.length - 20)]))); 148 | 149 | var mdc = util.Uint8Array2str(decrypted.subarray(decrypted.length - 20, decrypted.length)); 150 | 151 | if (this.hash !== mdc) { 152 | throw new Error('Modification detected.'); 153 | } else { 154 | this.packets.read(decrypted.subarray(0, decrypted.length - 22)); 155 | } 156 | }; 157 | -------------------------------------------------------------------------------- /src/packet/sym_encrypted_session_key.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Public-Key Encrypted Session Key Packets (Tag 1)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: A Public-Key Encrypted Session Key packet holds the session key 22 | * used to encrypt a message. Zero or more Public-Key Encrypted Session Key 23 | * packets and/or Symmetric-Key Encrypted Session Key packets may precede a 24 | * Symmetrically Encrypted Data Packet, which holds an encrypted message. The 25 | * message is encrypted with the session key, and the session key is itself 26 | * encrypted and stored in the Encrypted Session Key packet(s). The 27 | * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted 28 | * Session Key packet for each OpenPGP key to which the message is encrypted. 29 | * The recipient of the message finds a session key that is encrypted to their 30 | * public key, decrypts the session key, and then uses the session key to 31 | * decrypt the message. 32 | * @requires util 33 | * @requires crypto 34 | * @requires enums 35 | * @requires type/s2k 36 | * @module packet/sym_encrypted_session_key 37 | */ 38 | 39 | 'use strict'; 40 | 41 | import util from '../util.js'; 42 | import type_s2k from '../type/s2k.js'; 43 | import enums from '../enums.js'; 44 | import crypto from '../crypto'; 45 | 46 | /** 47 | * @constructor 48 | */ 49 | export default function SymEncryptedSessionKey() { 50 | this.tag = enums.packet.symEncryptedSessionKey; 51 | this.version = 4; 52 | this.sessionKey = null; 53 | this.sessionKeyEncryptionAlgorithm = null; 54 | this.sessionKeyAlgorithm = 'aes256'; 55 | this.encrypted = null; 56 | this.s2k = new type_s2k(); 57 | } 58 | 59 | /** 60 | * Parsing function for a symmetric encrypted session key packet (tag 3). 61 | * 62 | * @param {Uint8Array} input Payload of a tag 1 packet 63 | * @param {Integer} position Position to start reading from the input string 64 | * @param {Integer} len 65 | * Length of the packet or the remaining length of 66 | * input at position 67 | * @return {module:packet/sym_encrypted_session_key} Object representation 68 | */ 69 | SymEncryptedSessionKey.prototype.read = function(bytes) { 70 | // A one-octet version number. The only currently defined version is 4. 71 | this.version = bytes[0]; 72 | 73 | // A one-octet number describing the symmetric algorithm used. 74 | var algo = enums.read(enums.symmetric, bytes[1]); 75 | 76 | // A string-to-key (S2K) specifier, length as defined above. 77 | var s2klength = this.s2k.read(bytes.subarray(2, bytes.length)); 78 | 79 | // Optionally, the encrypted session key itself, which is decrypted 80 | // with the string-to-key object. 81 | var done = s2klength + 2; 82 | 83 | if (done < bytes.length) { 84 | this.encrypted = bytes.subarray(done, bytes.length); 85 | this.sessionKeyEncryptionAlgorithm = algo; 86 | } else { 87 | this.sessionKeyAlgorithm = algo; 88 | } 89 | }; 90 | 91 | SymEncryptedSessionKey.prototype.write = function() { 92 | var algo = this.encrypted === null ? 93 | this.sessionKeyAlgorithm : 94 | this.sessionKeyEncryptionAlgorithm; 95 | 96 | var bytes = util.concatUint8Array([new Uint8Array([this.version, enums.write(enums.symmetric, algo)]), this.s2k.write()]); 97 | 98 | if (this.encrypted !== null) { 99 | bytes = util.concatUint8Array([bytes, this.encrypted]); 100 | } 101 | return bytes; 102 | }; 103 | 104 | /** 105 | * Decrypts the session key (only for public key encrypted session key 106 | * packets (tag 1) 107 | * 108 | * @return {Uint8Array} The unencrypted session key 109 | */ 110 | SymEncryptedSessionKey.prototype.decrypt = function(passphrase) { 111 | var algo = this.sessionKeyEncryptionAlgorithm !== null ? 112 | this.sessionKeyEncryptionAlgorithm : 113 | this.sessionKeyAlgorithm; 114 | 115 | var length = crypto.cipher[algo].keySize; 116 | var key = this.s2k.produce_key(passphrase, length); 117 | 118 | if (this.encrypted === null) { 119 | this.sessionKey = key; 120 | 121 | } else { 122 | var decrypted = crypto.cfb.normalDecrypt( 123 | algo, key, this.encrypted, null); 124 | 125 | this.sessionKeyAlgorithm = enums.read(enums.symmetric, 126 | decrypted[0]); 127 | 128 | this.sessionKey = decrypted.subarray(1,decrypted.length); 129 | } 130 | }; 131 | 132 | SymEncryptedSessionKey.prototype.encrypt = function(passphrase) { 133 | var algo = this.sessionKeyEncryptionAlgorithm !== null ? 134 | this.sessionKeyEncryptionAlgorithm : 135 | this.sessionKeyAlgorithm; 136 | 137 | this.sessionKeyEncryptionAlgorithm = algo; 138 | 139 | var length = crypto.cipher[algo].keySize; 140 | var key = this.s2k.produce_key(passphrase, length); 141 | 142 | var algo_enum = new Uint8Array([ 143 | enums.write(enums.symmetric, this.sessionKeyAlgorithm)]); 144 | 145 | var private_key; 146 | if(this.sessionKey === null) { 147 | this.sessionKey = crypto.getRandomBytes(crypto.cipher[this.sessionKeyAlgorithm].keySize); 148 | } 149 | private_key = util.concatUint8Array([algo_enum, this.sessionKey]); 150 | 151 | this.encrypted = crypto.cfb.normalEncrypt( 152 | algo, key, private_key, null); 153 | }; 154 | 155 | /** 156 | * Fix custom types after cloning 157 | */ 158 | SymEncryptedSessionKey.prototype.postCloneTypeFix = function() { 159 | this.s2k = type_s2k.fromClone(this.s2k); 160 | }; 161 | -------------------------------------------------------------------------------- /src/packet/symmetrically_encrypted.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the Symmetrically Encrypted Data Packet (Tag 9)
20 | *
21 | * {@link http://tools.ietf.org/html/rfc4880#section-5.7|RFC4880 5.7}: The Symmetrically Encrypted Data packet contains data encrypted 22 | * with a symmetric-key algorithm. When it has been decrypted, it contains other 23 | * packets (usually a literal data packet or compressed data packet, but in 24 | * theory other Symmetrically Encrypted Data packets or sequences of packets 25 | * that form whole OpenPGP messages). 26 | * @requires crypto 27 | * @requires enums 28 | * @module packet/symmetrically_encrypted 29 | */ 30 | 31 | 'use strict'; 32 | 33 | import crypto from '../crypto'; 34 | import enums from '../enums.js'; 35 | import config from '../config'; 36 | 37 | /** 38 | * @constructor 39 | */ 40 | export default function SymmetricallyEncrypted() { 41 | this.tag = enums.packet.symmetricallyEncrypted; 42 | this.encrypted = null; 43 | /** Decrypted packets contained within. 44 | * @type {module:packet/packetlist} */ 45 | this.packets = null; 46 | this.ignore_mdc_error = config.ignore_mdc_error; 47 | } 48 | 49 | SymmetricallyEncrypted.prototype.read = function (bytes) { 50 | this.encrypted = bytes; 51 | }; 52 | 53 | SymmetricallyEncrypted.prototype.write = function () { 54 | return this.encrypted; 55 | }; 56 | 57 | /** 58 | * Symmetrically decrypt the packet data 59 | * 60 | * @param {module:enums.symmetric} sessionKeyAlgorithm 61 | * Symmetric key algorithm to use // See {@link http://tools.ietf.org/html/rfc4880#section-9.2|RFC4880 9.2} 62 | * @param {String} key 63 | * Key as string with the corresponding length to the 64 | * algorithm 65 | */ 66 | SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) { 67 | var decrypted = crypto.cfb.decrypt(sessionKeyAlgorithm, key, this.encrypted, true); 68 | 69 | // for modern cipher (blocklength != 64 bit, except for Twofish) MDC is required 70 | if (!this.ignore_mdc_error && 71 | (sessionKeyAlgorithm === 'aes128' || 72 | sessionKeyAlgorithm === 'aes192' || 73 | sessionKeyAlgorithm === 'aes256')) { 74 | throw new Error('Decryption failed due to missing MDC in combination with modern cipher.'); 75 | } 76 | 77 | this.packets.read(decrypted); 78 | }; 79 | 80 | SymmetricallyEncrypted.prototype.encrypt = function (algo, key) { 81 | var data = this.packets.write(); 82 | 83 | this.encrypted = crypto.cfb.encrypt( 84 | crypto.getPrefixRandom(algo), algo, data, key, true); 85 | }; 86 | -------------------------------------------------------------------------------- /src/packet/trust.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @requires enums 3 | * @module packet/trust 4 | */ 5 | 6 | 'use strict'; 7 | 8 | import enums from '../enums.js'; 9 | 10 | /** 11 | * @constructor 12 | */ 13 | export default function Trust() { 14 | this.tag = enums.packet.trust; 15 | } 16 | 17 | /** 18 | * Parsing function for a trust packet (tag 12). 19 | * Currently empty as we ignore trust packets 20 | * @param {String} byptes payload of a tag 12 packet 21 | */ 22 | Trust.prototype.read = function () {}; 23 | -------------------------------------------------------------------------------- /src/packet/user_attribute.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the User Attribute Packet (Tag 17)
20 | *
21 | * The User Attribute packet is a variation of the User ID packet. It 22 | * is capable of storing more types of data than the User ID packet, 23 | * which is limited to text. Like the User ID packet, a User Attribute 24 | * packet may be certified by the key owner ("self-signed") or any other 25 | * key owner who cares to certify it. Except as noted, a User Attribute 26 | * packet may be used anywhere that a User ID packet may be used. 27 | *
28 | * While User Attribute packets are not a required part of the OpenPGP 29 | * standard, implementations SHOULD provide at least enough 30 | * compatibility to properly handle a certification signature on the 31 | * User Attribute packet. A simple way to do this is by treating the 32 | * User Attribute packet as a User ID packet with opaque contents, but 33 | * an implementation may use any method desired. 34 | * module packet/user_attribute 35 | * @requires enums 36 | * @module packet/user_attribute 37 | */ 38 | 39 | 'use strict'; 40 | 41 | import util from '../util.js'; 42 | import packet from './packet.js'; 43 | import enums from '../enums.js'; 44 | 45 | /** 46 | * @constructor 47 | */ 48 | export default function UserAttribute() { 49 | this.tag = enums.packet.userAttribute; 50 | this.attributes = []; 51 | } 52 | 53 | /** 54 | * parsing function for a user attribute packet (tag 17). 55 | * @param {Uint8Array} input payload of a tag 17 packet 56 | */ 57 | UserAttribute.prototype.read = function(bytes) { 58 | var i = 0; 59 | while (i < bytes.length) { 60 | var len = packet.readSimpleLength(bytes.subarray(i, bytes.length)); 61 | i += len.offset; 62 | 63 | this.attributes.push(util.Uint8Array2str(bytes.subarray(i, i + len.len))); 64 | i += len.len; 65 | } 66 | }; 67 | 68 | /** 69 | * Creates a binary representation of the user attribute packet 70 | * @return {Uint8Array} string representation 71 | */ 72 | UserAttribute.prototype.write = function() { 73 | var arr = []; 74 | for (var i = 0; i < this.attributes.length; i++) { 75 | arr.push(packet.writeSimpleLength(this.attributes[i].length)); 76 | arr.push(util.str2Uint8Array(this.attributes[i])); 77 | } 78 | return util.concatUint8Array(arr); 79 | }; 80 | 81 | /** 82 | * Compare for equality 83 | * @param {module:user_attribute~UserAttribute} usrAttr 84 | * @return {Boolean} true if equal 85 | */ 86 | UserAttribute.prototype.equals = function(usrAttr) { 87 | if (!usrAttr || !(usrAttr instanceof UserAttribute)) { 88 | return false; 89 | } 90 | return this.attributes.every(function(attr, index) { 91 | return attr === usrAttr.attributes[index]; 92 | }); 93 | }; 94 | -------------------------------------------------------------------------------- /src/packet/userid.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the User ID Packet (Tag 13)
20 | *
21 | * A User ID packet consists of UTF-8 text that is intended to represent 22 | * the name and email address of the key holder. By convention, it 23 | * includes an RFC 2822 [RFC2822] mail name-addr, but there are no 24 | * restrictions on its content. The packet length in the header 25 | * specifies the length of the User ID. 26 | * @requires util 27 | * @requires enums 28 | * @module packet/userid 29 | */ 30 | 31 | 'use strict'; 32 | 33 | import util from '../util.js'; 34 | import enums from '../enums.js'; 35 | 36 | /** 37 | * @constructor 38 | */ 39 | export default function Userid() { 40 | this.tag = enums.packet.userid; 41 | /** A string containing the user id. Usually in the form 42 | * John Doe 43 | * @type {String} 44 | */ 45 | this.userid = ''; 46 | } 47 | 48 | /** 49 | * Parsing function for a user id packet (tag 13). 50 | * @param {Uint8Array} input payload of a tag 13 packet 51 | */ 52 | Userid.prototype.read = function (bytes) { 53 | this.userid = util.decode_utf8(util.Uint8Array2str(bytes)); 54 | }; 55 | 56 | /** 57 | * Creates a binary representation of the user id packet 58 | * @return {Uint8Array} binary representation 59 | */ 60 | Userid.prototype.write = function () { 61 | return util.str2Uint8Array(util.encode_utf8(this.userid)); 62 | }; 63 | -------------------------------------------------------------------------------- /src/type/keyid.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of type key id ({@link http://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3})
20 | *
21 | * A Key ID is an eight-octet scalar that identifies a key. 22 | * Implementations SHOULD NOT assume that Key IDs are unique. The 23 | * section "Enhanced Key Formats" below describes how Key IDs are 24 | * formed. 25 | * @requires util 26 | * @module type/keyid 27 | */ 28 | 29 | 'use strict'; 30 | 31 | import util from '../util.js'; 32 | 33 | /** 34 | * @constructor 35 | */ 36 | export default function Keyid() { 37 | this.bytes = ''; 38 | } 39 | 40 | /** 41 | * Parsing method for a key id 42 | * @param {String} input Input to read the key id from 43 | */ 44 | Keyid.prototype.read = function(bytes) { 45 | this.bytes = util.Uint8Array2str(bytes.subarray(0, 8)); 46 | }; 47 | 48 | Keyid.prototype.write = function() { 49 | return util.str2Uint8Array(this.bytes); 50 | }; 51 | 52 | Keyid.prototype.toHex = function() { 53 | return util.hexstrdump(this.bytes); 54 | }; 55 | 56 | Keyid.prototype.equals = function(keyid) { 57 | return this.bytes === keyid.bytes; 58 | }; 59 | 60 | Keyid.prototype.isNull = function() { 61 | return this.bytes === ''; 62 | }; 63 | 64 | Keyid.mapToHex = function (keyId) { 65 | return keyId.toHex(); 66 | }; 67 | 68 | Keyid.fromClone = function (clone) { 69 | var keyid = new Keyid(); 70 | keyid.bytes = clone.bytes; 71 | return keyid; 72 | }; 73 | 74 | Keyid.fromId = function (hex) { 75 | var keyid = new Keyid(); 76 | keyid.read(util.hex2bin(hex)); 77 | return keyid; 78 | }; 79 | -------------------------------------------------------------------------------- /src/type/mpi.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | // Hint: We hold our MPIs as an array of octets in big endian format preceeding a two 19 | // octet scalar: MPI: [a,b,c,d,e,f] 20 | // - MPI size: (a << 8) | b 21 | // - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8) 22 | 23 | /** 24 | * Implementation of type MPI ({@link http://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2})
25 | *
26 | * Multiprecision integers (also called MPIs) are unsigned integers used 27 | * to hold large integers such as the ones used in cryptographic 28 | * calculations. 29 | * An MPI consists of two pieces: a two-octet scalar that is the length 30 | * of the MPI in bits followed by a string of octets that contain the 31 | * actual integer. 32 | * @requires crypto/public_key/jsbn 33 | * @requires util 34 | * @module type/mpi 35 | */ 36 | 37 | 'use strict'; 38 | 39 | import BigInteger from '../crypto/public_key/jsbn.js'; 40 | import util from '../util.js'; 41 | 42 | /** 43 | * @constructor 44 | */ 45 | export default function MPI() { 46 | /** An implementation dependent integer */ 47 | this.data = null; 48 | } 49 | 50 | /** 51 | * Parsing function for a mpi ({@link http://tools.ietf.org/html/rfc4880#section3.2|RFC 4880 3.2}). 52 | * @param {String} input Payload of mpi data 53 | * @return {Integer} Length of data read 54 | */ 55 | MPI.prototype.read = function (bytes) { 56 | 57 | if(typeof bytes === 'string' || String.prototype.isPrototypeOf(bytes)) { 58 | bytes = util.str2Uint8Array(bytes); 59 | } 60 | 61 | var bits = (bytes[0] << 8) | bytes[1]; 62 | 63 | // Additional rules: 64 | // 65 | // The size of an MPI is ((MPI.length + 7) / 8) + 2 octets. 66 | // 67 | // The length field of an MPI describes the length starting from its 68 | // most significant non-zero bit. Thus, the MPI [00 02 01] is not 69 | // formed correctly. It should be [00 01 01]. 70 | 71 | // TODO: Verification of this size method! This size calculation as 72 | // specified above is not applicable in JavaScript 73 | var bytelen = Math.ceil(bits / 8); 74 | 75 | var raw = util.Uint8Array2str(bytes.subarray(2, 2 + bytelen)); 76 | this.fromBytes(raw); 77 | 78 | return 2 + bytelen; 79 | }; 80 | 81 | MPI.prototype.fromBytes = function (bytes) { 82 | this.data = new BigInteger(util.hexstrdump(bytes), 16); 83 | }; 84 | 85 | MPI.prototype.toBytes = function () { 86 | var bytes = util.Uint8Array2str(this.write()); 87 | return bytes.substr(2); 88 | }; 89 | 90 | MPI.prototype.byteLength = function () { 91 | return this.toBytes().length; 92 | }; 93 | 94 | /** 95 | * Converts the mpi object to a bytes as specified in {@link http://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2} 96 | * @return {Uint8Aray} mpi Byte representation 97 | */ 98 | MPI.prototype.write = function () { 99 | return util.str2Uint8Array(this.data.toMPI()); 100 | }; 101 | 102 | MPI.prototype.toBigInteger = function () { 103 | return this.data.clone(); 104 | }; 105 | 106 | MPI.prototype.fromBigInteger = function (bn) { 107 | this.data = bn.clone(); 108 | }; 109 | 110 | MPI.fromClone = function (clone) { 111 | clone.data.copyTo = BigInteger.prototype.copyTo; 112 | var bn = new BigInteger(); 113 | clone.data.copyTo(bn); 114 | var mpi = new MPI(); 115 | mpi.data = bn; 116 | return mpi; 117 | }; 118 | -------------------------------------------------------------------------------- /src/type/s2k.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /** 19 | * Implementation of the String-to-key specifier ({@link http://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7})
20 | *
21 | * String-to-key (S2K) specifiers are used to convert passphrase strings 22 | * into symmetric-key encryption/decryption keys. They are used in two 23 | * places, currently: to encrypt the secret part of private keys in the 24 | * private keyring, and to convert passphrases to encryption keys for 25 | * symmetrically encrypted messages. 26 | * @requires crypto 27 | * @requires enums 28 | * @requires util 29 | * @module type/s2k 30 | */ 31 | 32 | 'use strict'; 33 | 34 | import enums from '../enums.js'; 35 | import util from '../util.js'; 36 | import crypto from '../crypto'; 37 | 38 | /** 39 | * @constructor 40 | */ 41 | export default function S2K() { 42 | /** @type {module:enums.hash} */ 43 | this.algorithm = 'sha256'; 44 | /** @type {module:enums.s2k} */ 45 | this.type = 'iterated'; 46 | this.c = 96; 47 | /** Eight bytes of salt in a binary string. 48 | * @type {String} 49 | */ 50 | this.salt = crypto.random.getRandomBytes(8); 51 | } 52 | 53 | S2K.prototype.get_count = function () { 54 | // Exponent bias, defined in RFC4880 55 | var expbias = 6; 56 | 57 | return (16 + (this.c & 15)) << ((this.c >> 4) + expbias); 58 | }; 59 | 60 | /** 61 | * Parsing function for a string-to-key specifier ({@link http://tools.ietf.org/html/rfc4880#section-3.7|RFC 4880 3.7}). 62 | * @param {String} input Payload of string-to-key specifier 63 | * @return {Integer} Actual length of the object 64 | */ 65 | S2K.prototype.read = function (bytes) { 66 | var i = 0; 67 | this.type = enums.read(enums.s2k, bytes[i++]); 68 | this.algorithm = enums.read(enums.hash, bytes[i++]); 69 | 70 | switch (this.type) { 71 | case 'simple': 72 | break; 73 | 74 | case 'salted': 75 | this.salt = bytes.subarray(i, i + 8); 76 | i += 8; 77 | break; 78 | 79 | case 'iterated': 80 | this.salt = bytes.subarray(i, i + 8); 81 | i += 8; 82 | 83 | // Octet 10: count, a one-octet, coded value 84 | this.c = bytes[i++]; 85 | break; 86 | 87 | case 'gnu': 88 | if (util.Uint8Array2str(bytes.subarray(i, 3)) === "GNU") { 89 | i += 3; // GNU 90 | var gnuExtType = 1000 + bytes[i++]; 91 | if (gnuExtType === 1001) { 92 | this.type = gnuExtType; 93 | // GnuPG extension mode 1001 -- don't write secret key at all 94 | } else { 95 | throw new Error("Unknown s2k gnu protection mode."); 96 | } 97 | } else { 98 | throw new Error("Unknown s2k type."); 99 | } 100 | break; 101 | 102 | default: 103 | throw new Error("Unknown s2k type."); 104 | } 105 | 106 | return i; 107 | }; 108 | 109 | 110 | /** 111 | * Serializes s2k information 112 | * @return {Uint8Array} binary representation of s2k 113 | */ 114 | S2K.prototype.write = function () { 115 | 116 | var arr = [new Uint8Array([enums.write(enums.s2k, this.type), enums.write(enums.hash, this.algorithm)])]; 117 | 118 | switch (this.type) { 119 | case 'simple': 120 | break; 121 | case 'salted': 122 | arr.push(this.salt); 123 | break; 124 | case 'iterated': 125 | arr.push(this.salt); 126 | arr.push(new Uint8Array([this.c])); 127 | break; 128 | case 'gnu': 129 | throw new Error("GNU s2k type not supported."); 130 | default: 131 | throw new Error("Unknown s2k type."); 132 | } 133 | 134 | return util.concatUint8Array(arr); 135 | }; 136 | 137 | /** 138 | * Produces a key using the specified passphrase and the defined 139 | * hashAlgorithm 140 | * @param {String} passphrase Passphrase containing user input 141 | * @return {Uint8Array} Produced key with a length corresponding to 142 | * hashAlgorithm hash length 143 | */ 144 | S2K.prototype.produce_key = function (passphrase, numBytes) { 145 | passphrase = util.str2Uint8Array(util.encode_utf8(passphrase)); 146 | 147 | function round(prefix, s2k) { 148 | var algorithm = enums.write(enums.hash, s2k.algorithm); 149 | 150 | switch (s2k.type) { 151 | case 'simple': 152 | return crypto.hash.digest(algorithm, util.concatUint8Array([prefix,passphrase])); 153 | 154 | case 'salted': 155 | return crypto.hash.digest(algorithm, 156 | util.concatUint8Array([prefix, s2k.salt, passphrase])); 157 | 158 | case 'iterated': 159 | var isp = [], 160 | count = s2k.get_count(), 161 | data = util.concatUint8Array([s2k.salt,passphrase]); 162 | 163 | while (isp.length * data.length < count) { 164 | isp.push(data); 165 | } 166 | 167 | isp = util.concatUint8Array(isp); 168 | 169 | if (isp.length > count) { 170 | isp = isp.subarray(0, count); 171 | } 172 | 173 | return crypto.hash.digest(algorithm, util.concatUint8Array([prefix,isp])); 174 | 175 | case 'gnu': 176 | throw new Error("GNU s2k type not supported."); 177 | 178 | default: 179 | throw new Error("Unknown s2k type."); 180 | } 181 | } 182 | 183 | var arr = [], 184 | rlength = 0, 185 | prefix = new Uint8Array(numBytes); 186 | 187 | for(var i = 0; i { 39 | throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')'); 40 | }; 41 | this.seedRandom(INITIAL_RANDOM_SEED); 42 | // FIFO 43 | this.tasks = []; 44 | if (config) { 45 | this.worker.postMessage({ event:'configure', config }); 46 | } 47 | } 48 | 49 | /** 50 | * Message handling 51 | */ 52 | AsyncProxy.prototype.onMessage = function(event) { 53 | const msg = event.data; 54 | switch (msg.event) { 55 | case 'method-return': 56 | if (msg.err) { 57 | // fail 58 | this.tasks.shift().reject(new Error(msg.err)); 59 | } else { 60 | // success 61 | this.tasks.shift().resolve(msg.data); 62 | } 63 | break; 64 | case 'request-seed': 65 | this.seedRandom(RANDOM_SEED_REQUEST); 66 | break; 67 | default: 68 | throw new Error('Unknown Worker Event.'); 69 | } 70 | }; 71 | 72 | /** 73 | * Send message to worker with random data 74 | * @param {Integer} size Number of bytes to send 75 | */ 76 | AsyncProxy.prototype.seedRandom = function(size) { 77 | const buf = this.getRandomBuffer(size); 78 | this.worker.postMessage({ event:'seed-random', buf }, util.getTransferables.call(util, buf)); 79 | }; 80 | 81 | /** 82 | * Get Uint8Array with random numbers 83 | * @param {Integer} size Length of buffer 84 | * @return {Uint8Array} 85 | */ 86 | AsyncProxy.prototype.getRandomBuffer = function(size) { 87 | if (!size) { 88 | return null; 89 | } 90 | const buf = new Uint8Array(size); 91 | crypto.random.getRandomValues(buf); 92 | return buf; 93 | }; 94 | 95 | /** 96 | * Terminates the worker 97 | */ 98 | AsyncProxy.prototype.terminate = function() { 99 | this.worker.terminate(); 100 | }; 101 | 102 | /** 103 | * Generic proxy function that handles all commands from the public api. 104 | * @param {String} method the public api function to be delegated to the worker thread 105 | * @param {Object} options the api function's options 106 | * @return {Promise} see the corresponding public api functions for their return types 107 | */ 108 | AsyncProxy.prototype.delegate = function(method, options) { 109 | return new Promise((resolve, reject) => { 110 | // clone packets (for web worker structured cloning algorithm) 111 | this.worker.postMessage({ event:method, options:packet.clone.clonePackets(options) }, util.getTransferables.call(util, options)); 112 | 113 | // remember to handle parsing cloned packets from worker 114 | this.tasks.push({ resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject }); 115 | }); 116 | }; 117 | -------------------------------------------------------------------------------- /src/worker/worker.js: -------------------------------------------------------------------------------- 1 | // GPG4Browsers - An OpenPGP implementation in javascript 2 | // Copyright (C) 2011 Recurity Labs GmbH 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public 15 | // License along with this library; if not, write to the Free Software 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | 18 | /* globals self: true */ 19 | 20 | self.window = {}; // to make UMD bundles work 21 | 22 | importScripts('openpgp.js'); 23 | var openpgp = window.openpgp; 24 | 25 | var MIN_SIZE_RANDOM_BUFFER = 40000; 26 | var MAX_SIZE_RANDOM_BUFFER = 60000; 27 | 28 | openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); 29 | 30 | self.onmessage = function (event) { 31 | var msg = event.data || {}, 32 | options = msg.options || {}; 33 | 34 | switch (msg.event) { 35 | case 'configure': 36 | for (var i in msg.config) { 37 | openpgp.config[i] = msg.config[i]; 38 | } 39 | break; 40 | 41 | case 'seed-random': 42 | if (!(msg.buf instanceof Uint8Array)) { 43 | msg.buf = new Uint8Array(msg.buf); 44 | } 45 | openpgp.crypto.random.randomBuffer.set(msg.buf); 46 | break; 47 | 48 | default: 49 | if (typeof openpgp[msg.event] !== 'function') { 50 | throw new Error('Unknown Worker Event'); 51 | } 52 | 53 | // parse cloned packets 54 | openpgp[msg.event](openpgp.packet.clone.parseClonedPackets(options, msg.event)).then(function(data) { 55 | // clone packets (for web worker structured cloning algorithm) 56 | response({ event:'method-return', data:openpgp.packet.clone.clonePackets(data) }); 57 | }).catch(function(e) { 58 | response({ event:'method-return', err:e.message }); 59 | }); 60 | } 61 | }; 62 | 63 | function response(event) { 64 | if (openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) { 65 | self.postMessage({event: 'request-seed'}); 66 | } 67 | self.postMessage(event, openpgp.util.getTransferables.call(openpgp.util, event.data)); 68 | } --------------------------------------------------------------------------------