{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
-- |
-- XML Signature Syntax and Processing
--
-- <http://www.w3.org/TR/2008/REC-xmldsig-core-20080610/> (selected portions)
module SAML2.XML.Signature
  ( module SAML2.XML.Signature.Types
  , generateReference
  , SigningKey(..)
  , PublicKeys(..)
  , signingKeySignatureAlgorithm
  , signBase64
  , verifyBase64
  , generateSignature
  , verifySignature
  ) where

import Control.Applicative ((<|>))
import Control.Monad (guard, (<=<))
import Crypto.Number.Basic (numBytes)
import Crypto.Number.Serialize (i2ospOf_, os2ip)
import Crypto.Hash (hashlazy, SHA1(..), SHA256(..), SHA512(..), RIPEMD160(..))
import qualified Crypto.PubKey.DSA as DSA
import qualified Crypto.PubKey.RSA.Types as RSA
import qualified Crypto.PubKey.RSA.PKCS15 as RSA
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Lazy as BSL
import qualified Data.List.NonEmpty as NonEmpty
import Data.Maybe (isJust)
import Data.Monoid ((<>))
import qualified Data.X509 as X509
import Network.URI (URI(..))
import qualified Text.XML.HXT.Core as HXT
import qualified Text.XML.HXT.DOM.ShowXml as DOM
import qualified Text.XML.HXT.DOM.XmlNode as DOM
import qualified Text.XML.HXT.DOM.QualifiedName as DOM

import SAML2.XML
import SAML2.XML.Canonical
import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP
import SAML2.XML.Signature.Types

isDSElem :: HXT.ArrowXml a => String -> a HXT.XmlTree HXT.XmlTree
isDSElem :: String -> a XmlTree XmlTree
isDSElem String
n = a XmlTree XmlTree
forall (a :: * -> * -> *). ArrowXml a => a XmlTree XmlTree
HXT.isElem a XmlTree XmlTree -> a XmlTree XmlTree -> a XmlTree XmlTree
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
HXT.>>> QName -> a XmlTree XmlTree
forall (a :: * -> * -> *). ArrowXml a => QName -> a XmlTree XmlTree
HXT.hasQName (Namespace -> String -> QName
mkNName Namespace
ns String
n)

getID :: HXT.ArrowXml a => String -> a HXT.XmlTree HXT.XmlTree
getID :: String -> a XmlTree XmlTree
getID = a XmlTree XmlTree -> a XmlTree XmlTree
forall (a :: * -> * -> *) (t :: * -> *) b c.
(ArrowTree a, Tree t) =>
a (t b) c -> a (t b) c
HXT.deep (a XmlTree XmlTree -> a XmlTree XmlTree)
-> (String -> a XmlTree XmlTree) -> String -> a XmlTree XmlTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> (String -> Bool) -> a XmlTree XmlTree
forall (a :: * -> * -> *).
ArrowXml a =>
String -> (String -> Bool) -> a XmlTree XmlTree
HXT.hasAttrValue String
"ID" ((String -> Bool) -> a XmlTree XmlTree)
-> (String -> String -> Bool) -> String -> a XmlTree XmlTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(==)

applyCanonicalization :: CanonicalizationMethod -> Maybe String -> HXT.XmlTree -> IO BS.ByteString
applyCanonicalization :: CanonicalizationMethod -> Maybe String -> XmlTree -> IO ByteString
applyCanonicalization (CanonicalizationMethod (Identified CanonicalizationAlgorithm
a) Maybe InclusiveNamespaces
ins []) Maybe String
x XmlTree
y = CanonicalizationAlgorithm
-> Maybe InclusiveNamespaces
-> Maybe String
-> XmlTree
-> IO ByteString
canonicalize CanonicalizationAlgorithm
a Maybe InclusiveNamespaces
ins Maybe String
x XmlTree
y
applyCanonicalization CanonicalizationMethod
m Maybe String
_ XmlTree
_ = String -> IO ByteString
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO ByteString) -> String -> IO ByteString
forall a b. (a -> b) -> a -> b
$ String
"applyCanonicalization: unsupported " String -> String -> String
forall a. [a] -> [a] -> [a]
++ CanonicalizationMethod -> String
forall a. Show a => a -> String
show CanonicalizationMethod
m

applyTransformsBytes :: [Transform] -> BSL.ByteString -> IO BSL.ByteString
applyTransformsBytes :: [Transform] -> ByteString -> IO ByteString
applyTransformsBytes [] ByteString
v = ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
v
applyTransformsBytes (Transform
t : [Transform]
_) ByteString
_ = String -> IO ByteString
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"applyTransforms: unsupported Signature " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Transform -> String
forall a. Show a => a -> String
show Transform
t)

applyTransformsXML :: [Transform] -> HXT.XmlTree -> IO BSL.ByteString
applyTransformsXML :: [Transform] -> XmlTree -> IO ByteString
applyTransformsXML (Transform (Identified (TransformCanonicalization CanonicalizationAlgorithm
a)) Maybe InclusiveNamespaces
ins [TransformElement]
x : [Transform]
tl) =
  [Transform] -> ByteString -> IO ByteString
applyTransformsBytes [Transform]
tl (ByteString -> IO ByteString)
-> (ByteString -> ByteString) -> ByteString -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
BSL.fromStrict
  (ByteString -> IO ByteString)
