saro-dat 1.0.0 → 4.3.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 +4 -4
- data/.gitignore +1 -0
- data/.idea/saro-dat.iml +3 -2
- data/PUBLISH.md +8 -2
- data/README.md +68 -39
- data/lib/saro/dat/crypto.rb +12 -15
- data/lib/saro/dat/dat.rb +3 -1
- data/lib/saro/dat/dat_certificate.rb +53 -26
- data/lib/saro/dat/dat_manager.rb +10 -7
- data/lib/saro/dat/signature.rb +94 -101
- data/lib/saro-dat.rb +1 -0
- data/saro-dat.gemspec +3 -2
- metadata +18 -5
- data/Gemfile.lock +0 -39
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bc8964419d2aa39030dbec0e990d8a22d1fa29a8f8eec15235f69e70fa81054
|
|
4
|
+
data.tar.gz: e2fb45d1bbb981ea6745fab4661db4f1db41063fe73bc147cd33276c60e4a0fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c306baa43850bc77ebd33a2c1717f1aaac63ca0c98b538da28cb8ca064e0ed475b0c2e866fee5b47700a49009c085621c5b21f1b989041028e0a481112092590
|
|
7
|
+
data.tar.gz: c75ede02ecff59c9e18a85116247eb3bae95c594940f8fedec457434a8f9c9dc9690667d1be103cb4abf03b18c483997a9f0fe4b6adbbb3f3e19c644ad9e66e2
|
data/.gitignore
CHANGED
data/.idea/saro-dat.iml
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
<orderEntry type="sourceFolder" forTests="false" />
|
|
14
14
|
<orderEntry type="library" scope="PROVIDED" name="base64 (v0.3.0, rbenv: 4.0.5) [gem]" level="application" />
|
|
15
15
|
<orderEntry type="library" scope="PROVIDED" name="benchmark (v0.5.0, rbenv: 4.0.5) [gem]" level="application" />
|
|
16
|
-
<orderEntry type="library" scope="PROVIDED" name="bundler (v4.0.
|
|
17
|
-
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.
|
|
16
|
+
<orderEntry type="library" scope="PROVIDED" name="bundler (v4.0.12, rbenv: 4.0.5) [gem]" level="application" />
|
|
17
|
+
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.7, rbenv: 4.0.5) [gem]" level="application" />
|
|
18
|
+
<orderEntry type="library" scope="PROVIDED" name="logger (v1.7.0, rbenv: 4.0.5) [gem]" level="application" />
|
|
18
19
|
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.27.0, rbenv: 4.0.5) [gem]" level="application" />
|
|
19
20
|
<orderEntry type="library" scope="PROVIDED" name="openssl (v4.0.2, rbenv: 4.0.5) [gem]" level="application" />
|
|
20
21
|
<orderEntry type="library" scope="PROVIDED" name="parallel (v2.1.0, rbenv: 4.0.5) [gem]" level="application" />
|
data/PUBLISH.md
CHANGED
|
@@ -9,7 +9,7 @@ bundle install
|
|
|
9
9
|
```
|
|
10
10
|
gem build saro-dat.gemspec
|
|
11
11
|
gem signin
|
|
12
|
-
gem push saro-dat-
|
|
12
|
+
gem push saro-dat-4.3.0.gem
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## install
|
|
@@ -23,10 +23,16 @@ source ~/.zshrc
|
|
|
23
23
|
# - mac
|
|
24
24
|
|
|
25
25
|
rbenv install -l
|
|
26
|
-
rbenv install 2.7.8
|
|
27
26
|
rbenv install 4.0.5
|
|
28
27
|
rbenv local 4.0.5
|
|
29
28
|
rbenv rehash
|
|
30
29
|
|
|
31
30
|
ruby -v
|
|
32
31
|
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## install test
|
|
35
|
+
```
|
|
36
|
+
bundle add saro-dat
|
|
37
|
+
bundle install
|
|
38
|
+
```
|
data/README.md
CHANGED
|
@@ -4,22 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
### [DAT Run Online](https://dat.saro.me)
|
|
6
6
|
|
|
7
|
-
### [What is DAT](https://dat.saro.me
|
|
7
|
+
### [What is DAT](https://dat.saro.me/intro)
|
|
8
8
|
|
|
9
|
-
### [Example](https://dat.saro.me
|
|
9
|
+
### [Example](https://dat.saro.me/libs/gems-saro-dat)
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
11
|
+
## Support algorithm
|
|
12
|
+
### Signature
|
|
13
|
+
| name | note |
|
|
14
|
+
|-----------------|-----------------------|
|
|
15
|
+
| ECDSA-P256 | = secp256r1 |
|
|
16
|
+
| ECDSA-P384 | = secp384r1 |
|
|
17
|
+
| ECDSA-P521 | = secp521r1 |
|
|
18
|
+
| HMAC-SHA256-MFS | = 256Bit Fixed Secret |
|
|
19
|
+
| HMAC-SHA384-MFS | = 384Bit Fixed Secret |
|
|
20
|
+
| HMAC-SHA512-MFS | = 512Bit Fixed Secret |
|
|
21
|
+
- MFS : Maximum(Same Bit) Fixed Secret
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
| name |
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
+
### Crypto
|
|
24
|
+
| name | note |
|
|
25
|
+
|------------|-------------------------------|
|
|
26
|
+
| IV-AES128-GCM | (IV=NONCE:96BIT) + AES128 GCM |
|
|
27
|
+
| IV-AES256-GCM | (IV=NONCE:96BIT) + AES256 GCM |
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
# Performance
|
|
@@ -29,34 +34,58 @@
|
|
|
29
34
|
```
|
|
30
35
|
Testing started at ...
|
|
31
36
|
Performance Test (Plain, Secure)
|
|
32
|
-
Plain:
|
|
33
|
-
Secure:
|
|
37
|
+
Plain: CcMjua0RA8I27be6W2lYMBmk5OcPrV8mu3ybrVwBOB2pEPbtfrvH0h0Z0VBhG1ID4zu51sDKxZZGFoku9TzrcPJLbb0ObRiF3NIF
|
|
38
|
+
Secure: EEYEOjriFkidJ8lKpYPkt1fwS01sZuJ7ysgCWd7XyarZfP6yrxkR9rciiJSUpuPZlBt0moFlKb0n2ZDivhvxLNjmO1eT8KjzhLlA
|
|
34
39
|
|
|
35
40
|
--- Multi-Thread ---
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
HMAC-SHA256-MFS IV-AES128-GCM Issue * 10000 : 141ms
|
|
42
|
+
HMAC-SHA256-MFS IV-AES128-GCM Parse * 10000 : 148ms
|
|
43
|
+
HMAC-SHA256-MFS IV-AES256-GCM Issue * 10000 : 126ms
|
|
44
|
+
HMAC-SHA256-MFS IV-AES256-GCM Parse * 10000 : 161ms
|
|
45
|
+
HMAC-SHA384-MFS IV-AES128-GCM Issue * 10000 : 162ms
|
|
46
|
+
HMAC-SHA384-MFS IV-AES128-GCM Parse * 10000 : 155ms
|
|
47
|
+
HMAC-SHA384-MFS IV-AES256-GCM Issue * 10000 : 126ms
|
|
48
|
+
HMAC-SHA384-MFS IV-AES256-GCM Parse * 10000 : 146ms
|
|
49
|
+
HMAC-SHA512-MFS IV-AES128-GCM Issue * 10000 : 124ms
|
|
50
|
+
HMAC-SHA512-MFS IV-AES128-GCM Parse * 10000 : 132ms
|
|
51
|
+
HMAC-SHA512-MFS IV-AES256-GCM Issue * 10000 : 128ms
|
|
52
|
+
HMAC-SHA512-MFS IV-AES256-GCM Parse * 10000 : 149ms
|
|
53
|
+
ECDSA-P256 IV-AES128-GCM Issue * 10000 : 171ms
|
|
54
|
+
ECDSA-P256 IV-AES128-GCM Parse * 10000 : 188ms
|
|
55
|
+
ECDSA-P256 IV-AES256-GCM Issue * 10000 : 158ms
|
|
56
|
+
ECDSA-P256 IV-AES256-GCM Parse * 10000 : 188ms
|
|
57
|
+
ECDSA-P384 IV-AES128-GCM Issue * 10000 : 260ms
|
|
58
|
+
ECDSA-P384 IV-AES128-GCM Parse * 10000 : 422ms
|
|
59
|
+
ECDSA-P384 IV-AES256-GCM Issue * 10000 : 259ms
|
|
60
|
+
ECDSA-P384 IV-AES256-GCM Parse * 10000 : 415ms
|
|
61
|
+
ECDSA-P521 IV-AES128-GCM Issue * 10000 : 307ms
|
|
62
|
+
ECDSA-P521 IV-AES128-GCM Parse * 10000 : 475ms
|
|
63
|
+
ECDSA-P521 IV-AES256-GCM Issue * 10000 : 311ms
|
|
64
|
+
ECDSA-P521 IV-AES256-GCM Parse * 10000 : 481ms
|
|
48
65
|
|
|
49
66
|
--- Single-Thread ---
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
HMAC-SHA256-MFS IV-AES128-GCM Issue * 10000 : 64ms
|
|
68
|
+
HMAC-SHA256-MFS IV-AES128-GCM Parse * 10000 : 65ms
|
|
69
|
+
HMAC-SHA256-MFS IV-AES256-GCM Issue * 10000 : 60ms
|
|
70
|
+
HMAC-SHA256-MFS IV-AES256-GCM Parse * 10000 : 68ms
|
|
71
|
+
HMAC-SHA384-MFS IV-AES128-GCM Issue * 10000 : 66ms
|
|
72
|
+
HMAC-SHA384-MFS IV-AES128-GCM Parse * 10000 : 83ms
|
|
73
|
+
HMAC-SHA384-MFS IV-AES256-GCM Issue * 10000 : 64ms
|
|
74
|
+
HMAC-SHA384-MFS IV-AES256-GCM Parse * 10000 : 67ms
|
|
75
|
+
HMAC-SHA512-MFS IV-AES128-GCM Issue * 10000 : 62ms
|
|
76
|
+
HMAC-SHA512-MFS IV-AES128-GCM Parse * 10000 : 67ms
|
|
77
|
+
HMAC-SHA512-MFS IV-AES256-GCM Issue * 10000 : 66ms
|
|
78
|
+
HMAC-SHA512-MFS IV-AES256-GCM Parse * 10000 : 68ms
|
|
79
|
+
ECDSA-P256 IV-AES128-GCM Issue * 10000 : 192ms
|
|
80
|
+
ECDSA-P256 IV-AES128-GCM Parse * 10000 : 403ms
|
|
81
|
+
ECDSA-P256 IV-AES256-GCM Issue * 10000 : 188ms
|
|
82
|
+
ECDSA-P256 IV-AES256-GCM Parse * 10000 : 404ms
|
|
83
|
+
ECDSA-P384 IV-AES128-GCM Issue * 10000 : 1049ms
|
|
84
|
+
ECDSA-P384 IV-AES128-GCM Parse * 10000 : 2157ms
|
|
85
|
+
ECDSA-P384 IV-AES256-GCM Issue * 10000 : 1060ms
|
|
86
|
+
ECDSA-P384 IV-AES256-GCM Parse * 10000 : 2155ms
|
|
87
|
+
ECDSA-P521 IV-AES128-GCM Issue * 10000 : 1374ms
|
|
88
|
+
ECDSA-P521 IV-AES128-GCM Parse * 10000 : 2428ms
|
|
89
|
+
ECDSA-P521 IV-AES256-GCM Issue * 10000 : 1368ms
|
|
90
|
+
ECDSA-P521 IV-AES256-GCM Parse * 10000 : 2417ms
|
|
62
91
|
```
|
data/lib/saro/dat/crypto.rb
CHANGED
|
@@ -7,8 +7,8 @@ require_relative 'util'
|
|
|
7
7
|
module Saro
|
|
8
8
|
module Dat
|
|
9
9
|
class DatCryptoAlgorithm
|
|
10
|
-
AES128GCMN = "
|
|
11
|
-
AES256GCMN = "
|
|
10
|
+
AES128GCMN = "IV-AES128-GCM"
|
|
11
|
+
AES256GCMN = "IV-AES256-GCM"
|
|
12
12
|
|
|
13
13
|
def self.all
|
|
14
14
|
[AES128GCMN, AES256GCMN]
|
|
@@ -16,8 +16,8 @@ module Saro
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
CRYPTO_CONFIG = {
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
"IV-AES128-GCM" => { name: "aes-128-gcm", length: 16 },
|
|
20
|
+
"IV-AES256-GCM" => { name: "aes-256-gcm", length: 32 }
|
|
21
21
|
}.freeze
|
|
22
22
|
|
|
23
23
|
def self.get_crypto_config(algorithm)
|
|
@@ -26,7 +26,7 @@ module Saro
|
|
|
26
26
|
raise ArgumentError, "Unsupported DAT Crypto Algorithm: #{algorithm}"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
class
|
|
29
|
+
class DatCrypto
|
|
30
30
|
attr_reader :algorithm
|
|
31
31
|
|
|
32
32
|
def initialize(algorithm, key_bytes, config = nil)
|
|
@@ -41,12 +41,13 @@ module Saro
|
|
|
41
41
|
new(algorithm, key_bytes, config)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def self.imports(algorithm,
|
|
45
|
-
|
|
44
|
+
def self.imports(algorithm, base64_str)
|
|
45
|
+
key_bytes = Saro::Dat::Util.decode_base64_url(base64_str)
|
|
46
|
+
new(algorithm, key_bytes)
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def exports
|
|
49
|
-
@key_bytes
|
|
50
|
+
Saro::Dat::Util.encode_base64_url_str(@key_bytes)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def encrypt(data)
|
|
@@ -87,13 +88,9 @@ module Saro
|
|
|
87
88
|
cipher.iv = nonce
|
|
88
89
|
cipher.auth_tag = tag
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
res
|
|
94
|
-
rescue OpenSSL::Cipher::CipherError => e
|
|
95
|
-
raise ArgumentError, "Decryption failed: #{e.message}"
|
|
96
|
-
end
|
|
91
|
+
res = cipher.update(ciphertext) + cipher.final
|
|
92
|
+
res.force_encoding('BINARY')
|
|
93
|
+
res
|
|
97
94
|
end
|
|
98
95
|
end
|
|
99
96
|
end
|
data/lib/saro/dat/dat.rb
CHANGED
|
@@ -38,11 +38,13 @@ module Saro
|
|
|
38
38
|
new(value)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def expired
|
|
41
|
+
def expired
|
|
42
42
|
return true unless @format
|
|
43
43
|
Time.now.to_i > @expire
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
alias_method :expired?, :expired
|
|
47
|
+
|
|
46
48
|
def body_string
|
|
47
49
|
return "" unless @dat.include?('.')
|
|
48
50
|
@dat.rpartition('.').first
|
|
@@ -7,53 +7,80 @@ require_relative 'util'
|
|
|
7
7
|
module Saro
|
|
8
8
|
module Dat
|
|
9
9
|
class DatCertificate
|
|
10
|
-
attr_reader :cid, :signature_key, :crypto_key, :
|
|
10
|
+
attr_reader :cid, :signature_key, :crypto_key, :dat_issuance_start_seconds, :dat_issuance_end_seconds, :dat_ttl_seconds
|
|
11
11
|
|
|
12
|
-
def initialize(cid,
|
|
12
|
+
def initialize(cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds, signature_key, crypto_key)
|
|
13
13
|
@cid = cid
|
|
14
|
+
@dat_issuance_start_seconds = dat_issuance_start_seconds
|
|
15
|
+
@dat_issuance_end_seconds = dat_issuance_start_seconds + dat_issuance_duration_seconds
|
|
16
|
+
@dat_ttl_seconds = dat_ttl_seconds
|
|
14
17
|
@signature_key = signature_key
|
|
15
18
|
@crypto_key = crypto_key
|
|
16
|
-
@dat_issue_begin = dat_issue_begin
|
|
17
|
-
@dat_issue_end = dat_issue_end
|
|
18
|
-
@dat_ttl = dat_ttl
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def exports(
|
|
21
|
+
def exports(verify_only = false)
|
|
22
22
|
cid_hex = @cid.to_s(16)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
dat_issuance_start_seconds = @dat_issuance_start_seconds.to_s
|
|
24
|
+
dat_issuance_duration_seconds = (@dat_issuance_end_seconds - @dat_issuance_start_seconds).to_s
|
|
25
|
+
dat_ttl_seconds = @dat_ttl_seconds.to_s
|
|
26
|
+
signature_algorithm = @signature_key.algorithm
|
|
27
|
+
crypto_algorithm = @crypto_key.algorithm
|
|
28
|
+
signature_key = @signature_key.exports(verify_only)
|
|
29
|
+
crypto_key = @crypto_key.exports
|
|
27
30
|
|
|
28
|
-
"#{cid_hex}.#{
|
|
31
|
+
"#{cid_hex}.#{dat_issuance_start_seconds}.#{dat_issuance_duration_seconds}.#{dat_ttl_seconds}.#{signature_algorithm}.#{crypto_algorithm}.#{signature_key}.#{crypto_key}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.generate(cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds, signature_algorithm, crypto_algorithm)
|
|
35
|
+
new(
|
|
36
|
+
cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds,
|
|
37
|
+
Saro::Dat::DatSignature.generate(signature_algorithm),
|
|
38
|
+
Saro::Dat::DatCrypto.generate(crypto_algorithm)
|
|
39
|
+
)
|
|
29
40
|
end
|
|
30
41
|
|
|
31
42
|
def self.imports(format_str)
|
|
32
|
-
|
|
33
|
-
raise ArgumentError, "Invalid Certificate format" if
|
|
43
|
+
parts = format_str.split(".")
|
|
44
|
+
raise ArgumentError, "Invalid Certificate format" if parts.length != 8
|
|
34
45
|
|
|
35
|
-
cid =
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
cid = parts[0].to_i(16)
|
|
47
|
+
dat_issuance_start_seconds = parts[1].to_i
|
|
48
|
+
dat_issuance_duration_seconds = parts[2].to_i
|
|
49
|
+
dat_ttl_seconds = parts[3].to_i
|
|
50
|
+
signature_algorithm = parts[4]
|
|
51
|
+
crypto_algorithm = parts[5]
|
|
52
|
+
signature_key = Saro::Dat::DatSignature.imports(signature_algorithm, parts[6])
|
|
53
|
+
crypto_key = Saro::Dat::DatCrypto.imports(crypto_algorithm, parts[7])
|
|
38
54
|
|
|
39
|
-
new(
|
|
40
|
-
cid, sig_key, cry_key,
|
|
41
|
-
split[5].to_i, split[6].to_i, split[7].to_i
|
|
42
|
-
)
|
|
55
|
+
new(cid, dat_issuance_start_seconds, dat_issuance_duration_seconds, dat_ttl_seconds, signature_key, crypto_key)
|
|
43
56
|
end
|
|
44
57
|
|
|
45
|
-
def issuable
|
|
58
|
+
def issuable
|
|
46
59
|
now = Time.now.to_i
|
|
47
|
-
|
|
60
|
+
signable && @dat_issuance_start_seconds <= now && now <= @dat_issuance_end_seconds
|
|
48
61
|
end
|
|
49
62
|
|
|
50
|
-
def expired
|
|
51
|
-
Time.now.to_i > (@
|
|
63
|
+
def expired
|
|
64
|
+
Time.now.to_i > (@dat_issuance_end_seconds + @dat_ttl_seconds)
|
|
52
65
|
end
|
|
53
66
|
|
|
54
|
-
def
|
|
55
|
-
@signature_key.
|
|
67
|
+
def signable
|
|
68
|
+
@signature_key.signable
|
|
56
69
|
end
|
|
70
|
+
|
|
71
|
+
def pair
|
|
72
|
+
@signature_key.pair
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def support_verify_only
|
|
76
|
+
@signature_key.support_verify_only
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# For Ruby conventions
|
|
80
|
+
alias_method :issuable?, :issuable
|
|
81
|
+
alias_method :expired?, :expired
|
|
82
|
+
alias_method :signable?, :signable
|
|
83
|
+
alias_method :pair?, :pair
|
|
57
84
|
end
|
|
58
85
|
end
|
|
59
86
|
end
|
data/lib/saro/dat/dat_manager.rb
CHANGED
|
@@ -17,29 +17,32 @@ module Saro
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def import_certificates(input_certs, clear: false)
|
|
20
|
+
renew_count = 0
|
|
20
21
|
@lock.with_write_lock do
|
|
21
22
|
certificates = clear ? [] : @certificates.dup
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
before_cids = Set.new(certificates.map(&:cid))
|
|
24
25
|
seen_cids = Set.new
|
|
25
26
|
|
|
26
27
|
input_certs.each do |cert|
|
|
27
28
|
raise ArgumentError, "Duplicate CID: #{cert.cid}" if seen_cids.include?(cert.cid)
|
|
28
29
|
seen_cids.add(cert.cid)
|
|
29
|
-
next if cert.expired
|
|
30
|
+
next if cert.expired
|
|
30
31
|
next if before_cids.include?(cert.cid)
|
|
31
32
|
|
|
32
33
|
certificates << cert
|
|
34
|
+
renew_count += 1
|
|
33
35
|
end
|
|
34
36
|
|
|
35
|
-
certificates.sort_by!(&:
|
|
37
|
+
certificates.sort_by!(&:dat_issuance_end_seconds)
|
|
36
38
|
|
|
37
39
|
# Find latest issuable certificate as issuer
|
|
38
|
-
issuer = certificates.reverse_each.find(&:issuable
|
|
40
|
+
issuer = certificates.reverse_each.find(&:issuable)
|
|
39
41
|
|
|
40
42
|
@issuer = issuer
|
|
41
43
|
@certificates = certificates
|
|
42
44
|
end
|
|
45
|
+
renew_count
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
def imports(format_str, clear: false)
|
|
@@ -52,9 +55,9 @@ module Saro
|
|
|
52
55
|
import_certificates(certs, clear: clear)
|
|
53
56
|
end
|
|
54
57
|
|
|
55
|
-
def exports(
|
|
58
|
+
def exports(verify_only = false)
|
|
56
59
|
@lock.with_read_lock do
|
|
57
|
-
@certificates.map { |cert| cert.exports(
|
|
60
|
+
@certificates.map { |cert| cert.exports(verify_only) }.join("\n")
|
|
58
61
|
end
|
|
59
62
|
end
|
|
60
63
|
|
|
@@ -82,7 +85,7 @@ module Saro
|
|
|
82
85
|
|
|
83
86
|
def self._issue(cert, plain, secure)
|
|
84
87
|
now = Time.now.to_i
|
|
85
|
-
expire = now + cert.
|
|
88
|
+
expire = now + cert.dat_ttl_seconds
|
|
86
89
|
cid_hex = cert.cid.to_s(16)
|
|
87
90
|
|
|
88
91
|
plain_bytes = plain.is_a?(String) ? plain.encode('utf-8') : (plain || "".b)
|
data/lib/saro/dat/signature.rb
CHANGED
|
@@ -6,25 +6,25 @@ require_relative 'util'
|
|
|
6
6
|
module Saro
|
|
7
7
|
module Dat
|
|
8
8
|
class DatSignatureAlgorithm
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
HMAC_SHA256_MFS = "HMAC-SHA256-MFS"
|
|
10
|
+
HMAC_SHA384_MFS = "HMAC-SHA384-MFS"
|
|
11
|
+
HMAC_SHA512_MFS = "HMAC-SHA512-MFS"
|
|
12
|
+
P256 = "ECDSA-P256"
|
|
13
|
+
P384 = "ECDSA-P384"
|
|
14
|
+
P521 = "ECDSA-P521"
|
|
12
15
|
|
|
13
16
|
def self.all
|
|
14
|
-
[P256, P384, P521]
|
|
17
|
+
[HMAC_SHA256_MFS, HMAC_SHA384_MFS, HMAC_SHA512_MFS, P256, P384, P521]
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
|
|
18
|
-
class DatSignatureKeyExportOption
|
|
19
|
-
PAIR = "PAIR"
|
|
20
|
-
SIGNING = "SIGNING"
|
|
21
|
-
VERIFYING = "VERIFYING"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
21
|
SIGNATURE_CONFIG = {
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
22
|
+
"HMAC-SHA256-MFS" => { name: "HMAC", hash: "SHA256", hmac_len: 32 },
|
|
23
|
+
"HMAC-SHA384-MFS" => { name: "HMAC", hash: "SHA384", hmac_len: 48 },
|
|
24
|
+
"HMAC-SHA512-MFS" => { name: "HMAC", hash: "SHA512", hmac_len: 64 },
|
|
25
|
+
"ECDSA-P256" => { name: "ECDSA", curve: "prime256v1", hash: "SHA256", private_len: 32, public_len: 65 },
|
|
26
|
+
"ECDSA-P384" => { name: "ECDSA", curve: "secp384r1", hash: "SHA384", private_len: 48, public_len: 97 },
|
|
27
|
+
"ECDSA-P521" => { name: "ECDSA", curve: "secp521r1", hash: "SHA512", private_len: 66, public_len: 133 }
|
|
28
28
|
}.freeze
|
|
29
29
|
|
|
30
30
|
def self.get_signature_config(algorithm)
|
|
@@ -33,13 +33,7 @@ module Saro
|
|
|
33
33
|
raise ArgumentError, "Unsupported DAT Crypto Algorithm: #{algorithm}"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
"prime256v1" => "1.2.840.10045.3.1.7",
|
|
38
|
-
"secp384r1" => "1.3.132.0.34",
|
|
39
|
-
"secp521r1" => "1.3.132.0.35"
|
|
40
|
-
}.freeze
|
|
41
|
-
|
|
42
|
-
class DatSignatureKey
|
|
36
|
+
class DatSignature
|
|
43
37
|
attr_reader :algorithm, :signing_key, :verifying_key
|
|
44
38
|
|
|
45
39
|
def initialize(algorithm, signing_key, verifying_key, config = nil)
|
|
@@ -51,13 +45,6 @@ module Saro
|
|
|
51
45
|
|
|
52
46
|
private_class_method def self.create_ec_key(curve_name, priv_bn = nil, pub_octet = nil)
|
|
53
47
|
if priv_bn
|
|
54
|
-
# EC Private Key structure (SEC1)
|
|
55
|
-
# ECPrivateKey ::= SEQUENCE {
|
|
56
|
-
# version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
|
|
57
|
-
# privateKey OCTET STRING,
|
|
58
|
-
# parameters [0] EXPLICIT ECParameters {{ NamedCurve }} OPTIONAL,
|
|
59
|
-
# publicKey [1] EXPLICIT BIT STRING OPTIONAL
|
|
60
|
-
# }
|
|
61
48
|
group = OpenSSL::PKey::EC::Group.new(curve_name)
|
|
62
49
|
pub_octet ||= group.generator.mul(priv_bn).to_octet_string(:uncompressed)
|
|
63
50
|
|
|
@@ -69,14 +56,6 @@ module Saro
|
|
|
69
56
|
])
|
|
70
57
|
OpenSSL::PKey::EC.new(asn1.to_der)
|
|
71
58
|
elsif pub_octet
|
|
72
|
-
# SubjectPublicKeyInfo
|
|
73
|
-
# SEQUENCE {
|
|
74
|
-
# SEQUENCE {
|
|
75
|
-
# OBJECT IDENTIFIER id-ecPublicKey (1.2.840.10045.2.1)
|
|
76
|
-
# OBJECT IDENTIFIER namedCurve
|
|
77
|
-
# }
|
|
78
|
-
# BIT STRING publicKey
|
|
79
|
-
# }
|
|
80
59
|
spki = OpenSSL::ASN1::Sequence.new([
|
|
81
60
|
OpenSSL::ASN1::Sequence.new([
|
|
82
61
|
OpenSSL::ASN1::ObjectId.new("id-ecPublicKey"),
|
|
@@ -92,75 +71,68 @@ module Saro
|
|
|
92
71
|
|
|
93
72
|
def self.generate(algorithm)
|
|
94
73
|
config = Saro::Dat.get_signature_config(algorithm)
|
|
95
|
-
|
|
96
|
-
|
|
74
|
+
if config[:name] == "HMAC"
|
|
75
|
+
key = OpenSSL::Random.random_bytes(config[:hmac_len])
|
|
76
|
+
new(algorithm, key, key, config)
|
|
77
|
+
else
|
|
78
|
+
key = OpenSSL::PKey::EC.generate(config[:curve])
|
|
79
|
+
new(algorithm, key, key, config)
|
|
80
|
+
end
|
|
97
81
|
end
|
|
98
82
|
|
|
99
|
-
def self.imports(algorithm,
|
|
83
|
+
def self.imports(algorithm, base64_str)
|
|
100
84
|
config = Saro::Dat.get_signature_config(algorithm)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
unless (1..2).cover?(parts.length)
|
|
104
|
-
raise ArgumentError, "Invalid DAT Signature Key Format: No keys found"
|
|
105
|
-
end
|
|
85
|
+
bytes_data = Saro::Dat::Util.decode_base64_url(base64_str)
|
|
106
86
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
87
|
+
if config[:name] == "HMAC"
|
|
88
|
+
if bytes_data.bytesize != config[:hmac_len]
|
|
89
|
+
raise ArgumentError, "Invalid HMAC key length: expected #{config[:hmac_len]}, got #{bytes_data.bytesize}"
|
|
90
|
+
end
|
|
91
|
+
new(algorithm, bytes_data, bytes_data, config)
|
|
92
|
+
else
|
|
93
|
+
private_len = config[:private_len]
|
|
94
|
+
public_len = config[:public_len]
|
|
95
|
+
|
|
96
|
+
signing_key = nil
|
|
97
|
+
verifying_key = nil
|
|
98
|
+
|
|
99
|
+
if bytes_data.bytesize == private_len + public_len
|
|
100
|
+
private_bytes = bytes_data[0, private_len]
|
|
101
|
+
public_bytes = bytes_data[private_len, public_len]
|
|
102
|
+
|
|
103
|
+
d_value = OpenSSL::BN.new(private_bytes, 2)
|
|
104
|
+
signing_key = create_ec_key(config[:curve], d_value, public_bytes)
|
|
105
|
+
verifying_key = signing_key
|
|
106
|
+
elsif bytes_data.bytesize == public_len
|
|
107
|
+
verifying_key = create_ec_key(config[:curve], nil, bytes_data)
|
|
108
|
+
else
|
|
109
|
+
raise ArgumentError, "Invalid ECDSA key length"
|
|
127
110
|
end
|
|
128
|
-
elsif !signing_key
|
|
129
|
-
raise ArgumentError, "Invalid DAT Signature Key Format: No keys found"
|
|
130
|
-
end
|
|
131
111
|
|
|
132
|
-
|
|
112
|
+
new(algorithm, signing_key, verifying_key, config)
|
|
113
|
+
end
|
|
133
114
|
end
|
|
134
115
|
|
|
135
|
-
def exports(
|
|
136
|
-
|
|
116
|
+
def exports(verify_only = false)
|
|
117
|
+
if verify_only && !support_verify_only
|
|
118
|
+
raise ArgumentError, "#{config[:name]} does not supported verifying only key"
|
|
119
|
+
end
|
|
137
120
|
|
|
138
|
-
if [
|
|
139
|
-
|
|
121
|
+
if @config[:name] == "HMAC"
|
|
122
|
+
Saro::Dat::Util.encode_base64_url_str(@verifying_key)
|
|
123
|
+
else
|
|
124
|
+
if verify_only || !@signing_key&.private_key
|
|
125
|
+
public_bytes = @verifying_key.public_key.to_octet_string(:uncompressed)
|
|
126
|
+
Saro::Dat::Util.encode_base64_url_str(public_bytes)
|
|
127
|
+
else
|
|
140
128
|
d_value = @signing_key.private_key
|
|
141
|
-
# Ensure fixed length padding
|
|
142
129
|
curve_size = (@signing_key.group.degree + 7) / 8
|
|
143
130
|
d_bytes = d_value.to_s(2).rjust(curve_size, "\x00".b)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
131
|
+
|
|
132
|
+
public_bytes = @verifying_key.public_key.to_octet_string(:uncompressed)
|
|
133
|
+
Saro::Dat::Util.encode_base64_url_str(d_bytes + public_bytes)
|
|
147
134
|
end
|
|
148
135
|
end
|
|
149
|
-
|
|
150
|
-
if [DatSignatureKeyExportOption::PAIR, DatSignatureKeyExportOption::VERIFYING].include?(option)
|
|
151
|
-
# Uncompressed point: 0x04 + R + S
|
|
152
|
-
public_bytes = @verifying_key.public_key.to_octet_string(:uncompressed)
|
|
153
|
-
rv_parts[1] = Saro::Dat::Util.encode_base64_url_str(public_bytes)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
case option
|
|
157
|
-
when DatSignatureKeyExportOption::SIGNING
|
|
158
|
-
rv_parts[0]
|
|
159
|
-
when DatSignatureKeyExportOption::VERIFYING
|
|
160
|
-
"~#{rv_parts[1]}"
|
|
161
|
-
else
|
|
162
|
-
"#{rv_parts[0]}~#{rv_parts[1]}"
|
|
163
|
-
end
|
|
164
136
|
end
|
|
165
137
|
|
|
166
138
|
def sign(body)
|
|
@@ -168,10 +140,13 @@ module Saro
|
|
|
168
140
|
body = body.encode('utf-8') if body.is_a?(String) && body.encoding != Encoding::BINARY
|
|
169
141
|
raise ArgumentError, "Sign Error - body is empty" if body.nil? || body.empty?
|
|
170
142
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
143
|
+
if @config[:name] == "HMAC"
|
|
144
|
+
OpenSSL::HMAC.digest(@config[:hash], @signing_key, body)
|
|
145
|
+
else
|
|
146
|
+
digest = OpenSSL::Digest.new(@config[:hash])
|
|
147
|
+
signature_der = @signing_key.dsa_sign_asn1(digest.digest(body))
|
|
148
|
+
der_to_raw_signature(signature_der)
|
|
149
|
+
end
|
|
175
150
|
end
|
|
176
151
|
|
|
177
152
|
def verify(body, signature)
|
|
@@ -184,19 +159,37 @@ module Saro
|
|
|
184
159
|
signature
|
|
185
160
|
end
|
|
186
161
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
162
|
+
if @config[:name] == "HMAC"
|
|
163
|
+
begin
|
|
164
|
+
actual_sig = OpenSSL::HMAC.digest(@config[:hash], @verifying_key, body)
|
|
165
|
+
# Use fixed-time comparison if possible
|
|
166
|
+
return actual_sig == sig_bytes
|
|
167
|
+
rescue StandardError
|
|
168
|
+
return false
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
begin
|
|
172
|
+
der_sig = raw_to_der_signature(sig_bytes)
|
|
173
|
+
digest = OpenSSL::Digest.new(@config[:hash])
|
|
174
|
+
@verifying_key.dsa_verify_asn1(digest.digest(body), der_sig)
|
|
175
|
+
rescue StandardError
|
|
176
|
+
false
|
|
177
|
+
end
|
|
193
178
|
end
|
|
194
179
|
end
|
|
195
180
|
|
|
196
|
-
def
|
|
181
|
+
def signable
|
|
197
182
|
!@signing_key.nil?
|
|
198
183
|
end
|
|
199
184
|
|
|
185
|
+
def pair
|
|
186
|
+
@config[:name] == "ECDSA"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def support_verify_only
|
|
190
|
+
@config[:name] == "ECDSA"
|
|
191
|
+
end
|
|
192
|
+
|
|
200
193
|
private
|
|
201
194
|
|
|
202
195
|
def der_to_raw_signature(signature_der)
|
data/lib/saro-dat.rb
CHANGED
data/saro-dat.gemspec
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = "saro-dat"
|
|
5
|
-
spec.version = "
|
|
5
|
+
spec.version = "4.3.0"
|
|
6
6
|
spec.authors = ["marker"]
|
|
7
7
|
spec.email = ["j@saro.me"]
|
|
8
8
|
|
|
9
9
|
spec.summary = "DAT (Data Access Token) Ruby implementation"
|
|
10
10
|
spec.description = "Ported from Python dat library"
|
|
11
|
-
spec.homepage = "https://dat.saro.me
|
|
11
|
+
spec.homepage = "https://dat.saro.me/libs/gems-saro-dat"
|
|
12
12
|
spec.license = "MIT"
|
|
13
13
|
spec.required_ruby_version = ">= 2.7.0"
|
|
14
14
|
|
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.add_dependency "concurrent-ruby", "~> 1.3.6"
|
|
31
31
|
spec.add_dependency "openssl", "~> 4.0.2"
|
|
32
32
|
spec.add_dependency "base64"
|
|
33
|
+
spec.add_dependency "logger"
|
|
33
34
|
|
|
34
35
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
35
36
|
spec.add_development_dependency "benchmark"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: saro-dat
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- marker
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: logger
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: minitest
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -106,7 +120,6 @@ files:
|
|
|
106
120
|
- ".idea/saro-dat.iml"
|
|
107
121
|
- ".ruby-version"
|
|
108
122
|
- Gemfile
|
|
109
|
-
- Gemfile.lock
|
|
110
123
|
- LICENSE
|
|
111
124
|
- PUBLISH.md
|
|
112
125
|
- README.md
|
|
@@ -118,11 +131,11 @@ files:
|
|
|
118
131
|
- lib/saro/dat/signature.rb
|
|
119
132
|
- lib/saro/dat/util.rb
|
|
120
133
|
- saro-dat.gemspec
|
|
121
|
-
homepage: https://dat.saro.me
|
|
134
|
+
homepage: https://dat.saro.me/libs/gems-saro-dat
|
|
122
135
|
licenses:
|
|
123
136
|
- MIT
|
|
124
137
|
metadata:
|
|
125
|
-
homepage_uri: https://dat.saro.me
|
|
138
|
+
homepage_uri: https://dat.saro.me/libs/gems-saro-dat
|
|
126
139
|
source_code_uri: https://github.com/saro-lab/dat-ruby
|
|
127
140
|
changelog_uri: https://github.com/saro-lab/dat-ruby/blob/main/CHANGELOG.md
|
|
128
141
|
keywords: dat, distributed, access, token, web, session, security, authentication
|
|
@@ -140,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
140
153
|
- !ruby/object:Gem::Version
|
|
141
154
|
version: '0'
|
|
142
155
|
requirements: []
|
|
143
|
-
rubygems_version: 4.0.
|
|
156
|
+
rubygems_version: 4.0.12
|
|
144
157
|
specification_version: 4
|
|
145
158
|
summary: DAT (Data Access Token) Ruby implementation
|
|
146
159
|
test_files: []
|
data/Gemfile.lock
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
saro-dat (1.0.0)
|
|
5
|
-
base64
|
|
6
|
-
concurrent-ruby (~> 1.3.6)
|
|
7
|
-
openssl (~> 4.0.2)
|
|
8
|
-
|
|
9
|
-
GEM
|
|
10
|
-
remote: https://rubygems.org/
|
|
11
|
-
specs:
|
|
12
|
-
base64 (0.3.0)
|
|
13
|
-
benchmark (0.5.0)
|
|
14
|
-
concurrent-ruby (1.3.6)
|
|
15
|
-
minitest (5.27.0)
|
|
16
|
-
openssl (4.0.2)
|
|
17
|
-
parallel (2.1.0)
|
|
18
|
-
|
|
19
|
-
PLATFORMS
|
|
20
|
-
arm64-darwin-25
|
|
21
|
-
ruby
|
|
22
|
-
|
|
23
|
-
DEPENDENCIES
|
|
24
|
-
benchmark
|
|
25
|
-
minitest (~> 5.0)
|
|
26
|
-
parallel
|
|
27
|
-
saro-dat!
|
|
28
|
-
|
|
29
|
-
CHECKSUMS
|
|
30
|
-
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
31
|
-
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
|
|
32
|
-
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
|
|
33
|
-
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
34
|
-
openssl (4.0.2) sha256=1037ad2868ae58df9ad917891c0c0f9815a1172f6846d4bcdd508e4c2ee747c2
|
|
35
|
-
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
|
36
|
-
saro-dat (1.0.0)
|
|
37
|
-
|
|
38
|
-
BUNDLED WITH
|
|
39
|
-
4.0.10
|