如何在 haskell 中验证 OpenSSH 签名?

问题描述 投票:0回答:1

我正在尝试验证 haskell 中的 ed25519 签名。

为此我创建了一个签名:

$ ssh-keygen -Y sign -f data/testkey_ed25519 -n file data/message.txt

我另外创建了 allowed_signers 文件,因为这是验证所必需的:

# allowed_signers
testprincipal ssh-ed25519 <public key> <comment>

ssh-keygen 然后可以验证签名:

$ ssh-keygen -Y verify -n file -s data/message.txt.sig -f data/authorized_signers -I testprincipal < data/message.txt
Good "file" signature for testprincipal with ED25519 key SHA256:RmoAVYLzWd7b2pTB0O1ovGu/KhXosg0zk++pJgIvQjw

我希望在 haskell 中实现 ssh-keygen 的此功能(无需外部调用),但我无法使用 crypton 重现结果:

{-# LANGUAGE OverloadedStrings #-}

module Main where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import qualified Extra
import qualified Crypto.PubKey.Ed25519 as Ed25519
import Crypto.Error
import qualified Data.ByteString.Base64 as Base64


unarmorSignature :: C.ByteString -> C.ByteString
unarmorSignature = removeHeader . removeFooter . removeLinebreaks
        where
                header = "-----BEGIN SSH SIGNATURE-----"
                footer = "-----END SSH SIGNATURE-----"
                removeHeader = C.drop (C.length header) . snd . C.breakSubstring header
                removeFooter = fst . C.breakSubstring footer
                removeLinebreaks = C.filter (/= '\n')

main :: IO ()
main = do
        message <- BS.readFile "data/message.txt"
        print message

        signatureWrapped <- BS.readFile "data/message.txt.sig"
        let signatureDecoded = Extra.fromRight' $ Base64.decode $ unarmorSignature signatureWrapped
        let signatureBS = BS.takeEnd 64 signatureDecoded
        signature <- throwCryptoErrorIO $ Ed25519.signature signatureBS
        print signature

        keyfileContent <- BS.readFile "data/authorized_signers"
        let _:_:key:_ = C.words keyfileContent
        let keyDecoded = Extra.fromRight' $ Base64.decode key
        let Just keyBS = C.stripPrefix "\NUL\NUL\NUL\vssh-ed25519\NUL\NUL\NUL " keyDecoded
        key <- throwCryptoErrorIO $ Ed25519.publicKey keyBS
        print key

        let result = Ed25519.verify key message signature
        print result

代码应该(非常粗略地)解析签名和 allowed_signers 文件格式(根据 RFC8709)并从中提取纯 ed25519 密钥和签名。 然后应该使用它们来验证签名是否是使用密钥和消息创建的。

程序应该在最后打印“true”而不是“false”。

可以在此处

找到完全可重现的示例
haskell openssl openssh
1个回答
0
投票

不幸的是,为了验证签名,签名的数据(

test\n
字符串)无法按原样传递。

根据协议,它应该采用以下格式:

byte[6]   MAGIC_PREAMBLE
string    namespace
string    reserved
string    hash_algorithm
string    H(message)

message
是此处的
test\n
字符串,但它也应该经过哈希处理并添加附加元数据。

实际打印

True
结果的草稿代码:

...
import qualified Data.ByteString.Base16 as Base16 -- imported base16-bytestring
import Crypto.Hash

...

sha512 :: C.ByteString -> String
sha512 bs = show (hash bs :: Digest SHA512)

intArrayToByteString :: [Int] -> C.ByteString
intArrayToByteString = BS.pack . map fromIntegral

hexStringToIntList :: String -> Either String [Int]
hexStringToIntList hexStr = do
    decoded <- Base16.decode (C.pack hexStr)
    return $ map fromIntegral (BS.unpack decoded)

...

let hash = Extra.fromRight' $ hexStringToIntList $ sha512 message

let intSignedText = [ 83, 83, 72, 83, 73, 71, 0, 0, 0, 4 -- SSHSIG magic header
                    , 102, 105, 108, 101 -- namespace ("file")
                    , 0, 0, 0, 0, 0, 0, 0, 6 -- reserved
                    , 115, 104, 97, 53, 49, 50, 0, 0, 0, 64 -- hash alg (sha512)
                    ] ++ hash
let result = Ed25519.verify key (intArrayToByteString intSignedText) signature
print result
© www.soinside.com 2019 - 2024. All rights reserved.