ssh-publickey 0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b36f93c81f7da614c5cee2607f98884dafdc571cf1b1e9f6ae648e2ffbd9563f
4
+ data.tar.gz: 77567cac8bd9eaf0fb657beb16aa64fb5aad11943314858d68ab1513483eaec2
5
+ SHA512:
6
+ metadata.gz: 623fb771883a64e32b2de5ea98639e98df9160fcd0bee2e0688eb5fcd86a38d8d3c30b4140d0ca55de8befeb361f1dab32f01478a05a089d7962e6af2f361b6f
7
+ data.tar.gz: c27a6629d01d0a9b45934d6abc6fdc46ead9ad7e80ac0604e85343f59b320694550e3008729c193ea8b2cf3c2716bd80ae9c2bbed662b599f62d91bb25c551d4
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # Convert SSH public key from/to RFC4716/OpenSSH format
2
+
3
+ Examples:
4
+
5
+ ~~~ruby
6
+ require 'ssh-publickey'
7
+
8
+ # Convert key
9
+ SSHPublicKey.openssh_to_rfc4716(k_openssh)
10
+ SSHPublicKey.rfc4716_to_openssh(k_rfc4716)
11
+
12
+ # Test key
13
+ SSHPublicKey.is_openssh?(k)
14
+ SSHPublicKey.is_rfc4716?(k)
15
+ ~~~
@@ -0,0 +1,131 @@
1
+ require_relative 'ssh-publickey/version'
2
+
3
+ # Convert SSH public key from/to RFC4716/OpenSSH format
4
+ #
5
+ class SSHPublicKey
6
+ # Report public key processing error
7
+ class PublicKeyError < StandardError
8
+ end
9
+
10
+
11
+ # Maximum text size in a line for PEM format
12
+ LINE_BREAK = 70
13
+
14
+
15
+ # Regex for authorized key
16
+ AUTHORIZED_KEY_REGEX =
17
+ /^(?: (?<directives>(?:[!-~]|\s(?=.*"))+) \s+ )? # Options
18
+ (?<type> [a-z0-9_-]+) \s+ # Type
19
+ (?<key> [A-Za-z0-9+\/]+=*) # Key
20
+ (?:\s+ (?<comment>.*?) \s* )? # Comments
21
+ $/x
22
+
23
+
24
+ # Regex for OpenSSH public key
25
+ # Same as authorized keys but without directives
26
+ PUBKEY_OPENSSH_REGEX =
27
+ /^ (?<type> [a-z0-9_-]+) \s+ # Type
28
+ (?<key> [A-Za-z0-9+\/]+=*) # Key
29
+ (?:\s+ (?<comment>.*?) \s* )? # Comments
30
+ $/x
31
+
32
+
33
+ # Regex for RFC4716 public key
34
+ PUBKEY_RFC4716_REGEX =
35
+ /^----\sBEGIN\sSSH2\sPUBLIC\sKEY\s----\R
36
+ (?<tags> (?:[^:\p{Space}\p{Cntrl}]{1,64} # Tags
37
+ \s*:\s*
38
+ (?:[^\\\r\n]*\\\R)* [^\\\r\n]+\R)*)
39
+ (?<key> (?:[A-Za-z0-9+\/]+\R)* # Key
40
+ [A-Za-z0-9+\/]+=*\R)
41
+ ----\sEND\sSSH2\sPUBLIC\sKEY\s----
42
+ $/xmu
43
+
44
+
45
+ # :nodoc:
46
+ PUBKEY_RFC4716_HEADERTAG_REGEX =
47
+ /[^:\p{Space}\p{Cntrl}]{1,64}
48
+ \s*:\s*
49
+ (?:[^\\\r\n]*\\\R)*[^\\\r\n]+\R/xmu
50
+
51
+
52
+ # Convert a public key in OpenSSH format to RFC4716 format
53
+ #
54
+ # @param [String] pubkey public key in OpenSSH format
55
+ #
56
+ # @return [String] public key in RFC4716 format
57
+ #
58
+ def self.openssh_to_rfc4716(pubkey)
59
+ unless m = AUTHORIZED_KEY_REGEX.match(pubkey)
60
+ raise PublicKeyError, "invalid OpenSSH public key"
61
+ end
62
+
63
+ # Lines are limited to 72 8-bytes char
64
+ # - limit ourselves to 70 to keep room for \\ and \n
65
+ # - comment part can be unicode so 1 char can be more that 1 byte
66
+ linesize = 0
67
+ comment = "Comment: #{m[:comment]}"
68
+ .each_char.slice_before {|c|
69
+ bytesize = c.bytes.size
70
+ if linesize + bytesize > LINE_BREAK
71
+ then linesize = 0 ; true
72
+ else linesize += bytesize ; false
73
+ end
74
+ }.map(&:join).join("\\\n")
75
+ key = m[:key].scan(/.{1,70}/).join("\n")
76
+
77
+ reskey = []
78
+ reskey << "---- BEGIN SSH2 PUBLIC KEY ----"
79
+ reskey << comment
80
+ reskey << key
81
+ reskey << "---- END SSH2 PUBLIC KEY ----"
82
+ reskey.join("\n")
83
+ end
84
+
85
+
86
+ # Convert a public key in RFC4716 format to OpenSSH format
87
+ #
88
+ # @param [String] pubkey public key in RFC4716 format
89
+ #
90
+ # @return [String] public key in OpenSSH format
91
+ #
92
+ def self.rfc4716_to_openssh(pubkey)
93
+ unless m = PUBKEY_RFC4716_REGEX.match(pubkey)
94
+ raise PublicKeyError, "invalid RFC4716 public key"
95
+ end
96
+
97
+ key = m[:key].gsub(/\R/, '')
98
+ keydata = m[:key].unpack1('m')
99
+ len = keydata.unpack1('N')
100
+ type = keydata[4,len]
101
+ tags = Hash[m[:tags].scan(PUBKEY_RFC4716_HEADERTAG_REGEX)
102
+ .map {|tag| tag.gsub(/\\\R/, '').strip }
103
+ .map {|tag| tag.split(/\s*:\s*/, 2) }]
104
+ comment = tags.transform_keys {|k| k.downcase }['comment']
105
+ directives = nil
106
+
107
+ [directives, type, key, comment ].compact.join(' ')
108
+ end
109
+
110
+
111
+ # Test if a public key is in OpenSSH format
112
+ #
113
+ # @param [String] pubkey public key in RFC4716 format
114
+ #
115
+ # @return [Boolean]
116
+ #
117
+ def self.is_openssh?(pubkey)
118
+ PUBKEY_RFC4716_REGEX.match?(pubkey)
119
+ end
120
+
121
+
122
+ # Test if a public key is in RFC4716 format
123
+ #
124
+ # @param [String] pubkey public key in RFC4716 format
125
+ #
126
+ # @return [Boolean]
127
+ #
128
+ def self.is_rfc4716?(pubkey)
129
+ PUBKEY_RFC4716_REGEX.match?(pubkey)
130
+ end
131
+ end
@@ -0,0 +1,4 @@
1
+ class SSHPublicKey
2
+ # Current version
3
+ VERSION = '0.1'
4
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'lib/ssh-publickey/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ssh-publickey'
7
+ s.version = SSHPublicKey::VERSION
8
+ s.summary = "SSH public key conversion"
9
+ s.description = "Convert SSH public key from/to RFC4716/OpenSSH format"
10
+ s.authors = [ "Stéphane D'Alu" ]
11
+ s.email = [ 'stephane.dalu@insa-lyon.fr' ]
12
+ s.files = %w[ README.md ssh-publickey.gemspec ] + Dir['lib/**/*.rb']
13
+ s.homepage = 'https://gitlab.com/sdalu/ssh-publickey'
14
+ s.license = 'MIT'
15
+
16
+ s.add_development_dependency 'yard', '~>0'
17
+ s.add_development_dependency 'rake', '~>13'
18
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ssh-publickey
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Stéphane D'Alu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13'
41
+ description: Convert SSH public key from/to RFC4716/OpenSSH format
42
+ email:
43
+ - stephane.dalu@insa-lyon.fr
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - lib/ssh-publickey.rb
50
+ - lib/ssh-publickey/version.rb
51
+ - ssh-publickey.gemspec
52
+ homepage: https://gitlab.com/sdalu/ssh-publickey
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.0.8
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: SSH public key conversion
75
+ test_files: []