-> (XmlTree -> IO ByteString) -> XmlTree -> IO ByteString
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< CanonicalizationMethod -> Maybe String -> XmlTree -> IO ByteString
applyCanonicalization (Identified URI CanonicalizationAlgorithm
-> Maybe InclusiveNamespaces -> [XmlTree] -> CanonicalizationMethod
CanonicalizationMethod (CanonicalizationAlgorithm
-> Identified URI CanonicalizationAlgorithm
forall b a. a -> Identified b a
Identified CanonicalizationAlgorithm
a) Maybe InclusiveNamespaces
ins ((TransformElement -> XmlTree) -> [TransformElement] -> [XmlTree]
forall a b. (a -> b) -> [a] -> [b]
map (PU TransformElement -> TransformElement -> XmlTree
forall a. PU a -> a -> XmlTree
XP.pickleDoc PU TransformElement
forall a. XmlPickler a => PU a
XP.xpickle) [TransformElement]
x)) Maybe String
forall a. Maybe a
Nothing
applyTransformsXML (Transform (Identified TransformAlgorithm
TransformEnvelopedSignature) Maybe InclusiveNamespaces
Nothing [] : [Transform]
tl) =
  -- XXX assumes "this" signature in top-level
  [Transform] -> XmlTree -> IO ByteString
applyTransformsXML [Transform]
tl
  (XmlTree -> IO ByteString)
-> (XmlTree -> XmlTree) -> XmlTree -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [XmlTree] -> XmlTree
forall a. [a] -> a
head ([XmlTree] -> XmlTree)
-> (XmlTree -> [XmlTree]) -> XmlTree -> XmlTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LA XmlTree XmlTree -> XmlTree -> [XmlTree]
forall a b. LA a b -> a -> [b]
HXT.runLA (LA XmlTree XmlTree -> LA XmlTree XmlTree
forall (a :: * -> * -> *) (t :: * -> *) b.
(ArrowTree a, Tree t) =>
a (t b) (t b) -> a (t b) (t b)
HXT.processChildren (LA XmlTree XmlTree -> LA XmlTree XmlTree)
-> LA XmlTree XmlTree -> LA XmlTree XmlTree
forall a b. (a -> b) -> a -> b
$ LA XmlTree XmlTree -> LA XmlTree XmlTree
forall (a :: * -> * -> *) (t :: * -> *) b.
(ArrowTree a, Tree t) =>
a (t b) (t b) -> a (t b) (t b)
HXT.processChildren
    (LA XmlTree XmlTree -> LA XmlTree XmlTree)
-> LA XmlTree XmlTree -> LA XmlTree XmlTree
forall a b. (a -> b) -> a -> b
$ LA XmlTree XmlTree -> LA XmlTree XmlTree
forall (a :: * -> * -> *) b c. ArrowIf a => a b c -> a b b
HXT.neg (String -> LA XmlTree XmlTree
forall (a :: * -> * -> *).
ArrowXml a =>
String -> a XmlTree XmlTree
isDSElem String
"Signature"))
applyTransformsXML [Transform]
tl = [Transform] -> ByteString -> IO ByteString
applyTransformsBytes [Transform]
tl (ByteString -> IO ByteString)
-> (XmlTree -> ByteString) -> XmlTree -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [XmlTree] -> ByteString
DOM.xshowBlob ([XmlTree] -> ByteString)
-> (XmlTree -> [XmlTree]) -> XmlTree -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. XmlTree -> [XmlTree]
forall (m :: * -> *) a. Monad m => a -> m a
return

applyTransforms :: Maybe Transforms -> HXT.XmlTree -> IO BSL.ByteString
applyTransforms :: Maybe Transforms -> XmlTree -> IO ByteString
applyTransforms = [Transform] -> XmlTree -> IO ByteString
applyTransformsXML ([Transform] -> XmlTree -> IO ByteString)
-> (Maybe Transforms -> [Transform])
-> Maybe Transforms
-> XmlTree
-> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Transform]
-> (Transforms -> [Transform]) -> Maybe Transforms -> [Transform]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (NonEmpty Transform -> [Transform]
forall a. NonEmpty a -> [a]
NonEmpty.toList (NonEmpty Transform -> [Transform])
-> (Transforms -> NonEmpty Transform) -> Transforms -> [Transform]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transforms -> NonEmpty Transform
transforms)

asType :: a -> proxy a -> proxy a
asType :: a -> proxy a -> proxy a
asType a
_ = proxy a -> proxy a
forall a. a -> a
id

