secure-keys 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6946af00c20082e078ea40c755f0738199bc46640496cf42eb95fa57c9c87483
4
- data.tar.gz: bb59bb065970abcd05a903030d48c8cd0164b5b8ee74b4f43b7368877abde082
3
+ metadata.gz: cdab80c05ee6ac029d64ec53aef700bc77e9126398f886f6fc7e0ae529297535
4
+ data.tar.gz: 38176c478f562dfcacd51e62b051f49705cec3cc66a586e20487038965a9976e
5
5
  SHA512:
6
- metadata.gz: f7b7aab488356cbc4afa09de94c9f36de7911820d10aaa52b7896f5edf645051c245fb8eb89866fae9a0ac32c0eecf450fd473423698346a17609f9faf07cf9a
7
- data.tar.gz: ccb083a8d120236864b12557205f28c4798d264cdc8008605c0899e23590dbd98dee1b32a93277b46bd867eb5c4293ff1c48796d7567c3122daf03e5713dd0bf
6
+ metadata.gz: 77e197f322a085f56ce57eef897fdcc156c2c5fe3be9decd1464b396080cfbab2acb39def1787c872b7980c8fb6b9530b6487ccbd2b8b87e93beff57a64f1d51
7
+ data.tar.gz: ffc4ba7aedeff64d0d30f214564b2ec48f4658d4a5eef8e428877b7d98ab896d5a108e0a9e50bdd9668cfa62496d0b9929b6efab50f42814ff4ae1871dc22b14
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div style="display: flex; gap: 10px; padding-bottom: 20px;">
2
- <img src="https://img.shields.io/badge/version-1.1.3-cyan" alt="SecureKeys Version 1.1.3">
2
+ <img src="https://img.shields.io/badge/version-1.1.4-cyan" alt="SecureKeys version">
3
3
 
4
4
  <img src="https://img.shields.io/badge/iOS-^13.0-blue" alt="iOS version 13.0">
5
5
 
@@ -137,13 +137,13 @@ secure-keys --help
137
137
 
138
138
  Usage: secure-keys [--options]
139
139
 
140
- -h, --help Use the provided commands to select the params
141
- --add-xcframework-to-target TARGET Add the xcframework to the target
142
- -d, --delimiter DELIMITER The delimiter to use for the key access (default: ",")
143
- -i, --identifier IDENTIFIER The identifier to use for the key access (default: "secure-keys")
144
- --verbose Enable verbose mode (default: false)
145
- -v, --version Show the secure-keys version
146
- -x, --xcodeproj XCODEPROJ The Xcode project path (default: the first found Xcode project)
140
+ -h, --help Use the provided commands to select the params
141
+ --xcframework Add the xcframework to the target
142
+ -d, --delimiter DELIMITER The delimiter to use for the key access (default: ",")
143
+ --[no-]generate Generate the SecureKeys.xcframework
144
+ -i, --identifier IDENTIFIER The identifier to use for the key access (default: "secure-keys")
145
+ --verbose Enable verbose mode (default: false)
146
+ -v, --version Show the secure-keys version
147
147
  ```
148
148
 
149
149
  To avoid defining the `SECURE_KEYS_IDENTIFIER` and `SECURE_KEYS_DELIMITER` env variables, you can use the `--identifier` and `--delimiter` options.
@@ -188,22 +188,68 @@ let apiKey: String = .key(for: .apiKey)
188
188
 
189
189
  ### Automatically
190
190
 
191
- From the `secure-keys` command, you can use the `--add-xcframework-to-target` option to add the `SecureKeys.xcframework` to the iOS project.
191
+ > [!IMPORTANT]
192
+ > You can see more information about the command using the `--help` option.
193
+
194
+ ```bash
195
+ secure-keys --xcframework --help
196
+
197
+ # Output
198
+ Usage: secure-keys --xcframework [--options]
199
+
200
+ -h, --help Use the provided commands to select the params
201
+ --[no-]add Add the SecureKeys XCFramework to the Xcode project (default: true)
202
+ -t, --target TARGET The target to add the xcframework
203
+ -r, --replace Replace the existing xcframework in the Xcode project (default: false)
204
+ -x, --xcodeproj XCODEPROJ The Xcode project path (default: the first found Xcode project)
205
+ ```
206
+
207
+ From the `secure-keys` command, you can use the `--xcframework` option to add the `SecureKeys.xcframework` to the iOS project.
208
+
209
+ ```bash
210
+ secure-keys --xcframework --target "YourTargetName" --add
211
+ ```
212
+
213
+ If you want to add the `SecureKeys.xcframework` to an iOS that already contains the `SecureKeys` source code, you can use the `--replace` option.
214
+
215
+ ```bash
216
+ secure-keys --xcframework --target "YourTargetName" --replace
217
+ ```
218
+
219
+ > [!IMPORTANT]
220
+ > If you don't need to generate the `SecureKeys.xcframework` every time, you can use the `--no-generate` option.
192
221
 
193
222
  ```bash
194
- secure-keys --add-xcframework-to-target "YourTargetName"
223
+ secure-keys --no-generate --xcframework --target "YourTargetName"
195
224
  ```
196
225
 
197
226
  Also, you can specify your Xcode project path using the `--xcodeproj` option.
198
227
 
199
228
  ```bash
