robocap-decryption-sdk 1.0.0
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 +7 -0
- data/README.md +39 -0
- data/exe/robocap-decryption-sdk +6 -0
- data/lib/robocap/sdk/cli.rb +154 -0
- data/lib/robocap/sdk/config.rb +67 -0
- data/lib/robocap/sdk/decrypt_cenc.rb +64 -0
- data/lib/robocap/sdk/errors.rb +69 -0
- data/lib/robocap/sdk/ffmpeg_cli.rb +109 -0
- data/lib/robocap/sdk/key_vault.rb +308 -0
- data/lib/robocap/sdk/mp4_cenc.rb +154 -0
- data/lib/robocap/sdk/ownership.rb +69 -0
- data/lib/robocap/sdk/rsa_delete.rb +41 -0
- data/lib/robocap/sdk/rsa_import.rb +32 -0
- data/lib/robocap/sdk/rsa_key_meta.rb +55 -0
- data/lib/robocap/sdk/rsa_oaep.rb +93 -0
- data/lib/robocap/sdk/vault_layout.rb +50 -0
- data/lib/robocap/sdk/version.rb +7 -0
- data/lib/robocap/sdk.rb +41 -0
- metadata +99 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'openssl'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
require_relative 'config'
|
|
6
|
+
require_relative 'errors'
|
|
7
|
+
|
|
8
|
+
module Robocap
|
|
9
|
+
module SDK
|
|
10
|
+
module RSAOAEP
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def cipher_len_for_key(key)
|
|
14
|
+
key.n.num_bytes
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def wrap_key(plaintext, public_key, plain_len:, cipher_len: nil)
|
|
18
|
+
unless plaintext.bytesize == plain_len
|
|
19
|
+
raise ArgumentError, "plaintext must be #{plain_len} bytes (got #{plaintext.bytesize})"
|
|
20
|
+
end
|
|
21
|
+
expected = cipher_len || cipher_len_for_key(public_key)
|
|
22
|
+
ciphertext = public_key.encrypt(
|
|
23
|
+
plaintext,
|
|
24
|
+
rsa_padding_mode: 'oaep',
|
|
25
|
+
rsa_oaep_md: 'sha256',
|
|
26
|
+
rsa_mgf1_md: 'sha256',
|
|
27
|
+
)
|
|
28
|
+
unless ciphertext.bytesize == expected
|
|
29
|
+
raise ArgumentError, "RSA ciphertext length #{ciphertext.bytesize} != #{expected}"
|
|
30
|
+
end
|
|
31
|
+
ciphertext
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def unwrap_key(ciphertext, private_key,
|
|
35
|
+
plain_len:, cipher_len: nil,
|
|
36
|
+
decode_error: ErrorCode::ERR_K2_DECODE,
|
|
37
|
+
length_error: ErrorCode::ERR_K2_PLAINTEXT_LENGTH)
|
|
38
|
+
expected = cipher_len || cipher_len_for_key(private_key)
|
|
39
|
+
unless ciphertext.bytesize == expected
|
|
40
|
+
raise Error.new(code: decode_error, message: "RSA ciphertext must be #{expected} bytes")
|
|
41
|
+
end
|
|
42
|
+
plaintext = begin
|
|
43
|
+
private_key.decrypt(
|
|
44
|
+
ciphertext,
|
|
45
|
+
rsa_padding_mode: 'oaep',
|
|
46
|
+
rsa_oaep_md: 'sha256',
|
|
47
|
+
rsa_mgf1_md: 'sha256',
|
|
48
|
+
)
|
|
49
|
+
rescue OpenSSL::PKey::PKeyError, OpenSSL::PKey::RSAError => exc
|
|
50
|
+
raise Error.new(code: decode_error, message: 'RSA-OAEP unwrap failed', detail: { reason: exc.message })
|
|
51
|
+
end
|
|
52
|
+
unless plaintext.bytesize == plain_len
|
|
53
|
+
raise Error.new(code: length_error, message: "Decrypted key length #{plaintext.bytesize} != #{plain_len}")
|
|
54
|
+
end
|
|
55
|
+
plaintext
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def wrap_aes_key(device_aes, public_key)
|
|
59
|
+
wrap_key(
|
|
60
|
+
device_aes, public_key,
|
|
61
|
+
plain_len: Config::AES_KEY_BYTES,
|
|
62
|
+
cipher_len: Config::RSA_CIPHERTEXT_BYTES,
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def unwrap_aes_key(ciphertext, private_key)
|
|
67
|
+
unwrap_key(
|
|
68
|
+
ciphertext, private_key,
|
|
69
|
+
plain_len: Config::AES_KEY_BYTES,
|
|
70
|
+
cipher_len: Config::RSA_CIPHERTEXT_BYTES,
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def wrap_cek(cek, public_key)
|
|
75
|
+
wrap_key(
|
|
76
|
+
cek, public_key,
|
|
77
|
+
plain_len: Config::CEK_BYTES,
|
|
78
|
+
cipher_len: Config::RSA_2048_CIPHERTEXT_BYTES,
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def unwrap_cek(ciphertext, private_key)
|
|
83
|
+
unwrap_key(
|
|
84
|
+
ciphertext, private_key,
|
|
85
|
+
plain_len: Config::CEK_BYTES,
|
|
86
|
+
cipher_len: Config::RSA_2048_CIPHERTEXT_BYTES,
|
|
87
|
+
decode_error: ErrorCode::ERR_CENC_CEKA_WRAP,
|
|
88
|
+
length_error: ErrorCode::ERR_CENC_CEKA_LENGTH,
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require 'tempfile'
|
|
6
|
+
require_relative 'errors'
|
|
7
|
+
|
|
8
|
+
module Robocap
|
|
9
|
+
module SDK
|
|
10
|
+
module VaultLayout
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def ensure_private_dir(path)
|
|
14
|
+
path = Pathname(path)
|
|
15
|
+
FileUtils.mkdir_p(path)
|
|
16
|
+
File.chmod(0o700, path) unless Gem.win_platform?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def atomic_write_bytes(target, data)
|
|
20
|
+
target = Pathname(target)
|
|
21
|
+
tmp = nil
|
|
22
|
+
begin
|
|
23
|
+
ensure_private_dir(target.parent)
|
|
24
|
+
tmp = Tempfile.new(['.tmp_', ''], target.parent.to_s, binmode: true)
|
|
25
|
+
tmp.write(data)
|
|
26
|
+
tmp.flush
|
|
27
|
+
tmp.fsync rescue nil
|
|
28
|
+
tmp.close
|
|
29
|
+
File.rename(tmp.path, target.to_s)
|
|
30
|
+
tmp = nil
|
|
31
|
+
rescue SystemCallError => exc
|
|
32
|
+
raise Error.new(
|
|
33
|
+
code: ErrorCode::ERR_VAULT_IO,
|
|
34
|
+
message: "Failed to write #{target}",
|
|
35
|
+
detail: { path: target.to_s, reason: exc.message },
|
|
36
|
+
)
|
|
37
|
+
ensure
|
|
38
|
+
if tmp
|
|
39
|
+
tmp.close unless tmp.closed?
|
|
40
|
+
File.unlink(tmp.path) if File.exist?(tmp.path)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def atomic_write_text(target, text, encoding: 'UTF-8')
|
|
46
|
+
atomic_write_bytes(target, text.encode(encoding).b)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/robocap/sdk.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'sdk/version'
|
|
4
|
+
require_relative 'sdk/config'
|
|
5
|
+
require_relative 'sdk/errors'
|
|
6
|
+
require_relative 'sdk/rsa_key_meta'
|
|
7
|
+
require_relative 'sdk/rsa_oaep'
|
|
8
|
+
require_relative 'sdk/vault_layout'
|
|
9
|
+
require_relative 'sdk/key_vault'
|
|
10
|
+
require_relative 'sdk/ffmpeg_cli'
|
|
11
|
+
require_relative 'sdk/mp4_cenc'
|
|
12
|
+
require_relative 'sdk/ownership'
|
|
13
|
+
require_relative 'sdk/rsa_import'
|
|
14
|
+
require_relative 'sdk/rsa_delete'
|
|
15
|
+
require_relative 'sdk/decrypt_cenc'
|
|
16
|
+
|
|
17
|
+
module Robocap
|
|
18
|
+
module SDK
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
def import_rsa_key_version(**kwargs)
|
|
22
|
+
RsaImport.call(**kwargs)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete_rsa_key_version(**kwargs)
|
|
26
|
+
RsaDelete.call(**kwargs)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def delete_rsa_key_dir(**kwargs)
|
|
30
|
+
RsaDelete.call_with_dir(**kwargs)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def decrypt_cenc_mp4(**kwargs)
|
|
34
|
+
DecryptCenc.call(**kwargs)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def verify_customer_private_key(**kwargs)
|
|
38
|
+
Ownership.verify(**kwargs)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: robocap-decryption-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Frodobots
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.2'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: minitest
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.20'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.20'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '13.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '13.0'
|
|
54
|
+
description: Ruby port of the Robocap CENC decryption SDK. Reads the same on-disk
|
|
55
|
+
vault and MP4 format as the Python SDK.
|
|
56
|
+
executables:
|
|
57
|
+
- robocap-decryption-sdk
|
|
58
|
+
extensions: []
|
|
59
|
+
extra_rdoc_files: []
|
|
60
|
+
files:
|
|
61
|
+
- README.md
|
|
62
|
+
- exe/robocap-decryption-sdk
|
|
63
|
+
- lib/robocap/sdk.rb
|
|
64
|
+
- lib/robocap/sdk/cli.rb
|
|
65
|
+
- lib/robocap/sdk/config.rb
|
|
66
|
+
- lib/robocap/sdk/decrypt_cenc.rb
|
|
67
|
+
- lib/robocap/sdk/errors.rb
|
|
68
|
+
- lib/robocap/sdk/ffmpeg_cli.rb
|
|
69
|
+
- lib/robocap/sdk/key_vault.rb
|
|
70
|
+
- lib/robocap/sdk/mp4_cenc.rb
|
|
71
|
+
- lib/robocap/sdk/ownership.rb
|
|
72
|
+
- lib/robocap/sdk/rsa_delete.rb
|
|
73
|
+
- lib/robocap/sdk/rsa_import.rb
|
|
74
|
+
- lib/robocap/sdk/rsa_key_meta.rb
|
|
75
|
+
- lib/robocap/sdk/rsa_oaep.rb
|
|
76
|
+
- lib/robocap/sdk/vault_layout.rb
|
|
77
|
+
- lib/robocap/sdk/version.rb
|
|
78
|
+
homepage: https://github.com/frodobots-org/robocap-decryption-sdk
|
|
79
|
+
licenses:
|
|
80
|
+
- Nonstandard
|
|
81
|
+
metadata: {}
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '3.2'
|
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
requirements: []
|
|
96
|
+
rubygems_version: 3.7.2
|
|
97
|
+
specification_version: 4
|
|
98
|
+
summary: Offline import of RSA keys and decrypt of CENC-encrypted MP4 files.
|
|
99
|
+
test_files: []
|