applyDigest :: DigestMethod -> BSL.ByteString -> BS.ByteString
applyDigest :: DigestMethod -> ByteString -> ByteString
applyDigest (DigestMethod (Identified DigestAlgorithm
DigestSHA1) []) = Digest SHA1 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
BA.convert (Digest SHA1 -> ByteString)
-> (ByteString -> Digest SHA1) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SHA1 -> Digest SHA1 -> Digest SHA1
forall a (proxy :: * -> *). a -> proxy a -> proxy a
asType SHA1
SHA1 (Digest SHA1 -> Digest SHA1)
-> (ByteString -> Digest SHA1) -> ByteString -> Digest SHA1
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest SHA1
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy
applyDigest (DigestMethod (Identified DigestAlgorithm
DigestSHA256) []) = Digest SHA256 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
BA.convert (Digest SHA256 -> ByteString)
-> (ByteString -> Digest SHA256) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SHA256 -> Digest SHA256 -> Digest SHA256
forall a (proxy :: * -> *). a -> proxy a -> proxy a
asType SHA256
SHA256 (Digest SHA256 -> Digest SHA256)
-> (ByteString -> Digest SHA256) -> ByteString -> Digest SHA256
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest SHA256
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy
applyDigest (DigestMethod (Identified DigestAlgorithm
DigestSHA512) []) = Digest SHA512 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
BA.convert (Digest SHA512 -> ByteString)
-> (ByteString -> Digest SHA512) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SHA512 -> Digest SHA512 -> Digest SHA512
forall a (proxy :: * -> *). a -> proxy a -> proxy a
asType SHA512
SHA512 (Digest SHA512 -> Digest SHA512)
-> (ByteString -> Digest SHA512) -> ByteString -> Digest SHA512
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest SHA512
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy
applyDigest (DigestMethod (Identified DigestAlgorithm
DigestRIPEMD160) []) = Digest RIPEMD160 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
BA.convert (Digest RIPEMD160 -> ByteString)
-> (ByteString -> Digest RIPEMD160) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RIPEMD160 -> Digest RIPEMD160 -> Digest RIPEMD160
forall a (proxy :: * -> *). a -> proxy a -> proxy a
asType RIPEMD160
RIPEMD160 (Digest RIPEMD160 -> Digest RIPEMD160)
-> (ByteString -> Digest RIPEMD160)
-> ByteString
-> Digest RIPEMD160
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest RIPEMD160
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy
applyDigest DigestMethod
d = String -> ByteString -> ByteString
forall a. HasCallStack => String -> a
error (String -> ByteString -> ByteString)
-> String -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ String
"unsupported " String -> String -> String
forall a. [a] -> [a] -> [a]
++ DigestMethod -> String
forall a. Show a => a -> String
show DigestMethod
d

generateReference :: Reference -> HXT.XmlTree -> IO Reference
generateReference :: Reference -> XmlTree -> IO Reference
generateReference Reference
r XmlTree
x = do
  ByteString
t <- Maybe Transforms -> XmlTree -> IO ByteString
applyTransforms (Reference -> Maybe Transforms
referenceTransforms Reference
r) XmlTree
x
  let d :: ByteString
d = DigestMethod -> ByteString -> ByteString
applyDigest (Reference -> DigestMethod
referenceDigestMethod Reference
r) ByteString
t
  Reference -> IO Reference
forall (m :: * -> *) a. Monad m => a -> m a
return Reference
r
    { referenceDigestValue :: ByteString
referenceDigestValue = ByteString
d }

verifyReference :: Reference -> HXT.XmlTree -> IO (Maybe String)
verifyReference :: Reference -> XmlTree -> IO (Maybe String)
verifyReference Reference
r XmlTree
doc = case Reference -> Maybe URI
referenceURI Reference
r of
  Just URI{ uriScheme :: URI -> String
uriScheme = String
"", uriAuthority :: URI -> Maybe URIAuth
uriAuthority = Maybe URIAuth
Nothing, uriPath :: URI -> String
uriPath = String
"", uriQuery :: URI -> String
uriQuery = String
"", uriFragment :: URI -> String
uriFragment = Char
'#':String
xid }
    | x :: [XmlTree]
x@[XmlTree
_] <- LA XmlTree XmlTree -> XmlTree -> [XmlTree]
forall a b. LA a b -> a -> [b]
HXT.runLA (String -> LA XmlTree XmlTree
forall (a :: * -> * -> *).
ArrowXml a =>
String -> a XmlTree XmlTree
getID String
xid) XmlTree
doc -> do
    ByteString
t <- Maybe Transforms -> XmlTree -> IO ByteString
applyTransforms (Reference -> Maybe Transforms
referenceTransforms Reference
r) (XmlTree -> IO ByteString) -> XmlTree -> IO ByteString
forall a b. (a -> b) -> a -> b
$ [XmlTree] -> [XmlTree] -> XmlTree
DOM.mkRoot [] [XmlTree]
x
    Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String
xid String -> Maybe () -> Maybe String
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (DigestMethod -> ByteString -> ByteString
applyDigest (Reference -> DigestMethod
referenceDigestMethod Reference
r) ByteString
t ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== Reference -> ByteString
referenceDigestValue Reference
r)
  Maybe URI
_ -> Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing

data SigningKey
  = SigningKeyDSA DSA.KeyPair
  | SigningKeyRSA RSA.KeyPair
  deriving (SigningKey -> SigningKey -> Bool
(SigningKey -> SigningKey -> Bool)
-> (SigningKey -> SigningKey -> Bool) -> Eq SigningKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SigningKey -> SigningKey -> Bool
$c/= :: SigningKey -> SigningKey -> Bool
== :: SigningKey -> SigningKey -> Bool
$c== :: SigningKey -> SigningKey -> Bool
Eq, Int -> SigningKey -> String -> String
[SigningKey] -> String -> String
SigningKey -> String
(Int -> SigningKey -> String -> String)
-> (SigningKey -> String)
-> ([SigningKey] -> String -> String)
-> Show SigningKey
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [SigningKey] -> String -> String
$cshowList :: [SigningKey] -> String -> String
show :: SigningKey -> String
$cshow :: SigningKey -> String
showsPrec :: Int -> SigningKey -> String -> String
$cshowsPrec :: Int -> SigningKey -> String -> String
Show)

