Support checking the sk_flags
This commit is contained in:
Родитель
b4b7cb649a
Коммит
c16b90ac63
|
@ -20,6 +20,14 @@ module SSHData
|
||||||
ALGO_ED25519, ALGO_SKECDSA256, ALGO_SKED25519
|
ALGO_ED25519, ALGO_SKECDSA256, ALGO_SKED25519
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DEFAULT_SK_VERIFY_OPTS = {
|
||||||
|
user_presence_required: true,
|
||||||
|
user_verification_required: false
|
||||||
|
}
|
||||||
|
|
||||||
|
SK_FLAG_USER_PRESENCE = 0b001
|
||||||
|
SK_FLAG_USER_VERIFICATION = 0b100
|
||||||
|
|
||||||
# Parse an OpenSSH public key in authorized_keys format (see sshd(8) manual
|
# Parse an OpenSSH public key in authorized_keys format (see sshd(8) manual
|
||||||
# page).
|
# page).
|
||||||
#
|
#
|
||||||
|
@ -78,6 +86,7 @@ module SSHData
|
||||||
end
|
end
|
||||||
|
|
||||||
require "ssh_data/public_key/base"
|
require "ssh_data/public_key/base"
|
||||||
|
require "ssh_data/public_key/security_key"
|
||||||
require "ssh_data/public_key/rsa"
|
require "ssh_data/public_key/rsa"
|
||||||
require "ssh_data/public_key/dsa"
|
require "ssh_data/public_key/dsa"
|
||||||
require "ssh_data/public_key/ecdsa"
|
require "ssh_data/public_key/ecdsa"
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
module SSHData
|
||||||
|
module PublicKey
|
||||||
|
module SecurityKey
|
||||||
|
def build_signing_blob(application, signed_data, signature)
|
||||||
|
read = 0
|
||||||
|
sig_algo, raw_sig, signature_read = Encoding.decode_signature(signature)
|
||||||
|
read += signature_read
|
||||||
|
sk_flags, sk_flags_read = Encoding.decode_uint8(signature, read)
|
||||||
|
read += sk_flags_read
|
||||||
|
counter, counter_read = Encoding.decode_uint32(signature, read)
|
||||||
|
read += counter_read
|
||||||
|
|
||||||
|
if read != signature.bytesize
|
||||||
|
raise DecodeError, "unexpected trailing data"
|
||||||
|
end
|
||||||
|
|
||||||
|
application_hash = OpenSSL::Digest::SHA256.digest(application)
|
||||||
|
message_hash = OpenSSL::Digest::SHA256.digest(signed_data)
|
||||||
|
|
||||||
|
blob =
|
||||||
|
application_hash +
|
||||||
|
Encoding.encode_uint8(sk_flags) +
|
||||||
|
Encoding.encode_uint32(counter) +
|
||||||
|
message_hash
|
||||||
|
|
||||||
|
[sig_algo, raw_sig, sk_flags, blob]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,7 @@
|
||||||
module SSHData
|
module SSHData
|
||||||
module PublicKey
|
module PublicKey
|
||||||
class SKECDSA < ECDSA
|
class SKECDSA < ECDSA
|
||||||
|
include SecurityKey
|
||||||
attr_reader :application
|
attr_reader :application
|
||||||
|
|
||||||
OPENSSL_CURVE_NAME_FOR_CURVE = {
|
OPENSSL_CURVE_NAME_FOR_CURVE = {
|
||||||
|
@ -34,34 +35,25 @@ module SSHData
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify(signed_data, signature)
|
def verify(signed_data, signature, **opts)
|
||||||
|
opts = DEFAULT_SK_VERIFY_OPTS.merge(opts)
|
||||||
|
|
||||||
read = 0
|
read = 0
|
||||||
sig_algo, raw_sig, signature_read = Encoding.decode_signature(signature)
|
sig_algo, raw_sig, sk_flags, blob = build_signing_blob(application, signed_data, signature)
|
||||||
read += signature_read
|
|
||||||
sk_flags, sk_flags_read = Encoding.decode_uint8(signature, read)
|
|
||||||
read += sk_flags_read
|
|
||||||
counter, counter_read = Encoding.decode_uint32(signature, read)
|
|
||||||
read += counter_read
|
|
||||||
|
|
||||||
if read != signature.bytesize
|
|
||||||
raise DecodeError, "unexpected trailing data"
|
|
||||||
end
|
|
||||||
|
|
||||||
self.class.check_algorithm!(sig_algo, curve)
|
self.class.check_algorithm!(sig_algo, curve)
|
||||||
|
|
||||||
application_hash = OpenSSL::Digest::SHA256.digest(application)
|
|
||||||
message_hash = OpenSSL::Digest::SHA256.digest(signed_data)
|
|
||||||
|
|
||||||
blob =
|
|
||||||
application_hash +
|
|
||||||
Encoding.encode_uint8(sk_flags) +
|
|
||||||
Encoding.encode_uint32(counter) +
|
|
||||||
message_hash
|
|
||||||
|
|
||||||
openssl_sig = self.class.openssl_signature(raw_sig)
|
openssl_sig = self.class.openssl_signature(raw_sig)
|
||||||
digest = DIGEST_FOR_CURVE[curve]
|
digest = DIGEST_FOR_CURVE[curve]
|
||||||
|
|
||||||
openssl.verify(digest.new, openssl_sig, blob)
|
result = openssl.verify(digest.new, openssl_sig, blob)
|
||||||
|
|
||||||
|
if opts[:user_presence_required] && (sk_flags & SK_FLAG_USER_PRESENCE != SK_FLAG_USER_PRESENCE)
|
||||||
|
false
|
||||||
|
elsif opts[:user_verification_required] && (sk_flags & SK_FLAG_USER_VERIFICATION != SK_FLAG_USER_VERIFICATION)
|
||||||
|
false
|
||||||
|
else
|
||||||
|
result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def ==(other)
|
def ==(other)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
module SSHData
|
module SSHData
|
||||||
module PublicKey
|
module PublicKey
|
||||||
class SKED25519 < ED25519
|
class SKED25519 < ED25519
|
||||||
|
include SecurityKey
|
||||||
attr_reader :application
|
attr_reader :application
|
||||||
|
|
||||||
def initialize(algo:, pk:, application:)
|
def initialize(algo:, pk:, application:)
|
||||||
|
@ -23,38 +24,27 @@ module SSHData
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify(signed_data, signature)
|
def verify(signed_data, signature, **opts)
|
||||||
self.class.ed25519_gem_required!
|
self.class.ed25519_gem_required!
|
||||||
|
opts = DEFAULT_SK_VERIFY_OPTS.merge(opts)
|
||||||
read = 0
|
sig_algo, raw_sig, sk_flags, blob = build_signing_blob(application, signed_data, signature)
|
||||||
sig_algo, raw_sig, signature_read = Encoding.decode_signature(signature)
|
|
||||||
read += signature_read
|
|
||||||
sk_flags, sk_flags_read = Encoding.decode_uint8(signature, read)
|
|
||||||
read += sk_flags_read
|
|
||||||
counter, counter_read = Encoding.decode_uint32(signature, read)
|
|
||||||
read += counter_read
|
|
||||||
|
|
||||||
if read != signature.bytesize
|
|
||||||
raise DecodeError, "unexpected trailing data"
|
|
||||||
end
|
|
||||||
|
|
||||||
if sig_algo != self.class.algorithm_identifier
|
if sig_algo != self.class.algorithm_identifier
|
||||||
raise DecodeError, "bad signature algorithm: #{sig_algo.inspect}"
|
raise DecodeError, "bad signature algorithm: #{sig_algo.inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
application_hash = OpenSSL::Digest::SHA256.digest(application)
|
result = begin
|
||||||
message_hash = OpenSSL::Digest::SHA256.digest(signed_data)
|
ed25519_key.verify(raw_sig, blob)
|
||||||
|
rescue Ed25519::VerifyError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
blob =
|
if opts[:user_presence_required] && (sk_flags & SK_FLAG_USER_PRESENCE != SK_FLAG_USER_PRESENCE)
|
||||||
application_hash +
|
|
||||||
Encoding.encode_uint8(sk_flags) +
|
|
||||||
Encoding.encode_uint32(counter) +
|
|
||||||
message_hash
|
|
||||||
|
|
||||||
begin
|
|
||||||
ed25519_key.verify(raw_sig, blob)
|
|
||||||
rescue Ed25519::VerifyError
|
|
||||||
false
|
false
|
||||||
|
elsif opts[:user_verification_required] && (sk_flags & SK_FLAG_USER_VERIFICATION != SK_FLAG_USER_VERIFICATION)
|
||||||
|
false
|
||||||
|
else
|
||||||
|
result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ module SSHData
|
||||||
@signature = signature
|
@signature = signature
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify(signed_data)
|
def verify(signed_data, **opts)
|
||||||
key = public_key
|
key = public_key
|
||||||
digest_algorithm = SUPPORTED_HASH_ALGORITHMS[@hash_algorithm]
|
digest_algorithm = SUPPORTED_HASH_ALGORITHMS[@hash_algorithm]
|
||||||
|
|
||||||
|
@ -93,7 +93,11 @@ module SSHData
|
||||||
Encoding.encode_string(@hash_algorithm) +
|
Encoding.encode_string(@hash_algorithm) +
|
||||||
Encoding.encode_string(message_digest)
|
Encoding.encode_string(message_digest)
|
||||||
|
|
||||||
key.verify(blob, @signature)
|
if key.class.include?(::SSHData::PublicKey::SecurityKey)
|
||||||
|
key.verify(blob, @signature, **opts)
|
||||||
|
else
|
||||||
|
key.verify(blob, @signature)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_key
|
def public_key
|
||||||
|
|
Загрузка…
Ссылка в новой задаче