200
- secure-keys --add-xcframework-to-target "YourTargetName" --xcodeproj "/path/to/your/project.xcodeproj"
229
+ secure-keys --xcframework --target "YourTargetName" --xcodeproj "/path/to/your/project.xcodeproj"
201
230
  ```
202
231
 
203
232
  > [!IMPORTANT]
204
233
  > By default, the xcodeproj path would be the first found Xcode project.
205
234
 
206
- This command will generate the `SecureKeys.xcframework` and add it to the iOS project.
235
+ If you don't want to use the CLI options, you can configure some env variable to interact with the `secure-keys` command.
236
+
237
+ ```bash
238
+ # e.g (Large version)
239
+ export SECURE_KEYS_XCFRAMEWORK_TARGET="YourTargetName"
240
+ export SECURE_KEYS_XCFRAMEWORK_ADD=true
241
+ export SECURE_KEYS_XCFRAMEWORK_REPLACE=true
242
+ export SECURE_KEYS_XCFRAMEWORK_XCODEPROJ="/path/to/your/project.xcodeproj"
243
+
244
+ # e.g (Short version)
245
+ export XCFRAMEWORK_TARGET="YourTargetName"
246
+ export XCFRAMEWORK_ADD=true
247
+ export XCFRAMEWORK_REPLACE=true
248
+ export XCFRAMEWORK_XCODEPROJ="/path/to/your/project.xcodeproj"
249
+
250
+ # Run the command
251
+ secure-keys --xcframework
252
+ ```
207
253
 
208
254
  ### Manually
209
255
 
data/bin/secure-keys CHANGED
@@ -5,4 +5,4 @@ require 'dotenv/load'
5
5
  require_relative '../lib/keys'
6
6
 
7
7
  # Generate the keys
8
- SecureKeys::Generator.new.setup
8
+ SecureKeys.run
@@ -10,21 +10,37 @@ module SecureKeys
10
10
  # Configure the default arguments
11
11
  @arguments = {
12
12
  delimiter: nil,
13
+ generate: true,
13
14
  identifier: nil,
14
- target: nil,
15
15
  verbose: false,
16
- xcodeproj: nil,
17
16
  }
18
17
 
19
18
  # Fetch the argument value by key
20
19
  # from CLI arguments or environment variables
21
20
  #
22
- # @param key [Symbol] the argument key
21
+ # @param key [Array|Symbol] the argument key
23
22
  # @param default [String] the default value
24
23
  #
25
24
  # @return [String] the argument value
26
25
  def self.fetch(key:, default: nil)
27
- @arguments[key.to_sym] || ENV.fetch("secure_keys_#{key}".upcase, nil) || default
26
+ keys = Array(key).map(&:to_sym)
27
+ joined_keys = keys.join('_').upcase
28
+ @arguments.dig(*keys) || ENV["SECURE_KEYS_#{joined_keys}"] || ENV[joined_keys] || default
29
+ end
30
+
31
+ # Set the value of the key
32
+ # @param key [Symbol] the key to be updated
33
+ # @param value [String] the value to be updated
34
+ def self.set(key:, value:)
35
+ @arguments[key.to_sym] = value
36
+ end
37
+
38
+ # Append the argument value by key
39
+ # @param key [Symbol] the argument key
40
+ # @param value [String] the argument value
41
+ def self.deep_merge(key:, value:)
42
+ @arguments[key.to_sym] ||= {} # Initialize the key if it doesn't exist
43
+ @arguments[key.to_sym].merge!(value)
28
44
  end
29
45
  end
30
46
  end
@@ -3,6 +3,7 @@
3
3
  require 'optparse'
4
4
  require_relative '../../globals/globals'
5
5
  require_relative './handler'
6
+ require_relative './xcframework/parser'
6
7
 
7
8
  module SecureKeys
8
9
  module Core
@@ -16,7 +17,8 @@ module SecureKeys
16
17
 
17
18
  # Configure the arguement parser
18
19
  configure!
19
- parse!(into: Handler.arguments)
20
+ order!(into: Handler.arguments)
21
+ configure_sub_arguments
20
22
  end
21
23
 
22
24
  private
@@ -28,10 +30,12 @@ module SecureKeys
28
30
  exit(0)
29
31
  end
30
32
 
31
- on('--add-xcframework-to-target TARGET', String, 'Add the xcframework to the target') do |target|
32
- Handler.arguments[:target] = target
33
+ on('--xcframework', 'Add the xcframework to the target') do
34
+ XCFramework::Parser.new
33
35
  end
36
+
34
37
  on('-d', '--delimiter DELIMITER', String, "The delimiter to use for the key access (default: \"#{Globals.default_key_delimiter}\")")
38
+ on('--[no-]generate', TrueClass, 'Generate the SecureKeys.xcframework')
35
39
  on('-i', '--identifier IDENTIFIER', String, "The identifier to use for the key access (default: \"#{Globals.default_key_access_identifier}\")")
36
40
  on('--verbose', TrueClass, 'Enable verbose mode (default: false)')
37
41
 
@@ -39,7 +43,11 @@ module SecureKeys
39
43
  puts "secure-keys version: v#{SecureKeys::VERSION}"
40
44
  exit(0)
41
45
  end
42
- on('-x', '--xcodeproj XCODEPROJ', String, 'The Xcode project path (default: the first found Xcode project)')
46
+ end
47
+
48
+ # Configure the sub arguments
49
+ def configure_sub_arguments
50
+ Handler.set(key: :xcframework, value: XCFramework::Handler.arguments)
43
51
  end
44
52
  end
45
53
  end
@@ -0,0 +1,23 @@
1
+ module SecureKeys
2
+ module Core
3
+ module Console
4
+ module Argument
5
+ module XCFramework
6
+ class Handler
7
+ class << self
8
+ attr_reader :arguments
9
+ end
10
+
11
+ # Configure the default arguments
12
+ @arguments = {
13
+ add: true,
14
+ replace: false,
15
+ target: nil,
16
+ xcodeproj: nil,
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require_relative '../../../globals/globals'
5
+ require_relative './handler'
6
+
7
+ module SecureKeys
8
+ module Core
9
+ module Console
10
+ module Argument
11
+ module XCFramework
12
+ class Parser < OptionParser
13
+ # Initialize the argument parser with the default options
14
+ def initialize
15
+ super('Usage: secure-keys --xcframework [--options]')
16
+ separator('')
17
+
18
+ # Configure the arguement parser
19
+ configure!
20
+ order!(into: Handler.arguments)
21
+ end
22
+
23
+ private
24
+
25
+ # Configure the argument parser
26
+ def configure!
27
+ on('-h', '--help', 'Use the provided commands to select the params') do
28
+ puts self
29
+ exit(0)
30
+ end
31
+
32
+ on('--[no-]add', TrueClass, 'Add the SecureKeys XCFramework to the Xcode project (default: true)')
33
+ on('-t', '--target TARGET', String, 'The target to add the xcframework')
34
+ on('-r', '--replace', TrueClass, 'Replace the existing xcframework in the Xcode project (default: false)')
35
+ on('-x', '--xcodeproj XCODEPROJ', String, 'The Xcode project path (default: the first found Xcode project)')
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -54,7 +54,7 @@ module SecureKeys
54
54
  # Log a verbose message
55
55
  # @param message [String] the message to log
56
56
  def verbose(message:)
57
- logger.debug(message.to_s) if AWSTracker::Global.verbose?
57
+ logger.debug(message.to_s) if Globals.verbose?
58
58
  end
59
59
 
60
60
  # Crash the terminal with a message
@@ -8,19 +8,10 @@ module SecureKeys
8
8
  # @param key [String] the key of the environment variable to fetch
9
9
  # @return [String] the value of the environment variable
10
10
  def fetch(key:)
11
- ENV[formatted_key(key:)]
11
+ ENV[key]
12
12
  rescue StandardError
13
13
  puts "❌ Error fetching the key: #{key} from ENV variables"
14
14
  end
15
-
16
- private
17
-
18
- # Formats the key to match the format of the environment variables.
19
- # @param key [String] the key to format
20
- # @return [String] the formatted key
21
- def formatted_key(key:)
22
- key.gsub('-', '_').uppercase
23
- end
24
15
  end
25
16
  end
26
17
  end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative './globals/globals'
4
+ require_relative './environment/ci'
5
+ require_relative './environment/keychain'
6
+ require_relative './utils/swift/writer'
7
+ require_relative './utils/swift/package'
8
+ require_relative './utils/swift/swift'
9
+ require_relative './utils/swift/xcframework'
10
+ require_relative './utils/openssl/cipher'
11
+
12
+ module SecureKeys
13
+ module Core
14
+ class Generator
15
+ private
16
+
17
+ attr_accessor :cipher, :secrets_source, :secret_keys, :mapped_keys, :xcframework
18
+
19
+ public
20
+
21
+ def initialize
22
+ # Configure cipher
23
+ self.cipher = OpenSSL::Cipher.new
24
+ self.secrets_source = Globals.secret_keys_source
25
+
26
+ # Define the keys that we want to map
27
+ self.secret_keys = secrets_source.fetch(key: Globals.key_access_identifier)
28
+ .to_s
29
+ .split(Globals.key_delimiter)
30
+ .map(&:strip)
31
+
32
+ # Add the keys that we want to map
33
+ self.mapped_keys = secret_keys.map do |key|
34
+ encrypted_data = cipher.encrypt(value: secrets_source.fetch(key:))
35
+ { name: key.camelize, **encrypted_data }
36
+ end
37
+
38
+ # Configure the XCFramework
39
+ self.xcframework = Swift::XCFramework.new
40
+ end
41
+
42
+ def generate
43
+ if Globals.generate_xcframework?
44
+ pre_actions
45
+ generate_swift_package
46
+ write_keys
47
+ xcframework.generate
48
+ end
49
+
50
+ xcframework.configure_xcframework_to_xcodeproj
51
+ post_actions if Globals.generate_xcframework?
52
+ end
53
+
54
+ private
55
+
56
+ def generate_swift_package
57
+ package = Swift::Package.new
58
+ package.generate
59
+ end
60
+
61
+ def write_keys
62
+ writer = Swift::Writer.new(mapped_keys:,
63
+ secure_key_bytes: cipher.secure_key_bytes)
64
+ writer.write
65
+ end
66
+
67
+ def pre_actions
68
+ # Remove the keys directory
69
+ system("rm -rf #{Swift::KEYS_DIRECTORY}")
70
+ end
71
+
72
+ def post_actions
73
+ # Remove the keys directory
74
+ system("rm -rf #{Swift::SWIFT_PACKAGE_DIRECTORY}")
75
+
76
+ # Remove the build directory
77
+ system("rm -rf #{Swift::KEYS_DIRECTORY}/#{Swift::BUILD_DIRECTORY}")
78
+ end
79
+ end
80
+ end
81
+ end
@@ -15,7 +15,7 @@ module SecureKeys
15
15
 
16
16
  # Check for Jenkins, Travis CI, ... environment variables
17
17
  %w[JENKINS_HOME JENKINS_URL TRAVIS CI APPCENTER_BUILD_ID TEAMCITY_VERSION GO_PIPELINE_NAME bamboo_buildKey GITLAB_CI XCS TF_BUILD GITHUB_ACTION GITHUB_ACTIONS BITRISE_IO BUDDY CODEBUILD_BUILD_ARN].any? do |current|
18
- ENV[current].to_s.eql?('true')
18
+ ENV[current].to_s.to_boolean
19
19
  end
20
20
  end
21
21
 
@@ -28,17 +28,39 @@ module SecureKeys
28
28
  # Check if the current instance is verbose
29
29
  # @return [Bool] true if the current instance is verbose
30
30
  def verbose?
31
- Core::Console::Argument::Handler.fetch(key: :verbose,
32
- default: ENV.fetch('VERBOSE', false))
31
+ Core::Console::Argument::Handler.fetch(key: :verbose)
33
32
  .to_s
34
- .downcase
35
- .eql?('true')
33
+ .to_boolean
34
+ end
35
+
36
+ # Check if the SecureKeys XCFramework should be generated
37
+ # @return [Bool] true if the SecureKeys XCFramework should be generated
38
+ def generate_xcframework?
39
+ Core::Console::Argument::Handler.fetch(key: :generate)
40
+ .to_s
41
+ .to_boolean
42
+ end
43
+
44
+ # Check if the SecureKeys XCFramework should be replaced
45
+ # @return [Bool] true if the SecureKeys XCFramework should be replaced
46
+ def replace_xcframework?
47
+ Core::Console::Argument::Handler.fetch(key: %i[xcframework replace])
48
+ .to_s
49
+ .to_boolean
50
+ end
51
+
52
+ # Check if the SecureKeys XCFramework should be added
53
+ # @return [Bool] true if the SecureKeys XCFramework should be added
54
+ def add_xcframework?
55
+ Core::Console::Argument::Handler.fetch(key: %i[xcframework add])
56
+ .to_s
57
+ .to_boolean
36
58
  end
37
59
 
38
60
  # Returns the Xcode project path
39
61
  # @return [String] Xcode project path
40
62
  def xcodeproj_path
41
- Core::Console::Argument::Handler.fetch(key: :xcodeproj,
63
+ Core::Console::Argument::Handler.fetch(key: %i[xcframework xcodeproj],
42
64
  default: Dir.glob('**/*.xcodeproj').first)
43
65
  end
44
66
 
@@ -48,6 +70,14 @@ module SecureKeys
48
70
  Dir.glob("**/#{Swift::KEYS_DIRECTORY}/#{Swift::XCFRAMEWORK_DIRECTORY}").first
49
71
  end
50
72
 
73
+ # Determine the secret keys source based on the environment
74
+ # @return [Object] secret keys source
75
+ def secret_keys_source
76
+ return SecureKeys::Core::Environment::CI.new if ci?
77
+
78
+ SecureKeys::Core::Environment::Keychain.new
79
+ end
80
+
51
81
  # Returns the supported iOS platforms
52
82
  # @return [Array] supported iOS platforms
53
83
  def ios_platforms
@@ -0,0 +1,13 @@
1
+ require_relative './string/to_bool'
2
+ require_relative './string/camelize'
3
+
4
+ module Kernel
5
+ include BooleanCasting
6
+ include Camelize
7
+
8
+ String.include(BooleanCasting)
9
+ String.singleton_class.include(BooleanCasting)
10
+
11
+ String.include(Camelize)
12
+ String.singleton_class.include(Camelize)
13
+ end
@@ -0,0 +1,11 @@
1
+ # Adds some useful methods to the String class
2
+ module Camelize
3
+ # Convert a string to camel case format
4
+ # @return [String] the camel case formatted string
5
+ def camelize
6
+ words = split(/(?<=[a-z])(?=[A-Z])|[-_\s]+/) # Split at lowercase-to-uppercase transitions or explicit separators
7
+ .map(&:downcase) # Convert everything to lowercase for consistency
8
+
9
+ words.map.with_index { |word, index| index.zero? ? word : word.capitalize }.join
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ # Adds some useful methods to the String class
2
+ module BooleanCasting
3
+ # Casts a string to a boolean
4
+ # @return [Boolean] the boolean value of the string
5
+ def to_bool
6
+ return self if [true, false].include?(self)
7
+ return false if nil? || empty?
8
+ return false if self =~ /^(false|f|no|n|0)$/i
9
+ return true if self =~ /^(true|t|yes|y|1)$/i
10
+
11
+ # If the string is not a boolean, return false by default
12
+ false
13
+ end
14
+ alias to_b to_bool
15
+ alias to_boolean to_bool
16
+ end
@@ -30,8 +30,6 @@ module SecureKeys
30
30
  end
31
31
  end
32
32
 
33
- private
34
-
35
33
  # Generate the formatted keys content using Swift code format
36
34
  # @return [String] The formatted keys content
37
35
  def formatted_keys
@@ -44,7 +42,7 @@ module SecureKeys
44
42
  /// The decrypted value of the key
45
43
  public var decryptedValue: String {
46
44
  switch self {
47
- #{mapped_keys.map { |key| switch_case_key_declaration_template(name: key[:name], value: key[:value], iv: key[:iv], tag: key[:tag]) }.join("\t\t\t")}
45
+ #{mapped_keys.map { |key| switch_case_key_declaration_template(name: key[:name], value: key[:value], iv: key[:iv], tag: key[:tag]) }.join("\t\t")}
48
46
  case .unknown: fatalError("Unknown key \\(rawValue)")
49
47
  }
50
48
  }
@@ -61,25 +59,7 @@ module SecureKeys
61
59
  import Foundation
62
60
  import CryptoKit
63
61
 
64
- // MARK: - Global methods
65
-
66
- /// Fetch the decrypted value of the key
67
- ///
68
- /// - Parameter:
69
- /// - key: The key to fetch the decrypted value for
70
- ///
71
- /// - Returns: The decrypted value of the key
72
- @available(iOS 13.0, *)
73
- public func key(for key: SecureKey) -> String { key.decryptedValue }
74
-
75
- /// Fetch the decrypted value of the key
76
- ///
77
- /// - Parameter:
78
- /// - key: The key to fetch the decrypted value for
79
- ///
80
- /// - Returns: The decrypted value of the key
81
- @available(iOS 13.0, *)
82
- public func key(_ key: SecureKey) -> String { key.decryptedValue }
62
+ #{key_swift_global_helper_functions}
83
63
 
84
64
  // MARK: - SecureKey enum
85
65
 
@@ -92,50 +72,95 @@ module SecureKeys
92
72
  #{content}
93
73
  }
94
74
 
95
- // MARK: - Decrypt keys from array extension
75
+ #{key_array_decrypt_swift_extension}
96
76
 
97
- @available(iOS 13.0, *)
98
- extension Array where Element == UInt8 {
77
+ #{key_string_swift_extension}
99
78
 
100
- // MARK: - Methods
79
+ // swiftlint:enable all
80
+ SWIFT
81
+ end
101
82
 
102
- func decrypt(key: [UInt8], iv: [UInt8], tag: [UInt8]) -> String {
103
- guard let sealedBox = try? AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: Data(iv)),
104
- ciphertext: Data(self),
105
- tag: Data(tag)),
106
- let decryptedData = try? AES.GCM.open(sealedBox, using: SymmetricKey(data: Data(key))),
107
- let decryptedKey = String(data: decryptedData, encoding: .utf8) else {
108
- fatalError("Failed to decrypt the key")
109
- }
110
- return decryptedKey
111
- }
112
- }
83
+ private
113
84
 
114
- // MARK: - String extension for secure keys
85
+ # Generate the key global helper functions
86
+ # @return [String] The key global helper functions
87
+ def key_swift_global_helper_functions
88
+ <<~SWIFT
89
+ // MARK: - Global methods
90
+
91
+ /// Fetch the decrypted value of the key
92
+ ///
93
+ /// - Parameter:
94
+ /// - key: The key to fetch the decrypted value for
95
+ ///
96
+ /// - Returns: The decrypted value of the key
97
+ @available(iOS 13.0, *)
98
+ public func key(for key: SecureKey) -> String { key.decryptedValue }
99
+
100
+ /// Fetch the decrypted value of the key
101
+ ///
102
+ /// - Parameter:
103
+ /// - key: The key to fetch the decrypted value for
104
+ ///
105
+ /// - Returns: The decrypted value of the key
106
+ @available(iOS 13.0, *)
107
+ public func key(_ key: SecureKey) -> String { key.decryptedValue }
108
+ SWIFT
109
+ end
115
110
 
116
- @available(iOS 13.0, *)
117
- extension String {
111
+ # Generate the key array decrypt extension
112
+ # @return [String] The key array decrypt extension
113
+ def key_array_decrypt_swift_extension
114
+ <<~SWIFT
115
+ // MARK: - Decrypt keys from array extension
116
+
117
+ @available(iOS 13.0, *)
118
+ extension Array where Element == UInt8 {
119
+
120
+ // MARK: - Methods
121
+
122
+ func decrypt(key: [UInt8], iv: [UInt8], tag: [UInt8]) -> String {
123
+ guard let sealedBox = try? AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: Data(iv)),
124
+ ciphertext: Data(self),
125
+ tag: Data(tag)),
126
+ let decryptedData = try? AES.GCM.open(sealedBox, using: SymmetricKey(data: Data(key))),
127
+ let decryptedKey = String(data: decryptedData, encoding: .utf8) else {
128
+ fatalError("Failed to decrypt the key")
129
+ }
130
+ return decryptedKey
131
+ }
132
+ }
133
+ SWIFT
134
+ end
135
+
136
+ # Generate the key string extension
137
+ # @return [String] The key string extension
138
+ def key_string_swift_extension
139
+ <<~SWIFT
140
+ // MARK: - String extension for secure keys
118
141
 
119
- // MARK: - Methods
142
+ @available(iOS 13.0, *)
143
+ extension String {
120
144
 
121
- /// Fetch the key from the secure keys enum
122
- public var secretKey: SecureKey { SecureKey(rawValue: self) ?? .unknown }
145
+ // MARK: - Methods
123
146
 
124
- /// Fetch the decrypted value of the key
125
- ///
126
- /// - Parameters:
127
- /// - key: The key to fetch the decrypted value for
128
- ///
129
- /// - Returns: The decrypted value of the key
130
- public static func key(for key: SecureKey) -> String { key.decryptedValue }
131
- }
147
+ /// Fetch the key from the secure keys enum
148
+ public var secretKey: SecureKey { SecureKey(rawValue: self) ?? .unknown }
132
149
 
133
- // swiftlint:enable all
150
+ /// Fetch the decrypted value of the key
151
+ ///
152
+ /// - Parameters:
153
+ /// - key: The key to fetch the decrypted value for
154
+ ///
155
+ /// - Returns: The decrypted value of the key
156
+ public static func key(for key: SecureKey) -> String { key.decryptedValue }
157
+ }
134
158
  SWIFT
135
159
  end
136
160
 
137
161
  # Generate the case key declaration template
138
162
  # @param name [String] The name of the key
163
+ # @return [String] The case key declaration template
139
164
  def case_key_declaration_template(name:)
140
165
  <<~SWIFT
141
166
  case #{name}
@@ -144,6 +169,10 @@ module SecureKeys
144
169
 
145
170
  # Generate the switch case key declaration template
146
171
  # @param name [String] The name of the key
172
+ # @param value [String] The value of the key
173
+ # @param iv [String] The IV of the key
174
+ # @param tag [String] The tag of the key
175
+ # @return [String] The switch case key declaration template
147
176
  def switch_case_key_declaration_template(name:, value:, iv:, tag:) # rubocop:disable Naming/MethodParameterName
148
177
  <<~SWIFT
149
178
  case .#{name}: #{value}.decrypt(key: #{secure_key_bytes}, iv: #{iv}, tag: #{tag})
@@ -22,7 +22,12 @@ module SecureKeys
22
22
  end
23
23
  end
24
24
  generate_key_xcframework
25
- add_xcframework_to_xcodeproj_target_if_needed
25
+ end
26
+
27
+ # Configure the XCFramework to the Xcode project
28
+ def configure_xcframework_to_xcodeproj
29
+ remove_xcframework_from_xcodeproj_target_if_needed if Globals.replace_xcframework?
30
+ add_xcframework_to_xcodeproj_target_if_needed if Globals.replace_xcframework? || Globals.add_xcframework?
26
31
  end
27
32
 
28
33
  private
@@ -85,7 +90,7 @@ module SecureKeys
85
90
  # Add the XCFramework to the Xcode project target if needed
86
91
  # @param target_name [String] The target name to add the XCFramework
87
92
  def add_xcframework_to_xcodeproj_target_if_needed(target_name: nil)
88
- target_name ||= Core::Console::Argument::Handler.fetch(key: :target)
93
+ target_name ||= Core::Console::Argument::Handler.fetch(key: %i[xcframework target])
89
94
  return if target_name.to_s.empty?
90
95
 
91
96
  Core::Console::Logger.important(message: "Adding the XCFramework to the target '#{target_name}'")
@@ -97,6 +102,21 @@ module SecureKeys
97
102
  xcodeproj.save
98
103
  Core::Console::Logger.success(message: "The XCFramework was added to the target '#{target_name}'")
99
104
  end
105
+
106
+ # Remove the XCFramework from the Xcode project target if needed
107
+ # @param target_name [String] The target name to remove the XCFramework
108
+ def remove_xcframework_from_xcodeproj_target_if_needed(target_name: nil)
109
+ target_name ||= Core::Console::Argument::Handler.fetch(key: %i[xcframework target])
110
+ return if target_name.to_s.empty?
111
+
112
+ Core::Console::Logger.important(message: "Removing the XCFramework from the target '#{target_name}'")
113
+ xcodeproj = Xcodeproj.xcodeproj
114
+ xcodeproj_target = Xcodeproj.xcodeproj_target_by_target_name(xcodeproj:, target_name:)
115
+ Xcodeproj.remove_xcframework(xcodeproj:, xcodeproj_target:, xcframework_path: Xcodeproj.xcframework_relative_path)
116
+
117
+ xcodeproj.save
118
+ Core::Console::Logger.success(message: "The XCFramework was removed from the target '#{target_name}'")
119
+ end
100
120
  end
101
121
  end
102
122
  end
@@ -42,14 +42,27 @@ module SecureKeys
42
42
  # Get the Xcodeproj
43
43
  # @return [Xcodeproj] The Xcodeproj
44
44
  def xcodeproj
45
- ::Xcodeproj::Project.open(SecureKeys::Globals.xcodeproj_path)
45
+ ::Xcodeproj::Project.open(Globals.xcodeproj_path)
46
+ end
47
+
48
+ # Remove all references to SecureKeys.xcframework from the Xcodeproj
49
+ # @param xcodeproj [Xcodeproj::Project] The Xcodeproj to remove the XCFramework
50
+ # @param xcodeproj_target [Xcodeproj::Project::Object::PBXNativeTarget] The target where the XCFramework is linked
51
+ # @param xcframework_path [String] The XCFramework path to remove
52
+ def remove_xcframework(xcodeproj:, xcodeproj_target:, xcframework_path:)
53
+ xcframework_filename = File.basename(xcframework_path)
54
+
55
+ remove_xcframework_from_build_phase(xcodeproj_target:, xcframework_filename:)
56
+ remove_xcframework_file_references(xcodeproj:, xcframework_filename:)
57
+ remove_xcframework_from_groups(xcodeproj:, xcframework_filename:)
58
+ remove_xcframework_from_search_paths(xcodeproj_target:, xcframework_filename:)
46
59
  end
47
60
 
48
61
  # Get the XCFramework relative path
49
62
  # @return [Pathname] The XCFramework relative path
50
63
  def xcframework_relative_path
51
- Pathname.new(SecureKeys::Globals.secure_keys_xcframework_path)
52
- .relative_path_from(Pathname.new(SecureKeys::Globals.xcodeproj_path).dirname)
64
+ Pathname.new(Globals.secure_keys_xcframework_path)
65
+ .relative_path_from(Pathname.new(Globals.xcodeproj_path).dirname)
53
66
  end
54
67
 
55
68
  # Check if the Xcode project has the secure keys XCFramework
@@ -60,10 +73,63 @@ module SecureKeys
60
73
  target.frameworks_build_phase.files.any? do |file|
61
74
  return false if file.file_ref.nil?
62
75
 
63
- file.file_ref.path.include?(SecureKeys::Globals.secure_keys_xcframework_path)
76
+ file.file_ref.path.include?(Globals.secure_keys_xcframework_path)
64
77
  end
65
78
  end
66
79
  end
80
+
81
+ # Remove the XCFramework from the "Link Binary With Libraries" build phase
82
+ # @param xcodeproj_target [Xcodeproj::Project::Object::PBXNativeTarget] The target where the XCFramework is linked
83
+ # @param xcframework_filename [String] The XCFramework filename to remove
84
+ def remove_xcframework_from_build_phase(xcodeproj_target:, xcframework_filename:)
85
+ build_phase_files = xcodeproj_target.frameworks_build_phase.files.select do |file|
86
+ file.file_ref&.path&.include?(xcframework_filename)
87
+ end
88
+
89
+ build_phase_files.each do |file|
90
+ file.remove_from_project
91
+ Core::Console::Logger.verbose(message: "Removed #{xcframework_filename} from Link Binary With Libraries in target #{xcodeproj_target.name}")
92
+ end
93
+ end
94
+
95
+ # Remove the XCFramework from the file references
96
+ # @param xcodeproj [Xcodeproj::Project] The Xcodeproj to remove the XCFramework
97
+ # @param xcframework_filename [String] The XCFramework filename to remove
98
+ def remove_xcframework_file_references(xcodeproj:, xcframework_filename:)
99
+ file_references = xcodeproj.files.select { |file| file.path&.include?(xcframework_filename) }
100
+ file_references.each do |file_ref|
101
+ file_ref.remove_from_project
102
+ Core::Console::Logger.verbose(message: "Removed #{xcframework_filename} file reference")
103
+ end
104
+
105
+ # Ensure no orphaned references remain
106
+ xcodeproj.objects.select { |obj| obj.isa == 'PBXFileReference' && obj.path&.include?(xcframework_filename) }.each(&:remove_from_project)
107
+ xcodeproj.objects.select { |obj| obj.isa == 'PBXBuildFile' && obj.file_ref&.path&.include?(xcframework_filename) }.each(&:remove_from_project)
108
+ end
109
+
110
+ # Remove the XCFramework from groups (e.g., Frameworks Group)
111
+ # @param xcodeproj [Xcodeproj::Project] The Xcodeproj to remove the XCFramework
112
+ # @param xcframework_filename [String] The XCFramework filename to remove
113
+ def remove_xcframework_from_groups(xcodeproj:, xcframework_filename:)
114
+ frameworks_group = xcodeproj.frameworks_group
115
+ group_references = frameworks_group.files.select { |file| file.path&.include?(xcframework_filename) }
116
+
117
+ group_references.each do |file_ref|
118
+ frameworks_group.remove_reference(file_ref)
119
+ Core::Console::Logger.verbose(message: "Removed #{xcframework_filename} from Frameworks group")
120
+ end
121
+ end
122
+
123
+ # Remove the XCFramework from FRAMEWORK_SEARCH_PATHS
124
+ # @param xcodeproj_target [Xcodeproj::Project::Object::PBXNativeTarget] The target where the XCFramework is linked
125
+ # @param xcframework_filename [String] The XCFramework filename to remove
126
+ def remove_xcframework_from_search_paths(xcodeproj_target:, xcframework_filename:)
127
+ xcodeproj_target.build_configurations.each do |config|
128
+ framework_search_paths = config.build_settings['FRAMEWORK_SEARCH_PATHS'] || []
129
+ new_search_paths = framework_search_paths.reject { |path| path.include?(xcframework_filename) }
130
+ config.build_settings['FRAMEWORK_SEARCH_PATHS'] = new_search_paths unless framework_search_paths == new_search_paths
131
+ end
132
+ end
67
133
  end
68
134
  end
69
135
  end
data/lib/keys.rb CHANGED
@@ -1,84 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require_relative './core/utils/extensions/kernel'
4
+ require_relative './core/generator'
3
5
  require_relative './core/globals/globals'
4
- require_relative './core/environment/ci'
5
- require_relative './core/environment/keychain'
6
- require_relative './core/utils/swift/writer'
7
- require_relative './core/utils/swift/package'
8
- require_relative './core/utils/swift/swift'
9
- require_relative './core/utils/swift/xcframework'
10
- require_relative './core/utils/openssl/cipher'
6
+ require_relative './core/console/logger'
11
7
  require_relative './core/console/arguments/parser'
8
+ require_relative './core/utils/swift/xcframework'
12
9
 
13
10
  module SecureKeys
14
- class Generator
15
- private
16
-
17
- attr_accessor :cipher, :secrets_source, :secret_keys, :mapped_keys
18
-
19
- public
20
-
21
- def initialize
22
- # Configure the argument parser
23
- SecureKeys::Core::Console::Argument::Parser.new
24
-
25
- puts "🔔 You're using a custom delimiter '#{SecureKeys::Globals.key_delimiter}'" unless SecureKeys::Globals.key_delimiter.eql?(SecureKeys::Globals.default_key_delimiter)
26
- puts "🔔 You're using a custom key access identifier '#{SecureKeys::Globals.key_access_identifier}'" unless SecureKeys::Globals.key_access_identifier.eql?(SecureKeys::Globals.default_key_access_identifier)
27
-
28
- # Configure cipher
29
- self.cipher = SecureKeys::OpenSSL::Cipher.new
30
-
31
- # Configure the secret source based on the environment
32
- if SecureKeys::Globals.ci?
33
- self.secrets_source = SecureKeys::Core::Environment::CI.new
34
- else
35
- self.secrets_source = SecureKeys::Core::Environment::Keychain.new
36
- end
37
-
38
- # Define the keys that we want to map
39
- self.secret_keys = secrets_source.fetch(key: SecureKeys::Globals.key_access_identifier)
40
- .to_s
41
- .split(SecureKeys::Globals.key_delimiter)
42
- .map(&:strip)
43
-
44
- # Add the keys that we want to map
45
- self.mapped_keys = secret_keys.map do |key|
46
- encrypted_data = cipher.encrypt(value: secrets_source.fetch(key:))
47
- # Convert the first key chart to downcase
48
- key[0] = key[0].downcase
49
- { name: key, **encrypted_data }
50
- end
51
- end
52
-
53
- def setup
54
- pre_actions
55
-
56
- package = SecureKeys::Swift::Package.new
57
- package.generate
58
-
59
- writer = SecureKeys::Swift::Writer.new(mapped_keys: mapped_keys,
60
- secure_key_bytes: cipher.secure_key_bytes)
61
- writer.write
62
-
63
- xcframework = SecureKeys::Swift::XCFramework.new
64
- xcframework.generate
65
-
66
- post_actions
67
- end
68
-
69
- private
70
-
71
- def pre_actions
72
- # Remove the keys directory
73
- system("rm -rf #{SecureKeys::Swift::KEYS_DIRECTORY}")
74
- end
75
-
76
- def post_actions
77
- # Remove the keys directory
78
- system("rm -rf #{SecureKeys::Swift::SWIFT_PACKAGE_DIRECTORY}")
11
+ def self.run
12
+ # Configure the argument parser
13
+ Core::Console::Argument::Parser.new
79
14
 
80
- # Remove the build directory
81
- system("rm -rf #{SecureKeys::Swift::KEYS_DIRECTORY}/#{SecureKeys::Swift::BUILD_DIRECTORY}")
82
- end
15
+ # Generate the keys
16
+ Core::Generator.new.generate
83
17
  end
84
18
  end
data/lib/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  module SecureKeys
4
- VERSION = '1.1.3'.freeze
4
+ VERSION = '1.1.4'.freeze
5
5
  SUMMARY = 'Secure Keys is a simple tool for managing your secret keys'.freeze
6
6
  DESCRIPTION = 'Secure Keys is a simple tool to manage your secret keys in your iOS project'.freeze
7
7
  HOMEPAGE_URI = 'https://github.com/derian-cordoba/secure-keys'.freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure-keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derian Córdoba
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-26 00:00:00.000000000 Z
11
+ date: 2025-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -230,11 +230,17 @@ extra_rdoc_files: []
230
230
  files:
231
231
  - "./lib/core/console/arguments/handler.rb"
232
232
  - "./lib/core/console/arguments/parser.rb"
233
+ - "./lib/core/console/arguments/xcframework/handler.rb"
234
+ - "./lib/core/console/arguments/xcframework/parser.rb"
233
235
  - "./lib/core/console/logger.rb"
234
236
  - "./lib/core/console/shell.rb"
235
237
  - "./lib/core/environment/ci.rb"
236
238
  - "./lib/core/environment/keychain.rb"
239
+ - "./lib/core/generator.rb"
237
240
  - "./lib/core/globals/globals.rb"
241
+ - "./lib/core/utils/extensions/kernel.rb"
242
+ - "./lib/core/utils/extensions/string/camelize.rb"
243
+ - "./lib/core/utils/extensions/string/to_bool.rb"
238
244
  - "./lib/core/utils/openssl/cipher.rb"
239
245
  - "./lib/core/utils/swift/package.rb"
240
246
  - "./lib/core/utils/swift/swift.rb"