data PublicKeys = PublicKeys
  { PublicKeys -> Maybe PublicKey
publicKeyDSA :: Maybe DSA.PublicKey
  , PublicKeys -> Maybe PublicKey
publicKeyRSA :: Maybe RSA.PublicKey
  } deriving (PublicKeys -> PublicKeys -> Bool
(PublicKeys -> PublicKeys -> Bool)
-> (PublicKeys -> PublicKeys -> Bool) -> Eq PublicKeys
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PublicKeys -> PublicKeys -> Bool
$c/= :: PublicKeys -> PublicKeys -> Bool
== :: PublicKeys -> PublicKeys -> Bool
$c== :: PublicKeys -> PublicKeys -> Bool
Eq, Int -> PublicKeys -> String -> String
[PublicKeys] -> String -> String
PublicKeys -> String
(Int -> PublicKeys -> String -> String)
-> (PublicKeys -> String)
-> ([PublicKeys] -> String -> String)
-> Show PublicKeys
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [PublicKeys] -> String -> String
$cshowList :: [PublicKeys] -> String -> String
show :: PublicKeys -> String
$cshow :: PublicKeys -> String
showsPrec :: Int -> PublicKeys -> String -> String
$cshowsPrec :: Int -> PublicKeys -> String -> String
Show)

#if MIN_VERSION_base(4,11,0)
instance Semigroup PublicKeys where
  PublicKeys Maybe PublicKey
dsa1 Maybe PublicKey
rsa1 <> :: PublicKeys -> PublicKeys -> PublicKeys
<> PublicKeys Maybe PublicKey
dsa2 Maybe PublicKey
rsa2 =
    Maybe PublicKey -> Maybe PublicKey -> PublicKeys
PublicKeys (Maybe PublicKey
dsa1 Maybe PublicKey -> Maybe PublicKey -> Maybe PublicKey
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe PublicKey
dsa2) (Maybe PublicKey
rsa1 Maybe PublicKey -> Maybe PublicKey -> Maybe PublicKey
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe PublicKey
rsa2)
#endif
instance Monoid PublicKeys where
  mempty :: PublicKeys
mempty = Maybe PublicKey -> Maybe PublicKey -> PublicKeys
PublicKeys Maybe PublicKey
forall a. Maybe a
Nothing Maybe PublicKey
forall a. Maybe a
Nothing
  PublicKeys Maybe PublicKey
dsa1 Maybe PublicKey
rsa1 mappend :: PublicKeys -> PublicKeys -> PublicKeys
`mappend` PublicKeys Maybe PublicKey
dsa2 Maybe PublicKey
rsa2 =
    Maybe PublicKey -> Maybe PublicKey -> PublicKeys
PublicKeys (Maybe PublicKey
dsa1 Maybe PublicKey -> Maybe PublicKey -> Maybe PublicKey
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe PublicKey
dsa2) (Maybe PublicKey
rsa1 Maybe PublicKey -> Maybe PublicKey -> Maybe PublicKey
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe PublicKey
rsa2)

signingKeySignatureAlgorithm :: SigningKey -> SignatureAlgorithm
signingKeySignatureAlgorithm :: SigningKey -> SignatureAlgorithm
signingKeySignatureAlgorithm (SigningKeyDSA KeyPair
_) = SignatureAlgorithm
SignatureDSA_SHA1
signingKeySignatureAlgorithm (SigningKeyRSA KeyPair
_) = SignatureAlgorithm
SignatureRSA_SHA1

signingKeyValue :: SigningKey -> KeyValue
signingKeyValue :: SigningKey -> KeyValue
signingKeyValue (SigningKeyDSA (KeyPair -> PublicKey
DSA.toPublicKey -> DSA.PublicKey Params
p PublicNumber
y)) = DSAKeyValue :: Maybe (PublicNumber, PublicNumber)
-> Maybe PublicNumber
-> PublicNumber
-> Maybe PublicNumber
-> Maybe (PublicNumber, PublicNumber)
-> KeyValue
DSAKeyValue
  { dsaKeyValuePQ :: Maybe (PublicNumber, PublicNumber)
dsaKeyValuePQ = (PublicNumber, PublicNumber) -> Maybe (PublicNumber, PublicNumber)
forall a. a -> Maybe a
Just (Params -> PublicNumber
DSA.params_p Params
p, Params -> PublicNumber
DSA.params_q Params
p)
  , dsaKeyValueG :: Maybe PublicNumber
dsaKeyValueG = PublicNumber -> Maybe PublicNumber
forall a. a -> Maybe a
Just (Params -> PublicNumber
DSA.params_g Params
p)
  , dsaKeyValueY :: PublicNumber
dsaKeyValueY = PublicNumber
y
  , dsaKeyValueJ :: Maybe PublicNumber
dsaKeyValueJ = Maybe PublicNumber
forall a. Maybe a
Nothing
  , dsaKeyValueSeedPgenCounter :: Maybe (PublicNumber, PublicNumber)
dsaKeyValueSeedPgenCounter = Maybe (PublicNumber, PublicNumber)
forall a. Maybe a
Nothing
  }
