├── .rvmrc ├── plugins └── siriproxy-example │ ├── Rakefile │ ├── .gitignore │ ├── Gemfile │ ├── siriproxy-example.gemspec │ └── lib │ └── siriproxy-example.rb ├── lib ├── siriproxy │ ├── version.rb │ ├── connection │ │ ├── guzzoni.rb │ │ └── iphone.rb │ ├── plugin.rb │ ├── plugin.rb.orig │ ├── interpret_siri.rb │ ├── plugin_manager.rb │ ├── command_line.rb │ └── connection.rb ├── siriproxy.rb └── siri_objects.rb ├── bin └── siriproxy ├── .gitignore ├── Rakefile ├── Gemfile ├── config.example.yml ├── siriproxy.gemspec ├── scripts ├── gen_certs.sh └── openssl.cnf └── README.md /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm 1.9.3@SiriProxy --create 2 | -------------------------------------------------------------------------------- /plugins/siriproxy-example/Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /lib/siriproxy/version.rb: -------------------------------------------------------------------------------- 1 | class SiriProxy 2 | VERSION = "0.3.0" 3 | end 4 | -------------------------------------------------------------------------------- /plugins/siriproxy-example/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | -------------------------------------------------------------------------------- /plugins/siriproxy-example/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in siriproxy-example.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /bin/siriproxy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') 3 | 4 | require 'siriproxy/command_line' 5 | 6 | SiriProxy::CommandLine.new 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | server.passless.crt 2 | server.passless.key 3 | .DS_Store 4 | demoCA/ 5 | newkey.pem 6 | newreq.pem 7 | config.yml 8 | Gemfile.lock 9 | *.gem 10 | .bundle 11 | pkg/* 12 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require "bundler/gem_tasks" 4 | 5 | Rake::TestTask.new do |t| 6 | t.libs << "test" 7 | t.test_files = FileList['test/*.rb'] 8 | t.verbose = true 9 | end 10 | -------------------------------------------------------------------------------- /lib/siriproxy/connection/guzzoni.rb: -------------------------------------------------------------------------------- 1 | ##### 2 | # This is the connection to the Guzzoni (the Siri server backend) 3 | ##### 4 | class SiriProxy::Connection::Guzzoni < SiriProxy::Connection 5 | def initialize 6 | super 7 | self.name = "Guzzoni" 8 | end 9 | 10 | def connection_completed 11 | super 12 | start_tls(:verify_peer => false) 13 | end 14 | 15 | def received_object(object) 16 | return plugin_manager.process_filters(object, :from_guzzoni) 17 | 18 | #plugin_manager.object_from_guzzoni(object, self) 19 | end 20 | 21 | def block_rest_of_session 22 | @block_rest_of_session = true 23 | end 24 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :gemcutter 2 | 3 | gemspec 4 | 5 | # load plugins 6 | require 'yaml' 7 | require 'ostruct' 8 | 9 | if !File.exists?(File.expand_path('~/.siriproxy/config.yml')) 10 | $stderr.puts "config.yml not found. Copy config.example.yml to config.yml, then modify it." 11 | exit 1 12 | end 13 | 14 | gem 'cora', :git => "git://github.com/chendo/cora.git" 15 | 16 | config = OpenStruct.new(YAML.load_file(File.expand_path('~/.siriproxy/config.yml'))) 17 | if config.plugins 18 | config.plugins.each do |plugin| 19 | if plugin.is_a? String 20 | gem "siriproxy-#{plugin.downcase}" 21 | else 22 | gem "siriproxy-#{plugin['gem'] || plugin['name'].downcase}", :path => plugin['path'], :git => plugin['git'], :branch => plugin['branch'], :require => plugin['require'] 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /plugins/siriproxy-example/siriproxy-example.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "siriproxy-example" 6 | s.version = "0.0.1" 7 | s.authors = ["plamoni"] 8 | s.email = [""] 9 | s.homepage = "" 10 | s.summary = %q{An Example Siri Proxy Plugin} 11 | s.description = %q{This is a "hello world" style plugin. It simply intercepts the phrase "text siri proxy" and responds with a message about the proxy being up and running. This is good base code for other plugins. } 12 | 13 | s.rubyforge_project = "siriproxy-example" 14 | 15 | s.files = `git ls-files 2> /dev/null`.split("\n") 16 | s.test_files = `git ls-files -- {test,spec,features}/* 2> /dev/null`.split("\n") 17 | s.executables = `git ls-files -- bin/* 2> /dev/null`.split("\n").map{ |f| File.basename(f) } 18 | s.require_paths = ["lib"] 19 | 20 | # specify any dependencies here; for example: 21 | # s.add_development_dependency "rspec" 22 | # s.add_runtime_dependency "rest-client" 23 | end 24 | -------------------------------------------------------------------------------- /lib/siriproxy/connection/iphone.rb: -------------------------------------------------------------------------------- 1 | ##### 2 | # This is the connection to the iPhone 3 | ##### 4 | class SiriProxy::Connection::Iphone < SiriProxy::Connection 5 | def initialize 6 | puts "Create server for iPhone connection" 7 | super 8 | self.name = "iPhone" 9 | end 10 | 11 | def post_init 12 | super 13 | start_tls(:cert_chain_file => File.expand_path("~/.siriproxy/server.passless.crt"), 14 | :private_key_file => File.expand_path("~/.siriproxy/server.passless.key"), 15 | :verify_peer => false) 16 | end 17 | 18 | def ssl_handshake_completed 19 | super 20 | self.other_connection = EventMachine.connect('guzzoni.apple.com', 443, SiriProxy::Connection::Guzzoni) 21 | self.plugin_manager.guzzoni_conn = self.other_connection 22 | other_connection.other_connection = self #hehe 23 | other_connection.plugin_manager = plugin_manager 24 | end 25 | 26 | def received_object(object) 27 | return plugin_manager.process_filters(object, :from_iphone) 28 | 29 | #plugin_manager.object_from_client(object, self) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/siriproxy.rb: -------------------------------------------------------------------------------- 1 | require 'eventmachine' 2 | require 'zlib' 3 | require 'pp' 4 | 5 | class String 6 | def to_hex(seperator=" ") 7 | bytes.to_a.map{|i| i.to_s(16).rjust(2, '0')}.join(seperator) 8 | end 9 | end 10 | 11 | class SiriProxy 12 | 13 | def initialize() 14 | # @todo shouldnt need this, make centralize logging instead 15 | $LOG_LEVEL = $APP_CONFIG.log_level.to_i 16 | EventMachine.run do 17 | begin 18 | puts "Starting SiriProxy on port #{$APP_CONFIG.port}.." 19 | EventMachine::start_server('0.0.0.0', $APP_CONFIG.port, SiriProxy::Connection::Iphone) { |conn| 20 | $stderr.puts "start conn #{conn.inspect}" 21 | conn.plugin_manager = SiriProxy::PluginManager.new() 22 | conn.plugin_manager.iphone_conn = conn 23 | } 24 | rescue RuntimeError => err 25 | if err.message == "no acceptor" 26 | raise "Cannot start the server on port #{$APP_CONFIG.port} - are you root, or have another process on this port already?" 27 | else 28 | raise 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /config.example.yml: -------------------------------------------------------------------------------- 1 | port: 443 2 | log_level: 1 3 | plugins: 4 | # NOTE: run bundle after changing plugin configurations to update required gems 5 | 6 | - name: 'Example' 7 | path: './plugins/siriproxy-example' 8 | 9 | # - name: 'Thermostat' 10 | # git: 'git://github.com/plamoni/SiriProxy-Thermostat.git' 11 | # host: '192.168.2.71' 12 | 13 | # - name: 'Twitter' 14 | # path: './plugins/siriproxy-twitter' # path works just like specifing in gemfile 15 | # consumer_key: "YOUR_KEY" 16 | # consumer_secret: "YOUR_SECRET" 17 | # oauth_token: "YOUR_TOKEN" 18 | # oauth_token_secret: "YOUR_TOKEN_SECRET" 19 | 20 | # Note: Eliza should not be run with other plugins 21 | # - name: 'Eliza' 22 | # path: './plugins/siriproxy-eliza' # path works just like specifing in gemfile 23 | 24 | # Below are not actual plugins, just further example of config options 25 | 26 | # - SimplePlugin # simple syntax for plugins that are in rubygems and have no config 27 | 28 | # - name: 'AnotherPlugin' 29 | # git: 'git://github.com/netpro2k/SiriProxy-AnotherPlugin.git' # git works just like specifying in Gemfile 30 | -------------------------------------------------------------------------------- /siriproxy.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "siriproxy/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "siriproxy" 7 | s.version = SiriProxy::VERSION 8 | s.authors = ["plamoni", "chendo", "netpro2k"] 9 | s.email = [] 10 | s.homepage = "" 11 | s.summary = %q{A (tampering) proxy server for Apple's Siri} 12 | s.description = %q{Siri Proxy is a proxy server for Apple's Siri "assistant." The idea is to allow for the creation of custom handlers for different actions. This can allow developers to easily add functionality to Siri.} 13 | 14 | s.rubyforge_project = "siriproxy" 15 | 16 | s.files = `git ls-files 2> /dev/null`.split("\n") 17 | s.test_files = `git ls-files -- {test,spec,features}/* 2> /dev/null`.split("\n") 18 | s.executables = `git ls-files -- bin/* 2> /dev/null`.split("\n").map{ |f| File.basename(f) } 19 | s.require_paths = ["lib"] 20 | 21 | s.required_ruby_version = Gem::Requirement.new(">= 1.9.2") 22 | 23 | s.add_runtime_dependency "CFPropertyList" 24 | s.add_runtime_dependency "eventmachine" 25 | s.add_runtime_dependency "uuidtools" 26 | s.add_development_dependency "rake" 27 | end 28 | -------------------------------------------------------------------------------- /lib/siriproxy/plugin.rb: -------------------------------------------------------------------------------- 1 | require 'cora' 2 | 3 | class SiriProxy::Plugin < Cora::Plugin 4 | def initialize(config) 5 | 6 | end 7 | 8 | def request_completed 9 | self.manager.send_request_complete_to_iphone 10 | end 11 | 12 | #use send_object(object, target: :guzzoni) to send to guzzoni 13 | def send_object(object, options={}) 14 | (object = object.to_hash) rescue nil #convert SiriObjects to a hash 15 | options[:target] = options[:target] ||= :iphone 16 | 17 | if(options[:target] == :iphone) 18 | self.manager.guzzoni_conn.inject_object_to_output_stream(object) 19 | elsif(options[:target] == :guzzoni) 20 | self.manager.iphone_conn.inject_object_to_output_stream(object) 21 | end 22 | end 23 | 24 | def last_ref_id 25 | self.manager.iphone_conn.last_ref_id 26 | end 27 | 28 | #direction should be :from_iphone, or :from_guzzoni 29 | def process_filters(object, direction) 30 | return nil if object == nil 31 | f = filters[object["class"]] 32 | if(f != nil && (f[:direction] == :both || f[:direction] == direction)) 33 | object = instance_exec(object, &f[:block]) 34 | end 35 | 36 | object 37 | end 38 | 39 | class << self 40 | def filter(class_names, options={}, &block) 41 | [class_names].flatten.each do |class_name| 42 | filters[class_name] = { 43 | direction: (options[:direction] ||= :both), 44 | block: block 45 | } 46 | end 47 | end 48 | 49 | def filters 50 | @filters ||= {} 51 | end 52 | end 53 | 54 | def filters 55 | self.class.filters 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /lib/siriproxy/plugin.rb.orig: -------------------------------------------------------------------------------- 1 | require 'cora' 2 | 3 | class SiriProxy::Plugin < Cora::Plugin 4 | def initialize(config) 5 | 6 | end 7 | 8 | def request_completed 9 | self.manager.send_request_complete_to_iphone 10 | end 11 | 12 | #use send_object(object, target: :guzzoni) to send to guzzoni 13 | def send_object(object, options={}) 14 | (object = object.to_hash) rescue nil #convert SiriObjects to a hash 15 | <<<<<<< HEAD 16 | options[:target] = options[:target] ||= :iphone 17 | 18 | ======= 19 | 20 | >>>>>>> fd2b325... Added initialize method that takes a config so plugins that don't define it won't break 21 | if(options[:target] == :iphone) 22 | self.manager.guzzoni_conn.inject_object_to_output_stream(object) 23 | elsif(options[:target] == :guzzoni) 24 | self.manager.iphone_conn.inject_object_to_output_stream(object) 25 | end 26 | end 27 | 28 | def last_ref_id 29 | self.manager.iphone_conn.last_ref_id 30 | end 31 | <<<<<<< HEAD 32 | 33 | #direction should be :from_iphone, or :from_guzzoni 34 | def process_filters(object, direction) 35 | return nil if object == nil 36 | f = filters[object["class"]] 37 | if(f != nil && (f[:direction] == :both || f[:direction] == direction)) 38 | object = instance_exec(object, &f[:block]) 39 | end 40 | 41 | object 42 | end 43 | 44 | class << self 45 | def filter(class_names, options={}, &block) 46 | [class_names].flatten.each do |class_name| 47 | filters[class_name] = { 48 | direction: (options[:direction] ||= :both), 49 | block: block 50 | } 51 | end 52 | end 53 | 54 | def filters 55 | @filters ||= {} 56 | end 57 | end 58 | 59 | def filters 60 | self.class.filters 61 | end 62 | 63 | end 64 | ======= 65 | end 66 | >>>>>>> fd2b325... Added initialize method that takes a config so plugins that don't define it won't break 67 | -------------------------------------------------------------------------------- /lib/siriproxy/interpret_siri.rb: -------------------------------------------------------------------------------- 1 | ###### 2 | # The idea behind this class is that you can call the different 3 | # methods to get different interpretations of a Siri object. 4 | # For instance, you can "unknown_intent" and it will check 5 | # to see if an object is a "Common#unknownIntent" response and 6 | # call the provided processor method with the appropriate info. 7 | # processor method signatures are provided in comments above each 8 | # method. 9 | # 10 | # each method will return "nil" if the object is not the valid 11 | # type. If it is, it will return the result of the processor. 12 | ##### 13 | class SiriProxy::Interpret 14 | class << self 15 | #Checks if the object is Guzzoni responding that it can't 16 | #determine the intent of the query 17 | #processor(object, connection, unknown_text) 18 | def unknown_intent(object, connection, processor) 19 | return false if object == nil 20 | return false if (!(object["properties"]["views"][0]["properties"]["dialogIdentifier"] == "Common#unknownIntent") rescue true) 21 | 22 | searchUtterance = object["properties"]["views"][1]["properties"]["commands"][0]["properties"]["commands"][0]["properties"]["utterance"] 23 | searchText = searchUtterance.split("^")[3] 24 | return processor.call(object, connection, searchText) 25 | 26 | return false 27 | end 28 | 29 | #Checks if the object is Guzzoni responding that it recognized 30 | #speech. Sends "best interpretation" phrase to processor 31 | #processor(object, connection, phrase) 32 | def speech_recognized(object) 33 | return nil if object == nil 34 | return nil if (!(object["class"] == "SpeechRecognized") rescue true) 35 | phrase = "" 36 | 37 | object["properties"]["recognition"]["properties"]["phrases"].map { |phraseObj| 38 | phraseObj["properties"]["interpretations"].first["properties"]["tokens"].map { |token| 39 | tokenProps = token["properties"] 40 | 41 | phrase = phrase[0..-2] if tokenProps["removeSpaceBefore"] 42 | phrase << tokenProps["text"] 43 | phrase << " " if !tokenProps["removeSpaceAfter"] 44 | } 45 | } 46 | 47 | phrase 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/siriproxy/plugin_manager.rb: -------------------------------------------------------------------------------- 1 | require 'cora' 2 | require 'pp' 3 | 4 | class SiriProxy::PluginManager < Cora 5 | attr_accessor :plugins, :iphone_conn, :guzzoni_conn 6 | 7 | def initialize() 8 | load_plugins() 9 | end 10 | 11 | def load_plugins() 12 | @plugins = [] 13 | if $APP_CONFIG.plugins 14 | $APP_CONFIG.plugins.each do |pluginConfig| 15 | if pluginConfig.is_a? String 16 | className = pluginConfig 17 | requireName = "siriproxy-#{className.downcase}" 18 | else 19 | className = pluginConfig['name'] 20 | requireName = pluginConfig['require'] || "siriproxy-#{className.downcase}" 21 | end 22 | require requireName 23 | plugin = SiriProxy::Plugin.const_get(className).new(pluginConfig) 24 | plugin.manager = self 25 | @plugins << plugin 26 | end 27 | end 28 | log "Plugins loaded: #{@plugins}" 29 | end 30 | 31 | def process_filters(object, direction) 32 | object_class = object.class #This way, if we change the object class we won't need to modify this code. 33 | plugins.each do |plugin| 34 | #log "Processing filters on #{plugin} for '#{object["class"]}'" 35 | new_obj = plugin.process_filters(object, direction) 36 | object = new_obj if(new_obj == false || new_obj.class == object_class) #prevent accidental poorly formed returns 37 | return nil if object == false #if any filter returns "false," then the object should be dropped 38 | end 39 | 40 | return object 41 | end 42 | 43 | def process(text) 44 | result = super(text) 45 | self.guzzoni_conn.block_rest_of_session if result 46 | return result 47 | end 48 | 49 | def send_request_complete_to_iphone 50 | log "Sending Request Completed" 51 | object = generate_request_completed(self.guzzoni_conn.last_ref_id) 52 | self.guzzoni_conn.inject_object_to_output_stream(object) 53 | end 54 | 55 | def respond(text, options={}) 56 | self.guzzoni_conn.inject_object_to_output_stream(generate_siri_utterance(self.guzzoni_conn.last_ref_id, text, (options[:spoken] or text), options[:prompt_for_response] == true)) 57 | end 58 | 59 | def no_matches 60 | return false 61 | end 62 | 63 | def log(text) 64 | puts "[Info - Plugin Manager] #{text}" if $LOG_LEVEL >= 1 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /scripts/gen_certs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | commonName=$2 4 | 5 | if [ "${commonName}" == "" ] 6 | then 7 | commonName="SiriProxyCA" 8 | fi 9 | 10 | # Feel free to change any of these defaults 11 | countryName="US" 12 | stateOrProvinceName="Missouri" 13 | localityName="" 14 | organizationName="Siri Proxy" 15 | organizationalUnitName="" 16 | emailAddress="" 17 | 18 | #You probably don't need to modify these unless you know what you're doing. 19 | SIRI_PROXY_ROOT=$1 20 | SIRI_PROXY_SETTINGS=~/.siriproxy 21 | LOG_FILE=$SIRI_PROXY_SETTINGS/cert.log 22 | TMP_DIR=/tmp 23 | TMP_CA_DIR=/tmp/siriCA #THIS ($dir) ALSO MUST BE MODIFIED IN openssl.cnf IF YOU CHANGE IT! 24 | 25 | ## Do not edit below here! 26 | 27 | echo "" > $LOG_FILE 28 | 29 | echo "Creating CA directory" 30 | mkdir -p $TMP_CA_DIR/{certs,crl,newcerts,private} 31 | touch $TMP_CA_DIR/index.txt 32 | echo 01 > $TMP_CA_DIR/crtnumber 33 | 34 | echo "Generating '${commonName}' CA request" 35 | echo "${countryName}" > $TMP_DIR/ca.args 36 | echo "${stateOrProvinceName}" >> $TMP_DIR/ca.args 37 | echo "${localityName}" >> $TMP_DIR/ca.args 38 | echo "${organizationName}" >> $TMP_DIR/ca.args 39 | echo "${organizationalUnitName}" >> $TMP_DIR/ca.args 40 | echo "${commonName}" >> $TMP_DIR/ca.args 41 | echo "${emailAddress}" >> $TMP_DIR/ca.args 42 | echo "" >> $TMP_DIR/ca.args 43 | echo "" >> $TMP_DIR/ca.args 44 | 45 | cat $TMP_DIR/ca.args | openssl req -new -config $SIRI_PROXY_ROOT/scripts/openssl.cnf -keyout $TMP_CA_DIR/private/cakey.pem -out $TMP_CA_DIR/careq.pem -passin pass:1234 -passout pass:1234 >> $LOG_FILE 2>> $LOG_FILE 46 | 47 | echo "Self-signing '${commonName}' CA" 48 | openssl ca -create_serial -passin pass:1234 -config $SIRI_PROXY_ROOT/scripts/openssl.cnf -out $TMP_CA_DIR/cacert.pem -outdir $TMP_CA_DIR/newcerts -days 1095 -batch -keyfile $TMP_CA_DIR/private/cakey.pem -selfsign -extensions v3_ca -infiles $TMP_CA_DIR/careq.pem >> $LOG_FILE 2>> $LOG_FILE 49 | 50 | echo "Generating guzzoni.apple.com certificate request" 51 | echo "Generating '${commonName}' CA request" 52 | echo "${countryName}" > $TMP_DIR/ca.args 53 | echo "${stateOrProvinceName}" >> $TMP_DIR/ca.args 54 | echo "${localityName}" >> $TMP_DIR/ca.args 55 | echo "${organizationName}" >> $TMP_DIR/ca.args 56 | echo "${organizationalUnitName}" >> $TMP_DIR/ca.args 57 | echo "guzzoni.apple.com" >> $TMP_DIR/ca.args 58 | echo "${emailAddress}" >> $TMP_DIR/ca.args 59 | echo "" >> $TMP_DIR/ca.args 60 | echo "" >> $TMP_DIR/ca.args 61 | cat $TMP_DIR/ca.args | openssl req -new -keyout $TMP_DIR/newkey.pem -config $SIRI_PROXY_ROOT/scripts/openssl.cnf -out $TMP_DIR/newreq.pem -days 1095 -passin pass:1234 -passout pass:1234 >> $LOG_FILE 2>> $LOG_FILE 62 | 63 | echo "Generating guzzoni.apple.com certificate" 64 | yes | openssl ca -policy policy_anything -out $TMP_DIR/newcert.pem -config $SIRI_PROXY_ROOT/scripts/openssl.cnf -passin pass:1234 -keyfile $TMP_CA_DIR/private/cakey.pem -cert $TMP_CA_DIR/cacert.pem -infiles $TMP_DIR/newreq.pem >> $LOG_FILE 2>> $LOG_FILE 65 | 66 | echo "Removing passphrase from guzzoni.apple.com key" 67 | yes | openssl rsa -in $TMP_DIR/newkey.pem -out $SIRI_PROXY_SETTINGS/server.passless.key -passin pass:1234 >> $LOG_FILE 2>> $LOG_FILE 68 | 69 | echo "Cleaning up..." 70 | mv $TMP_DIR/newcert.pem $SIRI_PROXY_SETTINGS/server.passless.crt 71 | mv $TMP_CA_DIR/cacert.pem $SIRI_PROXY_SETTINGS/ca.pem 72 | rm -rf $TMP_DIR/new{key,req}.pem $TMP_CA_DIR $TMP_DIR/ca.args 73 | 74 | echo "Done! (For details on any errors, check '${LOG_FILE}')" 75 | echo "-------------------------------------------------------------" 76 | echo "" 77 | echo "Please install ${SIRI_PROXY_SETTINGS}/ca.pem onto your phone!" 78 | echo "(Note: You can do this by emailing the file to yourself)" 79 | echo "" 80 | echo "-------------------------------------------------------------" -------------------------------------------------------------------------------- /plugins/siriproxy-example/lib/siriproxy-example.rb: -------------------------------------------------------------------------------- 1 | require 'cora' 2 | require 'siri_objects' 3 | require 'pp' 4 | 5 | ####### 6 | # This is a "hello world" style plugin. It simply intercepts the phrase "test siri proxy" and responds 7 | # with a message about the proxy being up and running (along with a couple other core features). This 8 | # is good base code for other plugins. 9 | # 10 | # Remember to add other plugins to the "config.yml" file if you create them! 11 | ###### 12 | 13 | class SiriProxy::Plugin::Example < SiriProxy::Plugin 14 | def initialize(config) 15 | #if you have custom configuration options, process them here! 16 | end 17 | 18 | #get the user's location and display it in the logs 19 | #filters are still in their early stages. Their interface may be modified 20 | filter "SetRequestOrigin", direction: :from_iphone do |object| 21 | puts "[Info - User Location] lat: #{object["properties"]["latitude"]}, long: #{object["properties"]["longitude"]}" 22 | 23 | #Note about returns from filters: 24 | # - Return false to stop the object from being forwarded 25 | # - Return a Hash to substitute or update the object 26 | # - Return nil (or anything not a Hash or false) to have the object forwarded (along with any 27 | # modifications made to it) 28 | end 29 | 30 | listen_for /test siri proxy/i do 31 | say "Siri Proxy is up and running!" #say something to the user! 32 | 33 | request_completed #always complete your request! Otherwise the phone will "spin" at the user! 34 | end 35 | 36 | #Demonstrate that you can have Siri say one thing and write another"! 37 | listen_for /you don't say/i do 38 | say "Sometimes I don't write what I say", spoken: "Sometimes I don't say what I write" 39 | end 40 | 41 | #demonstrate state change 42 | listen_for /siri proxy test state/i do 43 | set_state :some_state #set a state... this is useful when you want to change how you respond after certain conditions are met! 44 | say "I set the state, try saying 'confirm state change'" 45 | 46 | request_completed #always complete your request! Otherwise the phone will "spin" at the user! 47 | end 48 | 49 | listen_for /confirm state change/i, within_state: :some_state do #this only gets processed if you're within the :some_state state! 50 | say "State change works fine!" 51 | set_state nil #clear out the state! 52 | 53 | request_completed #always complete your request! Otherwise the phone will "spin" at the user! 54 | end 55 | 56 | #demonstrate asking a question 57 | listen_for /siri proxy test question/i do 58 | response = ask "Is this thing working?" #ask the user for something 59 | 60 | if(response =~ /yes/i) #process their response 61 | say "Great!" 62 | else 63 | say "You could have just said 'yes'!" 64 | end 65 | 66 | request_completed #always complete your request! Otherwise the phone will "spin" at the user! 67 | end 68 | 69 | #demonstrate capturing data from the user (e.x. "Siri proxy number 15") 70 | listen_for /siri proxy number ([0-9,]*[0-9])/i do |number| 71 | say "Detected number: #{number}" 72 | 73 | request_completed #always complete your request! Otherwise the phone will "spin" at the user! 74 | end 75 | 76 | #demonstrate injection of more complex objects without shortcut methods. 77 | listen_for /test map/i do 78 | add_views = SiriAddViews.new 79 | add_views.make_root(last_ref_id) 80 | map_snippet = SiriMapItemSnippet.new 81 | map_snippet.items << SiriMapItem.new 82 | utterance = SiriAssistantUtteranceView.new("Testing map injection!") 83 | add_views.views << utterance 84 | add_views.views << map_snippet 85 | 86 | #you can also do "send_object object, target: :guzzoni" in order to send an object to guzzoni 87 | send_object add_views #send_object takes a hash or a SiriObject object 88 | 89 | request_completed #always complete your request! Otherwise the phone will "spin" at the user! 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/siriproxy/command_line.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'yaml' 3 | require 'ostruct' 4 | 5 | # @todo want to make SiriProxy::Commandline without having to 6 | # require 'siriproxy'. Im sure theres a better way. 7 | class SiriProxy 8 | 9 | end 10 | 11 | class SiriProxy::CommandLine 12 | BANNER = <<-EOS 13 | Siri Proxy is a proxy server for Apple's Siri "assistant." The idea is to allow for the creation of custom handlers for different actions. This can allow developers to easily add functionality to Siri. 14 | 15 | See: http://github.com/westbaer/SiriProxy/ 16 | 17 | Usage: siriproxy COMMAND OPTIONS 18 | 19 | Commands: 20 | server Start up the Siri proxy server 21 | gencerts Generate a the certificates needed for SiriProxy 22 | bundle Install any dependancies needed by plugins 23 | console Launch the plugin test console 24 | update [dir] Updates to the latest code from GitHub or from a provided directory 25 | help Show this usage information 26 | 27 | Options: 28 | Option Command Description 29 | EOS 30 | 31 | def initialize 32 | @branch = nil 33 | parse_options 34 | command = ARGV.shift 35 | subcommand = ARGV.shift 36 | case command 37 | when 'server' then run_server(subcommand) 38 | when 'gencerts' then gen_certs 39 | when 'bundle' then run_bundle(subcommand) 40 | when 'console' then run_console 41 | when 'update' then update(subcommand) 42 | when 'help' then usage 43 | else usage 44 | end 45 | end 46 | 47 | def run_console 48 | load_code 49 | $LOG_LEVEL = 0 50 | # this is ugly, but works for now 51 | SiriProxy::PluginManager.class_eval do 52 | def respond(text, options={}) 53 | puts "=> #{text}" 54 | end 55 | def process(text) 56 | super(text) 57 | end 58 | def send_request_complete_to_iphone 59 | end 60 | def no_matches 61 | puts "No plugin responded" 62 | end 63 | end 64 | SiriProxy::Plugin.class_eval do 65 | def last_ref_id 66 | 0 67 | end 68 | def send_object(object, options={:target => :iphone}) 69 | puts "=> #{object}" 70 | end 71 | end 72 | 73 | cora = SiriProxy::PluginManager.new 74 | repl = -> prompt { print prompt; cora.process(gets.chomp!) } 75 | loop { repl[">> "] } 76 | end 77 | 78 | def run_bundle(subcommand='') 79 | setup_bundler_path 80 | puts `bundle #{subcommand} #{ARGV.join(' ')}` 81 | end 82 | 83 | def run_server(subcommand='start') 84 | load_code 85 | start_server 86 | # @todo: support for forking server into bg and start/stop/restart 87 | # subcommand ||= 'start' 88 | # case subcommand 89 | # when 'start' then start_server 90 | # when 'stop' then stop_server 91 | # when 'restart' then restart_server 92 | # end 93 | end 94 | 95 | def start_server 96 | proxy = SiriProxy.new 97 | proxy.start() 98 | end 99 | 100 | def gen_certs 101 | ca_name = @ca_name ||= "" 102 | command = File.join(File.dirname(__FILE__), '..', "..", "scripts", 'gen_certs.sh') 103 | sp_root = File.join(File.dirname(__FILE__), '..', "..") 104 | puts `#{command} "#{sp_root}" "#{ca_name}"` 105 | end 106 | 107 | def update(directory=nil) 108 | if(directory) 109 | puts "=== Installing from '#{directory}' ===" 110 | puts `cd #{directory} && rake install` 111 | puts "=== Bundling ===" if $?.exitstatus == 0 112 | puts `siriproxy bundle` if $?.exitstatus == 0 113 | puts "=== SUCCESS ===" if $?.exitstatus == 0 114 | 115 | exit $?.exitstatus 116 | else 117 | branch_opt = @branch ? "-b #{@branch}" : "" 118 | @branch = "master" if @branch == nil 119 | puts "=== Installing latest code from git://github.com/westbaer/SiriProxy.git [#{@branch}] ===" 120 | 121 | tmp_dir = "/tmp/SiriProxy.install." + (rand 9999).to_s.rjust(4, "0") 122 | 123 | `mkdir -p #{tmp_dir}` 124 | puts `git clone #{branch_opt} git://github.com/westbaer/SiriProxy.git #{tmp_dir}` if $?.exitstatus == 0 125 | puts "=== Performing Rake Install ===" if $?.exitstatus == 0 126 | puts `cd #{tmp_dir} && rake install` if $?.exitstatus == 0 127 | puts "=== Bundling ===" if $?.exitstatus == 0 128 | puts `siriproxy bundle` if $?.exitstatus == 0 129 | puts "=== Cleaning Up ===" and puts `rm -rf #{tmp_dir}` if $?.exitstatus == 0 130 | puts "=== SUCCESS ===" if $?.exitstatus == 0 131 | 132 | exit $?.exitstatus 133 | end 134 | end 135 | 136 | def usage 137 | puts "\n#{@option_parser}\n" 138 | end 139 | 140 | private 141 | 142 | def parse_options 143 | $APP_CONFIG = OpenStruct.new(YAML.load_file(File.expand_path('~/.siriproxy/config.yml'))) 144 | @branch = nil 145 | @option_parser = OptionParser.new do |opts| 146 | opts.on('-p', '--port PORT', '[server] port number for server (central or node)') do |port_num| 147 | $APP_CONFIG.port = port_num 148 | end 149 | opts.on('-l', '--log LOG_LEVEL', '[server] The level of debug information displayed (higher is more)') do |log_level| 150 | $APP_CONFIG.log_level = log_level 151 | end 152 | opts.on('-b', '--branch BRANCH', '[update] Choose the branch to update from (default: master)') do |branch| 153 | @branch = branch 154 | end 155 | opts.on('-n', '--name CA_NAME', '[gencerts] Define a common name for the CA (default: "SiriProxyCA")') do |ca_name| 156 | @ca_name = ca_name 157 | end 158 | opts.on_tail('-v', '--version', ' show version') do 159 | require "siriproxy/version" 160 | puts "SiriProxy version #{SiriProxy::VERSION}" 161 | exit 162 | end 163 | end 164 | @option_parser.banner = BANNER 165 | @option_parser.parse!(ARGV) 166 | end 167 | 168 | def setup_bundler_path 169 | require 'pathname' 170 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile", 171 | Pathname.new(__FILE__).realpath) 172 | end 173 | 174 | def load_code 175 | setup_bundler_path 176 | 177 | require 'bundler' 178 | require 'bundler/setup' 179 | 180 | require 'siriproxy' 181 | require 'siriproxy/connection' 182 | require 'siriproxy/connection/iphone' 183 | require 'siriproxy/connection/guzzoni' 184 | 185 | require 'siriproxy/plugin' 186 | require 'siriproxy/plugin_manager' 187 | end 188 | end 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Siri Proxy 2 | ========== 3 | 4 | About 5 | ----- 6 | Siri Proxy is a proxy server for Apple's Siri "assistant." The idea is to allow for the creation of custom handlers for different actions. This can allow developers to easily add functionality to Siri. 7 | 8 | The main example I provide is a plugin to control [my thermostat](http://www.radiothermostat.com/latestnews.html#advanced) with Siri. It responds to commands such as, "What's the status of the thermostat?", or "Set the thermostat to 68 degrees", or even "What's the inside temperature?" 9 | 10 | Notice About Plugins 11 | -------------------- 12 | 13 | We recently changed the way plugins work very significantly. That being the case, your old plugins won't work. 14 | 15 | New plugins should be independent Gems. Take a look at the included [example plugin](https://github.com/plamoni/SiriProxy/tree/master/plugins/siriproxy-example) for some inspiration. We will try to keep that file up to date with the latest features. 16 | 17 | The State of This Project 18 | ------------------------- 19 | 20 | Please remember that this project is super-pre-alpha right now. If you're not a developer with a good bit of experience with networks, you're probably not even going to get the proxy running. But if you do (we are willing to help to an extent, check the IRC chat and my Twitter feed [@plamoni](http://www.twitter.com/plamoni)), then test out building a plugin. It's very easy to do and takes almost no time at all for most experienced developers. Check the demo videos and other plugins below for inspiration! 21 | 22 | 23 | Find us on IRC 24 | -------------- 25 | 26 | We now have an IRC channel. Check out the #SiriProxy channel on irc.freenode.net. 27 | 28 | Demo Video 29 | ----------- 30 | 31 | See the system in action here: [http://www.youtube.com/watch?v=AN6wy0keQqo](http://www.youtube.com/watch?v=AN6wy0keQqo) 32 | 33 | More Demo Videos and Other Plugins 34 | ---------------------------------- 35 | 36 | For a list of current plugins and some more demo videos, check the [Plugins page](https://github.com/plamoni/SiriProxy/wiki/Plugins) on the wiki. 37 | 38 | Set-up Instructions 39 | ------------------- 40 | 41 | **Video of a complete installation on Ubuntu 11.10** 42 | 43 | [http://www.youtube.com/watch?v=GQXyJR6mOk0](http://www.youtube.com/watch?v=GQXyJR6mOk0) 44 | 45 | This is a video of a complete start-to-finish installation on a fresh install of Ubuntu 11.10. 46 | 47 | The commands used in the video can be found at [https://gist.github.com/1428474](https://gist.github.com/1428474). 48 | 49 | **Set up DNS** 50 | 51 | Before you can use SiriProxy, you must set up a DNS server on your network to forward requests for guzzoni.apple.com to the computer running the proxy (make sure that computer is not using your DNS server!). I recommend dnsmasq for this purpose. It's easy to get running and can easily handle this sort of behavior. ([http://www.youtube.com/watch?v=a9gO4L0U59s](http://www.youtube.com/watch?v=a9gO4L0U59s)) 52 | 53 | **Set up RVM and Ruby 1.9.3** 54 | 55 | If you don't already have Ruby 1.9.3 installed through RVM, please do so in order to make sure you can follow the steps later. Experts can ignore this. If you're unsure, follow these directions carefully: 56 | 57 | 1. Download and install RVM (if you don't have it already): 58 | * Download/install RVM: 59 | `bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)` 60 | * Activate RVM: 61 | `[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"` 62 | * (optional, but useful) Add RVM to your .bash_profile: 63 | `echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile` 64 | 2. Install Ruby 1.9.3 (if you don't have it already): 65 | `rvm install 1.9.3` 66 | 3. Set RVM to use/default to 1.9.3: 67 | `rvm use 1.9.3 --default` 68 | 69 | **Set up SiriProxy** 70 | 71 | Clone this repo locally, then navigate into the SiriProxy directory (the root of the repo). Then follow these instructions carefully. Note that nothing needs to be (or should be) done as root until you launch the server: 72 | 73 | 1. Install Rake and Bundler: 74 | `rvmsudo gem install rake bundler` 75 | 2. Install SiriProxy gem (do this from your SiriProxy directory): 76 | `rake install` 77 | 3. Make .siriproxy directory: 78 | `mkdir ~/.siriproxy` 79 | 4. Move default config file to .siriproxy (if you need to make configuration changes, do that now by editing the config.yml): 80 | `cp ./config.example.yml ~/.siriproxy/config.yml` 81 | 5. Generate certificates: 82 | `siriproxy gencerts` 83 | 6. Install `~/.siriproxy/ca.pem` on your phone. This can easily be done by emailing the file to yourself and clicking on it in the iPhone email app. Follow the prompts. 84 | 7. Bundle SiriProxy (this should be done every time you change the config.yml): 85 | `siriproxy bundle` 86 | 8. Start SiriProxy (must start as root because it uses a port < 1024): 87 | `rvmsudo siriproxy server` 88 | 9. Test that the server is running by saying "Test Siri Proxy" to your phone. 89 | 90 | Note: on some machines, rvmsudo changes "`~`" to "`/root/`". This means that you may need to symlink your "`.siriproxy`" directory to "`/root/`" in order to get the application to work: 91 | 92 | sudo ln -s ~/.siriproxy /root/.siriproxy 93 | 94 | **Updating SiriProxy** 95 | 96 | Once you're up and running, if you modify the code, or you want to grab the latest code from GitHub, you can do that easily using the "siriproxy update" command. Here's a couple of examples: 97 | 98 | siriproxy update 99 | 100 | Installs the latest code from the [master] branch on GitHub. 101 | 102 | siriproxy update /path/to/SiriProxy 103 | 104 | Installs the code from /path/to/SiriProxy 105 | 106 | siriproxy update -b gemify 107 | 108 | Installs the latest code from the [gemify] branch on GitHub 109 | 110 | 111 | FAQ 112 | --- 113 | 114 | **Will this let me run Siri on my iPhone 4, iPod Touch, iPhone 3G, Microwave, etc?** 115 | 116 | No. Please stop asking. 117 | 118 | **What is your opinion on h1siri, public SiriProxy servers, and other Siri "ports"?** 119 | 120 | Glad you asked! Watch this: [http://youtu.be/Y_Q6PfxBSbA](http://youtu.be/Y_Q6PfxBSbA) 121 | 122 | **How do I generate the certificate?** 123 | 124 | Certificates can now be easily generated using `siriproxy gencerts` once you install the SiriProxy gem. See the instructions above. 125 | 126 | **How do I set up a DNS server to forward Guzzoni.apple.com traffic to my computer?** 127 | 128 | Check out my video on this: 129 | 130 | [http://www.youtube.com/watch?v=a9gO4L0U59s](http://www.youtube.com/watch?v=a9gO4L0U59s) 131 | 132 | **Will this work outside my home network?** 133 | 134 | No, it won't. But, as suggested by STBullard on YouTube, you COULD VPN into your home network from outside your house in order to make this work. That would not require a jailbreak. Of course, it also means ALL your traffic gets funneled through your home network. The nice thing about adding an entry to your /etc/hosts file (on a jailbroken phone) is that it funnels only Siri traffic through your home network, and not all your traffic. 135 | 136 | **Can you provide me with an iPhone 4S UDID?** 137 | 138 | No. Don't even ask. 139 | 140 | **I'm getting a bunch of "[Info - Guzzoni] Object: SessionValidationFailed" messages. What's wrong?!** 141 | 142 | You're probably not using an iPhone 4S. You need to be using an iPhone 4S (or have a UDID you can sub in) in order to make use of SiriProxy. Sorry, this is not designed to be a way around that limitation. (Thanks to [@brownie545](http://www.twitter.com/brownie545) for providing information on what happens when you use a non-iPhone 4S) 143 | 144 | **How do I remove the certificate from my iPhone when I'm done?** 145 | 146 | Just go into your phone's Settings app, then go to "General->Profiles." Your CA will probably be the only thing listed under "Configuration Profiles." It will be listed as "SiriProxyCA" Just click it and click "Remove" and it will be removed. (Thanks to [@tidegu](http://www.twitter.com/tidegu) for asking!) 147 | 148 | **Does this require a jailbreak?** 149 | 150 | No. The only action you need to take on the phone is to install the root CA's public key. 151 | 152 | 153 | Acknowledgements 154 | ---------------- 155 | I really can't give enough credit to [Applidium](http://applidium.com/en/news/cracking_siri/) and the [tools they created](https://github.com/applidium/Cracking-Siri). While I've been toying with Siri for a while, their proof of concept for intercepting and interpreting the Siri protocol was invaluable. Although all the code included in the project (so far) is my own, much of the base logic behind my code is based on the sample code they provided. They do great work. 156 | 157 | I also want to give a shout-out to [Arch Reactor](http://www.archreactor.org) - my local Hackerspace. Hackerspaces are a fantastic place to go learn about stuff like this. I was able to get some help from folks there, and more importantly, I got encouragement to do stuff like this. Check [Hackerspaces.org](http://www.hackerspaces.org) for a hackerspace in your area and make sure to check it out! 158 | 159 | Licensing 160 | --------- 161 | 162 | Re-use of my code is fine under a Creative Commons 3.0 [Non-commercial, Attribution, Share-Alike](http://creativecommons.org/licenses/by-nc-sa/3.0/) license. In short, this means that you can use my code, modify it, do anything you want. Just don't sell it and make sure to give me a shout-out. Also, you must license your derivatives under a compatible license (sorry, no closed-source derivatives). If you would like to purchase a more permissive license (for a closed-source and/or commercial license), please contact me directly. See the Creative Commons site for more information. 163 | 164 | 165 | Disclaimer 166 | ---------- 167 | I'm not affiliated with Apple in any way. They don't endorse this application. They own all the rights to Siri (and all associated trademarks). 168 | 169 | This software is provided as-is with no warranty whatsoever. Apple could do things to block this kind of behavior if they want. Also, if you cause problems (by sending lots of trash to the Guzzoni servers or anything), I fully support Apple's right to ban your UDID (making your phone unable to use Siri). They can, and I wouldn't blame them if they do. 170 | 171 | I'm a huge fan of Apple and the work that they do. Siri is a very cool feature and I'm pretty excited to explore it and add functionality. Please refrain from using this software for anything malicious. 172 | 173 | Also, this is my first project done in Ruby. Please don't be too critical of my code. 174 | -------------------------------------------------------------------------------- /lib/siriproxy/connection.rb: -------------------------------------------------------------------------------- 1 | require 'cfpropertylist' 2 | require 'siriproxy/interpret_siri' 3 | 4 | class SiriProxy::Connection < EventMachine::Connection 5 | include EventMachine::Protocols::LineText2 6 | 7 | attr_accessor :other_connection, :name, :ssled, :output_buffer, :input_buffer, :processed_headers, :unzip_stream, :zip_stream, :consumed_ace, :unzipped_input, :unzipped_output, :last_ref_id, :plugin_manager 8 | 9 | def last_ref_id=(ref_id) 10 | @last_ref_id = ref_id 11 | self.other_connection.last_ref_id = ref_id if other_connection.last_ref_id != ref_id 12 | end 13 | 14 | def initialize 15 | super 16 | self.processed_headers = false 17 | self.output_buffer = "" 18 | self.input_buffer = "" 19 | self.unzipped_input = "" 20 | self.unzipped_output = "" 21 | self.unzip_stream = Zlib::Inflate.new 22 | self.zip_stream = Zlib::Deflate.new 23 | self.consumed_ace = false 24 | @auth_data = nil 25 | @faux = false 26 | end 27 | 28 | def post_init 29 | self.ssled = false 30 | end 31 | 32 | def encode_data(x) 33 | x = [x].pack("H*") 34 | x.blob = true 35 | x 36 | end 37 | 38 | def read_relative_file(x) 39 | val = nil 40 | begin 41 | val = File.open(File.expand_path(x), "r").read 42 | rescue SystemCallError 43 | end 44 | 45 | val 46 | end 47 | 48 | def write_relative_file(x, val) 49 | File.open(File.expand_path(x), "w") do |f| 50 | f.write(val) 51 | end 52 | end 53 | 54 | def read_auth_data 55 | map = Hash.new 56 | 57 | map["speech_id"] = read_relative_file("~/.siriproxy/speech_id") 58 | map["assistant_id"] = read_relative_file("~/.siriproxy/assistant_id") 59 | map["session_data"] = read_relative_file("~/.siriproxy/session_data") 60 | 61 | puts map 62 | map 63 | end 64 | 65 | def ssl_handshake_completed 66 | self.ssled = true 67 | 68 | @auth_data = read_auth_data() 69 | puts "[Info - #{self.name}] SSL completed for #{self.name}" if $LOG_LEVEL > 1 70 | end 71 | 72 | def receive_line(line) #Process header 73 | puts "[Header - #{self.name}] #{line}" if $LOG_LEVEL > 2 74 | if(line == "") #empty line indicates end of headers 75 | puts "[Debug - #{self.name}] Found end of headers" if $LOG_LEVEL > 3 76 | set_binary_mode 77 | self.processed_headers = true 78 | elsif line.match(/^User-Agent:/) 79 | if line.match(/iPhone4,1/) 80 | puts "[Warning] 4S device connected. Keys will be saved." 81 | @faux = false 82 | else 83 | puts "[Warning] Non-4S device connected." 84 | @faux = true 85 | end 86 | end 87 | self.output_buffer << (line + "\x0d\x0a") #Restore the CR-LF to the end of the line 88 | 89 | flush_output_buffer() 90 | end 91 | 92 | def receive_binary_data(data) 93 | self.input_buffer << data 94 | 95 | ##Consume the "0xAACCEE02" data at the start of the stream if necessary (by forwarding it to the output buffer) 96 | if(self.consumed_ace == false) 97 | self.output_buffer << input_buffer[0..3] 98 | self.input_buffer = input_buffer[4..-1] 99 | self.consumed_ace = true; 100 | end 101 | 102 | process_compressed_data() 103 | 104 | flush_output_buffer() 105 | end 106 | 107 | def flush_output_buffer 108 | return if output_buffer.empty? 109 | 110 | if other_connection.ssled 111 | puts "[Debug - #{self.name}] Forwarding #{self.output_buffer.length} bytes of data to #{other_connection.name}" if $LOG_LEVEL > 5 112 | #puts self.output_buffer.to_hex if $LOG_LEVEL > 5 113 | other_connection.send_data(output_buffer) 114 | self.output_buffer = "" 115 | else 116 | puts "[Debug - #{self.name}] Buffering some data for later (#{self.output_buffer.length} bytes buffered)" if $LOG_LEVEL > 5 117 | #puts self.output_buffer.to_hex if $LOG_LEVEL > 5 118 | end 119 | end 120 | 121 | def process_compressed_data 122 | self.unzipped_input << unzip_stream.inflate(self.input_buffer) 123 | self.input_buffer = "" 124 | puts "========UNZIPPED DATA (from #{self.name} =========" if $LOG_LEVEL > 5 125 | puts unzipped_input.to_hex if $LOG_LEVEL > 5 126 | puts "==================================================" if $LOG_LEVEL > 5 127 | 128 | while(self.has_next_object?) 129 | object = read_next_object_from_unzipped() 130 | 131 | if(object != nil) #will be nil if the next object is a ping/pong 132 | new_object = prep_received_object(object) #give the world a chance to mess with folks 133 | 134 | inject_object_to_output_stream(new_object) if new_object != nil #might be nil if "the world" decides to rid us of the object 135 | end 136 | end 137 | end 138 | 139 | def has_next_object? 140 | return false if unzipped_input.empty? #empty 141 | unpacked = unzipped_input[0...5].unpack('H*').first 142 | return true if(unpacked.match(/^0[34]/)) #Ping or pong 143 | 144 | if unpacked.match(/^[0-9][15-9]/) 145 | puts "ROGUE PACKET!!! WHAT IS IT?! TELL US!!! IN IRC!! COPY THE STUFF FROM BELOW" 146 | puts unpacked.to_hex 147 | end 148 | objectLength = unpacked.match(/^0200(.{6})/)[1].to_i(16) 149 | return ((objectLength + 5) < unzipped_input.length) #determine if the length of the next object (plus its prefix) is less than the input buffer 150 | end 151 | 152 | def read_next_object_from_unzipped 153 | unpacked = unzipped_input[0...5].unpack('H*').first 154 | info = unpacked.match(/^0(.)(.{8})$/) 155 | 156 | if(info[1] == "3" || info[1] == "4") #Ping or pong -- just get these out of the way (and log them for good measure) 157 | object = unzipped_input[0...5] 158 | self.unzipped_output << object 159 | 160 | type = (info[1] == "3") ? "Ping" : "Pong" 161 | puts "[#{type} - #{self.name}] (#{info[2].to_i(16)})" if $LOG_LEVEL > 3 162 | self.unzipped_input = unzipped_input[5..-1] 163 | 164 | flush_unzipped_output() 165 | return nil 166 | end 167 | 168 | object_size = info[2].to_i(16) 169 | prefix = unzipped_input[0...5] 170 | object_data = unzipped_input[5...object_size+5] 171 | self.unzipped_input = unzipped_input[object_size+5..-1] 172 | 173 | parse_object(object_data) 174 | end 175 | 176 | 177 | def parse_object(object_data) 178 | plist = CFPropertyList::List.new(:data => object_data) 179 | object = CFPropertyList.native_types(plist.value) 180 | 181 | object 182 | end 183 | 184 | def inject_object_to_output_stream(object) 185 | if object["refId"] != nil && !object["refId"].empty? 186 | @block_rest_of_session = false if @block_rest_of_session && self.last_ref_id != object["refId"] #new session 187 | self.last_ref_id = object["refId"] 188 | end 189 | 190 | puts "[Info - Forwarding object to #{self.other_connection.name}] #{object["class"]}" if $LOG_LEVEL > 1 191 | 192 | object_data = object.to_plist(:plist_format => CFPropertyList::List::FORMAT_BINARY) 193 | 194 | #Recalculate the size in case the object gets modified. If new size is 0, then remove the object from the stream entirely 195 | obj_len = object_data.length 196 | 197 | if(obj_len > 0) 198 | prefix = [(0x0200000000 + obj_len).to_s(16).rjust(10, '0')].pack('H*') 199 | self.unzipped_output << prefix + object_data 200 | end 201 | 202 | flush_unzipped_output() 203 | end 204 | 205 | def flush_unzipped_output 206 | self.zip_stream << self.unzipped_output 207 | self.unzipped_output = "" 208 | self.output_buffer << zip_stream.flush 209 | 210 | flush_output_buffer() 211 | end 212 | 213 | def prep_received_object(object) 214 | if object["refId"] == self.last_ref_id && @block_rest_of_session 215 | puts "[Info - Dropping Object from Guzzoni] #{object["class"]}" if $LOG_LEVEL > 1 216 | pp object if $LOG_LEVEL > 3 217 | return nil 218 | end 219 | 220 | if object["properties"] != nil 221 | if object["properties"]["sessionValidationData"] != nil 222 | if @faux == false 223 | # We're on a 4S 224 | data = object["properties"]["sessionValidationData"].unpack('H*').join("") 225 | write_relative_file("~/.siriproxy/session_data", data) 226 | else 227 | if @auth_data == nil 228 | puts "[Error] No session data available." 229 | else 230 | puts "[Info] Found cached session data." 231 | object["properties"]["sessionValidationData"] = encode_data(@auth_data["session_data"]) 232 | end 233 | end 234 | end 235 | 236 | if object["properties"]["speechId"] != nil 237 | if @faux == false 238 | # We're on a 4S 239 | data = object["properties"]["speechId"] 240 | write_relative_file("~/.siriproxy/speech_id", data) 241 | else 242 | if @auth_data == nil 243 | puts "[Error] No speech id available." 244 | else 245 | puts "[Info] Found cached speech id." 246 | object["properties"]["speechId"] = @auth_data["speech_id"] 247 | end 248 | end 249 | end 250 | 251 | if object["properties"]["assistantId"] != nil 252 | if @faux == false 253 | # We're on a 4S 254 | data = object["properties"]["assistantId"] 255 | write_relative_file("~/.siriproxy/assistant_id", data) 256 | else 257 | if @auth_data == nil 258 | puts "[Error] No assistant id available." 259 | else 260 | puts "[Info] Found cached assistant id." 261 | object["properties"]["assistantId"] = @auth_data["assistant_id"] 262 | end 263 | end 264 | end 265 | end 266 | 267 | puts "[Info - #{self.name}] Received Object: #{object["class"]}" if $LOG_LEVEL == 1 268 | puts "[Info - #{self.name}] Received Object: #{object["class"]} (group: #{object["group"]})" if $LOG_LEVEL == 2 269 | puts "[Info - #{self.name}] Received Object: #{object["class"]} (group: #{object["group"]}, ref_id: #{object["refId"]}, ace_id: #{object["aceId"]})" if $LOG_LEVEL > 2 270 | pp object if $LOG_LEVEL > 3 271 | 272 | #keeping this for filters 273 | new_obj = received_object(object) 274 | if new_obj == nil 275 | puts "[Info - Dropping Object from #{self.name}] #{object["class"]}" if $LOG_LEVEL > 1 276 | pp object if $LOG_LEVEL > 3 277 | return nil 278 | end 279 | 280 | #block the rest of the session if a plugin claims ownership 281 | speech = SiriProxy::Interpret.speech_recognized(object) 282 | if speech != nil 283 | inject_object_to_output_stream(object) 284 | block_rest_of_session if plugin_manager.process(speech) 285 | return nil 286 | end 287 | 288 | 289 | #object = new_obj if ((new_obj = SiriProxy::Interpret.unknown_intent(object, self, plugin_manager.method(:unknown_command))) != false) 290 | #object = new_obj if ((new_obj = SiriProxy::Interpret.speech_recognized(object, self, plugin_manager.method(:speech_recognized))) != false) 291 | 292 | object 293 | end 294 | 295 | #Stub -- override in subclass 296 | def received_object(object) 297 | 298 | object 299 | end 300 | 301 | end 302 | -------------------------------------------------------------------------------- /lib/siri_objects.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'uuidtools' 3 | 4 | def generate_siri_utterance(ref_id, text, speakableText=text, listenAfterSpeaking=false) 5 | object = SiriAddViews.new 6 | object.make_root(ref_id) 7 | object.views << SiriAssistantUtteranceView.new(text, speakableText, "Misc#ident", listenAfterSpeaking) 8 | return object.to_hash 9 | end 10 | 11 | def generate_request_completed(ref_id, callbacks=nil) 12 | object = SiriRequestCompleted.new() 13 | object.callbacks = callbacks if callbacks != nil 14 | object.make_root(ref_id) 15 | return object.to_hash 16 | end 17 | 18 | class SiriObject 19 | attr_accessor :klass, :group, :properties 20 | 21 | def initialize(klass, group) 22 | @klass = klass 23 | @group = group 24 | @properties = {} 25 | end 26 | 27 | #watch out for circular references! 28 | def to_hash 29 | hash = { 30 | "class" => self.klass, 31 | "group" => self.group, 32 | "properties" => {} 33 | } 34 | 35 | (hash["refId"] = ref_id) rescue nil 36 | (hash["aceId"] = ace_id) rescue nil 37 | 38 | properties.each_key { |key| 39 | if properties[key].class == Array 40 | hash["properties"][key] = [] 41 | self.properties[key].each { |val| hash["properties"][key] << (val.to_hash rescue val) } 42 | else 43 | hash["properties"][key] = (properties[key].to_hash rescue properties[key]) 44 | end 45 | } 46 | 47 | hash 48 | end 49 | 50 | def make_root(ref_id=nil, ace_id=nil) 51 | self.extend(SiriRootObject) 52 | 53 | self.ref_id = (ref_id || random_ref_id) 54 | self.ace_id = (ace_id || random_ace_id) 55 | end 56 | end 57 | 58 | def add_property_to_class(klass, prop) 59 | klass.send(:define_method, (prop.to_s + "=").to_sym) { |value| 60 | self.properties[prop.to_s] = value 61 | } 62 | 63 | klass.send(:define_method, prop.to_s.to_sym) { 64 | self.properties[prop.to_s] 65 | } 66 | end 67 | 68 | module SiriRootObject 69 | attr_accessor :ref_id, :ace_id 70 | 71 | def random_ref_id 72 | UUIDTools::UUID.random_create.to_s.upcase 73 | end 74 | 75 | def random_ace_id 76 | UUIDTools::UUID.random_create.to_s 77 | end 78 | end 79 | 80 | class SiriAddViews < SiriObject 81 | def initialize(scrollToTop=false, temporary=false, dialogPhase="Completion", views=[]) 82 | super("AddViews", "com.apple.ace.assistant") 83 | self.scrollToTop = scrollToTop 84 | self.views = views 85 | self.temporary = temporary 86 | self.dialogPhase = dialogPhase 87 | end 88 | end 89 | add_property_to_class(SiriAddViews, :scrollToTop) 90 | add_property_to_class(SiriAddViews, :views) 91 | add_property_to_class(SiriAddViews, :temporary) 92 | add_property_to_class(SiriAddViews, :dialogPhase) 93 | 94 | ##### 95 | # VIEWS 96 | ##### 97 | 98 | class SiriAssistantUtteranceView < SiriObject 99 | def initialize(text="", speakableText=text, dialogIdentifier="Misc#ident", listenAfterSpeaking=false) 100 | super("AssistantUtteranceView", "com.apple.ace.assistant") 101 | self.text = text 102 | self.speakableText = speakableText 103 | self.dialogIdentifier = dialogIdentifier 104 | self.listenAfterSpeaking = listenAfterSpeaking 105 | end 106 | end 107 | add_property_to_class(SiriAssistantUtteranceView, :text) 108 | add_property_to_class(SiriAssistantUtteranceView, :speakableText) 109 | add_property_to_class(SiriAssistantUtteranceView, :dialogIdentifier) 110 | add_property_to_class(SiriAssistantUtteranceView, :listenAfterSpeaking) 111 | 112 | class SiriMapItemSnippet < SiriObject 113 | def initialize(userCurrentLocation=true, items=[]) 114 | super("MapItemSnippet", "com.apple.ace.localsearch") 115 | self.userCurrentLocation = userCurrentLocation 116 | self.items = items 117 | end 118 | end 119 | add_property_to_class(SiriMapItemSnippet, :userCurrentLocation) 120 | add_property_to_class(SiriMapItemSnippet, :items) 121 | 122 | class SiriButton < SiriObject 123 | def initialize(text="Button Text", commands=[]) 124 | super("Button", "com.apple.ace.assistant") 125 | self.text = text 126 | self.commands = commands 127 | end 128 | end 129 | add_property_to_class(SiriButton, :text) 130 | add_property_to_class(SiriButton, :commands) 131 | 132 | class SiriAnswerSnippet < SiriObject 133 | def initialize(answers=[], confirmationOptions=nil) 134 | super("Snippet", "com.apple.ace.answer") 135 | self.answers = answers 136 | 137 | if confirmationOptions 138 | # need to figure out good way to do API for this 139 | self.confirmationOptions = confirmationOptions 140 | end 141 | 142 | end 143 | end 144 | add_property_to_class(SiriAnswerSnippet, :answers) 145 | add_property_to_class(SiriAnswerSnippet, :confirmationOptions) 146 | 147 | ##### 148 | # Items 149 | ##### 150 | 151 | class SiriMapItem < SiriObject 152 | def initialize(label="Apple Headquarters", location=SiriLocation.new, detailType="BUSINESS_ITEM") 153 | super("MapItem", "com.apple.ace.localsearch") 154 | self.label = label 155 | self.detailType = detailType 156 | self.location = location 157 | end 158 | end 159 | add_property_to_class(SiriMapItem, :label) 160 | add_property_to_class(SiriMapItem, :detailType) 161 | add_property_to_class(SiriMapItem, :location) 162 | 163 | ##### 164 | # Commands 165 | ##### 166 | 167 | class SiriSendCommands < SiriObject 168 | def initialize(commands=[]) 169 | super("SendCommands", "com.apple.ace.system") 170 | self.commands=commands 171 | end 172 | end 173 | add_property_to_class(SiriSendCommands, :commands) 174 | 175 | class SiriConfirmationOptions < SiriObject 176 | def initialize(submitCommands=[], cancelCommands=[], denyCommands=[], confirmCommands=[], denyText="Cancel", cancelLabel="Cancel", submitLabel="Send", confirmText="Send", cancelTrigger="Deny") 177 | super("ConfirmationOptions", "com.apple.ace.assistant") 178 | 179 | self.submitCommands = submitCommands 180 | self.cancelCommands = cancelCommands 181 | self.denyCommands = denyCommands 182 | self.confirmCommands = confirmCommands 183 | 184 | self.denyText = denyText 185 | self.cancelLabel = cancelLabel 186 | self.submitLabel = submitLabel 187 | self.confirmText = confirmText 188 | self.cancelTrigger = cancelTrigger 189 | end 190 | end 191 | add_property_to_class(SiriConfirmationOptions, :submitCommands) 192 | add_property_to_class(SiriConfirmationOptions, :cancelCommands) 193 | add_property_to_class(SiriConfirmationOptions, :denyCommands) 194 | add_property_to_class(SiriConfirmationOptions, :confirmCommands) 195 | add_property_to_class(SiriConfirmationOptions, :denyText) 196 | add_property_to_class(SiriConfirmationOptions, :cancelLabel) 197 | add_property_to_class(SiriConfirmationOptions, :submitLabel) 198 | add_property_to_class(SiriConfirmationOptions, :confirmText) 199 | add_property_to_class(SiriConfirmationOptions, :cancelTrigger) 200 | 201 | class SiriConfirmSnippetCommand < SiriObject 202 | def initialize(request_id = "") 203 | super("ConfirmSnippet", "com.apple.ace.assistant") 204 | self.request_id = request_id 205 | end 206 | end 207 | add_property_to_class(SiriConfirmSnippetCommand, :request_id) 208 | 209 | class SiriCancelSnippetCommand < SiriObject 210 | def initialize(request_id = "") 211 | super("ConfirmSnippet", "com.apple.ace.assistant") 212 | self.request_id = request_id 213 | end 214 | end 215 | add_property_to_class(SiriCancelSnippetCommand, :request_id) 216 | 217 | ##### 218 | # Objects 219 | ##### 220 | 221 | class SiriLocation < SiriObject 222 | def initialize(label="Apple", street="1 Infinite Loop", city="Cupertino", stateCode="CA", countryCode="US", postalCode="95014", latitude=37.3317031860352, longitude=-122.030089795589) 223 | super("Location", "com.apple.ace.system") 224 | self.label = label 225 | self.street = street 226 | self.city = city 227 | self.stateCode = stateCode 228 | self.countryCode = countryCode 229 | self.postalCode = postalCode 230 | self.latitude = latitude 231 | self.longitude = longitude 232 | end 233 | end 234 | add_property_to_class(SiriLocation, :label) 235 | add_property_to_class(SiriLocation, :street) 236 | add_property_to_class(SiriLocation, :city) 237 | add_property_to_class(SiriLocation, :stateCode) 238 | add_property_to_class(SiriLocation, :countryCode) 239 | add_property_to_class(SiriLocation, :postalCode) 240 | add_property_to_class(SiriLocation, :latitude) 241 | add_property_to_class(SiriLocation, :longitude) 242 | 243 | class SiriAnswer < SiriObject 244 | def initialize(title="", lines=[]) 245 | super("Object", "com.apple.ace.answer") 246 | self.title = title 247 | self.lines = lines 248 | end 249 | end 250 | add_property_to_class(SiriAnswer, :title) 251 | add_property_to_class(SiriAnswer, :lines) 252 | 253 | class SiriAnswerLine < SiriObject 254 | def initialize(text="", image="") 255 | super("ObjectLine", "com.apple.ace.answer") 256 | self.text = text 257 | self.image = image 258 | end 259 | end 260 | add_property_to_class(SiriAnswerLine, :text) 261 | add_property_to_class(SiriAnswerLine, :image) 262 | 263 | ##### 264 | # Guzzoni Commands (commands that typically come from the server side) 265 | ##### 266 | 267 | class SiriGetRequestOrigin < SiriObject 268 | def initialize(desiredAccuracy="HundredMeters", searchTimeout=8.0, maxAge=1800) 269 | super("GetRequestOrigin", "com.apple.ace.system") 270 | self.desiredAccuracy = desiredAccuracy 271 | self.searchTimeout = searchTimeout 272 | self.maxAge = maxAge 273 | end 274 | end 275 | add_property_to_class(SiriGetRequestOrigin, :desiredAccuracy) 276 | add_property_to_class(SiriGetRequestOrigin, :searchTimeout) 277 | add_property_to_class(SiriGetRequestOrigin, :maxAge) 278 | 279 | class SiriRequestCompleted < SiriObject 280 | def initialize(callbacks=[]) 281 | super("RequestCompleted", "com.apple.ace.system") 282 | self.callbacks = callbacks 283 | end 284 | end 285 | add_property_to_class(SiriRequestCompleted, :callbacks) 286 | 287 | ##### 288 | # iPhone Responses (misc meta data back to the server) 289 | ##### 290 | 291 | class SiriStartRequest < SiriObject 292 | def initialize(utterance="Testing", handsFree=false, proxyOnly=false) 293 | super("StartRequest", "com.apple.ace.system") 294 | self.utterance = utterance 295 | self.handsFree = handsFree 296 | if proxyOnly # dont send local when false since its non standard 297 | self.proxyOnly = proxyOnly 298 | end 299 | end 300 | end 301 | add_property_to_class(SiriStartRequest, :utterance) 302 | add_property_to_class(SiriStartRequest, :handsFree) 303 | add_property_to_class(SiriStartRequest, :proxyOnly) 304 | 305 | 306 | class SiriSetRequestOrigin < SiriObject 307 | def initialize(longitude=-122.030089795589, latitude=37.3317031860352, desiredAccuracy="HundredMeters", altitude=0.0, speed=1.0, direction=1.0, age=0, horizontalAccuracy=50.0, verticalAccuracy=10.0) 308 | super("SetRequestOrigin", "com.apple.ace.system") 309 | self.horizontalAccuracy = horizontalAccuracy 310 | self.latitude = latitude 311 | self.desiredAccuracy = desiredAccuracy 312 | self.altitude = altitude 313 | self.speed = speed 314 | self.longitude = longitude 315 | self.verticalAccuracy = verticalAccuracy 316 | self.direction = direction 317 | self.age = age 318 | end 319 | end 320 | add_property_to_class(SiriSetRequestOrigin, :horizontalAccuracy) 321 | add_property_to_class(SiriSetRequestOrigin, :latitude) 322 | add_property_to_class(SiriSetRequestOrigin, :desiredAccuracy) 323 | add_property_to_class(SiriSetRequestOrigin, :altitude) 324 | add_property_to_class(SiriSetRequestOrigin, :speed) 325 | add_property_to_class(SiriSetRequestOrigin, :longitude) 326 | add_property_to_class(SiriSetRequestOrigin, :verticalAccuracy) 327 | add_property_to_class(SiriSetRequestOrigin, :direction) 328 | add_property_to_class(SiriSetRequestOrigin, :age) 329 | 330 | 331 | 332 | -------------------------------------------------------------------------------- /scripts/openssl.cnf: -------------------------------------------------------------------------------- 1 | # 2 | # OpenSSL example configuration file. 3 | # This is mostly being used for generation of certificate requests. 4 | # 5 | 6 | # This definition stops the following lines choking if HOME isn't 7 | # defined. 8 | HOME = . 9 | RANDFILE = $ENV::HOME/.rnd 10 | 11 | # Extra OBJECT IDENTIFIER info: 12 | #oid_file = $ENV::HOME/.oid 13 | oid_section = new_oids 14 | 15 | # To use this configuration file with the "-extfile" option of the 16 | # "openssl x509" utility, name here the section containing the 17 | # X.509v3 extensions to use: 18 | # extensions = 19 | # (Alternatively, use a configuration file that has only 20 | # X.509v3 extensions in its main [= default] section.) 21 | 22 | [ new_oids ] 23 | 24 | # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. 25 | # Add a simple OID like this: 26 | # testoid1=1.2.3.4 27 | # Or use config file substitution like this: 28 | # testoid2=${testoid1}.5.6 29 | 30 | # Policies used by the TSA examples. 31 | tsa_policy1 = 1.2.3.4.1 32 | tsa_policy2 = 1.2.3.4.5.6 33 | tsa_policy3 = 1.2.3.4.5.7 34 | 35 | #################################################################### 36 | [ ca ] 37 | default_ca = CA_default # The default ca section 38 | 39 | 40 | #################################################################### 41 | [ CA_default ] 42 | 43 | dir = /tmp/siriCA # Where everything is kept 44 | certs = $dir/certs # Where the issued certs are kept 45 | crl_dir = $dir/crl # Where the issued crl are kept 46 | database = $dir/index.txt # database index file. 47 | #unique_subject = no # Set to 'no' to allow creation of 48 | # several ctificates with same subject. 49 | new_certs_dir = $dir/newcerts # default place for new certs. 50 | 51 | certificate = $dir/cacert.pem # The CA certificate 52 | serial = $dir/serial # The current serial number 53 | crlnumber = $dir/crlnumber # the current crl number 54 | # must be commented out to leave a V1 CRL 55 | crl = $dir/crl.pem # The current CRL 56 | private_key = $dir/private/cakey.pem# The private key 57 | RANDFILE = $dir/private/.rand # private random number file 58 | 59 | x509_extensions = usr_cert # The extentions to add to the cert 60 | 61 | # Comment out the following two lines for the "traditional" 62 | # (and highly broken) format. 63 | name_opt = ca_default # Subject Name options 64 | cert_opt = ca_default # Certificate field options 65 | 66 | # Extension copying option: use with caution. 67 | # copy_extensions = copy 68 | 69 | # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs 70 | # so this is commented out by default to leave a V1 CRL. 71 | # crlnumber must also be commented out to leave a V1 CRL. 72 | # crl_extensions = crl_ext 73 | 74 | default_days = 365 # how long to certify for 75 | default_crl_days= 30 # how long before next CRL 76 | default_md = sha1 # use public key default MD 77 | preserve = no # keep passed DN ordering 78 | 79 | # A few difference way of specifying how similar the request should look 80 | # For type CA, the listed attributes must be the same, and the optional 81 | # and supplied fields are just that :-) 82 | policy = policy_match 83 | 84 | # For the CA policy 85 | [ policy_match ] 86 | countryName = match 87 | stateOrProvinceName = match 88 | organizationName = match 89 | organizationalUnitName = optional 90 | commonName = supplied 91 | emailAddress = optional 92 | 93 | # For the 'anything' policy 94 | # At this point in time, you must list all acceptable 'object' 95 | # types. 96 | [ policy_anything ] 97 | countryName = optional 98 | stateOrProvinceName = optional 99 | localityName = optional 100 | organizationName = optional 101 | organizationalUnitName = optional 102 | commonName = supplied 103 | emailAddress = optional 104 | 105 | #################################################################### 106 | [ req ] 107 | default_bits = 1024 108 | default_keyfile = privkey.pem 109 | distinguished_name = req_distinguished_name 110 | attributes = req_attributes 111 | x509_extensions = v3_ca # The extentions to add to the self signed cert 112 | default_md = sha1 113 | 114 | 115 | # Passwords for private keys if not present they will be prompted for 116 | # input_password = secret 117 | # output_password = secret 118 | 119 | # This sets a mask for permitted string types. There are several options. 120 | # default: PrintableString, T61String, BMPString. 121 | # pkix : PrintableString, BMPString (PKIX recommendation before 2004) 122 | # utf8only: only UTF8Strings (PKIX recommendation after 2004). 123 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). 124 | # MASK:XXXX a literal mask value. 125 | # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. 126 | string_mask = utf8only 127 | 128 | # req_extensions = v3_req # The extensions to add to a certificate request 129 | 130 | [ req_distinguished_name ] 131 | countryName = Country Name (2 letter code) 132 | countryName_default = AU 133 | countryName_min = 2 134 | countryName_max = 2 135 | 136 | stateOrProvinceName = State or Province Name (full name) 137 | stateOrProvinceName_default = Some-State 138 | 139 | localityName = Locality Name (eg, city) 140 | 141 | 0.organizationName = Organization Name (eg, company) 142 | 0.organizationName_default = Internet Widgits Pty Ltd 143 | 144 | # we can do this but it is not needed normally :-) 145 | #1.organizationName = Second Organization Name (eg, company) 146 | #1.organizationName_default = World Wide Web Pty Ltd 147 | 148 | organizationalUnitName = Organizational Unit Name (eg, section) 149 | #organizationalUnitName_default = 150 | 151 | commonName = Common Name (eg, YOUR name) 152 | commonName_max = 64 153 | 154 | emailAddress = Email Address 155 | emailAddress_max = 64 156 | 157 | # SET-ex3 = SET extension number 3 158 | 159 | [ req_attributes ] 160 | challengePassword = A challenge password 161 | challengePassword_min = 4 162 | challengePassword_max = 20 163 | 164 | unstructuredName = An optional company name 165 | 166 | [ usr_cert ] 167 | 168 | # These extensions are added when 'ca' signs a request. 169 | 170 | # This goes against PKIX guidelines but some CAs do it and some software 171 | # requires this to avoid interpreting an end user certificate as a CA. 172 | 173 | basicConstraints=CA:FALSE 174 | 175 | # Here are some examples of the usage of nsCertType. If it is omitted 176 | # the certificate can be used for anything *except* object signing. 177 | 178 | # This is OK for an SSL server. 179 | # nsCertType = server 180 | 181 | # For an object signing certificate this would be used. 182 | # nsCertType = objsign 183 | 184 | # For normal client use this is typical 185 | # nsCertType = client, email 186 | 187 | # and for everything including object signing: 188 | # nsCertType = client, email, objsign 189 | 190 | # This is typical in keyUsage for a client certificate. 191 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 192 | 193 | # This will be displayed in Netscape's comment listbox. 194 | nsComment = "OpenSSL Generated Certificate" 195 | 196 | # PKIX recommendations harmless if included in all certificates. 197 | subjectKeyIdentifier=hash 198 | authorityKeyIdentifier=keyid,issuer 199 | 200 | # This stuff is for subjectAltName and issuerAltname. 201 | # Import the email address. 202 | # subjectAltName=email:copy 203 | # An alternative to produce certificates that aren't 204 | # deprecated according to PKIX. 205 | # subjectAltName=email:move 206 | 207 | # Copy subject details 208 | # issuerAltName=issuer:copy 209 | 210 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem 211 | #nsBaseUrl 212 | #nsRevocationUrl 213 | #nsRenewalUrl 214 | #nsCaPolicyUrl 215 | #nsSslServerName 216 | 217 | # This is required for TSA certificates. 218 | # extendedKeyUsage = critical,timeStamping 219 | 220 | [ v3_req ] 221 | 222 | # Extensions to add to a certificate request 223 | 224 | basicConstraints = CA:FALSE 225 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 226 | 227 | [ v3_ca ] 228 | 229 | 230 | # Extensions for a typical CA 231 | 232 | 233 | # PKIX recommendation. 234 | 235 | subjectKeyIdentifier=hash 236 | 237 | authorityKeyIdentifier=keyid:always,issuer 238 | 239 | # This is what PKIX recommends but some broken software chokes on critical 240 | # extensions. 241 | #basicConstraints = critical,CA:true 242 | # So we do this instead. 243 | basicConstraints = CA:true 244 | 245 | # Key usage: this is typical for a CA certificate. However since it will 246 | # prevent it being used as an test self-signed certificate it is best 247 | # left out by default. 248 | # keyUsage = cRLSign, keyCertSign 249 | 250 | # Some might want this also 251 | # nsCertType = sslCA, emailCA 252 | 253 | # Include email address in subject alt name: another PKIX recommendation 254 | # subjectAltName=email:copy 255 | # Copy issuer details 256 | # issuerAltName=issuer:copy 257 | 258 | # DER hex encoding of an extension: beware experts only! 259 | # obj=DER:02:03 260 | # Where 'obj' is a standard or added object 261 | # You can even override a supported extension: 262 | # basicConstraints= critical, DER:30:03:01:01:FF 263 | 264 | [ crl_ext ] 265 | 266 | # CRL extensions. 267 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. 268 | 269 | # issuerAltName=issuer:copy 270 | authorityKeyIdentifier=keyid:always 271 | 272 | [ proxy_cert_ext ] 273 | # These extensions should be added when creating a proxy certificate 274 | 275 | # This goes against PKIX guidelines but some CAs do it and some software 276 | # requires this to avoid interpreting an end user certificate as a CA. 277 | 278 | basicConstraints=CA:FALSE 279 | 280 | # Here are some examples of the usage of nsCertType. If it is omitted 281 | # the certificate can be used for anything *except* object signing. 282 | 283 | # This is OK for an SSL server. 284 | # nsCertType = server 285 | 286 | # For an object signing certificate this would be used. 287 | # nsCertType = objsign 288 | 289 | # For normal client use this is typical 290 | # nsCertType = client, email 291 | 292 | # and for everything including object signing: 293 | # nsCertType = client, email, objsign 294 | 295 | # This is typical in keyUsage for a client certificate. 296 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 297 | 298 | # This will be displayed in Netscape's comment listbox. 299 | nsComment = "OpenSSL Generated Certificate" 300 | 301 | # PKIX recommendations harmless if included in all certificates. 302 | subjectKeyIdentifier=hash 303 | authorityKeyIdentifier=keyid,issuer 304 | 305 | # This stuff is for subjectAltName and issuerAltname. 306 | # Import the email address. 307 | # subjectAltName=email:copy 308 | # An alternative to produce certificates that aren't 309 | # deprecated according to PKIX. 310 | # subjectAltName=email:move 311 | 312 | # Copy subject details 313 | # issuerAltName=issuer:copy 314 | 315 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem 316 | #nsBaseUrl 317 | #nsRevocationUrl 318 | #nsRenewalUrl 319 | #nsCaPolicyUrl 320 | #nsSslServerName 321 | 322 | # This really needs to be in place for it to be a proxy certificate. 323 | proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo 324 | 325 | #################################################################### 326 | [ tsa ] 327 | 328 | default_tsa = tsa_config1 # the default TSA section 329 | 330 | [ tsa_config1 ] 331 | 332 | # These are used by the TSA reply generation only. 333 | dir = ./demoCA # TSA root directory 334 | serial = $dir/tsaserial # The current serial number (mandatory) 335 | crypto_device = builtin # OpenSSL engine to use for signing 336 | signer_cert = $dir/tsacert.pem # The TSA signing certificate 337 | # (optional) 338 | certs = $dir/cacert.pem # Certificate chain to include in reply 339 | # (optional) 340 | signer_key = $dir/private/tsakey.pem # The TSA private key (optional) 341 | 342 | default_policy = tsa_policy1 # Policy if request did not specify it 343 | # (optional) 344 | other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) 345 | digests = md5, sha1 # Acceptable message digests (mandatory) 346 | accuracy = secs:1, millisecs:500, microsecs:100 # (optional) 347 | clock_precision_digits = 0 # number of digits after dot. (optional) 348 | ordering = yes # Is ordering defined for timestamps? 349 | # (optional, default: no) 350 | tsa_name = yes # Must the TSA name be included in the reply? 351 | # (optional, default: no) 352 | ess_cert_id_chain = no # Must the ESS cert id chain be included? 353 | # (optional, default: no) 354 | --------------------------------------------------------------------------------