signingKeyValue (SigningKeyRSA (KeyPair -> PublicKey
RSA.toPublicKey -> RSA.PublicKey Int
_ PublicNumber
n PublicNumber
e)) = RSAKeyValue :: PublicNumber -> PublicNumber -> KeyValue
RSAKeyValue
  { rsaKeyValueModulus :: PublicNumber
rsaKeyValueModulus = PublicNumber
n
  , rsaKeyValueExponent :: PublicNumber
rsaKeyValueExponent = PublicNumber
e
  }

publicKeyValues :: KeyValue -> PublicKeys
publicKeyValues :: KeyValue -> PublicKeys
publicKeyValues DSAKeyValue{ dsaKeyValuePQ :: KeyValue -> Maybe (PublicNumber, PublicNumber)
dsaKeyValuePQ = Just (PublicNumber
p, PublicNumber
q), dsaKeyValueG :: KeyValue -> Maybe PublicNumber
dsaKeyValueG = Just PublicNumber
g, dsaKeyValueY :: KeyValue -> PublicNumber
dsaKeyValueY = PublicNumber
y } = PublicKeys
forall a. Monoid a => a
mempty
  { publicKeyDSA :: Maybe PublicKey
publicKeyDSA = PublicKey -> Maybe PublicKey
forall a. a -> Maybe a
Just (PublicKey -> Maybe PublicKey) -> PublicKey -> Maybe PublicKey
forall a b. (a -> b) -> a -> b
$ PublicKey :: Params -> PublicNumber -> PublicKey
DSA.PublicKey
    { public_params :: Params
DSA.public_params = Params :: PublicNumber -> PublicNumber -> PublicNumber -> Params
DSA.Params
      { params_p :: PublicNumber
DSA.params_p = PublicNumber
p
      , params_q :: PublicNumber
DSA.params_q = PublicNumber
q
      , params_g :: PublicNumber
DSA.params_g = PublicNumber
g
      }
    , public_y :: PublicNumber
DSA.public_y = PublicNumber
y
    }
  }
publicKeyValues RSAKeyValue{ rsaKeyValueModulus :: KeyValue -> PublicNumber
rsaKeyValueModulus = PublicNumber
n, rsaKeyValueExponent :: KeyValue -> PublicNumber
rsaKeyValueExponent = PublicNumber
e } = PublicKeys
forall a. Monoid a => a
mempty
  { publicKeyRSA :: Maybe PublicKey
publicKeyRSA = PublicKey -> Maybe PublicKey
forall a. a -> Maybe a
Just (PublicKey -> Maybe PublicKey) -> PublicKey -> Maybe PublicKey
forall a b. (a -> b) -> a -> b
$ Int -> PublicNumber -> PublicNumber -> PublicKey
RSA.PublicKey (PublicNumber -> Int
numBytes PublicNumber
n) PublicNumber
n PublicNumber
e
  }
publicKeyValues KeyValue
_ = PublicKeys
forall a. Monoid a => a
mempty

signBytes :: SigningKey -> BS.ByteString -> IO BS.ByteString
signBytes :: SigningKey -> ByteString -> IO ByteString
signBytes (SigningKeyDSA KeyPair
k) ByteString
b = do
  Signature
s <- PrivateKey -> SHA1 -> ByteString -> IO Signature
forall msg hash (m :: * -> *).
(ByteArrayAccess msg, HashAlgorithm hash, MonadRandom m) =>
PrivateKey -> hash -> msg -> m Signature
DSA.sign (KeyPair -> PrivateKey
DSA.toPrivateKey KeyPair
k) SHA1
SHA1 ByteString
b
  ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> IO ByteString) -> ByteString -> IO ByteString
forall a b. (a -> b) -> a -> b
$ Int -> PublicNumber -> ByteString
forall ba. ByteArray ba => Int -> PublicNumber -> ba
i2ospOf_ Int
20 (Signature -> PublicNumber
DSA.sign_r Signature
s) ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Int -> PublicNumber -> ByteString
forall ba. ByteArray ba => Int -> PublicNumber -> ba
i2ospOf_ Int
20 (Signature -> PublicNumber
DSA.sign_s Signature
s)
signBytes (SigningKeyRSA KeyPair
k) ByteString
b =
  (Error -> IO ByteString)
-> (ByteString -> IO ByteString)
-> Either Error ByteString
-> IO ByteString
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> IO ByteString
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO ByteString)
-> (Error -> String) -> Error -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Error -> String
forall a. Show a => a -> String
show) ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Error ByteString -> IO ByteString)
-> IO (Either Error ByteString) -> IO ByteString
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Maybe SHA1
-> PrivateKey -> ByteString -> IO (Either Error ByteString)
forall hashAlg (m :: * -> *).
(HashAlgorithmASN1 hashAlg, MonadRandom m) =>
Maybe hashAlg
-> PrivateKey -> ByteString -> m (Either Error ByteString)
RSA.signSafer (SHA1 -> Maybe SHA1
forall a. a -> Maybe a
Just SHA1
SHA1) (KeyPair -> PrivateKey
RSA.toPrivateKey KeyPair
k) ByteString
b

-- | indicate verification result; return 'Nothing' if no matching key/alg pair is found
verifyBytes :: PublicKeys -> IdentifiedURI SignatureAlgorithm -> BS.ByteString -> BS.ByteString -> Maybe Bool
verifyBytes :: PublicKeys
-> IdentifiedURI SignatureAlgorithm
-> ByteString
-> ByteString
-> Maybe Bool
verifyBytes PublicKeys{ publicKeyDSA :: PublicKeys -> Maybe PublicKey
publicKeyDSA = Just PublicKey
k } (Identified SignatureAlgorithm
SignatureDSA_SHA1) ByteString
sig ByteString
m = Bool -> Maybe Bool
forall a. a -> Maybe a
Just (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$
  ByteString -> Int
BS.length ByteString
sig Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
40 Bool -> Bool -> Bool
&&
  SHA1 -> PublicKey -> Signature -> ByteString -> Bool
forall msg hash.
(ByteArrayAccess msg, HashAlgorithm hash) =>
hash -> PublicKey -> Signature -> msg -> Bool
DSA.verify SHA1
SHA1 PublicKey
k Signature :: PublicNumber -> PublicNumber -> Signature
DSA.Signature{ sign_r :: PublicNumber
DSA.sign_r = ByteString -> PublicNumber
forall ba. ByteArrayAccess ba => ba -> PublicNumber
os2ip ByteString
r, sign_s :: PublicNumber
DSA.sign_s = ByteString -> PublicNumber
forall ba. ByteArrayAccess ba => ba -> PublicNumber
os2ip ByteString
s } ByteString
m
  where (ByteString
r, ByteString
s) = Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
20 ByteString
sig
verifyBytes PublicKeys{ publicKeyRSA :: PublicKeys -> Maybe PublicKey
publicKeyRSA = Just PublicKey
k } (Identified SignatureAlgorithm
SignatureRSA_SHA1) ByteString
sig ByteString
m = Bool -> Maybe Bool
forall a. a -> Maybe a
Just (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$
  Maybe SHA1 -> PublicKey -> ByteString -> ByteString -> Bool
forall hashAlg.
HashAlgorithmASN1 hashAlg =>
Maybe hashAlg -> PublicKey -> ByteString -> ByteString -> Bool
RSA.verify (SHA1 -> Maybe SHA1
forall a. a -> Maybe a
Just SHA1
SHA1) PublicKey
k ByteString
m ByteString
sig
verifyBytes PublicKeys{ publicKeyRSA :: PublicKeys -> Maybe PublicKey
publicKeyRSA = Just PublicKey
k } (Identified SignatureAlgorithm
SignatureRSA_SHA256) ByteString
sig ByteString
m = Bool -> Maybe Bool
forall a. a -> Maybe a
Just (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$
  Maybe SHA256 -> PublicKey -> ByteString -> ByteString -> Bool
forall hashAlg.
HashAlgorithmASN1 hashAlg =>
Maybe hashAlg -> PublicKey -> ByteString -> ByteString -> Bool
RSA.verify (SHA256 -> Maybe SHA256
forall a. a -> Maybe a
Just SHA256
SHA256) PublicKey
k ByteString
m ByteString
sig
verifyBytes PublicKeys
_ IdentifiedURI SignatureAlgorithm
_ ByteString
_ ByteString
_ = Maybe Bool
forall a. Maybe a
Nothing

signBase64 :: SigningKey -> BS.ByteString -> IO BS.ByteString
signBase64 :: SigningKey -> ByteString -> IO ByteString
signBase64 SigningKey
sk = (ByteString -> ByteString) -> IO ByteString -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> ByteString
Base64.encode (IO ByteString -> IO ByteString)
-> (ByteString -> IO ByteString) -> ByteString -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SigningKey -> ByteString -> IO ByteString
signBytes SigningKey
sk

verifyBase64 :: PublicKeys -> IdentifiedURI SignatureAlgorithm -> BS.ByteString -> BS.ByteString -> Maybe Bool
verifyBase64 :: PublicKeys
-> IdentifiedURI SignatureAlgorithm
-> ByteString
-> ByteString
-> Maybe Bool
verifyBase64 PublicKeys
pk IdentifiedURI SignatureAlgorithm
alg ByteString
sig ByteString
m = (String -> Maybe Bool)
-> (ByteString -> Maybe Bool)
-> Either String ByteString
-> Maybe Bool
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Maybe Bool -> String -> Maybe Bool
forall a b. a -> b -> a
const (Maybe Bool -> String -> Maybe Bool)
-> Maybe Bool -> String -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False) (\ByteString
s -> PublicKeys
-> IdentifiedURI SignatureAlgorithm
-> ByteString
-> ByteString
-> Maybe Bool
verifyBytes PublicKeys
pk IdentifiedURI SignatureAlgorithm
alg ByteString
s ByteString
m) (Either String ByteString -> Maybe Bool)
-> Either String ByteString -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ ByteString -> Either String ByteString
Base64.decode ByteString
sig

generateSignature :: SigningKey -> SignedInfo -> IO Signature
generateSignature :: SigningKey -> SignedInfo -> IO Signature
generateSignature SigningKey
sk SignedInfo
si = do
  -- XXX: samlToDoc may not match later
  ByteString
six <- CanonicalizationMethod -> Maybe String -> XmlTree -> IO ByteString
applyCanonicalization (SignedInfo -> CanonicalizationMethod
signedInfoCanonicalizationMethod SignedInfo
si) Maybe String
forall a. Maybe a
Nothing (XmlTree -> IO ByteString) -> XmlTree -> IO ByteString
forall a b. (a -> b) -> a -> b
$ SignedInfo -> XmlTree
forall a. XmlPickler a => a -> XmlTree
samlToDoc SignedInfo
si
  ByteString
sv <- SigningKey -> ByteString -> IO ByteString
signBytes SigningKey
sk ByteString
six
  Signature -> IO Signature
forall (m :: * -> *) a. Monad m => a -> m a
return Signature :: Maybe String
-> SignedInfo
-> SignatureValue
-> Maybe KeyInfo
-> [Object]
-> Signature
Signature
    { signatureId :: Maybe String
signatureId = Maybe String
forall a. Maybe a
Nothing
    , signatureSignedInfo :: SignedInfo
signatureSignedInfo = SignedInfo
si
    , signatureSignatureValue :: SignatureValue
signatureSignatureValue = Maybe String -> ByteString -> SignatureValue
SignatureValue Maybe String
forall a. Maybe a
Nothing ByteString
sv
    , signatureKeyInfo :: Maybe KeyInfo
signatureKeyInfo = KeyInfo -> Maybe KeyInfo
forall a. a -> Maybe a
Just (KeyInfo -> Maybe KeyInfo) -> KeyInfo -> Maybe KeyInfo
forall a b. (a -> b) -> a -> b
$ Maybe String -> List1 KeyInfoElement -> KeyInfo
KeyInfo Maybe String
forall a. Maybe a
Nothing (List1 KeyInfoElement -> KeyInfo)
-> List1 KeyInfoElement -> KeyInfo
forall a b. (a -> b) -> a -> b
$ KeyValue -> KeyInfoElement
KeyInfoKeyValue (SigningKey -> KeyValue
signingKeyValue SigningKey
sk) KeyInfoElement -> [KeyInfoElement] -> List1 KeyInfoElement
forall a. a -> [a] -> NonEmpty a
NonEmpty.:| []
    , signatureObject :: [Object]
signatureObject = []
    }

-- Exception in IO:  something is syntactically wrong with the input
-- Nothing:          no matching key/alg pairs found
-- Just False:       signature verification failed || dangling refs || explicit ref is not among the signed ones
-- Just True:        everything is ok!
verifySignature :: PublicKeys -> String -> HXT.XmlTree -> IO (Maybe Bool)
verifySignature :: PublicKeys -> String -> XmlTree -> IO (Maybe Bool)
verifySignature PublicKeys
pks String
xid XmlTree
doc = do
  let namespaces :: NsEnv
namespaces = AssocList String String -> NsEnv
DOM.toNsEnv (AssocList String String -> NsEnv)
-> AssocList String String -> NsEnv
forall a b. (a -> b) -> a -> b
$ LA XmlTree (String, String) -> XmlTree -> AssocList String String
forall a b. LA a b -> a -> [b]
HXT.runLA LA XmlTree (String, String)
HXT.collectNamespaceDecl XmlTree
doc
  XmlTree
x <- case LA XmlTree XmlTree -> XmlTree -> [XmlTree]
forall a b. LA a b -> a -> [b]
HXT.runLA (String -> LA XmlTree XmlTree
forall (a :: * -> * -> *).
ArrowXml a =>
String -> a XmlTree XmlTree
getID String
xid LA XmlTree XmlTree -> LA XmlTree XmlTree -> LA XmlTree XmlTree
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
HXT.>>> NsEnv -> LA XmlTree XmlTree
forall (a :: * -> * -> *). ArrowXml a => NsEnv -> a XmlTree XmlTree
HXT.attachNsEnv NsEnv
namespaces) XmlTree
doc of
    [XmlTree
x] -> XmlTree -> IO XmlTree
forall (m :: * -> *) a. Monad m => a -> m a
return XmlTree
x
    [XmlTree]
_ -> String -> IO XmlTree
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"verifySignature: element not found"
  XmlTree
sx <- case String -> XmlTree -> [XmlTree]
child String
"Signature" XmlTree
x of
    [XmlTree
sx] -> XmlTree -> IO XmlTree
forall (m :: * -> *) a. Monad m => a -> m a
return XmlTree
sx
    [XmlTree]
_ -> String -> IO XmlTree
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"verifySignature: Signature not found"
  s :: Signature
s@Signature{ signatureSignedInfo :: Signature -> SignedInfo
signatureSignedInfo = SignedInfo
si } <- (String -> IO Signature)
-> (Signature -> IO Signature)
-> Either String Signature
-> IO Signature
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> IO Signature
forall (m :: * -> *) a. MonadFail m => String -> m a
fail Signature -> IO Signature
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String Signature -> IO Signature)
-> Either String Signature -> IO Signature
forall a b. (a -> b) -> a -> b
$ XmlTree -> Either String Signature
forall a. XmlPickler a => XmlTree -> Either String a
docToSAML XmlTree
sx
  ByteString
six <- CanonicalizationMethod -> Maybe String -> XmlTree -> IO ByteString
applyCanonicalization (SignedInfo -> CanonicalizationMethod
signedInfoCanonicalizationMethod SignedInfo
si) (String -> Maybe String
forall a. a -> Maybe a
Just String
xpath) (XmlTree -> IO ByteString) -> XmlTree -> IO ByteString
forall a b. (a -> b) -> a -> b
$ [XmlTree] -> [XmlTree] -> XmlTree
DOM.mkRoot [] [XmlTree
x]
  NonEmpty (Maybe String)
rl <- (Reference -> IO (Maybe String))
-> NonEmpty Reference -> IO (NonEmpty (Maybe String))
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (Reference -> XmlTree -> IO (Maybe String)
`verifyReference` XmlTree
x) (SignedInfo -> NonEmpty Reference
signedInfoReference SignedInfo
si)
  let verified :: Maybe Bool
      verified :: Maybe Bool
verified = PublicKeys
-> IdentifiedURI SignatureAlgorithm
-> ByteString
-> ByteString
-> Maybe Bool
verifyBytes PublicKeys
pks (SignatureMethod -> IdentifiedURI SignatureAlgorithm
signatureMethodAlgorithm (SignatureMethod -> IdentifiedURI SignatureAlgorithm)
-> SignatureMethod -> IdentifiedURI SignatureAlgorithm
forall a b. (a -> b) -> a -> b
$ SignedInfo -> SignatureMethod
signedInfoSignatureMethod SignedInfo
si) (SignatureValue -> ByteString
signatureValue (SignatureValue -> ByteString) -> SignatureValue -> ByteString
forall a b. (a -> b) -> a -> b
$ Signature -> SignatureValue
signatureSignatureValue Signature
s) ByteString
six
      valid :: Bool
      valid :: Bool
valid = Maybe String -> NonEmpty (Maybe String) -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem (String -> Maybe String
forall a. a -> Maybe a
Just String
xid) NonEmpty (Maybe String)
rl Bool -> Bool -> Bool
&& (Maybe String -> Bool) -> NonEmpty (Maybe String) -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Maybe String -> Bool
forall a. Maybe a -> Bool
isJust NonEmpty (Maybe String)
rl
  Maybe Bool -> IO (Maybe Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Bool -> IO (Maybe Bool)) -> Maybe Bool -> IO (Maybe Bool)
forall a b. (a -> b) -> a -> b
$ (Bool
valid Bool -> Bool -> Bool
&&) (Bool -> Bool) -> Maybe Bool -> Maybe Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bool
verified
  where
  child :: String -> XmlTree -> [XmlTree]
child String
n = LA XmlTree XmlTree -> XmlTree -> [XmlTree]
forall a b. LA a b -> a -> [b]
HXT.runLA (LA XmlTree XmlTree -> XmlTree -> [XmlTree])
-> LA XmlTree XmlTree -> XmlTree -> [XmlTree]
forall a b. (a -> b) -> a -> b
$ LA XmlTree XmlTree
forall (a :: * -> * -> *) (t :: * -> *) b.
(ArrowTree a, Tree t) =>
a (t b) (t b)
HXT.getChildren LA XmlTree XmlTree -> LA XmlTree XmlTree -> LA XmlTree XmlTree
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
HXT.>>> String -> LA XmlTree XmlTree
forall (a :: * -> * -> *).
ArrowXml a =>
String -> a XmlTree XmlTree
isDSElem String
n LA XmlTree XmlTree -> LA XmlTree XmlTree -> LA XmlTree XmlTree
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
HXT.>>> LA XmlTree (String, String) -> LA XmlTree XmlTree
HXT.cleanupNamespaces LA XmlTree (String, String)
HXT.collectPrefixUriPairs
  keyinfo :: KeyInfoElement -> PublicKeys
keyinfo (KeyInfoKeyValue KeyValue
kv) = KeyValue -> PublicKeys
publicKeyValues KeyValue
kv
  keyinfo (X509Data List1 X509Element
l) = (X509Element -> PublicKeys) -> List1 X509Element -> PublicKeys
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap X509Element -> PublicKeys
keyx509d List1 X509Element
l
  keyinfo KeyInfoElement
_ = PublicKeys
forall a. Monoid a => a
mempty
  keyx509d :: X509Element -> PublicKeys
keyx509d (X509Certificate SignedCertificate
sc) = PubKey -> PublicKeys
keyx509p (PubKey -> PublicKeys) -> PubKey -> PublicKeys
forall a b. (a -> b) -> a -> b
$ Certificate -> PubKey
X509.certPubKey (Certificate -> PubKey) -> Certificate -> PubKey
forall a b. (a -> b) -> a -> b
$ SignedCertificate -> Certificate
X509.getCertificate SignedCertificate
sc
  keyx509d X509Element
_ = PublicKeys
forall a. Monoid a => a
mempty
  keyx509p :: PubKey -> PublicKeys
keyx509p (X509.PubKeyRSA PublicKey
r) = PublicKeys
forall a. Monoid a => a
mempty{ publicKeyRSA :: Maybe PublicKey
publicKeyRSA = PublicKey -> Maybe PublicKey
forall a. a -> Maybe a
Just PublicKey
r }
  keyx509p (X509.PubKeyDSA PublicKey
d) = PublicKeys
forall a. Monoid a => a
mempty{ publicKeyDSA :: Maybe PublicKey
publicKeyDSA = PublicKey -> Maybe PublicKey
forall a. a -> Maybe a
Just PublicKey
d }
  keyx509p PubKey
_ = PublicKeys
forall a. Monoid a => a
mempty
  xpathsel :: String -> String
xpathsel String
t = String
"/*[local-name()='" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"' and namespace-uri()='" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Namespace -> String
namespaceURIString Namespace
ns String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"']"
  xpathbase :: String
xpathbase = String
"/*" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
xpathsel String
"Signature" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
xpathsel String
"SignedInfo" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"//"
  xpath :: String
xpath = String
xpathbase String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
". | " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
xpathbase String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"@* | " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
xpathbase String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"namespace::*"