hsaml2 (empty) → 0.1
raw patch · 43 files changed
+5253/−0 lines, 43 filesdep +HUnitdep +asn1-encodingdep +asn1-typessetup-changed
Dependencies added: HUnit, asn1-encoding, asn1-types, base, base64-bytestring, bytestring, cryptonite, data-default, hsaml2, http-types, hxt, hxt-charproperties, hxt-http, hxt-unicode, invertible, invertible-hxt, lens, memory, mtl, network-uri, process, semigroups, template-haskell, time, x509, zlib
Files
- LICENSE +202/−0
- SAML2.hs +23/−0
- SAML2/Bindings.hs +9/−0
- SAML2/Bindings/General.hs +18/−0
- SAML2/Bindings/HTTPPOST.hs +47/−0
- SAML2/Bindings/HTTPRedirect.hs +121/−0
- SAML2/Bindings/Identifiers.hs +30/−0
- SAML2/Bindings/Internal.hs +15/−0
- SAML2/Core.hs +107/−0
- SAML2/Core/Assertions.hs +454/−0
- SAML2/Core/Datatypes.hs +20/−0
- SAML2/Core/Identifiers.hs +94/−0
- SAML2/Core/Namespaces.hs +32/−0
- SAML2/Core/Protocols.hs +765/−0
- SAML2/Core/Signature.hs +55/−0
- SAML2/Core/Versioning.hs +37/−0
- SAML2/Lens.hs +14/−0
- SAML2/Metadata.hs +34/−0
- SAML2/Metadata/Metadata.hs +471/−0
- SAML2/Profiles.hs +9/−0
- SAML2/Profiles/ConfirmationMethod.hs +25/−0
- SAML2/XML.hs +120/−0
- SAML2/XML/ASN1.hs +30/−0
- SAML2/XML/Canonical.hs +66/−0
- SAML2/XML/Encryption.hs +207/−0
- SAML2/XML/LibXML2.hsc +115/−0
- SAML2/XML/Schema.hs +10/−0
- SAML2/XML/Schema/Datatypes.hs +176/−0
- SAML2/XML/Signature.hs +206/−0
- SAML2/XML/Signature/Types.hs +613/−0
- SAML2/XML/Types.hs +38/−0
- SAML2/XML/libxml2_stub.c +5/−0
- Setup.hs +2/−0
- hsaml2.cabal +109/−0
- test/Bindings/HTTPRedirect.hs +50/−0
- test/Main.hs +26/−0
- test/Metadata/Metadata.hs +493/−0
- test/XML.hs +29/−0
- test/XML/Canonical.hs +36/−0
- test/XML/Encryption.hs +92/−0
- test/XML/Signature.hs +154/−0
- test/XML/encryption-example.xml +53/−0
- test/XML/signature-example.xml +41/−0
+ LICENSE view
@@ -0,0 +1,202 @@++ Apache License+ Version 2.0, January 2004+ http://www.apache.org/licenses/++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++ 1. Definitions.++ "License" shall mean the terms and conditions for use, reproduction,+ and distribution as defined by Sections 1 through 9 of this document.++ "Licensor" shall mean the copyright owner or entity authorized by+ the copyright owner that is granting the License.++ "Legal Entity" shall mean the union of the acting entity and all+ other entities that control, are controlled by, or are under common+ control with that entity. For the purposes of this definition,+ "control" means (i) the power, direct or indirect, to cause the+ direction or management of such entity, whether by contract or+ otherwise, or (ii) ownership of fifty percent (50%) or more of the+ outstanding shares, or (iii) beneficial ownership of such entity.++ "You" (or "Your") shall mean an individual or Legal Entity+ exercising permissions granted by this License.++ "Source" form shall mean the preferred form for making modifications,+ including but not limited to software source code, documentation+ source, and configuration files.++ "Object" form shall mean any form resulting from mechanical+ transformation or translation of a Source form, including but+ not limited to compiled object code, generated documentation,+ and conversions to other media types.++ "Work" shall mean the work of authorship, whether in Source or+ Object form, made available under the License, as indicated by a+ copyright notice that is included in or attached to the work+ (an example is provided in the Appendix below).++ "Derivative Works" shall mean any work, whether in Source or Object+ form, that is based on (or derived from) the Work and for which the+ editorial revisions, annotations, elaborations, or other modifications+ represent, as a whole, an original work of authorship. For the purposes+ of this License, Derivative Works shall not include works that remain+ separable from, or merely link (or bind by name) to the interfaces of,+ the Work and Derivative Works thereof.++ "Contribution" shall mean any work of authorship, including+ the original version of the Work and any modifications or additions+ to that Work or Derivative Works thereof, that is intentionally+ submitted to Licensor for inclusion in the Work by the copyright owner+ or by an individual or Legal Entity authorized to submit on behalf of+ the copyright owner. For the purposes of this definition, "submitted"+ means any form of electronic, verbal, or written communication sent+ to the Licensor or its representatives, including but not limited to+ communication on electronic mailing lists, source code control systems,+ and issue tracking systems that are managed by, or on behalf of, the+ Licensor for the purpose of discussing and improving the Work, but+ excluding communication that is conspicuously marked or otherwise+ designated in writing by the copyright owner as "Not a Contribution."++ "Contributor" shall mean Licensor and any individual or Legal Entity+ on behalf of whom a Contribution has been received by Licensor and+ subsequently incorporated within the Work.++ 2. Grant of Copyright License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ copyright license to reproduce, prepare Derivative Works of,+ publicly display, publicly perform, sublicense, and distribute the+ Work and such Derivative Works in Source or Object form.++ 3. Grant of Patent License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ (except as stated in this section) patent license to make, have made,+ use, offer to sell, sell, import, and otherwise transfer the Work,+ where such license applies only to those patent claims licensable+ by such Contributor that are necessarily infringed by their+ Contribution(s) alone or by combination of their Contribution(s)+ with the Work to which such Contribution(s) was submitted. If You+ institute patent litigation against any entity (including a+ cross-claim or counterclaim in a lawsuit) alleging that the Work+ or a Contribution incorporated within the Work constitutes direct+ or contributory patent infringement, then any patent licenses+ granted to You under this License for that Work shall terminate+ as of the date such litigation is filed.++ 4. Redistribution. You may reproduce and distribute copies of the+ Work or Derivative Works thereof in any medium, with or without+ modifications, and in Source or Object form, provided that You+ meet the following conditions:++ (a) You must give any other recipients of the Work or+ Derivative Works a copy of this License; and++ (b) You must cause any modified files to carry prominent notices+ stating that You changed the files; and++ (c) You must retain, in the Source form of any Derivative Works+ that You distribute, all copyright, patent, trademark, and+ attribution notices from the Source form of the Work,+ excluding those notices that do not pertain to any part of+ the Derivative Works; and++ (d) If the Work includes a "NOTICE" text file as part of its+ distribution, then any Derivative Works that You distribute must+ include a readable copy of the attribution notices contained+ within such NOTICE file, excluding those notices that do not+ pertain to any part of the Derivative Works, in at least one+ of the following places: within a NOTICE text file distributed+ as part of the Derivative Works; within the Source form or+ documentation, if provided along with the Derivative Works; or,+ within a display generated by the Derivative Works, if and+ wherever such third-party notices normally appear. The contents+ of the NOTICE file are for informational purposes only and+ do not modify the License. You may add Your own attribution+ notices within Derivative Works that You distribute, alongside+ or as an addendum to the NOTICE text from the Work, provided+ that such additional attribution notices cannot be construed+ as modifying the License.++ You may add Your own copyright statement to Your modifications and+ may provide additional or different license terms and conditions+ for use, reproduction, or distribution of Your modifications, or+ for any such Derivative Works as a whole, provided Your use,+ reproduction, and distribution of the Work otherwise complies with+ the conditions stated in this License.++ 5. Submission of Contributions. Unless You explicitly state otherwise,+ any Contribution intentionally submitted for inclusion in the Work+ by You to the Licensor shall be under the terms and conditions of+ this License, without any additional terms or conditions.+ Notwithstanding the above, nothing herein shall supersede or modify+ the terms of any separate license agreement you may have executed+ with Licensor regarding such Contributions.++ 6. Trademarks. This License does not grant permission to use the trade+ names, trademarks, service marks, or product names of the Licensor,+ except as required for reasonable and customary use in describing the+ origin of the Work and reproducing the content of the NOTICE file.++ 7. Disclaimer of Warranty. Unless required by applicable law or+ agreed to in writing, Licensor provides the Work (and each+ Contributor provides its Contributions) on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+ implied, including, without limitation, any warranties or conditions+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+ PARTICULAR PURPOSE. You are solely responsible for determining the+ appropriateness of using or redistributing the Work and assume any+ risks associated with Your exercise of permissions under this License.++ 8. Limitation of Liability. In no event and under no legal theory,+ whether in tort (including negligence), contract, or otherwise,+ unless required by applicable law (such as deliberate and grossly+ negligent acts) or agreed to in writing, shall any Contributor be+ liable to You for damages, including any direct, indirect, special,+ incidental, or consequential damages of any character arising as a+ result of this License or out of the use or inability to use the+ Work (including but not limited to damages for loss of goodwill,+ work stoppage, computer failure or malfunction, or any and all+ other commercial damages or losses), even if such Contributor+ has been advised of the possibility of such damages.++ 9. Accepting Warranty or Additional Liability. While redistributing+ the Work or Derivative Works thereof, You may choose to offer,+ and charge a fee for, acceptance of support, warranty, indemnity,+ or other liability obligations and/or rights consistent with this+ License. However, in accepting such obligations, You may act only+ on Your own behalf and on Your sole responsibility, not on behalf+ of any other Contributor, and only if You agree to indemnify,+ defend, and hold each Contributor harmless for any liability+ incurred by, or claims asserted against, such Contributor by reason+ of your accepting any such warranty or additional liability.++ END OF TERMS AND CONDITIONS++ APPENDIX: How to apply the Apache License to your work.++ To apply the Apache License to your work, attach the following+ boilerplate notice, with the fields enclosed by brackets "[]"+ replaced with your own identifying information. (Don't include+ the brackets!) The text should be enclosed in the appropriate+ comment syntax for the file format. We also recommend that a+ file or class name and description of purpose be included on the+ same "printed page" as the copyright notice for easier+ identification within third-party archives.++ Copyright [yyyy] [name of copyright owner]++ Licensed under the Apache License, Version 2.0 (the "License");+ you may not use this file except in compliance with the License.+ You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++ Unless required by applicable law or agreed to in writing, software+ distributed under the License is distributed on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ See the License for the specific language governing permissions and+ limitations under the License.
+ SAML2.hs view
@@ -0,0 +1,23 @@+-- |+-- OASIS Security Assertion Markup Language (SAML) V2.0+--+module SAML2+ ( Identified(..)+ , namespaceURI+ , samlToXML+ , xmlToSAML+ -- * Assertions and Protocols+ , module SAML2.Core+ -- * Bindings+ , module SAML2.Bindings+ -- * Metadata+ , module SAML2.Metadata+ -- * Profiles+ , module SAML2.Profiles+ ) where++import SAML2.XML+import SAML2.Core+import SAML2.Bindings+import SAML2.Metadata+import SAML2.Profiles
+ SAML2/Bindings.hs view
@@ -0,0 +1,9 @@+-- |+-- Bindings for the OASIS Security Assertion Markup Language (SAML) V2.0+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf saml-bindings-2.0-os>+module SAML2.Bindings+ ( module SAML2.Bindings.Identifiers+ ) where++import SAML2.Bindings.Identifiers
+ SAML2/Bindings/General.hs view
@@ -0,0 +1,18 @@+-- |+-- General Considerations+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf saml-bindings-2.0-os> §3.1+module SAML2.Bindings.General where++import Data.ByteString (ByteString)+import Data.String (IsString(fromString))++type RelayState = ByteString++-- |The name of the parameter used by many protocols for the message itself for requests (False) or responses (True). Often combined with 'SAML2.Core.Protocols.isSAMLResponse'.+protocolParameter :: IsString a => Bool -> a+protocolParameter False = fromString "SAMLRequest"+protocolParameter True = fromString "SAMLResponse"++relayStateParameter :: IsString a => a+relayStateParameter = fromString "RelayState"
+ SAML2/Bindings/HTTPPOST.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}+-- |+-- HTTP POST Binding+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf saml-bindings-2.0-os> §3.5+module SAML2.Bindings.HTTPPOST+ ( encodeValue+ , encodeForm+ , decodeValue+ , decodeForm+ ) where++import Control.Lens ((^.), (.~))+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base64 as Base64+import qualified Data.ByteString.Lazy as BSL+import Data.Maybe (maybeToList)+import Data.Proxy (Proxy(..))++import SAML2.XML+import SAML2.Lens+import qualified SAML2.Core.Protocols as SAMLP+import SAML2.Core.Signature+import SAML2.Bindings.General+import SAML2.Bindings.Internal++encodeValue :: SAMLP.SAMLProtocol a => a -> BS.ByteString+encodeValue = Base64.encode . BSL.toStrict . samlToXML++encodeForm :: SAMLP.SAMLProtocol a => a -> [(BS.ByteString, BS.ByteString)]+encodeForm p =+ (protocolParameter (SAMLP.isSAMLResponse p), encodeValue p)+ : maybeToList ((relayStateParameter, ) <$> SAMLP.relayState (p ^. SAMLP.samlProtocol'))++decodeValue :: SAMLP.SAMLProtocol a => Bool -> BS.ByteString -> IO a+decodeValue verf v = do+ if verf+ then verifySAMLProtocol b+ else either fail return $ xmlToSAML b+ where b = BSL.fromStrict $ Base64.decodeLenient v++decodeForm :: forall a . (SAMLP.SAMLProtocol a) => Bool -> (BS.ByteString -> Maybe BS.ByteString) -> IO a+decodeForm verf f = do+ p <- decodeValue verf =<< maybe (fail "SAML parameter missing") return (lookupProtocolParameter (Proxy :: Proxy a) f)+ return $ SAMLP.samlProtocol' . $(fieldLens 'SAMLP.relayState) .~ (f relayStateParameter) $ p
+ SAML2/Bindings/HTTPRedirect.hs view
@@ -0,0 +1,121 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+-- |+-- HTTP Redirect Binding+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf saml-bindings-2.0-os> §3.4+module SAML2.Bindings.HTTPRedirect + ( encodeQuery+ , encodeHeaders+ , decodeURI+ ) where++import qualified Codec.Compression.Zlib.Raw as DEFLATE+import Control.Lens ((^.), (.~))+import Control.Monad (unless)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base64.Lazy as Base64+import qualified Data.ByteString.Char8 as BSC+import qualified Data.ByteString.Lazy as BSL+import Data.Maybe (fromMaybe, maybeToList)+import Data.Monoid ((<>))+import Data.Proxy (Proxy(..))+import Network.HTTP.Types.Header (ResponseHeaders, hLocation, hCacheControl, hPragma)+import Network.HTTP.Types.URI (Query, renderQuery, urlDecode)+import Network.HTTP.Types.QueryLike (toQuery)+import Network.URI (URI(uriPath), nullURI, uriQuery, parseURIReference)++import SAML2.Lens+import SAML2.XML+import qualified SAML2.XML.Signature as DS+import SAML2.Core.Namespaces+import SAML2.Core.Versioning+import qualified SAML2.Core.Protocols as SAMLP+import SAML2.Bindings.General+import SAML2.Bindings.Internal++data Encoding+ = EncodingDEFLATE+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI Encoding where+ identifier = samlURNIdentifier "bindings:URL-Encoding" . f where+ f EncodingDEFLATE = (SAML20, "DEFLATE")++paramSAML :: Bool -> BS.ByteString+paramSAML = protocolParameter++paramRelayState, paramSignature, paramSignatureAlgorithm, paramEncoding :: BS.ByteString+paramRelayState = relayStateParameter+paramSignature = "Signature"+paramSignatureAlgorithm = "SigAlg"+paramEncoding = "SAMLEncoding"++encodeQuery :: SAMLP.SAMLProtocol a => Maybe DS.SigningKey -> a -> IO Query+encodeQuery sk p = case sk of+ Nothing -> return sq+ Just k -> do+ let sq' = sq ++ toQuery [(paramSignatureAlgorithm, show $ identifier $ DS.signingKeySignatureAlgorithm k)]+ sig <- DS.signBase64 k $ renderQuery False sq'+ return $ sq' ++ toQuery [(paramSignature, sig)]+ where+ p' = SAMLP.samlProtocol' . $(fieldLens 'SAMLP.protocolSignature) .~ Nothing $ p+ pv = Base64.encode+ $ DEFLATE.compressWith DEFLATE.defaultCompressParams{ DEFLATE.compressLevel = DEFLATE.bestCompression }+ $ samlToXML p'+ sq = toQuery $ + (paramSAML $ SAMLP.isSAMLResponse p, BSL.toStrict pv)+ : maybeToList ((paramRelayState, ) <$> SAMLP.relayState (p' ^. SAMLP.samlProtocol'))++httpHeaders :: ResponseHeaders+httpHeaders =+ [ (hCacheControl, "no-cache,no-store")+ , (hPragma, "no cache")+ ]++encodeHeaders :: SAMLP.SAMLProtocol a => Maybe DS.SigningKey -> a -> IO ResponseHeaders+encodeHeaders sk p = do+ q <- encodeQuery sk p+ return $+ (hLocation, BSC.pack $ show (fromMaybe nullURI d){ uriQuery = BSC.unpack $ renderQuery True q })+ : httpHeaders+ where+ d = SAMLP.protocolDestination $ p ^. SAMLP.samlProtocol'++decodeURI :: forall a . SAMLP.SAMLProtocol a => DS.PublicKeys -> URI -> IO a+decodeURI pk ru = do+ pq <- maybe (fail "SAML parameter missing") return $ lookupProtocolParameter (Proxy :: Proxy a) ql+ pd <- case enc of+ Identified EncodingDEFLATE ->+ return $ DEFLATE.decompress $ Base64.decodeLenient $ BSL.fromStrict $ fst pq+ _ -> fail $ "Unsupported HTTP redirect encoding: " ++ show enc+ p <- either fail return $ xmlToSAML pd+ case ql paramSignatureAlgorithm of+ Just (sav, sas) -> do+ sigres $ DS.verifyBase64 pk (reidentify $ puri sav)+ (snd pq <> foldMap (BSC.cons '&' . snd) rsq <> BSC.cons '&' sas)+ (foldMap fst $ ql paramSignature)+ unless (SAMLP.protocolDestination (p ^. SAMLP.samlProtocol') == Just ru{ uriQuery = "" }) $+ fail "Destination incorrect"+ Nothing -> return ()+ return $ SAMLP.samlProtocol' . $(fieldLens 'SAMLP.relayState) .~ (fst <$> rsq) $ p+ where+ qs = BSC.pack $ uriQuery ru+ pqp s = (urlDecode True k, (maybe BSC.empty (urlDecode True . snd) $ BS.uncons v, s)) where+ (k, v) = BSC.break ('=' ==) s+ q = map pqp $ BSC.splitWith (`elem` ['&',';']) $ case BSC.uncons qs of+ Just ('?', qs') -> qs'+ _ -> qs+ ql v = lookup v q+ puri bs = fromMaybe nullURI{ uriPath = s } $ parseURIReference s where s = BSC.unpack bs+ enc = maybe (Identified EncodingDEFLATE) reidentify $ fmap (puri . fst) $ ql paramEncoding+ rsq = ql paramRelayState+ sigres (Just True) = return ()+ sigres (Just False) = fail "Signature verification failed"+ sigres Nothing = fail "Could not verify signature"+
+ SAML2/Bindings/Identifiers.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- |+-- Protocol Bindings identifiers+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf saml-bindings-2.0-os> §3.X.1+module SAML2.Bindings.Identifiers where++import SAML2.XML+import SAML2.Core.Namespaces+import SAML2.Core.Versioning++data Binding+ = BindingSOAP -- ^§3.2+ | BindingPAOS -- ^§3.3+ | BindingHTTPRedirect -- ^§3.4+ | BindingHTTPPOST -- ^§3.5+ | BindingHTTPArtifact -- ^§3.6+ | BindingURI -- ^§3.7+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI Binding where+ identifier = samlURNIdentifier "bindings" . f where+ f BindingSOAP = (SAML20, "SOAP")+ f BindingPAOS = (SAML20, "PAOS")+ f BindingHTTPRedirect = (SAML20, "HTTP-Redirect")+ f BindingHTTPPOST = (SAML20, "HTTP-POST")+ f BindingHTTPArtifact = (SAML20, "HTTP-Artifact")+ f BindingURI = (SAML20, "URI")
+ SAML2/Bindings/Internal.hs view
@@ -0,0 +1,15 @@+module SAML2.Bindings.Internal where++import Control.Applicative ((<|>))+import Data.Proxy (Proxy)+import Data.String (IsString)++import qualified SAML2.Core.Protocols as SAMLP+import SAML2.Bindings.General++lookupProtocolParameter :: (SAMLP.SAMLProtocol m, IsString p) => Proxy m -> (p -> Maybe a) -> Maybe a+lookupProtocolParameter p f =+ case SAMLP.isSAMLResponse_ p of+ Just r -> f (protocolParameter r)+ Nothing -> f (protocolParameter False) <|> f (protocolParameter True)+
+ SAML2/Core.hs view
@@ -0,0 +1,107 @@+-- |+-- Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os>+module SAML2.Core+ ( -- * §1+ samlURN+ , XString+ , AnyURI+ , DateTime+ , ID+ , NCName+ -- * §2+ , SAML.ns+ , BaseID(..)+ , NameID(..)+ , simpleNameID+ , EncryptedNameID+ , Identifier(..)+ , EncryptedID+ , EncryptedElement(..)+ , PossiblyEncrypted(..)+ , AssertionRef(..)+ , Issuer(..)+ , AssertionIDRef(..)+ , Assertion(..)+ , EncryptedAssertion+ , Subject(..)+ , noSubject+ , SubjectConfirmation(..)+ , SubjectConfirmationData(..)+ , Conditions(..)+ , Condition(..)+ , Audience(..)+ , Advice+ , AdviceElement(..)+ , Statement(..)+ , AuthnStatement(..)+ , SubjectLocality(..)+ , AuthnContext(..)+ , AuthnContextDecl(..)+ , AttributeStatement(..)+ , Attribute(..)+ , EncryptedAttribute+ , AuthzDecisionStatement(..)+ , DecisionType(..)+ , Action(..)+ , Evidence(..)+ -- * §3+ , nsP+ , ProtocolType(..)+ , RequestAbstractType(..)+ , StatusResponseType(..)+ , Status(..)+ , StatusCode(..)+ , StatusCode1(..)+ , StatusCode2(..)+ , successStatus+ , AssertionIDRequest(..)+ , SubjectQueryAbstractType(..)+ , AuthnQuery(..)+ , RequestedAuthnContext(..)+ , AuthnContextRefs(..)+ , AuthnContextComparisonType(..)+ , AttributeQuery(..)+ , AuthzDecisionQuery(..)+ , Response(..)+ , AuthnRequest(..)+ , AssertionConsumerService(..)+ , NameIDPolicy(..)+ , Scoping(..)+ , IDPList(..)+ , IDPEntry(..)+ , ArtifactResolve(..)+ , ArtifactResponse(..)+ , ManageNameIDRequest(..)+ , NewID(..)+ , NewEncryptedID+ , ManageNameIDResponse(..)+ , LogoutRequest(..)+ , LogoutResponse(..)+ , LogoutReason(..)+ , NameIDMappingRequest(..)+ , NameIDMappingResponse(..)+ , AnyRequest(..)+ , AnyResponse(..)+ , AnyProtocol(..)+ -- * §4+ , SAMLVersion(..)+ , samlVersion+ -- * §8+ , ActionNamespace(..)+ , AttributeNameFormat(..)+ , NameIDFormat(..)+ , Consent(..)+ ) where++import SAML2.XML.Types+import SAML2.Core.Namespaces+import SAML2.Core.Datatypes+import SAML2.Core.Assertions as SAML+import SAML2.Core.Protocols as SAMLP+import SAML2.Core.Versioning+import SAML2.Core.Identifiers++nsP :: Namespace+nsP = SAMLP.ns
+ SAML2/Core/Assertions.hs view
@@ -0,0 +1,454 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+-- |+-- SAML Assertions+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §2+module SAML2.Core.Assertions where++import qualified Text.XML.HXT.Arrow.Pickle.Schema as XPS++import SAML2.Lens+import SAML2.XML+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Schema as XS+import qualified SAML2.XML.Signature.Types as DS+import qualified SAML2.XML.Encryption as XEnc+import SAML2.Core.Namespaces+import SAML2.Core.Versioning+import SAML2.Core.Identifiers+import SAML2.Profiles.ConfirmationMethod++ns :: Namespace+ns = mkNamespace "" $ samlURN SAML20 ["assertion"]++xpElem :: String -> XP.PU a -> XP.PU a+xpElem = xpTrimElemNS ns++-- |§2.2.1+data BaseID id = BaseID+ { baseNameQualifier :: Maybe XString+ , baseSPNameQualifier :: Maybe XString+ , baseID :: !id+ } deriving (Eq, Show)++xpBaseID :: XP.PU id -> XP.PU (BaseID id)+xpBaseID idp = [XP.biCase|((n, s), i) <-> BaseID n s i|]+ XP.>$< (XP.xpAttrImplied "NameQualifier" XS.xpString+ XP.>*< XP.xpAttrImplied "SPNameQualifier" XS.xpString+ XP.>*< idp)++-- |§2.2.3+data NameID = NameID+ { nameBaseID :: BaseID XString+ , nameIDFormat :: IdentifiedURI NameIDFormat+ , nameSPProvidedID :: Maybe XString+ } deriving (Eq, Show)++simpleNameID :: NameIDFormat -> XString -> NameID+simpleNameID f s = NameID (BaseID Nothing Nothing s) (Identified f) Nothing++xpNameIDDefaulting :: IdentifiedURI NameIDFormat -> XP.PU NameID+xpNameIDDefaulting fmt = [XP.biCase|((f, p), b) <-> NameID b f p|]+ XP.>$< (XP.xpDefault fmt (XP.xpAttr "Format" XP.xpickle)+ XP.>*< XP.xpAttrImplied "SPProvidedID" XS.xpString+ XP.>*< xpBaseID XS.xpString)++xpNameID :: XP.PU NameID+xpNameID = xpNameIDDefaulting $ Identified NameIDFormatUnspecified++instance XP.XmlPickler NameID where+ xpickle = xpElem "NameID" xpNameID++type EncryptedNameID = EncryptedElement NameID++instance XP.XmlPickler EncryptedNameID where+ xpickle = xpElem "EncryptedID" xpEncryptedElement++data Identifier+ = IdentifierName NameID+ | IdentifierBase (BaseID Nodes)+ deriving (Eq, Show)++instance XP.XmlPickler Identifier where+ xpickle = [XP.biCase|+ Left n <-> IdentifierName n+ Right b <-> IdentifierBase b |]+ XP.>$< (XP.xpickle XP.>|< xpElem "BaseID" (xpBaseID XP.xpTrees))++-- |§2.2.4+type EncryptedID = EncryptedElement Identifier++instance XP.XmlPickler EncryptedID where+ xpickle = xpElem "EncryptedID" xpEncryptedElement++data EncryptedElement a = EncryptedElement+ { encryptedData :: XEnc.EncryptedData+ , encryptedKey :: [XEnc.EncryptedKey]+ } deriving (Eq, Show)++xpEncryptedElement :: XP.PU (EncryptedElement a)+xpEncryptedElement = [XP.biCase|(d, k) <-> EncryptedElement d k|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpList XP.xpickle)++data PossiblyEncrypted a+ = NotEncrypted !a+ | SoEncrypted (EncryptedElement a)+ deriving (Eq, Show)++xpPossiblyEncrypted :: (XP.XmlPickler a, XP.XmlPickler (EncryptedElement a)) => XP.PU (PossiblyEncrypted a)+xpPossiblyEncrypted = [XP.biCase|+ Left a <-> NotEncrypted a+ Right a <-> SoEncrypted a |]+ XP.>$< (XP.xpickle XP.>|< XP.xpickle)++data AssertionRef+ = AssertionRefID AssertionIDRef+ | AssertionURIRef AnyURI -- ^§2.3.2+ | AssertionRef (PossiblyEncrypted Assertion)+ deriving (Eq, Show)++instance XP.XmlPickler AssertionRef where+ xpickle = [XP.biCase|+ Left (Left i) <-> AssertionRefID i+ Left (Right u) <-> AssertionURIRef u+ Right a <-> AssertionRef a|]+ XP.>$< (XP.xpickle+ XP.>|< xpElem "AssertionURIRef" XS.xpAnyURI+ XP.>|< xpPossiblyEncrypted)++-- |§2.2.5+newtype Issuer = Issuer{ issuer :: NameID }+ deriving (Eq, Show)++instance XP.XmlPickler Issuer where+ xpickle = xpElem "Issuer" $ [XP.biCase|+ n <-> Issuer n|]+ XP.>$< xpNameIDDefaulting (Identified NameIDFormatEntity)++-- |§2.3.1+newtype AssertionIDRef = AssertionIDRef{ assertionIDRef :: ID }+ deriving (Eq, Show)++instance XP.XmlPickler AssertionIDRef where+ xpickle = xpElem "AssertionIDRef" $ [XP.biCase|+ i <-> AssertionIDRef i|]+ XP.>$< XS.xpID++-- |§2.3.3+data Assertion = Assertion+ { assertionVersion :: SAMLVersion+ , assertionID :: ID+ , assertionIssueInstant :: DateTime+ , assertionIssuer :: Issuer+ , assertionSignature :: Maybe DS.Signature+ , assertionSubject :: Subject -- ^use 'noSubject' to omit+ , assertionConditions :: Maybe Conditions+ , assertionAdvice :: Maybe Advice+ , assertionStatement :: [Statement]+ } deriving (Eq, Show)++instance XP.XmlPickler Assertion where+ xpickle = xpElem "Assertion" $+ [XP.biCase|+ ((((((((v, i), t), n), s), Nothing), c), a), l) <-> Assertion v i t n s (Subject Nothing []) c a l+ ((((((((v, i), t), n), s), Just r), c), a), l) <-> Assertion v i t n s r c a l|] + XP.>$< (XP.xpAttr "Version" XP.xpickle+ XP.>*< XP.xpAttr "ID" XS.xpID+ XP.>*< XP.xpAttr "IssueInstant" XS.xpDateTime+ XP.>*< XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption (xpElem "Advice" $ XP.xpList XP.xpickle)+ XP.>*< XP.xpList XP.xpickle)++instance DS.Signable Assertion where+ signature' = $(fieldLens 'assertionSignature)+ signedID = assertionID++-- |§2.3.4+type EncryptedAssertion = EncryptedElement Assertion++instance XP.XmlPickler EncryptedAssertion where+ xpickle = xpElem "EncryptedAssertion" xpEncryptedElement++-- |§2.4.1+data Subject = Subject+ { subjectIdentifier :: Maybe (PossiblyEncrypted Identifier)+ , subjectConfirmation :: [SubjectConfirmation]+ } deriving (Eq, Show)++instance XP.XmlPickler Subject where+ xpickle = xpElem "Subject" $ [XP.biCase|+ (i, c) <-> Subject i c|]+ XP.>$< (XP.xpOption xpPossiblyEncrypted+ XP.>*< XP.xpList XP.xpickle)++noSubject :: Subject+noSubject = Subject Nothing []++-- |§2.4.1.1+data SubjectConfirmation = SubjectConfirmation+ { subjectConfirmationMethod :: IdentifiedURI ConfirmationMethod+ , subjectConfirmationIdentifier :: Maybe (PossiblyEncrypted Identifier)+ , subjectConfirmationData :: Maybe SubjectConfirmationData+ } deriving (Eq, Show)++instance XP.XmlPickler SubjectConfirmation where+ xpickle = xpElem "SubjectConfirmation" $ [XP.biCase|+ ((m, i), d) <-> SubjectConfirmation m i d|]+ XP.>$< (XP.xpAttr "Method" XP.xpickle+ XP.>*< XP.xpOption xpPossiblyEncrypted+ XP.>*< XP.xpOption XP.xpickle)++-- |§2.4.1.2+data SubjectConfirmationData = SubjectConfirmationData+ { subjectConfirmationNotBefore+ , subjectConfirmationNotOnOrAfter :: Maybe DateTime+ , subjectConfirmationRecipient :: Maybe AnyURI+ , subjectConfirmationInResponseTo :: Maybe ID+ , subjectConfirmationAddress :: Maybe IP+ , subjectConfirmationKeyInfo :: [DS.KeyInfo]+ , subjectConfirmationXML :: Nodes -- ^anything+ } deriving (Eq, Show)++instance XP.XmlPickler SubjectConfirmationData where+ xpickle = xpElem "SubjectConfirmationData" $ [XP.biCase|+ ((((((s, e), r), i), a), k), x) <-> SubjectConfirmationData s e r i a k x|]+ XP.>$< (XP.xpAttrImplied "NotBefore" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "NotOnOrAfter" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "Recipient" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "InResponseTo" XS.xpNCName+ XP.>*< XP.xpAttrImplied "Address" xpIP+ XP.>*< XP.xpList XP.xpickle+ XP.>*< XP.xpAny)++-- |§2.5.1+data Conditions = Conditions+ { conditionsNotBefore+ , conditionsNotOnOrAfter :: Maybe DateTime+ , conditions :: [Condition]+ } deriving (Eq, Show)++instance XP.XmlPickler Conditions where+ xpickle = xpElem "Conditions" $ [XP.biCase|+ ((s, e), c) <-> Conditions s e c|]+ XP.>$< (XP.xpAttrImplied "NotBefore" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "NotOnOrAfter" XS.xpDateTime+ XP.>*< XP.xpList XP.xpickle)++data Condition+ = Condition Node -- ^§2.5.1.3+ | AudienceRestriction (List1 Audience) -- ^§2.5.1.4+ | OneTimeUse -- ^§2.5.1.5+ | ProxyRestriction+ { proxyRestrictionCount :: Maybe XS.NonNegativeInteger+ , proxyRestrictionAudience :: [Audience]+ } -- ^§2.5.1.6+ deriving (Eq, Show)++instance XP.XmlPickler Condition where+ xpickle = [XP.biCase|+ Left (Left (Left a)) <-> AudienceRestriction a+ Left (Left (Right ())) <-> OneTimeUse+ Left (Right (c, a)) <-> ProxyRestriction c a+ Right x <-> Condition x|]+ XP.>$< (xpElem "AudienceRestriction" (xpList1 XP.xpickle)+ XP.>|< xpElem "OneTimeUse" XP.xpUnit+ XP.>|< xpElem "ProxyRestriction"+ (XP.xpAttrImplied "Count" XS.xpNonNegativeInteger+ XP.>*< XP.xpList XP.xpickle)+ XP.>|< xpTrimAnyElem)++-- |§2.5.1.4+newtype Audience = Audience{ audience :: AnyURI }+ deriving (Eq, Show)++instance XP.XmlPickler Audience where+ xpickle = xpElem "Audience" $ [XP.biCase|+ u <-> Audience u|]+ XP.>$< XS.xpAnyURI++-- |§2.6.1+type Advice = [AdviceElement]+data AdviceElement+ = AdviceAssertion AssertionRef+ | Advice Node+ deriving (Eq, Show)++instance XP.XmlPickler AdviceElement where+ xpickle = [XP.biCase|+ Left a <-> AdviceAssertion a+ Right x <-> Advice x|]+ XP.>$< (XP.xpickle+ XP.>|< xpTrimAnyElem)++-- |§2.7.1+data Statement+ = StatementAuthn AuthnStatement+ | StatementAttribute AttributeStatement+ | StatementAuthzDecision AuthzDecisionStatement+ | Statement Node+ deriving (Eq, Show)++instance XP.XmlPickler Statement where+ xpickle = [XP.biCase|+ Left (Left (Left s)) <-> StatementAuthn s+ Left (Left (Right s)) <-> StatementAttribute s+ Left (Right s) <-> StatementAuthzDecision s+ Right x <-> Statement x|]+ XP.>$< (XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< xpTrimAnyElem)++-- |§2.7.2+data AuthnStatement = AuthnStatement+ { authnStatementInstant :: DateTime+ , authnStatementSessionIndex :: Maybe XString+ , authnStatementSessionNotOnOrAfter :: Maybe DateTime+ , authnStatementSubjectLocality :: Maybe SubjectLocality+ , authnStatementContext :: AuthnContext+ } deriving (Eq, Show)++instance XP.XmlPickler AuthnStatement where+ xpickle = xpElem "AuthnStatement" $ [XP.biCase|+ ((((t, i), e), l), c) <-> AuthnStatement t i e l c|]+ XP.>$< (XP.xpAttr "AuthnInstant" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "SessionIndex" XS.xpString+ XP.>*< XP.xpAttrImplied "SessionNotOnOrAfter" XS.xpDateTime+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpickle)++-- |§2.7.2.1+data SubjectLocality = SubjectLocality+ { subjectLocalityAddress :: Maybe IP+ , subjectLocalityDNSName :: Maybe XString+ } deriving (Eq, Show)++instance XP.XmlPickler SubjectLocality where+ xpickle = xpElem "SubjectLocality" $ [XP.biCase|+ (a, d) <-> SubjectLocality a d|]+ XP.>$< (XP.xpAttrImplied "Address" xpIP+ XP.>*< XP.xpAttrImplied "DNSName" XS.xpString)++-- |§2.7.2.2+data AuthnContext = AuthnContext+ { authnContextClassRef :: Maybe AnyURI+ , authnContextDecl :: Maybe AuthnContextDecl+ , authnContextAuthenticatingAuthority :: [AnyURI]+ } deriving (Eq, Show)++instance XP.XmlPickler AuthnContext where+ xpickle = xpElem "AuthnContext" $ [XP.biCase|+ ((c, d), a) <-> AuthnContext c d a|]+ XP.>$< (XP.xpOption (xpElem "AuthnContextClassRef" XS.xpAnyURI)+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpList (xpElem "AuthenticatingAuthority" XS.xpAnyURI))++data AuthnContextDecl+ = AuthnContextDecl Nodes+ | AuthnContextDeclRef AnyURI+ deriving (Eq, Show)++instance XP.XmlPickler AuthnContextDecl where+ xpickle = [XP.biCase|+ Left d <-> AuthnContextDecl d+ Right r <-> AuthnContextDeclRef r|]+ XP.>$< (xpElem "AuthnContextDecl" XP.xpAny+ XP.>|< xpElem "AuthnContextDeclRef" XS.xpAnyURI)++-- |§2.7.3+newtype AttributeStatement = AttributeStatement{ attributeStatement :: List1 (PossiblyEncrypted Attribute) }+ deriving (Eq, Show)++instance XP.XmlPickler AttributeStatement where+ xpickle = xpElem "AttributeStatement" $ [XP.biCase|+ l <-> AttributeStatement l|]+ XP.>$< xpList1 xpPossiblyEncrypted++-- |§2.7.3.1+data Attribute = Attribute+ { attributeName :: XString+ , attributeNameFormat :: IdentifiedURI AttributeNameFormat+ , attributeFriendlyName :: Maybe XString+ , attributeAttrs :: Nodes -- attributes+ , attributeValues :: [Nodes] -- ^§2.7.3.1.1+ } deriving (Eq, Show)++xpAttributeType :: XP.PU Attribute+xpAttributeType = [XP.biCase|+ ((((n, f), u), x), v) <-> Attribute n f u x v|]+ XP.>$< (XP.xpAttr "Name" XS.xpString+ XP.>*< XP.xpDefault (Identified AttributeNameFormatUnspecified) (XP.xpAttr "NameFormat" XP.xpickle)+ XP.>*< XP.xpAttrImplied "FriendlyName" XS.xpString+ XP.>*< XP.xpAnyAttrs+ XP.>*< XP.xpList (xpElem "AttributeValue" XP.xpAny))++instance XP.XmlPickler Attribute where+ xpickle = xpElem "Attribute" xpAttributeType++-- |§2.7.3.2+type EncryptedAttribute = EncryptedElement Attribute++instance XP.XmlPickler EncryptedAttribute where+ xpickle = xpElem "EncryptedAttribute" xpEncryptedElement++-- |§2.7.4+data AuthzDecisionStatement = AuthzDecisionStatement+ { authzDecisionStatementResource :: AnyURI+ , authzDecisionStatementDecision :: DecisionType+ , authzDecisionStatementAction :: List1 Action+ , authzDecisionStatementEvidence :: Evidence+ } deriving (Eq, Show)++instance XP.XmlPickler AuthzDecisionStatement where+ xpickle = xpElem "AuthzDecisionStatement" $ [XP.biCase|+ (((r, d), a), e) <-> AuthzDecisionStatement r d a e|]+ XP.>$< (XP.xpAttr "Resource" XS.xpAnyURI+ XP.>*< XP.xpAttr "Decision" XP.xpickle+ XP.>*< xpList1 XP.xpickle+ XP.>*< XP.xpickle)++-- |§2.7.4.1+data DecisionType+ = DecisionTypePermit+ | DecisionTypeDeny+ | DecisionTypeIndeterminate+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable XString DecisionType where+ identifier DecisionTypePermit = "Permit"+ identifier DecisionTypeDeny = "Deny"+ identifier DecisionTypeIndeterminate = "Indeterminate"+instance XP.XmlPickler DecisionType where+ xpickle = xpIdentifier (XP.xpTextDT (XPS.scDT (namespaceURIString ns) "DecisionType" [])) "DecisionType"++-- |§2.7.4.2+data Action = Action+ { actionNamespace :: IdentifiedURI ActionNamespace+ , action :: XString+ } deriving (Eq, Show)++instance XP.XmlPickler Action where+ xpickle = xpElem "Action" $ [XP.biCase|+ (n, a) <-> Action n a|]+ XP.>$< (XP.xpAttr "Namespace" XP.xpickle+ XP.>*< XP.xpText0)++-- |§2.7.4.3+newtype Evidence = Evidence{ evidence :: [AssertionRef] }+ deriving (Eq, Show, Monoid)++instance XP.XmlPickler Evidence where+ xpickle = [XP.biCase|+ Nothing <-> Evidence []+ Just l <-> Evidence l|]+ XP.>$< XP.xpOption (xpElem "Evidence" $ XP.xpList1 XP.xpickle)
+ SAML2/Core/Datatypes.hs view
@@ -0,0 +1,20 @@+-- |+-- Common Data Types+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §1.3+module SAML2.Core.Datatypes where++import Prelude hiding (String)++import qualified SAML2.XML.Schema.Datatypes as XS++-- |§1.3.1+type XString = XS.String+-- |§1.3.2+type AnyURI = XS.AnyURI+-- |§1.3.3+type DateTime = XS.DateTime+-- |§1.3.4+type ID = XS.ID+-- |§1.3.4+type NCName = XS.NCName
+ SAML2/Core/Identifiers.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- |+-- SAML-Defined Identifiers+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §8+module SAML2.Core.Identifiers where++import Data.Default (Default(..))++import SAML2.XML+import SAML2.Core.Namespaces+import SAML2.Core.Versioning++-- |§8.1+data ActionNamespace+ = ActionNamespaceRWEDC -- ^§8.1.1: Read Write Execute Delete Control+ | ActionNamespaceRWEDCNegation -- ^§8.1.2: RWEDC ~RWEDC+ | ActionNamespaceGHPP -- ^§8.1.3: GET HEAD PUT POST+ | ActionNamespaceUNIX -- ^§8.1.4: octal+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable URI ActionNamespace where+ identifier = samlURNIdentifier "action" . f where+ f ActionNamespaceRWEDC = (SAML10, "rwedc")+ f ActionNamespaceRWEDCNegation = (SAML10, "rwedc-negation")+ f ActionNamespaceGHPP = (SAML10, "ghpp")+ f ActionNamespaceUNIX = (SAML10, "unix")++-- |§8.2+data AttributeNameFormat+ = AttributeNameFormatUnspecified -- ^§8.2.1: Text+ | AttributeNameFormatURI -- ^§8.2.2: URI+ | AttributeNameFormatBasic -- ^§8.2.3: Name+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable URI AttributeNameFormat where+ identifier = samlURNIdentifier "attrname-format" . f where+ f AttributeNameFormatUnspecified = (SAML20, "unspecified")+ f AttributeNameFormatURI = (SAML20, "uri")+ f AttributeNameFormatBasic = (SAML20, "basic")++-- |§8.3+data NameIDFormat+ = NameIDFormatUnspecified -- ^§8.3.1: Text+ | NameIDFormatEmail -- ^§8.3.2: rfc2822+ | NameIDFormatX509 -- ^§8.3.3: XML signature+ | NameIDFormatWindows -- ^§8.3.4: Maybe Domain, User+ | NameIDFormatKerberos -- ^§8.3.5: rfc1510+ | NameIDFormatEntity -- ^§8.3.6: SAML endpoint (BaseId and SPProvidedID must be Nothing)+ | NameIDFormatPersistent -- ^§8.3.7: String <= 256 char (NameQualifier same as idp ident/Nothing, SPNameQualifier same as sp ident/Nothing, SPProvidedID alt ident from sp)+ | NameIDFormatTransient -- ^§8.3.8: String <= 256 char+ | NameIDFormatEncrypted -- ^§3.4.1.1: only for NameIDPolicy+ deriving (Eq, Enum, Bounded, Show)+ +instance Default NameIDFormat where+ def = NameIDFormatUnspecified++instance Identifiable URI NameIDFormat where+ identifier = samlURNIdentifier "nameid-format" . f where+ f NameIDFormatUnspecified = (SAML11, "unspecified")+ f NameIDFormatEmail = (SAML11, "emailAddress")+ f NameIDFormatX509 = (SAML11, "X509SubjectName")+ f NameIDFormatWindows = (SAML11, "WindowsDomainQualifiedName")+ f NameIDFormatKerberos = (SAML20, "kerberos")+ f NameIDFormatEntity = (SAML20, "entity")+ f NameIDFormatPersistent = (SAML20, "persistent")+ f NameIDFormatTransient = (SAML20, "transient")+ f NameIDFormatEncrypted = (SAML20, "encrypted")++-- |§8.4+data Consent+ = ConsentUnspecified -- ^§8.4.1+ | ConsentObtained -- ^§8.4.2+ | ConsentPrior -- ^§8.4.3+ | ConsentImplicit -- ^§8.4.4+ | ConsentExplicit -- ^§8.4.5+ | ConsentUnavailable -- ^§8.4.6+ | ConsentInapplicable -- ^§8.4.7+ deriving (Eq, Enum, Bounded, Show)++instance Default Consent where+ def = ConsentUnspecified++instance Identifiable URI Consent where+ identifier = samlURNIdentifier "consent" . f where+ f ConsentUnspecified = (SAML20, "unspecified")+ f ConsentObtained = (SAML20, "obtained")+ f ConsentPrior = (SAML20, "prior")+ f ConsentImplicit = (SAML20, "current-implicit")+ f ConsentExplicit = (SAML20, "current-explicit")+ f ConsentUnavailable = (SAML20, "unavailable")+ f ConsentInapplicable = (SAML20, "inapplicable")
+ SAML2/Core/Namespaces.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE FlexibleContexts #-}+-- |+-- Schema Organization and Namespaces+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §1.2+module SAML2.Core.Namespaces + ( samlURN+ , samlURNIdentifier+ ) where++import Data.Monoid ((<>))+import Network.URI (URI(..))++import SAML2.Core.Versioning++samlURN :: SAMLVersion -> [String] -> URI+samlURN v l = URI+ { uriScheme = "urn:"+ , uriAuthority = Nothing+ , uriPath = "oasis:names:tc:SAML" <> concatMap (':':) (show v : l)+ , uriQuery = ""+ , uriFragment = ""+ }++samlURNIdentifier :: String -> (SAMLVersion, String) -> URI+samlURNIdentifier t (v, n) = samlURN v [t, n]++-- samlURNIdentifier :: String -> (a -> (SAMLVersion, String)) -> (a -> samlURN)+-- samlURNIdentifier = (.) . uncurry . samlURNIdentifier++-- xpSAMLURNIdentifier :: Identifiable URI a => String -> XP.PU a+-- xpSAMLURNIdentifier = xpIdentifier XS.xpAnyURI
+ SAML2/Core/Protocols.hs view
@@ -0,0 +1,765 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- |+-- SAML Protocols+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §3+module SAML2.Core.Protocols where++import Control.Lens (Lens', lens, (.~), (^.), view)+import Data.Maybe (fromMaybe)+import Data.Proxy (Proxy, asProxyTypeOf)+import qualified Text.XML.HXT.Arrow.Pickle.Schema as XPS++import SAML2.Lens+import SAML2.XML+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Schema as XS+import qualified SAML2.XML.Signature.Types as DS+import SAML2.Core.Namespaces+import SAML2.Core.Versioning+import qualified SAML2.Core.Assertions as SAML+import SAML2.Core.Identifiers+import SAML2.Bindings.General (RelayState)+import SAML2.Bindings.Identifiers++ns :: Namespace+ns = mkNamespace "samlp" $ samlURN SAML20 ["protocol"]++xpElem :: String -> XP.PU a -> XP.PU a+xpElem = xpTrimElemNS ns++data ProtocolType = ProtocolType+ { protocolID :: XS.ID+ , protocolVersion :: SAMLVersion+ , protocolIssueInstant :: DateTime+ , protocolDestination :: Maybe AnyURI+ , protocolConsent :: IdentifiedURI Consent+ , protocolIssuer :: Maybe SAML.Issuer+ , protocolSignature :: Maybe DS.Signature+ , protocolExtensions :: [Node]+ , relayState :: Maybe RelayState -- ^out-of-band data, not part of XML+ } deriving (Eq, Show)++instance XP.XmlPickler ProtocolType where+ xpickle = XP.xpWrap (pt, tp)+ $ (XP.xpAttr "ID" XS.xpID+ XP.>*< XP.xpAttr "Version" XP.xpickle+ XP.>*< XP.xpAttr "IssueInstant" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "Destination" XS.xpAnyURI+ XP.>*< XP.xpDefault (Identified ConsentUnspecified) (XP.xpAttr "Consent" XP.xpickle)+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption (xpElem "Extensions" $ XP.xpList1 xpTrimAnyElem))+ where+ pt (((((((i, v), t), d), c), u), g), x) = ProtocolType i v t d c u g (fromMaybe [] x) Nothing+ tp (ProtocolType i v t d c u g [] _) = (((((((i, v), t), d), c), u), g), Nothing)+ tp (ProtocolType i v t d c u g x _) = (((((((i, v), t), d), c), u), g), Just x)++instance DS.Signable ProtocolType where+ signature' = $(fieldLens 'protocolSignature)+ signedID = protocolID+class (XP.XmlPickler a, DS.Signable a, Show a) => SAMLProtocol a where+ samlProtocol' :: Lens' a ProtocolType+ isSAMLResponse :: a -> Bool+ isSAMLResponse_ :: Proxy a -> Maybe Bool+ isSAMLResponse_ = Just . isSAMLResponse . asProxyTypeOf undefined ++-- |§3.2.1+newtype RequestAbstractType = RequestAbstractType+ { requestProtocol :: ProtocolType+ } deriving (Eq, Show)++instance XP.XmlPickler RequestAbstractType where+ xpickle = [XP.biCase|p <-> RequestAbstractType p|]+ XP.>$< XP.xpickle++class SAMLProtocol a => SAMLRequest a where+ samlRequest' :: Lens' a RequestAbstractType++requestProtocol' :: Lens' RequestAbstractType ProtocolType+requestProtocol' = $(fieldLens 'requestProtocol)++-- |§3.2.2+data StatusResponseType = StatusResponseType+ { statusProtocol :: !ProtocolType+ , statusInResponseTo :: Maybe XS.NCName+ , status :: Status+ } deriving (Eq, Show)++instance XP.XmlPickler StatusResponseType where+ xpickle = [XP.biCase|((p, r), s) <-> StatusResponseType p r s|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpAttrImplied "InResponseTo" XS.xpNCName+ XP.>*< XP.xpickle)++class SAMLProtocol a => SAMLResponse a where+ samlResponse' :: Lens' a StatusResponseType++statusProtocol' :: Lens' StatusResponseType ProtocolType+statusProtocol' = $(fieldLens 'statusProtocol)++-- |§3.2.2.1+data Status = Status+ { statusCode :: StatusCode+ , statusMessage :: Maybe XString -- ^§3.2.2.3+ , statusDetail :: Maybe Nodes -- ^§3.2.2.4+ } deriving (Eq, Show)++instance XP.XmlPickler Status where+ xpickle = xpElem "Status" $ [XP.biCase|+ ((c, m), d) <-> Status c m d|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpOption (xpElem "StatusMessage" XP.xpText0)+ XP.>*< XP.xpOption (xpElem "StatusDetail" XP.xpAnyCont))++-- |§3.2.2.2+data StatusCode = StatusCode+ { statusCode1 :: StatusCode1+ , statusCodes :: [IdentifiedURI StatusCode2]+ } deriving (Eq, Show)++instance XP.XmlPickler StatusCode where+ xpickle = xpElem "StatusCode" $ [XP.biCase|+ (v, c) <-> StatusCode v c|]+ XP.>$< (XP.xpAttr "Value" XP.xpickle+ XP.>*< xpStatusCodes) where+ xpStatusCodes = [XP.biCase|+ Nothing <-> []+ Just (v, c) <-> v : c|]+ XP.>$< XP.xpOption (xpElem "StatusCode" $+ XP.xpAttr "Value" XP.xpickle+ XP.>*< xpStatusCodes)++data StatusCode1+ = StatusSuccess+ | StatusRequester+ | StatusResponder+ | StatusVersionMismatch+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI StatusCode1 where+ identifier = samlURNIdentifier "status" . f where+ f StatusSuccess = (SAML20, "Success")+ f StatusRequester = (SAML20, "Requester")+ f StatusResponder = (SAML20, "Responder")+ f StatusVersionMismatch = (SAML20, "VersionMismatch")+instance XP.XmlPickler StatusCode1 where+ xpickle = xpIdentifier XS.xpAnyURI "status"++data StatusCode2+ = StatusAuthnFailed+ | StatusInvalidAttrNameOrValue + | StatusInvalidNameIDPolicy + | StatusNoAuthnContext + | StatusNoAvailableIDP + | StatusNoPassive + | StatusNoSupportedIDP + | StatusPartialLogout + | StatusProxyCountExceeded + | StatusRequestDenied + | StatusRequestUnsupported + | StatusRequestVersionDeprecated+ | StatusRequestVersionTooHigh + | StatusRequestVersionTooLow + | StatusResourceNotRecognized + | StatusTooManyResponses + | StatusUnknownAttrProfile + | StatusUnknownPrincipal + | StatusUnsupportedBinding + deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI StatusCode2 where+ identifier = samlURNIdentifier "status" . f where+ f StatusAuthnFailed = (SAML20, "AuthnFailed")+ f StatusInvalidAttrNameOrValue = (SAML20, "InvalidAttrNameOrValue")+ f StatusInvalidNameIDPolicy = (SAML20, "InvalidNameIDPolicy")+ f StatusNoAuthnContext = (SAML20, "NoAuthnContext")+ f StatusNoAvailableIDP = (SAML20, "NoAvailableIDP")+ f StatusNoPassive = (SAML20, "NoPassive")+ f StatusNoSupportedIDP = (SAML20, "NoSupportedIDP")+ f StatusPartialLogout = (SAML20, "PartialLogout")+ f StatusProxyCountExceeded = (SAML20, "ProxyCountExceeded")+ f StatusRequestDenied = (SAML20, "RequestDenied")+ f StatusRequestUnsupported = (SAML20, "RequestUnsupported")+ f StatusRequestVersionDeprecated = (SAML20, "RequestVersionDeprecated")+ f StatusRequestVersionTooHigh = (SAML20, "RequestVersionTooHigh")+ f StatusRequestVersionTooLow = (SAML20, "RequestVersionTooLow")+ f StatusResourceNotRecognized = (SAML20, "ResourceNotRecognized")+ f StatusTooManyResponses = (SAML20, "TooManyResponses")+ f StatusUnknownAttrProfile = (SAML20, "UnknownAttrProfile")+ f StatusUnknownPrincipal = (SAML20, "UnknownPrincipal")+ f StatusUnsupportedBinding = (SAML20, "UnsupportedBinding")++successStatus :: Status+successStatus = Status (StatusCode StatusSuccess []) Nothing Nothing++-- |§3.3.1+data AssertionIDRequest = AssertionIDRequest+ { assertionIDRequest :: !RequestAbstractType+ , assertionIDRequestRef :: List1 (SAML.AssertionIDRef)+ } deriving (Eq, Show)++instance XP.XmlPickler AssertionIDRequest where+ xpickle = xpElem "AssertionIDRequest" $ [XP.biCase|+ (q, r) <-> AssertionIDRequest q r|]+ XP.>$< (XP.xpickle+ XP.>*< xpList1 XP.xpickle)+instance DS.Signable AssertionIDRequest where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AssertionIDRequest where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest AssertionIDRequest where+ samlRequest' = $(fieldLens 'assertionIDRequest)++-- |§3.3.2.1+data SubjectQueryAbstractType = SubjectQueryAbstractType+ { subjectQuery :: !RequestAbstractType+ , subjectQuerySubject :: SAML.Subject+ } deriving (Eq, Show)++instance XP.XmlPickler SubjectQueryAbstractType where+ xpickle = [XP.biCase|+ (q, r) <-> SubjectQueryAbstractType q r|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpickle)++subjectQuery' :: Lens' SubjectQueryAbstractType RequestAbstractType+subjectQuery' = $(fieldLens 'subjectQuery)++-- |§3.3.2.2+data AuthnQuery = AuthnQuery+ { authnQuery :: !SubjectQueryAbstractType+ , authnQuerySessionIndex :: Maybe XString+ , authnQueryRequestedAuthnContext :: Maybe RequestedAuthnContext+ } deriving (Eq, Show)++instance XP.XmlPickler AuthnQuery where+ xpickle = xpElem "AuthnQuery" $ [XP.biCase|+ ((q, i), c) <-> AuthnQuery q i c|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpAttrImplied "SessionIndex" XS.xpString+ XP.>*< XP.xpOption XP.xpickle)+instance DS.Signable AuthnQuery where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AuthnQuery where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest AuthnQuery where+ samlRequest' = authnQuery' . subjectQuery' where+ authnQuery' = $(fieldLens 'authnQuery)++-- |§3.3.2.2.1+data RequestedAuthnContext = RequestedAuthnContext+ { requestedAuthnContextComparison :: Maybe AuthnContextComparisonType+ , requestedAuthnContextRefs :: AuthnContextRefs+ } deriving (Eq, Show)++instance XP.XmlPickler RequestedAuthnContext where+ xpickle = xpElem "RequestedAuthnContext" $ [XP.biCase|+ (c, r) <-> RequestedAuthnContext c r|]+ XP.>$< (XP.xpAttrImplied "Comparison" XP.xpickle+ XP.>*< XP.xpickle)++data AuthnContextRefs+ = AuthnContextClassRefs (List1 AnyURI)+ | AuthnContextDeclRefs (List1 AnyURI)+ deriving (Eq, Show)++instance XP.XmlPickler AuthnContextRefs where+ xpickle = [XP.biCase|+ Left l <-> AuthnContextClassRefs l+ Right l <-> AuthnContextDeclRefs l|]+ XP.>$< (xpList1 (SAML.xpElem "AuthnContextClassRef" XS.xpAnyURI)+ XP.>|< xpList1 (SAML.xpElem "AuthnContextDeclRef" XS.xpAnyURI))++data AuthnContextComparisonType+ = ComparisonExact+ | ComparisonMinimum+ | ComparisonMaximum+ | ComparisonBetter+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable XString AuthnContextComparisonType where+ identifier ComparisonExact = "exact"+ identifier ComparisonMinimum = "minimum"+ identifier ComparisonMaximum = "maximum"+ identifier ComparisonBetter = "better"+instance XP.XmlPickler AuthnContextComparisonType where+ xpickle = xpIdentifier (XP.xpTextDT (XPS.scDT (namespaceURIString ns) "AuthnContextComparisonType" [])) "AuthnContextComparisonType"++-- |§3.3.2.3+data AttributeQuery = AttributeQuery+ { attributeQuery :: !SubjectQueryAbstractType+ , attributeQueryAttributes :: [SAML.Attribute]+ } deriving (Eq, Show)++instance XP.XmlPickler AttributeQuery where+ xpickle = xpElem "AttributeQuery" $ [XP.biCase|+ (q, a) <-> AttributeQuery q a|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpList XP.xpickle)+instance DS.Signable AttributeQuery where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AttributeQuery where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest AttributeQuery where+ samlRequest' = attributeQuery' . subjectQuery' where+ attributeQuery' = $(fieldLens 'attributeQuery)++-- |§3.3.2.4+data AuthzDecisionQuery = AuthzDecisionQuery+ { authzDecisionQuery :: !SubjectQueryAbstractType+ , authzDecisionQueryResource :: AnyURI+ , authzDecisionQueryActions :: [SAML.Action]+ , authzDecisionQueryEvidence :: SAML.Evidence+ } deriving (Eq, Show)++instance XP.XmlPickler AuthzDecisionQuery where+ xpickle = xpElem "AuthzDecisionQuery" $ [XP.biCase|+ (((q, r), a), e) <-> AuthzDecisionQuery q r a e|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpAttr "Resource" XS.xpAnyURI+ XP.>*< XP.xpList XP.xpickle+ XP.>*< XP.xpickle)+instance DS.Signable AuthzDecisionQuery where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AuthzDecisionQuery where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest AuthzDecisionQuery where+ samlRequest' = authzDecisionQuery' . subjectQuery' where+ authzDecisionQuery' = $(fieldLens 'authzDecisionQuery)++-- |§3.3.3+data Response = Response+ { response :: !StatusResponseType+ , responseAssertions :: [SAML.PossiblyEncrypted SAML.Assertion]+ } deriving (Eq, Show)++instance XP.XmlPickler Response where+ xpickle = xpElem "Response" $ [XP.biCase|+ (r, a) <-> Response r a|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpList SAML.xpPossiblyEncrypted)+instance DS.Signable Response where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol Response where+ samlProtocol' = samlResponse' . statusProtocol'+ isSAMLResponse _ = True+instance SAMLResponse Response where+ samlResponse' = $(fieldLens 'response)++-- |§3.4.1+data AuthnRequest = AuthnRequest+ { authnRequest :: !RequestAbstractType+ , authnRequestForceAuthn :: XS.Boolean+ , authnRequestIsPassive :: XS.Boolean+ , authnRequestAssertionConsumerService :: AssertionConsumerService+ , authnRequestAssertionConsumingServiceIndex :: Maybe XS.UnsignedShort+ , authnRequestProviderName :: Maybe XString+ , authnRequestSubject :: Maybe SAML.Subject+ , authnRequestNameIDPolicy :: Maybe NameIDPolicy+ , authnRequestConditions :: Maybe SAML.Conditions+ , authnRequestRequestedAuthnContext :: Maybe RequestedAuthnContext+ , authnRequestScoping :: Maybe Scoping+ } deriving (Eq, Show)++data AssertionConsumerService+ = AssertionConsumerServiceIndex XS.UnsignedShort+ | AssertionConsumerServiceURL+ { authnRequestAssertionConsumerServiceURL :: Maybe AnyURI+ , authnRequestProtocolBinding :: Maybe (IdentifiedURI Binding)+ }+ deriving (Eq, Show)++instance XP.XmlPickler AuthnRequest where+ xpickle = xpElem "AuthnRequest" $ [XP.biCase|+ ((((((((((q, f), p), Left i), g), n), s), np), c), r), sc) <-> AuthnRequest q f p (AssertionConsumerServiceIndex i) g n s np c r sc+ ((((((((((q, f), p), Right (u, b)), g), n), s), np), c), r), sc) <-> AuthnRequest q f p (AssertionConsumerServiceURL u b) g n s np c r sc|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpDefault False (XP.xpAttr "ForceAuthn" XS.xpBoolean)+ XP.>*< XP.xpDefault False (XP.xpAttr "IsPassive" XS.xpBoolean)+ XP.>*< (XP.xpAttr "AssertionConsumerServiceIndex" XS.xpUnsignedShort+ XP.>|< (XP.xpAttrImplied "AssertionConsumerServiceURL" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "ProtocolBinding" XP.xpickle))+ XP.>*< XP.xpAttrImplied "AttributeConsumingServiceIndex" XS.xpUnsignedShort+ XP.>*< XP.xpAttrImplied "ProviderName" XS.xpString+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle)+instance DS.Signable AuthnRequest where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AuthnRequest where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest AuthnRequest where+ samlRequest' = $(fieldLens 'authnRequest)++-- |§3.4.1.1+data NameIDPolicy = NameIDPolicy+ { nameIDPolicyFormat :: IdentifiedURI NameIDFormat+ , nameIDPolicySPNameQualifier :: Maybe XString+ , nameIDPolicyAllowCreate :: Bool+ } deriving (Eq, Show)++instance XP.XmlPickler NameIDPolicy where+ xpickle = xpElem "NameIDPolicy" $ [XP.biCase|+ ((f, q), c) <-> NameIDPolicy f q c|]+ XP.>$< (XP.xpDefault (Identified NameIDFormatUnspecified) (XP.xpAttr "Format" XP.xpickle)+ XP.>*< XP.xpAttrImplied "SPNameQualifier" XS.xpString+ XP.>*< XP.xpDefault False (XP.xpAttr "AllowCreate" XS.xpBoolean))++-- |§3.4.1.2+data Scoping = Scoping+ { scopingProxyCount :: Maybe XS.NonNegativeInteger+ , scopingIDPList :: Maybe IDPList+ , scopingRequesterID :: [AnyURI]+ } deriving (Eq, Show)++instance XP.XmlPickler Scoping where+ xpickle = xpElem "Scoping" $ [XP.biCase|+ ((c, i), r) <-> Scoping c i r|]+ XP.>$< (XP.xpAttrImplied "ProxyCount" XS.xpNonNegativeInteger+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpList (xpElem "RequesterID" XS.xpAnyURI))++-- |§3.4.1.3+data IDPList = IDPList+ { idpList :: List1 IDPEntry+ , idpGetComplete :: Maybe AnyURI+ } deriving (Eq, Show)++instance XP.XmlPickler IDPList where+ xpickle = xpElem "IDPList" $ [XP.biCase|+ (l, c) <-> IDPList l c|]+ XP.>$< (xpList1 XP.xpickle+ XP.>*< XP.xpOption (xpElem "GetComplete" XS.xpAnyURI))++-- |§3.4.1.3.1+data IDPEntry = IDPEntry+ { idpEntryProviderID :: AnyURI+ , idpEntryName :: Maybe XString+ , idpEntryLoc :: Maybe AnyURI+ } deriving (Eq, Show)++instance XP.XmlPickler IDPEntry where+ xpickle = xpElem "IDPEntry" $ [XP.biCase|+ ((p, n), l) <-> IDPEntry p n l|]+ XP.>$< (XP.xpAttr "ProviderID" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "Name" XS.xpString+ XP.>*< XP.xpAttrImplied "Loc" XS.xpAnyURI)++-- |§3.5.1+data ArtifactResolve = ArtifactResolve+ { artifactResolve :: !RequestAbstractType+ , artifactResolveArtifact :: XString+ } deriving (Eq, Show)++instance XP.XmlPickler ArtifactResolve where+ xpickle = xpElem "ArtifactResolve" $ [XP.biCase|+ (r, a) <-> ArtifactResolve r a|]+ XP.>$< (XP.xpickle+ XP.>*< xpElem "Artifact" XS.xpString)+instance DS.Signable ArtifactResolve where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol ArtifactResolve where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest ArtifactResolve where+ samlRequest' = $(fieldLens 'artifactResolve)++-- |§3.5.2+data ArtifactResponse = ArtifactResponse+ { artifactResponse :: !StatusResponseType+ , artifactResponseMessage :: Maybe Node+ } deriving (Eq, Show)++instance XP.XmlPickler ArtifactResponse where+ xpickle = xpElem "ArtifactResponse" $ [XP.biCase|+ (r, a) <-> ArtifactResponse r a|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpOption xpTrimAnyElem)+instance DS.Signable ArtifactResponse where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol ArtifactResponse where+ samlProtocol' = samlResponse' . statusProtocol'+ isSAMLResponse _ = True+instance SAMLResponse ArtifactResponse where+ samlResponse' = $(fieldLens 'artifactResponse)++-- |§3.6.1+data ManageNameIDRequest = ManageNameIDRequest+ { manageNameIDRequest :: !RequestAbstractType+ , manageNameIDRequestNameID :: SAML.PossiblyEncrypted SAML.NameID+ , manageNameIDRequestNewID :: Maybe (SAML.PossiblyEncrypted NewID)+ } deriving (Eq, Show)++instance XP.XmlPickler ManageNameIDRequest where+ xpickle = xpElem "ManageNameIDRequest" $ [XP.biCase|+ ((r, o), Left n) <-> ManageNameIDRequest r o (Just n)+ ((r, o), Right ()) <-> ManageNameIDRequest r o Nothing|]+ XP.>$< (XP.xpickle+ XP.>*< SAML.xpPossiblyEncrypted+ XP.>*< (SAML.xpPossiblyEncrypted+ XP.>|< xpElem "Terminate" XP.xpUnit))+instance DS.Signable ManageNameIDRequest where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol ManageNameIDRequest where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest ManageNameIDRequest where+ samlRequest' = $(fieldLens 'manageNameIDRequest)++newtype NewID = NewID{ newID :: XString }+ deriving (Eq, Show)++instance XP.XmlPickler NewID where+ xpickle = xpElem "NewID" $ [XP.biCase|+ n <-> NewID n|]+ XP.>$< XS.xpString++type NewEncryptedID = SAML.EncryptedElement NewID++instance XP.XmlPickler NewEncryptedID where+ xpickle = xpElem "NewEncryptedID" SAML.xpEncryptedElement++-- |§3.6.2+newtype ManageNameIDResponse = ManageNameIDResponse+ { manageNameIDResponse :: StatusResponseType }+ deriving (Eq, Show)++instance XP.XmlPickler ManageNameIDResponse where+ xpickle = xpElem "ManageNameIDResponse" $ [XP.biCase|+ r <-> ManageNameIDResponse r|]+ XP.>$< XP.xpickle+instance DS.Signable ManageNameIDResponse where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol ManageNameIDResponse where+ samlProtocol' = samlResponse' . statusProtocol'+ isSAMLResponse _ = True+instance SAMLResponse ManageNameIDResponse where+ samlResponse' = $(fieldLens 'manageNameIDResponse)++-- |§3.7.1+data LogoutRequest = LogoutRequest+ { logoutRequest :: !RequestAbstractType+ , logoutRequestReason :: Maybe (Identified XString LogoutReason)+ , logoutRequestNotOnOrAfter :: Maybe XS.DateTime+ , logoutRequestIdentifier :: SAML.PossiblyEncrypted SAML.Identifier+ , logoutRequestSessionIndex :: Maybe XString+ } deriving (Eq, Show)++instance XP.XmlPickler LogoutRequest where+ xpickle = xpElem "LogoutRequest" $ [XP.biCase|+ ((((q, r), t), i), s) <-> LogoutRequest q r t i s|]+ XP.>$< (XP.xpickle+ XP.>*< XP.xpAttrImplied "Reason" XP.xpickle+ XP.>*< XP.xpAttrImplied "NotOnOrAfter" XS.xpDateTime+ XP.>*< SAML.xpPossiblyEncrypted+ XP.>*< XP.xpOption (xpElem "SessionIndex" XS.xpString))+instance DS.Signable LogoutRequest where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol LogoutRequest where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest LogoutRequest where+ samlRequest' = $(fieldLens 'logoutRequest)++-- |§3.7.2+newtype LogoutResponse = LogoutResponse+ { logoutResponse :: StatusResponseType }+ deriving (Eq, Show)++instance XP.XmlPickler LogoutResponse where+ xpickle = xpElem "LogoutResponse" $ [XP.biCase|+ r <-> LogoutResponse r|]+ XP.>$< XP.xpickle+instance DS.Signable LogoutResponse where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol LogoutResponse where+ samlProtocol' = samlResponse' . statusProtocol'+ isSAMLResponse _ = True+instance SAMLResponse LogoutResponse where+ samlResponse' = $(fieldLens 'logoutResponse)++-- |§3.7.3+data LogoutReason+ = LogoutReasonUser+ | LogoutReasonAdmin+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable XString LogoutReason where+ identifier = show . samlURNIdentifier "logout" . f where+ f LogoutReasonUser = (SAML20, "user")+ f LogoutReasonAdmin = (SAML20, "admin")+instance XP.XmlPickler (Identified XString LogoutReason) where+ xpickle = xpIdentified XS.xpString++-- |§3.8.1+data NameIDMappingRequest = NameIDMappingRequest+ { nameIDMappingRequest :: !RequestAbstractType+ , nameIDMappingRequestIdentifier :: SAML.PossiblyEncrypted SAML.Identifier+ , nameIDMappingRequestPolicy :: NameIDPolicy+ } deriving (Eq, Show)++instance XP.XmlPickler NameIDMappingRequest where+ xpickle = xpElem "NameIDMappingRequest" $ [XP.biCase|+ ((r, i), p) <-> NameIDMappingRequest r i p|]+ XP.>$< (XP.xpickle+ XP.>*< SAML.xpPossiblyEncrypted+ XP.>*< XP.xpickle)+instance DS.Signable NameIDMappingRequest where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol NameIDMappingRequest where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest NameIDMappingRequest where+ samlRequest' = $(fieldLens 'nameIDMappingRequest)++-- |§3.8.2+data NameIDMappingResponse = NameIDMappingResponse+ { nameIDMappingResponse :: !StatusResponseType+ , nameIDMappingResponseNameID :: SAML.PossiblyEncrypted SAML.NameID+ } deriving (Eq, Show)++instance XP.XmlPickler NameIDMappingResponse where+ xpickle = xpElem "NameIDMappingResponse" $ [XP.biCase|+ (r, a) <-> NameIDMappingResponse r a|]+ XP.>$< (XP.xpickle+ XP.>*< SAML.xpPossiblyEncrypted)+instance DS.Signable NameIDMappingResponse where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol NameIDMappingResponse where+ samlProtocol' = samlResponse' . statusProtocol'+ isSAMLResponse _ = True+instance SAMLResponse NameIDMappingResponse where+ samlResponse' = $(fieldLens 'nameIDMappingResponse)++data AnyRequest+ = RequestAssertionIDRequest !AssertionIDRequest+ | RequestAuthnQuery !AuthnQuery + | RequestAttributeQuery !AttributeQuery + | RequestAuthzDecisionQuery !AuthzDecisionQuery + | RequestAuthnRequest !AuthnRequest+ | RequestArtifactResolve !ArtifactResolve + | RequestManageNameIDRequest !ManageNameIDRequest + | RequestLogoutRequest !LogoutRequest + | RequestNameIDMappingRequest !NameIDMappingRequest + deriving (Eq, Show)++instance XP.XmlPickler AnyRequest where+ xpickle = [XP.biCase|+ Left (Left (Left (Left (Left (Left (Left (Left r))))))) <-> RequestAssertionIDRequest r+ Left (Left (Left (Left (Left (Left (Left (Right r))))))) <-> RequestAuthnQuery r+ Left (Left (Left (Left (Left (Left (Right r)))))) <-> RequestAttributeQuery r+ Left (Left (Left (Left (Left (Right r))))) <-> RequestAuthzDecisionQuery r+ Left (Left (Left (Left (Right r)))) <-> RequestAuthnRequest r+ Left (Left (Left (Right r))) <-> RequestArtifactResolve r+ Left (Left (Right r)) <-> RequestManageNameIDRequest r+ Left (Right r) <-> RequestLogoutRequest r+ Right r <-> RequestNameIDMappingRequest r|]+ XP.>$< (XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle)+instance DS.Signable AnyRequest where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AnyRequest where+ samlProtocol' = samlRequest' . requestProtocol'+ isSAMLResponse _ = False+instance SAMLRequest AnyRequest where+ samlRequest' = lens g s where+ g (RequestAssertionIDRequest r) = r ^. samlRequest'+ g (RequestAuthnQuery r) = r ^. samlRequest'+ g (RequestAttributeQuery r) = r ^. samlRequest'+ g (RequestAuthzDecisionQuery r) = r ^. samlRequest'+ g (RequestAuthnRequest r) = r ^. samlRequest'+ g (RequestArtifactResolve r) = r ^. samlRequest'+ g (RequestManageNameIDRequest r) = r ^. samlRequest'+ g (RequestLogoutRequest r) = r ^. samlRequest'+ g (RequestNameIDMappingRequest r) = r ^. samlRequest'+ s (RequestAssertionIDRequest r) q = RequestAssertionIDRequest $ samlRequest' .~ q $ r+ s (RequestAuthnQuery r) q = RequestAuthnQuery $ samlRequest' .~ q $ r+ s (RequestAttributeQuery r) q = RequestAttributeQuery $ samlRequest' .~ q $ r+ s (RequestAuthzDecisionQuery r) q = RequestAuthzDecisionQuery $ samlRequest' .~ q $ r+ s (RequestAuthnRequest r) q = RequestAuthnRequest $ samlRequest' .~ q $ r+ s (RequestArtifactResolve r) q = RequestArtifactResolve $ samlRequest' .~ q $ r+ s (RequestManageNameIDRequest r) q = RequestManageNameIDRequest $ samlRequest' .~ q $ r+ s (RequestLogoutRequest r) q = RequestLogoutRequest $ samlRequest' .~ q $ r+ s (RequestNameIDMappingRequest r) q = RequestNameIDMappingRequest $ samlRequest' .~ q $ r++data AnyResponse+ = ResponseResponse !Response + | ResponseArtifactResponse !ArtifactResponse + deriving (Eq, Show)++instance XP.XmlPickler AnyResponse where+ xpickle = [XP.biCase|+ Left r <-> ResponseResponse r+ Right r <-> ResponseArtifactResponse r|]+ XP.>$< (XP.xpickle+ XP.>|< XP.xpickle)+instance DS.Signable AnyResponse where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AnyResponse where+ samlProtocol' = samlResponse' . statusProtocol'+ isSAMLResponse _ = True+instance SAMLResponse AnyResponse where+ samlResponse' = lens g s where+ g (ResponseResponse r) = r ^. samlResponse'+ g (ResponseArtifactResponse r) = r ^. samlResponse'+ s (ResponseResponse r) q = ResponseResponse $ samlResponse' .~ q $ r+ s (ResponseArtifactResponse r) q = ResponseArtifactResponse $ samlResponse' .~ q $ r+ +data AnyProtocol+ = ProtocolRequest !AnyRequest+ | ProtocolResponse !AnyResponse+ deriving (Eq, Show)++instance XP.XmlPickler AnyProtocol where+ xpickle = [XP.biCase|+ Left r <-> ProtocolRequest r+ Right r <-> ProtocolResponse r|]+ XP.>$< (XP.xpickle+ XP.>|< XP.xpickle)+instance DS.Signable AnyProtocol where+ signature' = samlProtocol' . DS.signature'+ signedID = protocolID . view samlProtocol'+instance SAMLProtocol AnyProtocol where+ samlProtocol' = lens g s where+ g (ProtocolRequest r) = r ^. samlProtocol'+ g (ProtocolResponse r) = r ^. samlProtocol'+ s (ProtocolRequest r) q = ProtocolRequest $ samlProtocol' .~ q $ r+ s (ProtocolResponse r) q = ProtocolResponse $ samlProtocol' .~ q $ r+ isSAMLResponse (ProtocolRequest _) = False+ isSAMLResponse (ProtocolResponse _) = True+ isSAMLResponse_ _ = Nothing
+ SAML2/Core/Signature.hs view
@@ -0,0 +1,55 @@+-- |+-- SAML and XML Signature Syntax and Processing+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §5+module SAML2.Core.Signature+ ( signSAMLProtocol+ , verifySAMLProtocol+ ) where++import Control.Lens ((^.), (.~))+import Control.Monad (unless)+import qualified Data.ByteString.Lazy as BSL+import Data.List.NonEmpty (NonEmpty((:|)))+import Network.URI (URI(uriFragment), nullURI)++import SAML2.XML+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Canonical as C14N+import qualified SAML2.XML.Signature as DS+import qualified SAML2.Core.Protocols as SAMLP++signSAMLProtocol :: SAMLP.SAMLProtocol a => DS.SigningKey -> a -> IO a+signSAMLProtocol sk m = do+ r <- DS.generateReference DS.Reference+ { DS.referenceId = Nothing+ , DS.referenceURI = Just nullURI{ uriFragment = '#':SAMLP.protocolID p }+ , DS.referenceType = Nothing+ , DS.referenceTransforms = Just $ DS.Transforms+ $ DS.simpleTransform DS.TransformEnvelopedSignature+ :| DS.simpleTransform (DS.TransformCanonicalization $ C14N.CanonicalXMLExcl10 False)+ : []+ , DS.referenceDigestMethod = DS.simpleDigest DS.DigestSHA1+ , DS.referenceDigestValue = error "signSAMLProtocol: referenceDigestValue"+ } $ XP.pickleDoc XP.xpickle m+ s' <- DS.generateSignature sk $ maybe DS.SignedInfo+ { DS.signedInfoId = Nothing+ , DS.signedInfoCanonicalizationMethod = DS.simpleCanonicalization $ C14N.CanonicalXMLExcl10 False+ , DS.signedInfoSignatureMethod = DS.SignatureMethod+ { DS.signatureMethodAlgorithm = Identified $ DS.signingKeySignatureAlgorithm sk+ , DS.signatureMethodHMACOutputLength = Nothing+ , DS.signatureMethod = []+ }+ , DS.signedInfoReference = r :| []+ } DS.signatureSignedInfo $ SAMLP.protocolSignature p+ return $ DS.signature' .~ Just s' $ m+ where+ p = m ^. SAMLP.samlProtocol'++verifySAMLProtocol :: SAMLP.SAMLProtocol a => BSL.ByteString -> IO a+verifySAMLProtocol b = do+ x <- maybe (fail "invalid XML") return $ xmlToDoc b+ m <- either fail return $ docToSAML x+ v <- DS.verifySignature mempty (DS.signedID m) x+ unless (or v) $ fail "verifySAMLProtocol: invalid or missing signature"+ return m
+ SAML2/Core/Versioning.hs view
@@ -0,0 +1,37 @@+-- |+-- SAML Versioning+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf saml-core-2.0-os> §4+module SAML2.Core.Versioning+ ( SAMLVersion(..)+ , samlVersion+ ) where++import Data.Version (Version, makeVersion)++import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP++data SAMLVersion+ = SAML10+ | SAML11+ | SAML20+ deriving (Eq, Ord, Enum, Bounded)++samlVersion :: SAMLVersion -> Version+samlVersion SAML10 = makeVersion [1,0]+samlVersion SAML11 = makeVersion [1,1]+samlVersion SAML20 = makeVersion [2,0]++instance Show SAMLVersion where+ show SAML10 = "1.0"+ show SAML11 = "1.1"+ show SAML20 = "2.0"++instance Read SAMLVersion where+ readsPrec _ ('1':'.':'0':s) = [(SAML10, s)]+ readsPrec _ ('1':'.':'1':s) = [(SAML11, s)]+ readsPrec _ ('2':'.':'0':s) = [(SAML20, s)]+ readsPrec _ _ = []++instance XP.XmlPickler SAMLVersion where+ xpickle = XP.xpPrim
+ SAML2/Lens.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}+module SAML2.Lens+ ( fieldLens+ ) where++import Control.Lens.Lens (lens)+import qualified Language.Haskell.TH as TH++fieldLens :: TH.Name -> TH.ExpQ+fieldLens f = do+ a <- TH.newName "a"+ b <- TH.newName "b"+ return $ TH.VarE 'lens `TH.AppE` TH.VarE f+ `TH.AppE` TH.LamE [TH.VarP a, TH.VarP b] (TH.RecUpdE (TH.VarE a) [(f, TH.VarE b)])
+ SAML2/Metadata.hs view
@@ -0,0 +1,34 @@+-- |+-- Metadata for the OASIS Security Assertion Markup Language (SAML) V2.0+--+-- <http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf saml-metadata-2.0-os>+module SAML2.Metadata+ ( -- * §2+ nsMD+ , EntityID+ , Endpoint(..)+ , IndexedEndpoint(..)+ , Localized(..)+ , LocalizedName+ , LocalizedURI+ , Metadata(..)+ , Extensions(..)+ , Descriptors(..)+ , Descriptor(..)+ , Organization(..)+ , Contact(..)+ , ContactType(..)+ , AdditionalMetadataLocation(..)+ , RoleDescriptor(..)+ , KeyDescriptor(..)+ , KeyTypes(..)+ , SSODescriptor(..)+ , AttributeConsumingService(..)+ , RequestedAttribute(..)+ ) where++import SAML2.XML.Types+import SAML2.Metadata.Metadata++nsMD :: Namespace+nsMD = ns
+ SAML2/Metadata/Metadata.hs view
@@ -0,0 +1,471 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+-- |+-- Metadata for SAML V2.0+-- +-- <http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf saml-metadata-2.0-os> §2+module SAML2.Metadata.Metadata where++import Data.Foldable (fold)+import qualified Network.URI as URI+import qualified Text.XML.HXT.Arrow.Pickle.Schema as XPS++import SAML2.Lens+import SAML2.XML+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Schema as XS+import qualified SAML2.XML.Signature.Types as DS+import qualified SAML2.XML.Encryption as XEnc+import SAML2.Core.Namespaces+import SAML2.Core.Versioning+import SAML2.Core.Identifiers+import qualified SAML2.Core.Assertions as SAML+import SAML2.Bindings.Identifiers++ns :: Namespace+ns = mkNamespace "md" $ samlURN SAML20 ["metadata"]++xpElem :: String -> XP.PU a -> XP.PU a+xpElem = xpTrimElemNS ns++-- |§2.2.1+type EntityID = AnyURI++xpEntityID :: XP.PU EntityID+xpEntityID = XS.xpAnyURI -- XXX maxLength=1024++-- |§2.2.2+data Endpoint = Endpoint+ { endpointBinding :: IdentifiedURI Binding+ , endpointLocation :: AnyURI+ , endpointResponseLocation :: Maybe AnyURI+ , endpointAttrs :: Nodes+ , endpointXML :: Nodes+ } deriving (Eq, Show)++instance XP.XmlPickler Endpoint where+ xpickle = [XP.biCase|+ ((((b, l), r), a), x) <-> Endpoint b l r a x|]+ XP.>$< (XP.xpAttr "Binding" XP.xpickle+ XP.>*< XP.xpAttr "Location" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "ResponseLocation" XS.xpAnyURI+ XP.>*< XP.xpAnyAttrs+ XP.>*< XP.xpList xpTrimAnyElem)++-- |§2.2.3+data IndexedEndpoint = IndexedEndpoint+ { indexedEndpoint :: Endpoint+ , indexedEndpointIndex :: XS.UnsignedShort+ , indexedEndpointIsDefault :: XS.Boolean+ } deriving (Eq, Show)++instance XP.XmlPickler IndexedEndpoint where+ xpickle = [XP.biCase|+ ((i, d), e) <-> IndexedEndpoint e i d|]+ XP.>$< (XP.xpAttr "index" XS.xpUnsignedShort+ XP.>*< XP.xpDefault False (XP.xpAttr "isDefault" XS.xpBoolean)+ XP.>*< XP.xpickle)++data Localized a = Localized+ { localizedLang :: XS.Language+ , localized :: a+ } deriving (Eq, Show)++xpLocalized :: XP.PU a -> XP.PU (Localized a)+xpLocalized p = [XP.biCase|+ (l, x) <-> Localized l x|]+ XP.>$< (xpXmlLang+ XP.>*< p)++-- |§2.2.4+type LocalizedName = Localized XS.String++instance XP.XmlPickler LocalizedName where+ xpickle = xpLocalized XS.xpString++-- |§2.2.5+type LocalizedURI = Localized XS.AnyURI++instance XP.XmlPickler LocalizedURI where+ xpickle = xpLocalized XS.xpAnyURI++data Metadata+ = EntityDescriptor+ { entityID :: EntityID+ , metadataID :: Maybe XS.ID+ , metadataValidUntil :: Maybe XS.DateTime+ , metadataCacheDuration :: Maybe XS.Duration+ , entityAttrs :: Nodes+ , metadataSignature :: Maybe DS.Signature+ , metadataExtensions :: Extensions+ , entityDescriptors :: Descriptors+ , entityOrganization :: Maybe Organization+ , entityContactPerson :: [Contact]+ , entityAditionalMetadataLocation :: [AdditionalMetadataLocation]+ } -- ^§2.3.2+ | EntitiesDescriptor+ { metadataID :: Maybe XS.ID+ , metadataValidUntil :: Maybe XS.DateTime+ , metadataCacheDuration :: Maybe XS.Duration+ , entitiesName :: Maybe XS.String+ , metadataSignature :: Maybe DS.Signature+ , metadataExtensions :: Extensions+ , entities :: List1 Metadata+ } -- ^§2.3.1+ deriving (Eq, Show)++instance XP.XmlPickler Metadata where+ xpickle = [XP.biCase|+ Left ((((((((((e, i), vu), cd), xa), sig), ext), desc), org), cp), aml) <-> EntityDescriptor e i vu cd xa sig ext desc org cp aml+ Right ((((((i, vu), cd), n), sig), ext), l) <-> EntitiesDescriptor i vu cd n sig ext l|]+ XP.>$< (xpElem "EntityDescriptor"+ (XP.xpAttr "entityID" xpEntityID+ XP.>*< XP.xpAttrImplied "ID" XS.xpID+ XP.>*< XP.xpAttrImplied "validUntil" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "cacheDuration" XS.xpDuration+ XP.>*< XP.xpAnyAttrs+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< XP.xpList XP.xpickle+ XP.>*< XP.xpList XP.xpickle)+ XP.>|< xpElem "EntitiesDescriptor"+ (XP.xpAttrImplied "ID" XS.xpID+ XP.>*< XP.xpAttrImplied "validUntil" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "cacheDuration" XS.xpDuration+ XP.>*< XP.xpAttrImplied "Name" XS.xpString+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpList1 XP.xpickle))++instance DS.Signable Metadata where+ signature' = $(fieldLens 'metadataSignature)+ signedID = fold . metadataID++-- |§2.3.1 empty list means missing+newtype Extensions = Extensions{ extensions :: Nodes }+ deriving (Eq, Show, Monoid)++instance XP.XmlPickler Extensions where+ xpickle = XP.xpDefault (Extensions []) $+ xpElem "Extensions" $ [XP.biCase|+ x <-> Extensions x|]+ XP.>$< (XP.xpList1 xpTrimAnyElem)++data Descriptors+ = Descriptors{ descriptors :: List1 Descriptor }+ | AffiliationDescriptor+ { affiliationDescriptorAffiliationOwnerID :: EntityID+ , affiliationDescriptorID :: Maybe XS.ID+ , affiliationDescriptorValidUntil :: Maybe XS.DateTime+ , affiliationDescriptorCacheDuration :: Maybe XS.Duration+ , affiliationDescriptorAttrs :: Nodes+ , affiliationDescriptorSignature :: Maybe DS.Signature+ , affiliationDescriptorExtensions :: Extensions+ , affiliationDescriptorAffiliateMember :: List1 EntityID+ , affiliationDescriptorKeyDescriptor :: [KeyDescriptor]+ } -- ^§2.5+ deriving (Eq, Show)++instance XP.XmlPickler Descriptors where+ xpickle = [XP.biCase|+ Left l <-> Descriptors l+ Right ((((((((o, i), vu), cd), a), sig), ext), am), kd) <-> AffiliationDescriptor o i vu cd a sig ext am kd|]+ XP.>$< (xpList1 XP.xpickle+ XP.>|< xpElem "AffiliationDescriptor"+ (XP.xpAttr "affiliationOwnerID" xpEntityID+ XP.>*< XP.xpAttrImplied "ID" XS.xpID+ XP.>*< XP.xpAttrImplied "validUntil" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "cacheDuration" XS.xpDuration+ XP.>*< XP.xpAnyAttrs+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpList1 (xpElem "AffiliateMember" xpEntityID)+ XP.>*< XP.xpList XP.xpickle))++data Descriptor+ = Descriptor+ { descriptorRole :: !RoleDescriptor+ } -- ^§2.4.1+ | IDPSSODescriptor+ { descriptorRole :: !RoleDescriptor+ , descriptorSSO :: !SSODescriptor+ , descriptorWantAuthnRequestsSigned :: XS.Boolean+ , descriptorSingleSignOnService :: List1 Endpoint+ , descriptorNameIDMappingService :: [Endpoint]+ , descriptorAssertionIDRequestService :: [Endpoint]+ , descriptorAttributeProfile :: [XS.AnyURI]+ , descriptorAttribute :: [SAML.Attribute]+ } -- ^§2.4.3+ | SPSSODescriptor+ { descriptorRole :: !RoleDescriptor+ , descriptorSSO :: !SSODescriptor+ , descriptorAuthnRequestsSigned :: XS.Boolean+ , descriptorWantAssertionsSigned :: XS.Boolean+ , descriptorAssertionConsumerService :: List1 IndexedEndpoint+ , descriptorAttributeConsumingService :: [AttributeConsumingService]+ } -- ^§2.4.4+ | AuthnAuthorityDescriptor+ { descriptorRole :: !RoleDescriptor+ , descriptorAuthnQueryService :: List1 Endpoint+ , descriptorAssertionIDRequestService :: [Endpoint]+ , descriptorNameIDFormat :: [IdentifiedURI NameIDFormat]+ } -- ^§2.4.5+ | AttributeAuthorityDescriptor+ { descriptorRole :: !RoleDescriptor+ , descriptorAttributeService :: List1 Endpoint+ , descriptorAssertionIDRequestService :: [Endpoint]+ , descriptorNameIDFormat :: [IdentifiedURI NameIDFormat]+ , descriptorAttributeProfile :: [XS.AnyURI]+ , descriptorAttribute :: [SAML.Attribute]+ } -- ^§2.4.7+ | PDPDescriptor+ { descriptorRole :: !RoleDescriptor+ , descriptorAuthzService :: List1 Endpoint+ , descriptorAssertionIDRequestService :: [Endpoint]+ , descriptorNameIDFormat :: [IdentifiedURI NameIDFormat]+ } -- ^§2.4.6+ deriving (Eq, Show)++instance XP.XmlPickler Descriptor where+ xpickle = [XP.biCase|+ Left (Left (Left (Left (Left r)))) <-> Descriptor r+ Left (Left (Left (Left (Right (((((((ws, r), s), sso), nim), air), ap), a))))) <-> IDPSSODescriptor r s ws sso nim air ap a+ Left (Left (Left (Right (((((a, w), r), s), e), t)))) <-> SPSSODescriptor r s a w e t+ Left (Left (Right (((r, a), s), n))) <-> AuthnAuthorityDescriptor r a s n+ Left (Right (((((r, a), s), n), tp), t)) <-> AttributeAuthorityDescriptor r a s n tp t+ Right (((r, a), s), n) <-> PDPDescriptor r a s n|]+ XP.>$< (xpElem "RoleDescriptor" XP.xpickle+ XP.>|< xpElem "IDPSSODescriptor"+ (XP.xpDefault False (XP.xpAttr "WantAuthnRequestsSigned" XS.xpBoolean)+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpList1 (xpElem "SingleSignOnService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "NameIDMappingService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "AssertionIDRequestService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "AttributeProfile" XS.xpAnyURI)+ XP.>*< XP.xpList XP.xpickle)+ XP.>|< xpElem "SPSSODescriptor"+ (XP.xpDefault False (XP.xpAttr "AuthnRequestsSigned" XS.xpBoolean)+ XP.>*< XP.xpDefault False (XP.xpAttr "WantAssertionsSigned" XS.xpBoolean)+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpList1 (xpElem "AssertionConsumerService" XP.xpickle)+ XP.>*< XP.xpList XP.xpickle)+ XP.>|< xpElem "AuthnAuthorityDescriptor"+ (XP.xpickle+ XP.>*< xpList1 (xpElem "AuthnQueryService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "AssertionIDRequestService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "NameIDFormat" XP.xpickle))+ XP.>|< xpElem "AttributeAuthorityDescriptor"+ (XP.xpickle+ XP.>*< xpList1 (xpElem "AttributeService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "AssertionIDRequestService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "NameIDFormat" XP.xpickle)+ XP.>*< XP.xpList (xpElem "AttributeProfile" XS.xpAnyURI)+ XP.>*< XP.xpList XP.xpickle)+ XP.>|< xpElem "PDPDescriptor"+ (XP.xpickle+ XP.>*< xpList1 (xpElem "AuthzService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "AssertionIDRequestService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "NameIDFormat" XP.xpickle)))++-- |§2.3.2.1+data Organization = Organization+ { organizationAttrs :: Nodes+ , organizationExtensions :: Extensions+ , organizationName :: List1 LocalizedName+ , organizationDisplayName :: List1 LocalizedName+ , organizationURL :: List1 LocalizedURI+ } deriving (Eq, Show)++instance XP.XmlPickler Organization where+ xpickle = xpElem "Organization" $+ [XP.biCase|+ ((((a, e), n), d), u) <-> Organization a e n d u|]+ XP.>$< (XP.xpAnyAttrs+ XP.>*< XP.xpickle+ XP.>*< xpList1 (xpElem "OrganizationName" XP.xpickle)+ XP.>*< xpList1 (xpElem "OrganizationDisplayName" XP.xpickle)+ XP.>*< xpList1 (xpElem "OrganizationURL" XP.xpickle))++-- |§2.3.2.2+data Contact = ContactPerson+ { contactType :: ContactType+ , contactAttrs :: Nodes+ , contactExtensions :: Extensions+ , contactCompany :: Maybe XS.String+ , contactGivenName :: Maybe XS.String+ , contactSurName :: Maybe XS.String+ , contactEmailAddress :: [XS.AnyURI]+ , contactTelephoneNumber :: [XS.String]+ } deriving (Eq, Show)++instance XP.XmlPickler Contact where+ xpickle = xpElem "ContactPerson" $+ [XP.biCase|+ (((((((t, a), ext), c), g), s), e), tn) <-> ContactPerson t a ext c g s e tn|]+ XP.>$< (XP.xpAttr "contactType" XP.xpickle+ XP.>*< XP.xpAnyAttrs+ XP.>*< XP.xpickle+ XP.>*< XP.xpOption (xpElem "Company" XS.xpString)+ XP.>*< XP.xpOption (xpElem "GivenName" XS.xpString)+ XP.>*< XP.xpOption (xpElem "SurName" XS.xpString)+ XP.>*< XP.xpList (xpElem "EmailAddress" XS.xpAnyURI)+ XP.>*< XP.xpList (xpElem "TelephoneNumber" XS.xpString))++data ContactType+ = ContactTypeTechnical+ | ContactTypeSupport+ | ContactTypeAdministrative+ | ContactTypeBilling+ | ContactTypeOther+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable XString ContactType where+ identifier ContactTypeTechnical = "technical"+ identifier ContactTypeSupport = "support"+ identifier ContactTypeAdministrative = "administrative"+ identifier ContactTypeBilling = "billing"+ identifier ContactTypeOther = "other"+instance XP.XmlPickler ContactType where+ xpickle = xpIdentifier (XP.xpTextDT (XPS.scDT (namespaceURIString ns) "ContactTypeType" [])) "ContactTypeType"++-- |§2.3.2.3+data AdditionalMetadataLocation = AdditionalMetadataLocation+ { additionalMetadataLocationNamespace :: XS.AnyURI+ , additionalMetadataLocation :: XS.AnyURI+ } deriving (Eq, Show)++instance XP.XmlPickler AdditionalMetadataLocation where+ xpickle = xpElem "AdditionalMetadataLocation" $+ [XP.biCase|+ (n, l) <-> AdditionalMetadataLocation n l|]+ XP.>$< (XP.xpAttr "namespace" XS.xpAnyURI+ XP.>*< XS.xpAnyURI)++-- |§2.4.1+data RoleDescriptor = RoleDescriptor+ { roleDescriptorID :: Maybe XS.ID+ , roleDescriptorValidUntil :: Maybe XS.DateTime+ , roleDescriptorCacheDuration :: Maybe XS.Duration+ , roleDescriptorProtocolSupportEnumeration :: [XS.AnyURI]+ , roleDescriptorErrorURL :: Maybe XS.AnyURI+ , roleDescriptorAttrs :: Nodes+ , roleDescriptorSignature :: Maybe DS.Signature+ , roleDescriptorExtensions :: Extensions+ , roleDescriptorKeyDescriptor :: [KeyDescriptor]+ , roleDescriptorOrganization :: Maybe Organization+ , roleDescriptorContactPerson :: [Contact]+ } deriving (Eq, Show)++instance XP.XmlPickler RoleDescriptor where+ xpickle = [XP.biCase|+ ((((((((((i, vu), cd), ps), eu), a), sig), ext), key), org), cp) <-> RoleDescriptor i vu cd ps eu a sig ext key org cp|]+ XP.>$< (XP.xpAttrImplied "ID" XS.xpID+ XP.>*< XP.xpAttrImplied "validUntil" XS.xpDateTime+ XP.>*< XP.xpAttrImplied "cacheDuration" XS.xpDuration+ XP.>*< XP.xpAttr "protocolSupportEnumeration" xpAnyURIList+ XP.>*< XP.xpAttrImplied "errorURL" XS.xpAnyURI+ XP.>*< XP.xpAnyAttrs+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< XP.xpList XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpList XP.xpickle)+ where+ xpAnyURIList = XP.xpWrapEither+ ( mapM (maybe (Left "invalid anyURI") Right . URI.parseURIReference) . words+ , tail . foldr ((.) (' ':) . URI.uriToString id) ""+ ) $ XP.xpTextDT $ XPS.scDT (namespaceURIString ns) "anyURIListType" []++instance DS.Signable RoleDescriptor where+ signature' = $(fieldLens 'roleDescriptorSignature)+ signedID = fold . roleDescriptorID++-- |§2.4.1.1+data KeyDescriptor = KeyDescriptor+ { keyDescriptorUse :: KeyTypes+ , keyDescriptorKeyInfo :: DS.KeyInfo+ , keyDescriptorEncryptionMethod :: [XEnc.EncryptionMethod]+ } deriving (Eq, Show)++instance XP.XmlPickler KeyDescriptor where+ xpickle = xpElem "KeyDescriptor" $+ [XP.biCase|+ ((t, i), m) <-> KeyDescriptor t i m|]+ XP.>$< (XP.xpDefault KeyTypeBoth (XP.xpAttr "use" XP.xpickle)+ XP.>*< XP.xpickle+ XP.>*< XP.xpList (xpElem "EncryptionMethod" XEnc.xpEncryptionMethodType))++data KeyTypes+ = KeyTypeSigning+ | KeyTypeEncryption+ | KeyTypeBoth+ deriving (Eq, Enum, Bounded, Show)++-- |Does the second KeyTypes include the first type of use?+keyType :: KeyTypes -> KeyTypes -> Bool+keyType _ KeyTypeBoth = True+keyType k t = k == t++instance Identifiable XString KeyTypes where+ identifier KeyTypeSigning = "signing"+ identifier KeyTypeEncryption = "encryption"+ identifier KeyTypeBoth = ""+ identifiedValues = [KeyTypeEncryption, KeyTypeSigning]+instance XP.XmlPickler KeyTypes where+ xpickle = xpIdentifier (XP.xpTextDT (XPS.scDT (namespaceURIString ns) "KeyTypes" [])) "KeyTypes"++-- |§2.4.2+data SSODescriptor = SSODescriptor+ { ssoDescriptorArtifactResolutionService :: [IndexedEndpoint]+ , ssoDescriptorSingleLogoutService :: [Endpoint]+ , ssoDescriptorManageNameIDService :: [Endpoint]+ , ssoDescriptorNameIDFormat :: [IdentifiedURI NameIDFormat]+ } deriving (Eq, Show)++instance XP.XmlPickler SSODescriptor where+ xpickle = [XP.biCase|+ (((a, s), m), n) <-> SSODescriptor a s m n|]+ XP.>$< (XP.xpList (xpElem "ArtifactResolutionService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "SingleLogoutService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "ManageNameIDService" XP.xpickle)+ XP.>*< XP.xpList (xpElem "NameIDFormat" XP.xpickle))++-- |§2.4.4.1+data AttributeConsumingService = AttributeConsumingService+ { attributeConsumingServiceIndex :: XS.UnsignedShort+ , attributeConsumingServiceIsDefault :: Bool+ , attributeConsumingServiceServiceName :: List1 LocalizedName+ , attributeConsumingServiceServiceDescription :: [LocalizedName]+ , attributeConsumingServiceRequestedAttribute :: List1 RequestedAttribute+ } deriving (Eq, Show)++instance XP.XmlPickler AttributeConsumingService where+ xpickle = xpElem "AttributeConsumingService" $+ [XP.biCase|+ ((((i, d), sn), sd), ra) <-> AttributeConsumingService i d sn sd ra|]+ XP.>$< (XP.xpAttr "index" XS.xpUnsignedShort+ XP.>*< XP.xpDefault False (XP.xpAttr "isDefault" XS.xpBoolean)+ XP.>*< xpList1 (xpElem "ServiceName" XP.xpickle)+ XP.>*< XP.xpList (xpElem "ServiceDescription" XP.xpickle)+ XP.>*< xpList1 XP.xpickle)++-- |§2.4.4.1.1+data RequestedAttribute = RequestedAttribute+ { requestedAttribute :: !SAML.Attribute+ , requestedAttributeIsRequired :: Bool+ } deriving (Eq, Show)++instance XP.XmlPickler RequestedAttribute where+ xpickle = xpElem "RequestedAttribute" $+ [XP.biCase|+ (r, a) <-> RequestedAttribute a r|]+ XP.>$< (XP.xpDefault False (XP.xpAttr "isRequired" XS.xpBoolean)+ XP.>*< SAML.xpAttributeType)
+ SAML2/Profiles.hs view
@@ -0,0 +1,9 @@+-- |+-- Profiles for the OASIS Security Assertion Markup Language (SAML) V2.0+--+-- <https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf saml-profiles-2.0-os>+module SAML2.Profiles+ ( module SAML2.Profiles.ConfirmationMethod+ ) where++import SAML2.Profiles.ConfirmationMethod
+ SAML2/Profiles/ConfirmationMethod.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- |+-- Confirmation Method Identifiers+-- +-- <https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf saml-profiles-2.0-os> §3+module SAML2.Profiles.ConfirmationMethod where++import SAML2.XML+import SAML2.Core.Namespaces+import SAML2.Core.Versioning++-- |§3+data ConfirmationMethod+ = ConfirmationMethodHolderOfKey+ | ConfirmationMethodSenderVouches+ | ConfirmationMethodBearer+ deriving (Eq, Enum, Bounded, Show)++instance Identifiable URI ConfirmationMethod where+ identifier = samlURNIdentifier "cm" . f where+ f ConfirmationMethodHolderOfKey = (SAML20, "holder-of-key")+ f ConfirmationMethodSenderVouches = (SAML20, "sender-vouches")+ f ConfirmationMethodBearer = (SAML20, "bearer")
+ SAML2/XML.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeOperators #-}+module SAML2.XML+ ( module SAML2.XML.Types+ , module SAML2.Core.Datatypes+ , URI+ , xpTrimAnyElem+ , xpTrimElemNS+ , xpXmlLang+ , IP, xpIP+ , Identified(..)+ , Identifiable(..)+ , unidentify+ , xpIdentified+ , xpIdentifier+ , IdentifiedURI+ , samlToDoc+ , samlToXML+ , docToSAML+ , docToXML+ , xmlToSAML+ , xmlToDoc+ ) where++import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Lazy.Char8 as BSLC+import Data.Default (Default(..))+import qualified Data.Invertible as Inv+import Data.Maybe (listToMaybe)+import Network.URI (URI)+import qualified Text.XML.HXT.Core as HXT++import SAML2.XML.Types+import SAML2.Core.Datatypes+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Schema as XS++xpTrimAnyElem :: XP.PU HXT.XmlTree+xpTrimAnyElem = XP.xpTrim XP.xpAnyElem++xpTrimElemNS :: Namespace -> String -> XP.PU a -> XP.PU a+xpTrimElemNS ns n c = XP.xpTrim $ XP.xpElemQN (mkNName ns n) (c XP.>* XP.xpWhitespace)++xpXmlLang :: XP.PU XS.Language+xpXmlLang = XP.xpAttrQN (mkNName xmlNS "lang") $ XS.xpLanguage++type IP = XS.String++xpIP :: XP.PU IP+xpIP = XS.xpString++data Identified b a+ = Identified !a+ | Unidentified !b+ deriving (Eq, Show)++instance Default a => Default (Identified b a) where+ def = Identified def++class Eq b => Identifiable b a | a -> b where+ identifier :: a -> b+ identifiedValues :: [a]+ default identifiedValues :: (Bounded a, Enum a) => [a]+ identifiedValues = [minBound..maxBound]+ reidentify :: b -> Identified b a+ reidentify u = maybe (Unidentified u) Identified $ lookup u l where+ l = [ (identifier a, a) | a <- identifiedValues ]++unidentify :: Identifiable b a => Identified b a -> b+unidentify (Identified a) = identifier a+unidentify (Unidentified b) = b++identify :: Identifiable b a => b Inv.<-> Identified b a+identify = reidentify Inv.:<->: unidentify++xpIdentified :: Identifiable b a => XP.PU b -> XP.PU (Identified b a)+xpIdentified = Inv.fmap identify++xpIdentifier :: Identifiable b a => XP.PU b -> String -> XP.PU a+xpIdentifier b t = XP.xpWrapEither+ ( \u -> case reidentify u of+ Identified a -> Right a+ Unidentified _ -> Left ("invalid " ++ t)+ , identifier+ ) b++type IdentifiedURI = Identified URI++instance Identifiable URI a => XP.XmlPickler (Identified URI a) where+ xpickle = xpIdentified XS.xpAnyURI++samlToDoc :: XP.XmlPickler a => a -> HXT.XmlTree+samlToDoc = head+ . HXT.runLA (HXT.processChildren $ HXT.cleanupNamespaces HXT.collectPrefixUriPairs)+ . XP.pickleDoc XP.xpickle++docToXML :: HXT.XmlTree -> BSL.ByteString+docToXML = BSL.concat . HXT.runLA (HXT.xshowBlob HXT.getChildren)++samlToXML :: XP.XmlPickler a => a -> BSL.ByteString+samlToXML = docToXML . samlToDoc++xmlToDoc :: BSL.ByteString -> Maybe HXT.XmlTree+xmlToDoc = listToMaybe . HXT.runLA+ (HXT.xreadDoc+ HXT.>>> HXT.removeWhiteSpace+ HXT.>>> HXT.neg HXT.isXmlPi+ HXT.>>> HXT.propagateNamespaces)+ . BSLC.unpack -- XXX encoding?++docToSAML :: XP.XmlPickler a => HXT.XmlTree -> Either String a+docToSAML = XP.unpickleDoc' XP.xpickle+ . head+ . HXT.runLA (HXT.processBottomUp (HXT.processAttrl (HXT.neg HXT.isNamespaceDeclAttr)))++xmlToSAML :: XP.XmlPickler a => BSL.ByteString -> Either String a+xmlToSAML = maybe (Left "invalid XML") docToSAML . xmlToDoc
+ SAML2/XML/ASN1.hs view
@@ -0,0 +1,30 @@+module SAML2.XML.ASN1 where++import Control.Arrow (left)+import Data.ASN1.Types (ASN1, ASN1Object(..))+import Data.ASN1.BinaryEncoding (BER(BER), DER(DER))+import Data.ASN1.Encoding (decodeASN1', encodeASN1')+import qualified Data.X509 as X509++import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Schema as XS++xpASN1 :: XP.PU [ASN1]+xpASN1 = XP.xpWrapEither+ ( left show . decodeASN1' BER+ , encodeASN1' DER+ ) XS.xpBase64Binary++xpASN1Object :: ASN1Object a => XP.PU a+xpASN1Object = XP.xpWrapEither+ ( either Left check . fromASN1+ , (`toASN1` [])+ ) xpASN1 where+ check (x, []) = Right x+ check _ = Left "trailing ASN1 data"++xpX509Signed :: (Show a, Eq a, ASN1Object a) => XP.PU (X509.SignedExact a)+xpX509Signed = XP.xpWrapEither+ ( X509.decodeSignedObject+ , X509.encodeSignedObject+ ) XS.xpBase64Binary
+ SAML2/XML/Canonical.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+-- |+-- XML Canonicalization+--+-- For <http://www.w3.org/TR/2008/REC-xmldsig-core-20080610/> §6.5+module SAML2.XML.Canonical where++import Control.Monad ((<=<))+import qualified Data.ByteString as BS+import Data.Tree.Class (getChildren)+import qualified Text.XML.HXT.Core as HXT++import SAML2.XML+import qualified SAML2.XML.LibXML2 as LibXML2+import qualified SAML2.XML.Schema as XS+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP++-- |§6.5+data CanonicalizationAlgorithm+ = CanonicalXML10+ { canonicalWithComments :: Bool+ } -- ^§6.5.1 <http://www.w3.org/TR/xml-c14n/ xml-c14n>+ | CanonicalXML11+ { canonicalWithComments :: Bool+ } -- ^§6.5.2 <http://www.w3.org/TR/xml-c14n11/ xml-c14n11>+ | CanonicalXMLExcl10+ { canonicalWithComments :: Bool+ } -- ^<http://www.w3.org/TR/xml-exc-c14n/ xml-exc-c14n>+ deriving (Eq, Show)++instance Identifiable URI CanonicalizationAlgorithm where+ identifier (CanonicalXML10 False) = httpURI "www.w3.org" "/TR/2001/REC-xml-c14n-20010315" "" ""+ identifier (CanonicalXML10 True) = httpURI "www.w3.org" "/TR/2001/REC-xml-c14n-20010315" "" "#WithComments"+ identifier (CanonicalXML11 False) = httpURI "www.w3.org" "/2006/12/xml-c14n11" "" ""+ identifier (CanonicalXML11 True) = httpURI "www.w3.org" "/2006/12/xml-c14n11" "" "#WithComments"+ identifier (CanonicalXMLExcl10 False) = httpURI "www.w3.org" "/2001/10/xml-exc-c14n" "" "#"+ identifier (CanonicalXMLExcl10 True) = httpURI "www.w3.org" "/2001/10/xml-exc-c14n" "" "#WithComments"+ identifiedValues =+ [ CanonicalXML10 False+ , CanonicalXML10 True+ , CanonicalXML11 False+ , CanonicalXML11 True+ , CanonicalXMLExcl10 False+ , CanonicalXMLExcl10 True+ ]++newtype InclusiveNamespaces = InclusiveNamespaces+ { inclusiveNamespacesPrefixList :: XS.NMTOKENS+ } deriving (Eq, Show)++instance XP.XmlPickler InclusiveNamespaces where+ xpickle = xpTrimElemNS (mkNamespace "ec" (httpURI "www.w3.org" "/2001/10/xml-exc-c14n" "" "#")) "InclusiveNamespaces" $+ [XP.biCase|n <-> InclusiveNamespaces n|]+ XP.>$< XP.xpAttr "PrefixList" XS.xpNMTOKENS++-- |Canonicalize and serialize an XML document+canonicalize :: CanonicalizationAlgorithm -> Maybe InclusiveNamespaces -> Maybe String -> HXT.XmlTree -> IO BS.ByteString+canonicalize a i s =+ LibXML2.c14n (cm a) (inclusiveNamespacesPrefixList <$> i) (canonicalWithComments a) s+ <=< LibXML2.fromXmlTrees . getChildren where+ cm CanonicalXML10{} = LibXML2.C14N_1_0+ cm CanonicalXML11{} = LibXML2.C14N_1_1+ cm CanonicalXMLExcl10{} = LibXML2.C14N_EXCLUSIVE_1_0
+ SAML2/XML/Encryption.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- |+-- XML Encryption Syntax and Processing+--+-- <http://www.w3.org/TR/xmlenc-core1/> (selected portions)+module SAML2.XML.Encryption where++import SAML2.XML+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Schema as XS+import qualified SAML2.XML.Signature.Types as DS++nsFrag :: String -> URI+nsFrag = httpURI "www.w3.org" "/2001/04/xmlenc" "" . ('#':)++ns :: Namespace +ns = mkNamespace "xenc" $ nsFrag ""++xpElem :: String -> XP.PU a -> XP.PU a+xpElem = xpTrimElemNS ns++-- |§3.1+data EncryptedType = EncryptedType+ { encryptedID :: Maybe ID+ , encryptedType :: Maybe AnyURI+ , encryptedMimeType :: Maybe XString+ , encryptedEncoding :: Maybe (IdentifiedURI DS.EncodingAlgorithm)+ , encryptedEncryptionMethod :: Maybe EncryptionMethod+ , encryptedKeyInfo :: Maybe DS.KeyInfo+ , encryptedCipherData :: CipherData+ , encryptedEncryptionProperties :: Maybe EncryptionProperties+ } deriving (Eq, Show)++instance XP.XmlPickler EncryptedType where+ xpickle = [XP.biCase|(((((((i, t), m), e), c), k), d), p) <-> EncryptedType i t m e c k d p|]+ XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpAttrImplied "Type" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "MimeType" XS.xpString+ XP.>*< XP.xpAttrImplied "Encoding" XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< XP.xpOption XP.xpickle)++-- |§3.2+data EncryptionMethod = EncryptionMethod+ { encryptionAlgorithm :: IdentifiedURI EncryptionAlgorithm+ , encryptionKeySize :: Maybe Int+ , encryptionOAEPparams :: Maybe XS.Base64Binary+ , encryptionDigestMethod :: Maybe DS.DigestMethod+ , encryption :: Nodes+ } deriving (Eq, Show)++xpEncryptionMethodType :: XP.PU EncryptionMethod+xpEncryptionMethodType =+ [XP.biCase|((((a, s), p), d), x) <-> EncryptionMethod a s p d x|] + XP.>$< (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XP.xpOption (xpElem "KeySize" XP.xpickle)+ XP.>*< XP.xpOption (xpElem "OAEPparams" XS.xpBase64Binary)+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpAnyCont)++instance XP.XmlPickler EncryptionMethod where+ xpickle = xpElem "EncryptionMethod" xpEncryptionMethodType++-- |§3.3+data CipherData+ = CipherValue XS.Base64Binary+ | CipherReference+ { cipherURI :: AnyURI+ , cipherTransforms :: List1 DS.Transform+ }+ deriving (Eq, Show)++instance XP.XmlPickler CipherData where+ xpickle = xpElem "CipherData" $+ [XP.biCase|+ Left b <-> CipherValue b+ Right (u, t) <-> CipherReference u t |]+ XP.>$< (xpElem "CipherValue" XS.xpBase64Binary+ XP.>|< xpElem "CipherReference"+ (XP.xpAttr "URI" XS.xpAnyURI+ XP.>*< xpElem "Transforms" (xpList1 XP.xpickle)))++-- |§3.4+newtype EncryptedData = EncryptedData{ encryptedData :: EncryptedType }+ deriving (Eq, Show)++instance XP.XmlPickler EncryptedData where+ xpickle = xpElem "EncryptedData" $+ [XP.biCase|e <-> EncryptedData e|] + XP.>$< XP.xpickle++-- |§3.5.1+data EncryptedKey = EncryptedKey+ { encryptedKey :: !EncryptedType+ , encryptedKeyRecipient :: Maybe XString+ , encryptedKeyReferenceList :: [Reference] -- ^empty for missing+ , encryptedKeyCarriedKeyName :: Maybe XString+ } deriving (Eq, Show)++instance XP.XmlPickler EncryptedKey where+ xpickle = xpElem "EncryptedKey" $+ [XP.biCase|+ (e, ((r, Nothing), n)) <-> EncryptedKey e r [] n+ (e, ((r, Just l), n)) <-> EncryptedKey e r l n+ |] + XP.>$< (XP.xpickle+ XP.>*< (XP.xpAttrImplied "Recipient" XS.xpString+ XP.>*< XP.xpOption (xpElem "ReferenceList" $ XP.xpList1 XP.xpickle)+ XP.>*< XP.xpOption (xpElem "CarriedKeyName" XS.xpString)))++-- |§3.6+data Reference+ = DataReference+ { referenceURI :: URI+ , reference :: Nodes+ }+ | KeyReference+ { referenceURI :: URI+ , reference :: Nodes+ }+ deriving (Eq, Show)++instance XP.XmlPickler Reference where+ xpickle = [XP.biCase|+ Left (u, r) <-> DataReference u r+ Right (u, r) <-> KeyReference u r |]+ XP.>$< (refs "DataReference" XP.>|< refs "KeyReference")+ where+ refs n = xpElem n+ $ XP.xpAttr "URI" XS.xpAnyURI+ XP.>*< XP.xpList xpTrimAnyElem++-- |§3.7+data EncryptionProperties = EncryptionProperties+ { encryptionPropertiesId :: Maybe ID+ , encryptionProperties :: List1 EncryptionProperty+ } deriving (Eq, Show)++instance XP.XmlPickler EncryptionProperties where+ xpickle = xpElem "EncryptionProperties" $+ [XP.biCase|(i, l) <-> EncryptionProperties i l|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< xpList1 XP.xpickle)++data EncryptionProperty = EncryptionProperty+ { encryptionPropertyId :: Maybe ID+ , encryptionPropertyTarget :: Maybe AnyURI+ , encryptionProperty :: Nodes+ } deriving (Eq, Show)++instance XP.XmlPickler EncryptionProperty where+ xpickle = xpElem "EncryptionProperty" $+ [XP.biCase|((i, t), x) <-> EncryptionProperty i t x|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpAttrImplied "Target" XS.xpAnyURI+ XP.>*< XP.xpAny)++-- |§5.1+data EncryptionAlgorithm+ = BlockEncryptionTripleDES -- ^§5.2.2+ | BlockEncryptionAES128 -- ^§5.2.3+ | BlockEncryptionAES192 -- ^§5.2.3+ | BlockEncryptionAES256 -- ^§5.2.3+ | BlockEncryptionAES128GCM -- ^§5.2.4+ | BlockEncryptionAES192GCM -- ^§5.2.4+ | BlockEncryptionAES256GCM -- ^§5.2.4+ | KeyTransportRSA1_5 -- ^§5.5.1+ | KeyTransportRSAOAEPMGF1P -- ^§5.5.2+ | KeyTransportRSAOAEP -- ^§5.5.2+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI EncryptionAlgorithm where+ identifier BlockEncryptionTripleDES = nsFrag "tripledes-cbc"+ identifier BlockEncryptionAES128 = nsFrag "aes128-cbc"+ identifier BlockEncryptionAES256 = nsFrag "aes256-cbc"+ identifier BlockEncryptionAES192 = nsFrag "aes192-cbc"+ identifier BlockEncryptionAES128GCM = httpURI "www.w3.org" "/2009/xmlenc11" "" "#aes128-gcm"+ identifier BlockEncryptionAES192GCM = httpURI "www.w3.org" "/2009/xmlenc11" "" "#aes192-gcm"+ identifier BlockEncryptionAES256GCM = httpURI "www.w3.org" "/2009/xmlenc11" "" "#aes256-gcm"+ identifier KeyTransportRSA1_5 = nsFrag "rsa-1_5"+ identifier KeyTransportRSAOAEPMGF1P = nsFrag "rsa-oaep-mgf1p"+ identifier KeyTransportRSAOAEP = httpURI "www.w3.org" "/2009/xmlenc11" "" "#rsa-oaep"++-- |§5.5+data AgreementMethod = AgreementMethod+ { agreementMethodAlgorithm :: IdentifiedURI EncryptionAlgorithm+ , agreementMethodKA_Nonce :: Maybe XS.Base64Binary+ , agreementMethodDigestMethod :: Maybe DS.DigestMethod+ -- Nodes...+ , agreementMethodOriginatorKeyInfo :: Maybe DS.KeyInfo+ , agreementMethodRecipientKeyInfo :: Maybe DS.KeyInfo+ }++instance XP.XmlPickler AgreementMethod where+ xpickle = xpElem "AgreementMethod" $+ [XP.biCase|((((a, n), d), o), r) <-> AgreementMethod a n d o r|]+ XP.>$< (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XP.xpOption (xpElem "KA-Nonce" XS.xpBase64Binary)+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpOption (xpElem "OriginatorKeyInfo" DS.xpKeyInfoType)+ XP.>*< XP.xpOption (xpElem "RecipientKeyInfo" DS.xpKeyInfoType))
+ SAML2/XML/LibXML2.hsc view
@@ -0,0 +1,115 @@+module SAML2.XML.LibXML2+ ( Doc+ , fromXmlTrees+ , C14NMode(..)+ , c14n+ ) where++import Control.Exception (bracket)+import Control.Monad ((<=<))+import Data.Bits ((.|.))+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Unsafe as BSU+import Data.Maybe (fromMaybe)+import Data.String.Unicode (unicodeCharToUtf8')+import Data.Word (Word8)+import Foreign.C.Error (throwErrnoIf, throwErrnoIfNull)+import Foreign.C.String (CString, withCString)+import Foreign.C.Types (CInt(..))+import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)+import Foreign.Marshal (alloca, withArray0, withMany, maybeWith)+import Foreign.Ptr (Ptr, FunPtr, nullPtr, castPtr)+import Foreign.Storable (peek, peekByteOff)+import qualified Text.XML.HXT.Core as HXT+import qualified Text.XML.HXT.DOM.ShowXml as HXTS++#include <libxml/parser.h>+#include <libxml/parser.h>+#include <libxml/c14n.h>++type XMLChar = #type xmlChar+data XMLDoc+data XMLXPathContext+data XMLXPathObject+data XMLNodeSet++foreign import ccall unsafe "libxml/parser.h xmlReadMemory"+ xmlReadMemory :: CString -> CInt -> CString -> CString -> CInt -> IO (Ptr XMLDoc)++foreign import ccall unsafe "libxml/tree.h &xmlFreeDoc"+ xmlFreeDoc :: FunPtr ((Ptr XMLDoc) -> IO ())++foreign import ccall unsafe "libxml/xpath.h xmlXPathNewContext"+ xmlXPathNewContext :: Ptr XMLDoc -> IO (Ptr XMLXPathContext)++foreign import ccall unsafe "libxml/xpath.h xmlXPathFreeContext"+ xmlXPathFreeContext :: Ptr XMLXPathContext -> IO ()++foreign import ccall unsafe "libxml/xpath.h xmlXPathEval"+ xmlXPathEval :: Ptr XMLChar -> Ptr XMLXPathContext -> IO (Ptr XMLXPathObject)++foreign import ccall unsafe "libxml/xpath.h xmlXPathFreeObject"+ xmlXPathFreeObject :: Ptr XMLXPathObject -> IO ()++foreign import ccall unsafe "libxml/c14n.h xmlC14NDocDumpMemory"+ xmlC14NDocDumpMemory :: Ptr XMLDoc -> Ptr XMLNodeSet -> CInt -> Ptr (Ptr XMLChar) -> CInt -> Ptr (Ptr XMLChar) -> IO CInt++foreign import ccall unsafe "xmlFree_stub"+ xmlFree :: Ptr a -> IO ()++newtype Doc = Doc{ unDoc :: ForeignPtr XMLDoc }++newDoc :: Ptr XMLDoc -> IO Doc+newDoc = fmap Doc . newForeignPtr xmlFreeDoc++fromBytes :: BS.ByteString -> IO Doc+fromBytes s = do+ d <- BSU.unsafeUseAsCStringLen s $ \(p, l) ->+ throwErrnoIfNull "xmlReadMemory" $+ xmlReadMemory p (fromIntegral l) nullPtr nullPtr (#{const XML_PARSE_NOENT} .|. #{const XML_PARSE_DTDLOAD} .|. #{const XML_PARSE_DTDATTR} .|. #{const XML_PARSE_NONET} .|. #{const XML_PARSE_COMPACT})+ newDoc d++fromXmlTrees :: HXT.XmlTrees -> IO Doc+fromXmlTrees = fromBytes . BSL.toStrict . HXTS.xshow' cq aq unicodeCharToUtf8'+ where+ cq '&' = ("&" ++)+ cq '<' = ("<" ++)+ cq '>' = (">" ++)+ cq '\13' = ("
" ++)+ cq c = (c:)+ aq '"' = (""" ++)+ aq '\9' = ("	" ++)+ aq '\10' = ("
" ++)+ aq c = cq c++withXMLXPathNodeList :: Ptr XMLDoc -> String -> (Ptr XMLNodeSet -> IO a) -> IO a+withXMLXPathNodeList d s f = + bracket (xmlXPathNewContext d) xmlXPathFreeContext $ \c ->+ withCString s $ \p ->+ bracket+ (throwErrnoIfNull "xmlXPathEval" $ xmlXPathEval ((castPtr :: CString -> Ptr Word8) p) c)+ xmlXPathFreeObject+ $ f <=< #peek xmlXPathObject, nodesetval++data C14NMode+ = C14N_1_0+ | C14N_EXCLUSIVE_1_0+ | C14N_1_1++c14nmode :: C14NMode -> CInt+c14nmode C14N_1_0 = #{const XML_C14N_1_0}+c14nmode C14N_EXCLUSIVE_1_0 = #{const XML_C14N_EXCLUSIVE_1_0}+c14nmode C14N_1_1 = #{const XML_C14N_1_1}++c14n :: C14NMode -> Maybe [String] -> Bool -> Maybe String -> Doc -> IO BS.ByteString+c14n m i c s d =+ withForeignPtr (unDoc d) $ \dp ->+ withMany withCString (fromMaybe [] i) $ \il ->+ maybeWith (withArray0 nullPtr) (il <$ i) $ \ip ->+ maybeWith (withXMLXPathNodeList dp) s $ \sn ->+ alloca $ \p -> do+ r <- throwErrnoIf (< 0) "xmlC14NDocDumpMemory" $+ xmlC14NDocDumpMemory dp sn (c14nmode m) ((castPtr :: Ptr CString -> Ptr (Ptr Word8)) ip) (fromIntegral $ fromEnum c) p+ pp <- peek p+ BSU.unsafePackCStringFinalizer pp (fromIntegral r) (xmlFree pp)
+ SAML2/XML/Schema.hs view
@@ -0,0 +1,10 @@+module SAML2.XML.Schema+ ( ns+ , module SAML2.XML.Schema.Datatypes+ ) where++import SAML2.XML.Types+import SAML2.XML.Schema.Datatypes++ns :: Namespace +ns = mkNamespace "xs" $ httpURI "www.w3.org" "/2001/XMLSchema" "" ""
+ SAML2/XML/Schema/Datatypes.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ViewPatterns #-}+-- |+-- XML Schema Datatypes+--+-- <http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/> (selected portions)+module SAML2.XML.Schema.Datatypes where++import Prelude hiding (String)++import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Base64 as B64+import Data.Char (isDigit)+import Data.Char.Properties.XMLCharProps (isXmlSpaceChar, isXmlNameChar)+import Data.Fixed (Pico, showFixed)+import qualified Data.Time.Clock as Time+import Data.Time.Format (formatTime, parseTimeM, defaultTimeLocale)+import Data.Word (Word16)+import qualified Network.URI as URI+import qualified Text.XML.HXT.Arrow.Pickle.Schema as XPS+import Text.XML.HXT.DOM.QualifiedName (isNCName)+import qualified Text.XML.HXT.DOM.XmlNode as XN+import qualified Text.XML.HXT.XMLSchema.DataTypeLibW3CNames as XSD++import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP++-- |§3.2.1+type String = [Char]++xpString :: XP.PU String+xpString = XP.xpTextDT (XPS.scDTxsd XSD.xsd_string [])++-- |§3.2.1+type Boolean = Bool++xpBoolean :: XP.PU Boolean+xpBoolean = XP.xpWrapEither+ ( \s -> case s of+ "true" -> Right True+ "false" -> Right False+ "1" -> Right True+ "0" -> Right False+ _ -> Left "invalid boolean"+ , \b -> if b then "true" else "false"+ ) $ XP.xpTextDT $ XPS.scDTxsd XSD.xsd_boolean []++-- |§3.2.6 specifies a complete ISO8601 6-component duration; for SAML2 purposes we don't overly care+type Duration = Time.NominalDiffTime++xpDuration :: XP.PU Duration+xpDuration = XP.xpWrapEither+ ( maybe (Left "invalid duration") (Right . realToFrac) . prd+ , \t -> (if signum t < 0 then ('-':) else id)+ $ 'P':'T': showFixed True (abs $ realToFrac t :: Pico) ++ "S"+ ) $ XP.xpTextDT $ XPS.scDTxsd XSD.xsd_duration [] where+ prd ('-':s) = negate <$> prp s+ prd ('+':s) = prp s+ prd s = prp s+ prp ('P':s) = pru (0 :: Pico) prt [('Y',31556952),('M',2629746),('D',86400)] s+ prp _ = Nothing + prt x "" = Just x+ prt x ('T':s) = pru x prs [('H',3600),('M',60)] s+ prt _ _ = Nothing+ prs x "" = Just x+ prs x s = case span isDigit s of+ (d@(_:_),'.':(span isDigit -> (p,"S"))) -> Just $ x + read (d ++ '.' : p)+ (d@(_:_),"S") -> Just $ x + read d+ _ -> Nothing+ pru x c ul s = case span isDigit s of+ (d@(_:_),uc:sr) | (_,uv):ur <- dropWhile ((uc /=) . fst) ul -> pru (x + uv * read d) c ur sr+ _ -> c x s++-- |§3.2.7 theoretically allows timezones, but SAML2 does not use them+type DateTime = Time.UTCTime++xpDateTime :: XP.PU DateTime+xpDateTime = XP.PU+ { XP.theSchema = XPS.scDTxsd XSD.xsd_dateTime []+ , XP.appPickle = XP.putCont . XN.mkText . formatTime defaultTimeLocale fmt+ , XP.appUnPickle = XP.getCont >>= XP.liftMaybe "dateTime expects text" . XN.getText >>= parseTimeM True defaultTimeLocale fmtz+ }+ where+ fmt = "%0Y-%m-%dT%H:%M:%S%Q"+ fmtz = fmt ++ "%Z"++-- |§3.2.16+type Base64Binary = BS.ByteString++xpBase64Binary :: XP.PU Base64Binary+xpBase64Binary = XP.xpWrapEither+ ( B64.decode . BS.pack . filter (not . isXmlSpaceChar)+ , BS.unpack . B64.encode+ ) $ XP.xpText0DT $ XPS.scDTxsd XSD.xsd_base64Binary []++-- |§3.2.17+type AnyURI = URI.URI++xpAnyURI :: XP.PU AnyURI+xpAnyURI = XP.xpWrapEither + ( maybe (Left "invalid anyURI") Right . URI.parseURIReference+ , \u -> URI.uriToString id u "")+ $ XP.xpText0DT $ XPS.scDTxsd XSD.xsd_anyURI []++-- |§3.3.1+type NormalizedString = String+-- |§3.3.2+type Token = NormalizedString+-- |§3.3.3+type Language = Token++xpLanguage :: XP.PU Language+xpLanguage = XP.xpTextDT $ XPS.scDTxsd XSD.xsd_language []++-- |§3.3.4+type NMTOKEN = Token++isNMTOKEN :: Token -> Bool+isNMTOKEN [] = False+isNMTOKEN s = all isXmlNameChar s++xpNMTOKEN :: XP.PU NMTOKEN+xpNMTOKEN = XP.xpWrapEither+ ( \x -> if isNMTOKEN x then Right x else Left "NMTOKEN expected"+ , id+ ) $ XP.xpTextDT $ XPS.scDTxsd XSD.xsd_NMTOKEN []++-- |§3.3.5+type NMTOKENS = [NMTOKEN]++xpNMTOKENS :: XP.PU NMTOKENS+xpNMTOKENS = XP.xpWrapEither+ ( \x -> case words x of+ [] -> Left "NMTOKENS expected"+ l | all isNMTOKEN l -> Right l+ _ -> Left "NMTOKENS expected"+ , unwords+ ) $ XP.xpTextDT $ XPS.scDTxsd XSD.xsd_NMTOKENS []++-- |§3.3.8+type ID = String+type NCName = String++xpNCName :: XP.PU NCName+xpNCName = XP.xpWrapEither+ ( \x -> if isNCName x then Right x else Left "NCName expected"+ , id+ ) $ XP.xpTextDT $ XPS.scDTxsd XSD.xsd_NCName []++xpID :: XP.PU ID+xpID = xpNCName{ XP.theSchema = XPS.scDTxsd XSD.xsd_ID [] }++-- |§3.3.13+xpInteger :: XP.PU Integer+xpInteger = XP.xpPrim{ XP.theSchema = XPS.scDTxsd XSD.xsd_integer [] }++-- |§3.3.20+type NonNegativeInteger = Word++xpNonNegativeInteger :: XP.PU NonNegativeInteger+xpNonNegativeInteger = XP.xpPrim{ XP.theSchema = XPS.scDTxsd XSD.xsd_nonNegativeInteger [] }++-- |§3.3.23+type UnsignedShort = Word16++xpUnsignedShort :: XP.PU UnsignedShort+xpUnsignedShort = XP.xpPrim{ XP.theSchema = XPS.scDTxsd XSD.xsd_unsignedShort [] }++-- |§3.3.20+type PositiveInteger = NonNegativeInteger++xpPositiveInteger :: XP.PU PositiveInteger+xpPositiveInteger = XP.xpWrapEither+ ( \x -> if x > 0 then Right x else Left "0 is not positive"+ , id+ ) $ XP.xpPrim{ XP.theSchema = XPS.scDTxsd XSD.xsd_positiveInteger [] }+
+ SAML2/XML/Signature.hs view
@@ -0,0 +1,206 @@+{-# 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 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 n = HXT.isElem HXT.>>> HXT.hasQName (mkNName ns n)++getID :: HXT.ArrowXml a => String -> a HXT.XmlTree HXT.XmlTree+getID = HXT.deep . HXT.hasAttrValue "ID" . (==)++applyCanonicalization :: CanonicalizationMethod -> Maybe String -> HXT.XmlTree -> IO BS.ByteString+applyCanonicalization (CanonicalizationMethod (Identified a) ins []) = canonicalize a ins+applyCanonicalization m = fail $ "applyCanonicalization: unsupported " ++ show m++applyTransformsBytes :: [Transform] -> BSL.ByteString -> IO BSL.ByteString+applyTransformsBytes [] = return+applyTransformsBytes (t : _) = fail ("applyTransforms: unsupported Signature " ++ show t)++applyTransformsXML :: [Transform] -> HXT.XmlTree -> IO BSL.ByteString+applyTransformsXML (Transform (Identified (TransformCanonicalization a)) ins x : tl) =+ applyTransformsBytes tl . BSL.fromStrict+ <=< applyCanonicalization (CanonicalizationMethod (Identified a) ins (map (XP.pickleDoc XP.xpickle) x)) Nothing+applyTransformsXML (Transform (Identified TransformEnvelopedSignature) Nothing [] : tl) =+ -- XXX assumes "this" signature in top-level+ applyTransformsXML tl+ . head . HXT.runLA (HXT.processChildren $ HXT.processChildren+ $ HXT.neg (isDSElem "Signature"))+applyTransformsXML tl = applyTransformsBytes tl . DOM.xshowBlob . return++applyTransforms :: Maybe Transforms -> HXT.XmlTree -> IO BSL.ByteString+applyTransforms = applyTransformsXML . maybe [] (NonEmpty.toList . transforms)++asType :: a -> proxy a -> proxy a+asType _ = id++applyDigest :: DigestMethod -> BSL.ByteString -> BS.ByteString+applyDigest (DigestMethod (Identified DigestSHA1) []) = BA.convert . asType SHA1 . hashlazy+applyDigest (DigestMethod (Identified DigestSHA256) []) = BA.convert . asType SHA256 . hashlazy+applyDigest (DigestMethod (Identified DigestSHA512) []) = BA.convert . asType SHA512 . hashlazy+applyDigest (DigestMethod (Identified DigestRIPEMD160) []) = BA.convert . asType RIPEMD160 . hashlazy+applyDigest d = error $ "unsupported " ++ show d++generateReference :: Reference -> HXT.XmlTree -> IO Reference+generateReference r x = do+ t <- applyTransforms (referenceTransforms r) x+ let d = applyDigest (referenceDigestMethod r) t+ return r+ { referenceDigestValue = d }++verifyReference :: Reference -> HXT.XmlTree -> IO (Maybe String)+verifyReference r doc = case referenceURI r of+ Just URI{ uriScheme = "", uriAuthority = Nothing, uriPath = "", uriQuery = "", uriFragment = '#':xid }+ | x@[_] <- HXT.runLA (getID xid) doc -> do+ t <- applyTransforms (referenceTransforms r) $ DOM.mkRoot [] x+ return $ xid <$ guard (applyDigest (referenceDigestMethod r) t == referenceDigestValue r)+ _ -> return Nothing++data SigningKey+ = SigningKeyDSA DSA.KeyPair+ | SigningKeyRSA RSA.KeyPair+ deriving (Eq, Show)++data PublicKeys = PublicKeys+ { publicKeyDSA :: Maybe DSA.PublicKey+ , publicKeyRSA :: Maybe RSA.PublicKey+ } deriving (Eq, Show)++instance Monoid PublicKeys where+ mempty = PublicKeys Nothing Nothing+ PublicKeys dsa1 rsa1 `mappend` PublicKeys dsa2 rsa2 =+ PublicKeys (dsa1 <|> dsa2) (rsa1 <|> rsa2)++signingKeySignatureAlgorithm :: SigningKey -> SignatureAlgorithm+signingKeySignatureAlgorithm (SigningKeyDSA _) = SignatureDSA_SHA1+signingKeySignatureAlgorithm (SigningKeyRSA _) = SignatureRSA_SHA1++signingKeyValue :: SigningKey -> KeyValue+signingKeyValue (SigningKeyDSA (DSA.toPublicKey -> DSA.PublicKey p y)) = DSAKeyValue+ { dsaKeyValuePQ = Just (DSA.params_p p, DSA.params_q p)+ , dsaKeyValueG = Just (DSA.params_g p)+ , dsaKeyValueY = y+ , dsaKeyValueJ = Nothing+ , dsaKeyValueSeedPgenCounter = Nothing+ }+signingKeyValue (SigningKeyRSA (RSA.toPublicKey -> RSA.PublicKey _ n e)) = RSAKeyValue+ { rsaKeyValueModulus = n+ , rsaKeyValueExponent = e+ }++publicKeyValues :: KeyValue -> PublicKeys+publicKeyValues DSAKeyValue{ dsaKeyValuePQ = Just (p, q), dsaKeyValueG = Just g, dsaKeyValueY = y } = mempty+ { publicKeyDSA = Just $ DSA.PublicKey+ { DSA.public_params = DSA.Params+ { DSA.params_p = p+ , DSA.params_q = q+ , DSA.params_g = g+ }+ , DSA.public_y = y+ }+ }+publicKeyValues RSAKeyValue{ rsaKeyValueModulus = n, rsaKeyValueExponent = e } = mempty+ { publicKeyRSA = Just $ RSA.PublicKey (numBytes n) n e+ }+publicKeyValues _ = mempty++signBytes :: SigningKey -> BS.ByteString -> IO BS.ByteString+signBytes (SigningKeyDSA k) b = do+ s <- DSA.sign (DSA.toPrivateKey k) SHA1 b+ return $ i2ospOf_ 20 (DSA.sign_r s) <> i2ospOf_ 20 (DSA.sign_s s)+signBytes (SigningKeyRSA k) b =+ either (fail . show) return =<< RSA.signSafer (Just SHA1) (RSA.toPrivateKey k) b++verifyBytes :: PublicKeys -> IdentifiedURI SignatureAlgorithm -> BS.ByteString -> BS.ByteString -> Maybe Bool+verifyBytes PublicKeys{ publicKeyDSA = Just k } (Identified SignatureDSA_SHA1) sig m = Just $+ BS.length sig == 40 &&+ DSA.verify SHA1 k DSA.Signature{ DSA.sign_r = os2ip r, DSA.sign_s = os2ip s } m+ where (r, s) = BS.splitAt 20 sig+verifyBytes PublicKeys{ publicKeyRSA = Just k } (Identified SignatureRSA_SHA1) sig m = Just $+ RSA.verify (Just SHA1) k m sig+verifyBytes _ _ _ _ = Nothing++signBase64 :: SigningKey -> BS.ByteString -> IO BS.ByteString+signBase64 sk = fmap Base64.encode . signBytes sk++verifyBase64 :: PublicKeys -> IdentifiedURI SignatureAlgorithm -> BS.ByteString -> BS.ByteString -> Maybe Bool+verifyBase64 pk alg m = either (const $ Just False) (verifyBytes pk alg m) . Base64.decode where++generateSignature :: SigningKey -> SignedInfo -> IO Signature+generateSignature sk si = do+ -- XXX: samlToDoc may not match later+ six <- applyCanonicalization (signedInfoCanonicalizationMethod si) Nothing $ samlToDoc si+ sv <- signBytes sk six+ return Signature+ { signatureId = Nothing+ , signatureSignedInfo = si+ , signatureSignatureValue = SignatureValue Nothing sv+ , signatureKeyInfo = Just $ KeyInfo Nothing $ KeyInfoKeyValue (signingKeyValue sk) NonEmpty.:| []+ , signatureObject = []+ }++verifySignature :: PublicKeys -> String -> HXT.XmlTree -> IO (Maybe Bool)+verifySignature pks xid doc = do+ x <- case HXT.runLA (getID xid) doc of+ [x] -> return x+ _ -> fail "verifySignature: element not found"+ sx <- case child "Signature" x of+ [sx] -> return sx+ _ -> fail "verifySignature: Signature not found"+ s@Signature{ signatureSignedInfo = si } <- either fail return $ docToSAML sx+ six <- applyCanonicalization (signedInfoCanonicalizationMethod si) (Just xpath) $ DOM.mkRoot [] [x]+ rl <- mapM (`verifyReference` x) (signedInfoReference si)+ let keys = pks <> foldMap (foldMap keyinfo . keyInfoElements) (signatureKeyInfo s)+ return $ ((any (Just xid ==) rl && all isJust rl) &&)+ <$> verifyBytes keys (signatureMethodAlgorithm $ signedInfoSignatureMethod si) (signatureValue $ signatureSignatureValue s) six+ where+ child n = HXT.runLA $ HXT.getChildren HXT.>>> isDSElem n HXT.>>> HXT.cleanupNamespaces HXT.collectPrefixUriPairs+ keyinfo (KeyInfoKeyValue kv) = publicKeyValues kv+ keyinfo (X509Data l) = foldMap keyx509d l+ keyinfo _ = mempty+ keyx509d (X509Certificate sc) = keyx509p $ X509.certPubKey $ X509.getCertificate sc+ keyx509d _ = mempty+ keyx509p (X509.PubKeyRSA r) = mempty{ publicKeyRSA = Just r }+ keyx509p (X509.PubKeyDSA d) = mempty{ publicKeyDSA = Just d }+ keyx509p _ = mempty+ xpathsel t = "/*[local-name()='" ++ t ++ "' and namespace-uri()='" ++ namespaceURIString ns ++ "']"+ xpathbase = "/*" ++ xpathsel "Signature" ++ xpathsel "SignedInfo" ++ "//"+ xpath = xpathbase ++ ". | " ++ xpathbase ++ "@* | " ++ xpathbase ++ "namespace::*"
+ SAML2/XML/Signature/Types.hs view
@@ -0,0 +1,613 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- |+-- XML Signature Syntax and Processing+--+-- <http://www.w3.org/TR/xmldsig-core1/> (selected portions)+module SAML2.XML.Signature.Types where++import Control.Lens (Lens')+import Crypto.Number.Serialize (i2osp, os2ip)+import qualified Data.X509 as X509++import SAML2.XML+import qualified SAML2.XML.Schema as XS+import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP+import qualified SAML2.XML.Canonical as C14N+import SAML2.XML.ASN1++nsFrag :: String -> URI+nsFrag = httpURI "www.w3.org" "/2000/09/xmldsig" "" . ('#':)++nsFrag11 :: String -> URI+nsFrag11 = httpURI "www.w3.org" "/2009/xmldsig11" "" . ('#':)++ns :: Namespace +ns = mkNamespace "ds" $ nsFrag ""++ns11 :: Namespace +ns11 = mkNamespace "dsig11" $ nsFrag11 ""++xpElem :: String -> XP.PU a -> XP.PU a+xpElem = xpTrimElemNS ns++xpElem11 :: String -> XP.PU a -> XP.PU a+xpElem11 = xpTrimElemNS ns11++-- |§4.1+type CryptoBinary = Integer -- as Base64Binary++xpCryptoBinary :: XP.PU CryptoBinary+xpCryptoBinary = XP.xpWrap (os2ip, i2osp) XS.xpBase64Binary++-- |§4.2+data Signature = Signature+ { signatureId :: Maybe ID+ , signatureSignedInfo :: SignedInfo+ , signatureSignatureValue :: SignatureValue+ , signatureKeyInfo :: Maybe KeyInfo+ , signatureObject :: [Object]+ } deriving (Eq, Show)++instance XP.XmlPickler Signature where+ xpickle = xpElem "Signature" $+ [XP.biCase|((((i, s), v), k), o) <-> Signature i s v k o|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpList XP.xpickle)++class Signable a where+ signature' :: Lens' a (Maybe Signature)+ signedID :: a -> XS.ID++-- |§4.3+data SignatureValue = SignatureValue+ { signatureValueId :: Maybe ID+ , signatureValue :: XS.Base64Binary+ } deriving (Eq, Show)++instance XP.XmlPickler SignatureValue where+ xpickle = xpElem "SignatureValue" $+ [XP.biCase|(i, v) <-> SignatureValue i v|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XS.xpBase64Binary)++-- |§4.4+data SignedInfo = SignedInfo+ { signedInfoId :: Maybe ID+ , signedInfoCanonicalizationMethod :: CanonicalizationMethod+ , signedInfoSignatureMethod :: SignatureMethod+ , signedInfoReference :: List1 Reference+ } deriving (Eq, Show)++instance XP.XmlPickler SignedInfo where+ xpickle = xpElem "SignedInfo" $+ [XP.biCase|(((i, c), s), r) <-> SignedInfo i c s r|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpList1 XP.xpickle)++-- |§4.4.1+data CanonicalizationMethod = CanonicalizationMethod + { canonicalizationMethodAlgorithm :: IdentifiedURI C14N.CanonicalizationAlgorithm+ , canonicalizationMethodInclusiveNamespaces :: Maybe C14N.InclusiveNamespaces+ , canonicalizationMethod :: Nodes+ } deriving (Eq, Show)++instance XP.XmlPickler CanonicalizationMethod where+ xpickle = xpElem "CanonicalizationMethod" $+ [XP.biCase|((a, n), x) <-> CanonicalizationMethod a n x|] + XP.>$< (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpAnyCont)++simpleCanonicalization :: C14N.CanonicalizationAlgorithm -> CanonicalizationMethod+simpleCanonicalization a = CanonicalizationMethod (Identified a) Nothing []++-- |§4.4.2+data SignatureMethod = SignatureMethod+ { signatureMethodAlgorithm :: IdentifiedURI SignatureAlgorithm+ , signatureMethodHMACOutputLength :: Maybe Int+ , signatureMethod :: Nodes+ } deriving (Eq, Show)++instance XP.XmlPickler SignatureMethod where+ xpickle = xpElem "SignatureMethod" $+ [XP.biCase|((a, l), x) <-> SignatureMethod a l x|] + XP.>$< (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XP.xpOption (xpElem "HMACOutputLength" XP.xpickle)+ XP.>*< XP.xpAnyCont)++-- |§4.4.3+data Reference = Reference+ { referenceId :: Maybe ID+ , referenceURI :: Maybe AnyURI+ , referenceType :: Maybe AnyURI -- xml object type+ , referenceTransforms :: Maybe Transforms+ , referenceDigestMethod :: DigestMethod+ , referenceDigestValue :: XS.Base64Binary -- ^§4.3.3.6+ } deriving (Eq, Show)++instance XP.XmlPickler Reference where+ xpickle = xpElem "Reference" $+ [XP.biCase|(((((i, u), t), f), m), v) <-> Reference i u t f m v|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpAttrImplied "URI" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "Type" XS.xpAnyURI+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpElem "DigestValue" XS.xpBase64Binary)++-- |§4.4.3.4+newtype Transforms = Transforms{ transforms :: List1 Transform }+ deriving (Eq, Show)++instance XP.XmlPickler Transforms where+ xpickle = xpElem "Transforms" $+ [XP.biCase|l <-> Transforms l|]+ XP.>$< xpList1 XP.xpickle++data Transform = Transform+ { transformAlgorithm :: IdentifiedURI TransformAlgorithm+ , transformInclusiveNamespaces :: Maybe C14N.InclusiveNamespaces+ , transform :: [TransformElement]+ } deriving (Eq, Show)++instance XP.XmlPickler Transform where+ xpickle = xpElem "Transform" $+ [XP.biCase|((a, n), l) <-> Transform a n l|]+ XP.>$< (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XP.xpOption XP.xpickle+ XP.>*< XP.xpList XP.xpickle)++simpleTransform :: TransformAlgorithm -> Transform+simpleTransform a = Transform (Identified a) Nothing []++data TransformElement+ = TransformElementXPath XString+ | TransformElement Node + deriving (Eq, Show)++instance XP.XmlPickler TransformElement where+ xpickle = [XP.biCase|+ Left s <-> TransformElementXPath s+ Right x <-> TransformElement x |]+ XP.>$< (xpElem "XPath" XS.xpString+ XP.>|< xpTrimAnyElem)++-- |§4.4.3.5+data DigestMethod = DigestMethod+ { digestAlgorithm :: IdentifiedURI DigestAlgorithm+ , digest :: [Node]+ } deriving (Eq, Show)++instance XP.XmlPickler DigestMethod where+ xpickle = xpElem "DigestMethod" $+ [XP.biCase|(a, d) <-> DigestMethod a d|]+ XP.>$< (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XP.xpAnyCont)++simpleDigest :: DigestAlgorithm -> DigestMethod+simpleDigest a = DigestMethod (Identified a) []++-- |§4.5+data KeyInfo = KeyInfo+ { keyInfoId :: Maybe ID+ , keyInfoElements :: List1 KeyInfoElement+ } deriving (Eq, Show)++xpKeyInfoType :: XP.PU KeyInfo+xpKeyInfoType = [XP.biCase|(i, l) <-> KeyInfo i l|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< xpList1 XP.xpickle)++instance XP.XmlPickler KeyInfo where+ xpickle = xpElem "KeyInfo" xpKeyInfoType++data KeyInfoElement+ = KeyName XString -- ^§4.5.1+ | KeyInfoKeyValue KeyValue -- ^§4.5.2+ | RetrievalMethod+ { retrievalMethodURI :: URI+ , retrievalMethodType :: Maybe URI+ , retrievalMethodTransforms :: Maybe Transforms+ } -- ^§4.5.3+ | X509Data+ { x509Data :: List1 X509Element+ } -- ^§4.5.4+ | PGPData+ { pgpKeyID :: Maybe XS.Base64Binary+ , pgpKeyPacket :: Maybe XS.Base64Binary+ , pgpData :: Nodes+ } -- ^§4.5.5+ | SPKIData + { spkiData :: List1 SPKIElement+ } -- ^§4.5.6+ | MgmtData XString -- ^§4.5.7+ | KeyInfoElement Node+ deriving (Eq, Show)++instance XP.XmlPickler KeyInfoElement where+ xpickle = [XP.biCase|+ Left (Left (Left (Left (Left (Left (Left n)))))) <-> KeyName n+ Left (Left (Left (Left (Left (Left (Right v)))))) <-> KeyInfoKeyValue v+ Left (Left (Left (Left (Left (Right ((u, t), f)))))) <-> RetrievalMethod u t f+ Left (Left (Left (Left (Right l)))) <-> X509Data l+ Left (Left (Left (Right ((i, p), x)))) <-> PGPData i p x+ Left (Left (Right l)) <-> SPKIData l+ Left (Right m) <-> MgmtData m+ Right x <-> KeyInfoElement x|]+ XP.>$< (xpElem "KeyName" XS.xpString+ XP.>|< XP.xpickle+ XP.>|< xpElem "RetrievalMethod"+ (XP.xpAttr "URI" XS.xpAnyURI+ XP.>*< XP.xpAttrImplied "Type" XS.xpAnyURI+ XP.>*< XP.xpOption XP.xpickle)+ XP.>|< xpElem "X509Data" (xpList1 XP.xpickle)+ XP.>|< xpElem "PGPData"+ (XP.xpOption (xpElem "PGPKeyID" XS.xpBase64Binary)+ XP.>*< XP.xpOption (xpElem "PGPKeyPacket" XS.xpBase64Binary)+ XP.>*< XP.xpList xpTrimAnyElem)+ XP.>|< xpElem "SPKIData" (xpList1 XP.xpickle)+ XP.>|< xpElem "MgmtData" XS.xpString+ XP.>|< XP.xpTree)++-- |§4.5.2+data KeyValue+ = DSAKeyValue+ { dsaKeyValuePQ :: Maybe (CryptoBinary, CryptoBinary)+ , dsaKeyValueG :: Maybe CryptoBinary+ , dsaKeyValueY :: CryptoBinary+ , dsaKeyValueJ :: Maybe CryptoBinary+ , dsaKeyValueSeedPgenCounter :: Maybe (CryptoBinary, CryptoBinary)+ } -- ^§4.5.2.1+ | RSAKeyValue+ { rsaKeyValueModulus+ , rsaKeyValueExponent :: CryptoBinary+ } -- ^§4.5.2.2+ | ECKeyValue+ { ecKeyValueId :: Maybe XS.ID+ , ecKeyValue :: ECKeyValue+ , ecKeyValuePublicKey :: ECPoint+ } -- ^§4.5.2.3+ | KeyValue Node+ deriving (Eq, Show)++instance XP.XmlPickler KeyValue where+ xpickle = xpElem "KeyValue" $+ [XP.biCase|+ Left (Left (Left ((((pq, g), y), j), sp))) <-> DSAKeyValue pq g y j sp+ Left (Left (Right (m, e))) <-> RSAKeyValue m e+ Left (Right ((i, v), p)) <-> ECKeyValue i v p+ Right x <-> KeyValue x|]+ XP.>$< (xpElem "DSAKeyValue" + (XP.xpOption+ (xpElem "P" xpCryptoBinary+ XP.>*< xpElem "Q" xpCryptoBinary)+ XP.>*< XP.xpOption (xpElem "G" xpCryptoBinary)+ XP.>*< xpElem "Y" xpCryptoBinary+ XP.>*< XP.xpOption (xpElem "J" xpCryptoBinary)+ XP.>*< (XP.xpOption+ (xpElem "Seed" xpCryptoBinary+ XP.>*< xpElem "PgenCounter" xpCryptoBinary)))+ XP.>|< xpElem "RSAKeyValue" + (xpElem "Modulus" xpCryptoBinary+ XP.>*< xpElem "Exponent" xpCryptoBinary)+ XP.>|< xpElem11 "ECKeyValue"+ (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpickle+ XP.>*< xpElem11 "PublicKey" xpCryptoBinary)+ XP.>|< XP.xpTree)++data ECKeyValue+ = ECParameters+ { ecParametersFieldID :: ECFieldID+ , ecParametersCurve :: ECCurve+ , ecParametersBase :: ECPoint+ , ecParametersOrder :: CryptoBinary+ , ecParametersCoFactor :: Maybe Integer+ , ecParametersValidationData :: Maybe ECValidationData+ } -- ^§4.5.2.3.1+ | ECNamedCurve+ { ecNamedCurveURI :: XS.AnyURI+ }+ deriving (Eq, Show)++type ECPoint = CryptoBinary++instance XP.XmlPickler ECKeyValue where+ xpickle =+ [XP.biCase|+ Left (((((f, c), b), o), cf), vd) <-> ECParameters f c b o cf vd+ Right u <-> ECNamedCurve u|]+ XP.>$< (xpElem11 "ECParameters" + (XP.xpickle+ XP.>*< XP.xpickle+ XP.>*< xpElem11 "Base" xpCryptoBinary+ XP.>*< xpElem11 "Order" xpCryptoBinary+ XP.>*< XP.xpOption (xpElem11 "CoFactor" XS.xpInteger)+ XP.>*< XP.xpOption XP.xpickle)+ XP.>|< xpElem11 "NamedCurve"+ (XP.xpAttr "URI" XS.xpAnyURI))++data ECFieldID+ = ECPrime+ { ecP :: CryptoBinary+ }+ | ECTnB+ { ecM :: XS.PositiveInteger+ , ecK :: XS.PositiveInteger+ }+ | ECPnB+ { ecM :: XS.PositiveInteger+ , ecK1, ecK2, ecK3 :: XS.PositiveInteger+ }+ | ECGnB+ { ecM :: XS.PositiveInteger+ }+ | ECFieldID Node+ deriving (Eq, Show)++instance XP.XmlPickler ECFieldID where+ xpickle = xpElem11 "FieldID" $+ [XP.biCase|+ Left (Left (Left (Left p))) <-> ECPrime p+ Left (Left (Left (Right (m, k)))) <-> ECTnB m k+ Left (Left (Right (((m, k1), k2), k3))) <-> ECPnB m k1 k2 k3+ Left (Right m) <-> ECGnB m+ Right x <-> ECFieldID x|]+ XP.>$< (xpElem11 "Prime" + (xpElem11 "P" xpCryptoBinary)+ XP.>|< xpElem11 "TnB" + (xpElem11 "M" XS.xpPositiveInteger+ XP.>*< xpElem11 "K" XS.xpPositiveInteger)+ XP.>|< xpElem11 "PnB" + (xpElem11 "M" XS.xpPositiveInteger+ XP.>*< xpElem11 "K1" XS.xpPositiveInteger+ XP.>*< xpElem11 "K2" XS.xpPositiveInteger+ XP.>*< xpElem11 "K3" XS.xpPositiveInteger)+ XP.>|< xpElem11 "GnB" + (xpElem11 "M" XS.xpPositiveInteger)+ XP.>|< xpTrimAnyElem)++data ECCurve = ECCurve+ { ecCurveA, ecCurveB :: CryptoBinary+ } deriving (Eq, Show)++instance XP.XmlPickler ECCurve where+ xpickle = xpElem11 "Curve" $+ [XP.biCase|+ (a, b) <-> ECCurve a b|]+ XP.>$< (xpElem11 "A" xpCryptoBinary+ XP.>*< xpElem11 "B" xpCryptoBinary)++data ECValidationData = ECValidationData+ { ecValidationDataHashAlgorithm :: AnyURI+ , ecValidationDataSeed :: CryptoBinary+ } deriving (Eq, Show)++instance XP.XmlPickler ECValidationData where+ xpickle = xpElem11 "ValidationData" $+ [XP.biCase|+ (a, s) <-> ECValidationData a s|]+ XP.>$< (XP.xpAttr "hashAlgorithm" XS.xpAnyURI+ XP.>*< xpElem11 "seed" xpCryptoBinary)++-- |§4.5.4.1+type X509DistinguishedName = XString++xpX509DistinguishedName :: XP.PU X509DistinguishedName+xpX509DistinguishedName = XS.xpString++data X509Element+ = X509IssuerSerial+ { x509IssuerName :: X509DistinguishedName+ , x509SerialNumber :: Int+ }+ | X509SKI XS.Base64Binary+ | X509SubjectName X509DistinguishedName+ | X509Certificate X509.SignedCertificate+ | X509CRL X509.SignedCRL+ | X509Digest+ { x509DigestAlgorithm :: IdentifiedURI DigestAlgorithm+ , x509Digest :: XS.Base64Binary+ }+ | X509Element Node+ deriving (Eq, Show)++instance XP.XmlPickler X509Element where+ xpickle = [XP.biCase|+ Left (Left (Left (Left (Left (Left (n, i)))))) <-> X509IssuerSerial n i+ Left (Left (Left (Left (Left (Right n))))) <-> X509SubjectName n+ Left (Left (Left (Left (Right b)))) <-> X509SKI b+ Left (Left (Left (Right b))) <-> X509Certificate b+ Left (Left (Right b)) <-> X509CRL b+ Left (Right (a, d)) <-> X509Digest a d+ Right x <-> X509Element x|]+ XP.>$< (xpElem "X509IssuerSerial"+ (xpElem "X509IssuerName" xpX509DistinguishedName+ XP.>*< xpElem "X509SerialNumber" XP.xpickle)+ XP.>|< xpElem "X509SubjectName" xpX509DistinguishedName+ XP.>|< xpElem "X509SKI" XS.xpBase64Binary+ XP.>|< xpElem "X509Certificate" xpX509Signed+ XP.>|< xpElem "X509CRL" xpX509Signed+ XP.>|< xpElem11 "X509Digest"+ (XP.xpAttr "Algorithm" XP.xpickle+ XP.>*< XS.xpBase64Binary)+ XP.>|< xpTrimAnyElem)++-- |§4.4.6+data SPKIElement+ = SPKISexp XS.Base64Binary+ | SPKIElement Node+ deriving (Eq, Show)++instance XP.XmlPickler SPKIElement where+ xpickle = [XP.biCase|+ Left b <-> SPKISexp b+ Right x <-> SPKIElement x|]+ XP.>$< (xpElem "SPKISexp" XS.xpBase64Binary+ XP.>|< xpTrimAnyElem)++-- |§4.5+data Object = Object+ { objectId :: Maybe ID+ , objectMimeType :: Maybe XString+ , objectEncoding :: Maybe (IdentifiedURI EncodingAlgorithm)+ , objectXML :: [ObjectElement]+ } deriving (Eq, Show)++instance XP.XmlPickler Object where+ xpickle = xpElem "Object" $+ [XP.biCase|(((i, m), e), x) <-> Object i m e x|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpAttrImplied "MimeType" XS.xpString+ XP.>*< XP.xpAttrImplied "Encoding" XP.xpickle+ XP.>*< XP.xpList XP.xpickle)++data ObjectElement+ = ObjectSignature Signature+ | ObjectSignatureProperties SignatureProperties+ | ObjectManifest Manifest+ | ObjectElement Node+ deriving (Eq, Show)++instance XP.XmlPickler ObjectElement where+ xpickle = [XP.biCase|+ Left (Left (Left s)) <-> ObjectSignature s+ Left (Left (Right p)) <-> ObjectSignatureProperties p+ Left (Right m) <-> ObjectManifest m+ Right x <-> ObjectElement x|]+ XP.>$< (XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpickle+ XP.>|< XP.xpTree)++-- |§5.1+data Manifest = Manifest+ { manifestId :: Maybe ID+ , manifestReferences :: List1 Reference+ } deriving (Eq, Show)++instance XP.XmlPickler Manifest where+ xpickle = xpElem "Manifest" $+ [XP.biCase|(i, r) <-> Manifest i r|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< xpList1 XP.xpickle)++-- |§5.2+data SignatureProperties = SignatureProperties+ { signaturePropertiesId :: Maybe ID+ , signatureProperties :: List1 SignatureProperty+ } deriving (Eq, Show)++instance XP.XmlPickler SignatureProperties where+ xpickle = xpElem "SignatureProperties" $+ [XP.biCase|(i, p) <-> SignatureProperties i p|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< xpList1 XP.xpickle)++data SignatureProperty = SignatureProperty+ { signaturePropertyId :: Maybe ID+ , signaturePropertyTarget :: AnyURI+ , signatureProperty :: List1 Node+ } deriving (Eq, Show)++instance XP.XmlPickler SignatureProperty where+ xpickle = xpElem "SignatureProperty" $+ [XP.biCase|((i, t), x) <-> SignatureProperty i t x|] + XP.>$< (XP.xpAttrImplied "Id" XS.xpID+ XP.>*< XP.xpAttr "Target" XS.xpAnyURI+ XP.>*< xpList1 XP.xpTree)++-- |§6.1+data EncodingAlgorithm+ = EncodingBase64+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI EncodingAlgorithm where+ identifier EncodingBase64 = nsFrag "base64"++-- |§6.2+data DigestAlgorithm+ = DigestSHA1 -- ^§6.2.1+ | DigestSHA224 -- ^§6.2.2+ | DigestSHA256 -- ^§6.2.3+ | DigestSHA384 -- ^§6.2.4+ | DigestSHA512 -- ^§6.2.5+ | DigestRIPEMD160 -- ^xmlenc §5.7.4+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI DigestAlgorithm where+ identifier DigestSHA1 = nsFrag "sha1"+ identifier DigestSHA224 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#sha224"+ identifier DigestSHA256 = httpURI "www.w3.org" "/2001/04/xmlenc" "" "#sha256"+ identifier DigestSHA384 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#sha384"+ identifier DigestSHA512 = httpURI "www.w3.org" "/2001/04/xmlenc" "" "#sha512"+ identifier DigestRIPEMD160 = httpURI "www.w3.org" "/2001/04/xmlenc" "" "#ripemd160"++-- |§6.3+data MACAlgorithm+ = MACHMAC_SHA1 -- ^§6.3.1+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI MACAlgorithm where+ identifier MACHMAC_SHA1 = nsFrag "hmac-sha1"++-- |§6.4+data SignatureAlgorithm+ = SignatureDSA_SHA1+ | SignatureDSA_SHA256+ | SignatureRSA_SHA1+ | SignatureRSA_SHA224+ | SignatureRSA_SHA256+ | SignatureRSA_SHA384+ | SignatureRSA_SHA512+ | SignatureECDSA_SHA1+ | SignatureECDSA_SHA224+ | SignatureECDSA_SHA256+ | SignatureECDSA_SHA384+ | SignatureECDSA_SHA512+ deriving (Eq, Bounded, Enum, Show)++instance Identifiable URI SignatureAlgorithm where+ identifier SignatureDSA_SHA1 = nsFrag "dsa-sha1"+ identifier SignatureDSA_SHA256 = nsFrag11 "dsa-sha256"+ identifier SignatureRSA_SHA1 = nsFrag "rsa-sha1"+ identifier SignatureRSA_SHA224 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#rsa-sha224"+ identifier SignatureRSA_SHA256 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#rsa-sha256"+ identifier SignatureRSA_SHA384 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#rsa-sha384"+ identifier SignatureRSA_SHA512 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#rsa-sha512"+ identifier SignatureECDSA_SHA1 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#ecdsa-sha1"+ identifier SignatureECDSA_SHA224 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#ecdsa-sha224"+ identifier SignatureECDSA_SHA256 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#ecdsa-sha256"+ identifier SignatureECDSA_SHA384 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#ecdsa-sha384"+ identifier SignatureECDSA_SHA512 = httpURI "www.w3.org" "/2001/04/xmldsig-more" "" "#ecdsa-sha512"++-- |§6.6+data TransformAlgorithm+ = TransformCanonicalization C14N.CanonicalizationAlgorithm -- ^§6.6.1+ | TransformBase64 -- ^§6.6.2+ | TransformXPath -- ^§6.6.3+ | TransformEnvelopedSignature -- ^§6.6.4+ | TransformXSLT -- ^§6.6.5+ deriving (Eq, Show)++instance Identifiable URI TransformAlgorithm where+ identifier (TransformCanonicalization c) = identifier c+ identifier TransformBase64 = nsFrag "base64"+ identifier TransformXPath = httpURI "www.w3.org" "/TR/1999/REC-xpath-19991116" "" ""+ identifier TransformEnvelopedSignature = nsFrag "enveloped-signature"+ identifier TransformXSLT = httpURI "www.w3.org" "/TR/1999/REC-xslt-19991116" "" ""+ identifiedValues =+ map TransformCanonicalization identifiedValues +++ [ TransformBase64+ , TransformXSLT+ , TransformXPath+ , TransformEnvelopedSignature+ ]
+ SAML2/XML/Types.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE QuasiQuotes #-}+module SAML2.XML.Types where++import Data.List.NonEmpty (NonEmpty(..))+import Network.URI (URI(..), URIAuth(..), uriToString)+import qualified Text.XML.HXT.DOM.TypeDefs as HXT++import qualified Text.XML.HXT.Arrow.Pickle.Xml.Invertible as XP++type Node = HXT.XmlTree+-- instance XP.XmlPickler XML.Node where xpickle = XP.xpTree+type Nodes = HXT.XmlTrees+-- instance XP.XmlPickler XML.Nodes where xpickle = XP.xpTrees+type List1 a = NonEmpty a++xpList1 :: XP.PU a -> XP.PU (List1 a)+xpList1 f = [XP.biCase|a:l <-> a:|l|] XP.>$< XP.xpList1 f++type QName = HXT.QName++data Namespace = Namespace+ { namespacePrefix :: !String+ , namespaceURI :: !URI+ , namespaceURIString :: !String+ }++mkNamespace :: String -> URI -> Namespace+mkNamespace p u = Namespace p u $ uriToString id u ""++mkNName :: Namespace -> String -> QName+mkNName ns n = HXT.mkQName (namespacePrefix ns) n (namespaceURIString ns)++httpURI :: String -> String -> String -> String -> URI+httpURI host = URI "http:" $ Just $ URIAuth "" host ""++xmlNS, xmlnsNS :: Namespace+xmlNS = mkNamespace "xml" $ httpURI "www.w3.org" "/XML/1998/namespace" "" ""+xmlnsNS = mkNamespace "xmlns" $ httpURI "www.w3.org" "/2000/xmlns/" "" ""
+ SAML2/XML/libxml2_stub.c view
@@ -0,0 +1,5 @@+#include <libxml/globals.h>++void xmlFree_stub(void *p) {+ return xmlFree(p);+}
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hsaml2.cabal view
@@ -0,0 +1,109 @@+name: hsaml2+version: 0.1+synopsis: OASIS Security Assertion Markup Language (SAML) V2.0+description: Direct implementation of the SAML XML standard (https://www.oasis-open.org/standards#samlv2.0), along with some related dependencies. This is currently partial, as the standard is quite extensive, but is sufficient to build a functioning SP and fully validate responses. The module layout basically follows the standard definition documentation. Its use still requires a fairly extensive understanding of SAML.+license: Apache-2.0+license-file: LICENSE+author: Dylan Simon+maintainer: dylan@dylex.net+copyright: 2016+category: Security, Network, Web+build-type: Simple+cabal-version: >=1.10+tested-with: GHC == 7.10.3++extra-source-files:+ test/XML/signature-example.xml+ test/XML/encryption-example.xml++source-repository head+ type: git+ location: https://github.com/dylex/hsaml2++library+ exposed-modules:+ SAML2+ SAML2.XML+ SAML2.XML.Schema+ SAML2.XML.Schema.Datatypes+ SAML2.XML.Canonical+ SAML2.XML.ASN1+ SAML2.XML.Signature+ SAML2.XML.Signature.Types+ SAML2.XML.Encryption+ SAML2.XML.Types+ SAML2.Core+ SAML2.Core.Namespaces+ SAML2.Core.Datatypes+ SAML2.Core.Assertions+ SAML2.Core.Protocols+ SAML2.Core.Versioning+ SAML2.Core.Signature+ SAML2.Core.Identifiers+ SAML2.Profiles+ SAML2.Profiles.ConfirmationMethod+ SAML2.Bindings+ SAML2.Bindings.General+ SAML2.Bindings.Identifiers+ SAML2.Bindings.HTTPRedirect+ SAML2.Bindings.HTTPPOST+ SAML2.Metadata+ SAML2.Metadata.Metadata+ other-modules:+ SAML2.Lens+ SAML2.XML.LibXML2+ SAML2.Bindings.Internal+ c-sources:+ SAML2/XML/libxml2_stub.c+ build-depends: + asn1-encoding,+ asn1-types >= 0.2,+ base >=4.8 && <5,+ base64-bytestring,+ bytestring,+ cryptonite,+ data-default,+ http-types,+ hxt,+ hxt-charproperties,+ hxt-unicode,+ invertible,+ invertible-hxt,+ lens,+ memory,+ mtl,+ network-uri,+ process,+ semigroups,+ template-haskell,+ time,+ x509,+ zlib+ pkgconfig-depends: libxml-2.0+ default-language: Haskell2010+ ghc-options: -Wall++test-suite tests+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ other-modules:+ Bindings.HTTPRedirect+ Metadata.Metadata+ XML+ XML.Canonical+ XML.Encryption+ XML.Signature+ default-language: Haskell2010+ ghc-options: -Wall+ build-depends:+ base,+ bytestring,+ hsaml2,+ hxt,+ hxt-http,+ network-uri,+ semigroups,+ time,+ x509,+ HUnit
+ test/Bindings/HTTPRedirect.hs view
@@ -0,0 +1,50 @@+module Bindings.HTTPRedirect (tests) where++import qualified Data.ByteString.Char8 as BSC+import Data.Time (UTCTime(..), fromGregorian)+import qualified Test.HUnit as U++import SAML2.XML+import SAML2.Core.Versioning+import SAML2.Core.Identifiers+import SAML2.Core.Assertions+import SAML2.Core.Protocols+import SAML2.Bindings.HTTPRedirect++import XML++tests :: U.Test+tests = U.test+ [ U.TestCase $ U.assertEqual "request"+ (RequestLogoutRequest $ LogoutRequest+ (RequestAbstractType $ ProtocolType+ "d2b7c388cec36fa7c39c28fd298644a8"+ SAML20+ (UTCTime (fromGregorian 2004 1 21) (19*60*60+49))+ Nothing+ (Identified ConsentUnspecified)+ (Just $ Issuer $ simpleNameID NameIDFormatEntity "https://IdentityProvider.com/SAML")+ Nothing+ []+ (Just $ BSC.pack "0043bfc1bc45110dae17004005b13a2b"))+ Nothing+ Nothing+ (NotEncrypted $ IdentifierName $ simpleNameID NameIDFormatPersistent "005a06e0-ad82-110d-a556-004005b13a2b")+ (Just "1"))+ =<< decodeURI mempty (uri "https://ServiceProvider.com/SAML/SLO/Browser?SAMLRequest=fVFdS8MwFH0f7D%2BUvGdNsq62oSsIQyhMESc%2B%2BJYlmRbWpObeyvz3puv2IMjyFM7HPedyK1DdsZdb%2F%2BEHfLFfgwVMTt3RgTwzazIEJ72CFqRTnQWJWu7uH7dSLJjsg0ev%2FZFMlttiBWADtt6R%2BSyJr9msiRH7O70sCm31Mj%2Bo%2BC%2B1KA5GlEWeZaogSQMw2MYBKodrIhjLKONU8FdeSsZkVr6T5M0GiHMjvWCknqZXZ2OoPxF7kGnaGOuwxZ%2Fn4L9bY8NC%2By4du1XpRXnxPcXizSZ58KFTeHujEWkNPZylsh9bAMYYUjO2Uiy3jCpTCMo5M1StVjmN9SO150sl9lU6RV2Dp0vsLIy7NM7YU82r9B90PrvCf85W%2FwL8zSVQzAEAAA%3D%3D&RelayState=0043bfc1bc45110dae17004005b13a2b")+ , U.TestCase $ U.assertEqual "response"+ (LogoutResponse $ StatusResponseType+ (ProtocolType+ "b0730d21b628110d8b7e004005b13a2b"+ SAML20+ (UTCTime (fromGregorian 2004 1 21) (19*60*60+49))+ Nothing+ (Identified ConsentUnspecified)+ (Just $ Issuer $ simpleNameID NameIDFormatEntity "https://ServiceProvider.com/SAML")+ Nothing+ []+ (Just $ BSC.pack "0043bfc1bc45110dae17004005b13a2b"))+ (Just "d2b7c388cec36fa7c39c28fd298644a8")+ successStatus)+ =<< decodeURI mempty (uri "https://IdentityProvider.com/SAML/SLO/Response?SAMLResponse=fVFNa4QwEL0X%2Bh8k912TaDUGFUp7EbZQ6rKH3mKcbQVNJBOX%2FvxaXQ9tYec0vHlv3nzkqIZ%2BlAf7YSf%2FBjhagxB8Db1BuZQKMjkjrcIOpVEDoPRa1o8vB8n3VI7OeqttT1bJbbJCBOc7a8j9XTBH9VyQhqYRbTlrEi4Yo61oUqA0pvShYZHiDQkqs411tAVpeZPqSAgNOkrOas4zzcW55ZlI4liJrTXiBJVBr4wvCJ877ijbcXZkmaRUxtk7CU7gcB5mLu8pKVddvghd%2Ben9iDIMa3CXTsOrs5euBbfXdgh%2F9snDK%2FEqW69Ye%2BUnvGL%2F8CfbQnBS%2FQS3z4QLW9aT1oBIws0j%2FGOyAb9%2FV34Dw5k779IBAAA%3D&RelayState=0043bfc1bc45110dae17004005b13a2b")+ ]
+ test/Main.hs view
@@ -0,0 +1,26 @@+module Main (main) where++import System.Exit (exitSuccess, exitFailure)+import qualified Test.HUnit as U++import qualified XML.Canonical+import qualified XML.Signature+import qualified XML.Encryption+import qualified Bindings.HTTPRedirect+import qualified Metadata.Metadata++tests :: U.Test+tests = U.test+ [ U.TestLabel "XML.Canonical" XML.Canonical.tests+ , U.TestLabel "XML.Signature" XML.Signature.tests+ , U.TestLabel "XML.Encryption" XML.Encryption.tests+ , U.TestLabel "Bindings.HTTPRedirect" Bindings.HTTPRedirect.tests+ , U.TestLabel "Metadata.Metadata" Metadata.Metadata.tests+ ]++main :: IO ()+main = do+ r <- U.runTestTT tests+ if U.errors r == 0 && U.failures r == 0+ then exitSuccess+ else exitFailure
+ test/Metadata/Metadata.hs view
@@ -0,0 +1,493 @@+{-# LANGUAGE OverloadedStrings #-}+module Metadata.Metadata (tests) where++import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.X509 as X509+import qualified Test.HUnit as U+import qualified Text.XML.HXT.Arrow.Pickle.Xml as XP+import qualified Text.XML.HXT.DOM.QualifiedName as HXT+import qualified Text.XML.HXT.DOM.XmlNode as HXT++import SAML2.XML+import qualified SAML2.XML.Signature as DS+import qualified SAML2.XML.Encryption as XEnc+import SAML2.Core.Versioning+import SAML2.Core.Namespaces+import SAML2.Core.Identifiers+import qualified SAML2.Core.Assertions as SAML+import qualified SAML2.Core.Protocols as SAMLP+import SAML2.Bindings.Identifiers+import SAML2.Metadata.Metadata++import XML++tests :: U.Test+tests = U.test+ [ testXML "test/Metadata/metadata-idp.xml" $+ EntityDescriptor+ (uri "https://IdentityProvider.com/SAML")+ Nothing+ Nothing+ (Just 31729999.8)+ []+ Nothing+ (Extensions [])+ (Descriptors $+ IDPSSODescriptor+ (RoleDescriptor+ Nothing Nothing Nothing+ [namespaceURI SAMLP.ns]+ Nothing [] Nothing+ (Extensions [])+ [KeyDescriptor+ KeyTypeSigning+ (DS.KeyInfo+ Nothing+ (DS.KeyName "IdentityProvider.com SSO Key"+ :| []))+ []]+ Nothing+ [])+ (SSODescriptor+ [IndexedEndpoint+ (Endpoint+ (Identified BindingSOAP)+ (uri "https://IdentityProvider.com/SAML/Artifact")+ Nothing [] [])+ 0+ True]+ [Endpoint+ (Identified BindingSOAP)+ (uri "https://IdentityProvider.com/SAML/SLO/SOAP")+ Nothing [] []+ ,Endpoint+ (Identified BindingHTTPRedirect)+ (uri "https://IdentityProvider.com/SAML/SLO/Browser")+ (Just $ uri "https://IdentityProvider.com/SAML/SLO/Response")+ [] []]+ []+ [Identified NameIDFormatX509+ ,Identified NameIDFormatPersistent+ ,Identified NameIDFormatTransient])+ True+ (Endpoint+ (Identified BindingHTTPRedirect)+ (uri "https://IdentityProvider.com/SAML/SSO/Browser")+ Nothing [] []+ :| [Endpoint+ (Identified BindingHTTPPOST)+ (uri "https://IdentityProvider.com/SAML/SSO/Browser")+ Nothing [] []])+ [] [] []+ [ SAML.Attribute+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.6"+ (Identified AttributeNameFormatURI)+ (Just "eduPersonPrincipalName")+ [] []+ , SAML.Attribute+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.1"+ (Identified AttributeNameFormatURI)+ (Just "eduPersonAffiliation")+ []+ [ [HXT.mkText "member"]+ , [HXT.mkText "student"]+ , [HXT.mkText "faculty"]+ , [HXT.mkText "employee"]+ , [HXT.mkText "staff"]+ ]]+ :| AttributeAuthorityDescriptor+ (RoleDescriptor+ Nothing Nothing Nothing+ [namespaceURI SAMLP.ns]+ Nothing [] Nothing+ (Extensions [])+ [KeyDescriptor+ KeyTypeSigning+ (DS.KeyInfo+ Nothing+ (DS.KeyName "IdentityProvider.com AA Key"+ :| []))+ []]+ Nothing [])+ (Endpoint+ (Identified BindingSOAP)+ (uri "https://IdentityProvider.com/SAML/AA/SOAP")+ Nothing [] []+ :| [])+ [Endpoint+ (Identified BindingURI)+ (uri "https://IdentityProvider.com/SAML/AA/URI")+ Nothing [] []]+ [Identified NameIDFormatX509+ ,Identified NameIDFormatPersistent+ ,Identified NameIDFormatTransient]+ []+ [ SAML.Attribute+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.6"+ (Identified AttributeNameFormatURI)+ (Just "eduPersonPrincipalName")+ [] []+ , SAML.Attribute+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.1"+ (Identified AttributeNameFormatURI)+ (Just "eduPersonAffiliation")+ []+ [ [HXT.mkText "member"]+ , [HXT.mkText "student"]+ , [HXT.mkText "faculty"]+ , [HXT.mkText "employee"]+ , [HXT.mkText "staff"]+ ]]+ : [])+ (Just $ Organization+ []+ (Extensions [])+ (Localized "en" "Identity Providers R US" :| [])+ (Localized "en" "Identity Providers R US, a Division of Lerxst Corp." :| [])+ (Localized "en" (uri "https://IdentityProvider.com") :| []))+ []+ []++ , testXML "test/Metadata/metadata-sp.xml" $+ EntityDescriptor+ (uri "https://ServiceProvider.com/SAML")+ Nothing Nothing Nothing [] Nothing+ (Extensions [])+ (Descriptors $+ SPSSODescriptor+ (RoleDescriptor+ Nothing Nothing Nothing+ [namespaceURI SAMLP.ns]+ Nothing [] Nothing+ (Extensions [])+ [ KeyDescriptor+ KeyTypeSigning+ (DS.KeyInfo+ Nothing+ (DS.KeyName "ServiceProvider.com SSO Key"+ :| []))+ []+ , KeyDescriptor+ KeyTypeEncryption+ (DS.KeyInfo+ Nothing+ (DS.KeyName "ServiceProvider.com Encrypt Key"+ :| []))+ [XEnc.EncryptionMethod+ (Identified XEnc.KeyTransportRSA1_5)+ Nothing Nothing Nothing []]+ ]+ Nothing+ [])+ (SSODescriptor+ []+ [Endpoint+ (Identified BindingSOAP)+ (uri "https://ServiceProvider.com/SAML/SLO/SOAP")+ Nothing [] []+ ,Endpoint+ (Identified BindingHTTPRedirect)+ (uri "https://ServiceProvider.com/SAML/SLO/Browser")+ (Just $ uri "https://ServiceProvider.com/SAML/SLO/Response")+ [] []]+ []+ [Identified NameIDFormatTransient])+ True+ False+ (IndexedEndpoint+ (Endpoint+ (Identified BindingHTTPArtifact)+ (uri "https://ServiceProvider.com/SAML/SSO/Artifact")+ Nothing [] [])+ 0+ True+ :| IndexedEndpoint+ (Endpoint+ (Identified BindingHTTPPOST)+ (uri "https://ServiceProvider.com/SAML/SSO/POST")+ Nothing [] [])+ 1+ False+ : [])+ [ AttributeConsumingService+ 0+ False+ (Localized "en" "Academic Journals R US" :| [])+ []+ (RequestedAttribute+ (SAML.Attribute+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.7"+ (Identified AttributeNameFormatURI)+ (Just "eduPersonEntitlement")+ []+ [ [HXT.mkText "https://ServiceProvider.com/entitlements/123456789"]+ ])+ False+ :| [])+ ]+ :| [])+ (Just $ Organization+ []+ (Extensions [])+ (Localized "en" "Academic Journals R US" :| [])+ (Localized "en" "Academic Journals R US, a Division of Dirk Corp." :| [])+ (Localized "en" (uri "https://ServiceProvider.com") :| []))+ [] []++ , testXML "test/Metadata/metadata-osf.xml" $+ EntityDescriptor+ (uri "https://accounts.osf.io/shibboleth")+ (Just "_d1a16315f5a36fc0a7d997a1a71a77edd110a396")+ Nothing Nothing [] Nothing+ (Extensions $+ let alg t a =+ HXT.mkElement (HXT.mkQName "alg" (t ++ "Method") "urn:oasis:names:tc:SAML:metadata:algsupport")+ [HXT.mkAttr (HXT.mkName "Algorithm") [HXT.mkText $ show $ identifier a]]+ [] in+ [ alg "Digest" DS.DigestSHA512+ , alg "Digest" DS.DigestSHA384+ , alg "Digest" DS.DigestSHA256+ , alg "Digest" DS.DigestSHA224+ , alg "Digest" DS.DigestSHA1+ , alg "Signing" DS.SignatureECDSA_SHA512+ , alg "Signing" DS.SignatureECDSA_SHA384+ , alg "Signing" DS.SignatureECDSA_SHA256+ , alg "Signing" DS.SignatureECDSA_SHA224+ , alg "Signing" DS.SignatureRSA_SHA512+ , alg "Signing" DS.SignatureRSA_SHA384+ , alg "Signing" DS.SignatureRSA_SHA256+ , alg "Signing" DS.SignatureDSA_SHA256+ , alg "Signing" DS.SignatureECDSA_SHA1+ , alg "Signing" DS.SignatureRSA_SHA1+ , alg "Signing" DS.SignatureDSA_SHA1+ ])+ (Descriptors+ (SPSSODescriptor+ (RoleDescriptor + Nothing Nothing Nothing+ [ samlURN SAML20 ["protocol"]+ , samlURN SAML11 ["protocol"]+ , samlURN SAML10 ["protocol"]+ ]+ Nothing [] Nothing+ (Extensions [+ pickleElem (xpTrimElemNS (mkNamespace "init" $ uri "urn:oasis:names:tc:SAML:profiles:SSO:request-init") "RequestInitiator" XP.xpickle) $+ Endpoint+ (Unidentified $ uri "urn:oasis:names:tc:SAML:profiles:SSO:request-init")+ (uri "https://accounts.osf.io/Shibboleth.sso/Login")+ Nothing [] []+ ])+ [ KeyDescriptor + KeyTypeBoth+ (DS.KeyInfo + Nothing+ (DS.KeyName "f04f8d134cd2"+ :| DS.X509Data + (DS.X509SubjectName "CN=f04f8d134cd2"+ :| DS.X509Certificate (either error id $ X509.decodeSignedObject+ "0\130\STX\235\&0\130\SOH\211\160\ETX\STX\SOH\STX\STX\t\NUL\154\132\171@\222\174iQ0\r\ACK\t*\134H\134\247\r\SOH\SOH\ENQ\ENQ\NUL0\ETB1\NAK0\DC3\ACK\ETXU\EOT\ETX\DC3\ff04f8d134cd20\RS\ETB\r151229001137Z\ETB\r251226001137Z0\ETB1\NAK0\DC3\ACK\ETXU\EOT\ETX\DC3\ff04f8d134cd20\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\221{\CAN\250\180^\163X\171u\163+\214/\160\220W%8\164O\166\157\&7H\139\242\221\153\148\173\138\226\159n\159\176%~Z\179\144\137\150\218\159\225t\239\171\255&\183\&5\170\231\147\&7W\ETB[\171+\227\147\144\242\154)hvx\ACK\158\158\229\187\157H|{g>Y\166tJ\232+\165\\\251\250\218\177\129\129\244\CANx\r\b\223\245\182k\151\ENQE,xz\231\210)\ESC\150=\134\241~\171]\163\131R?5\210\ETX\245\157\253\131j\152q\193(\195\244\254\186\229\CAN\136\174\173\206B}w\227\&9\166_q\176J\193\r\219\227\191\255\SI\SI$2\153\191\231\160f\tbni\USn)\183\rVKF\221\184\SYNBH\216\178\&8\236\&9d3\RS{\STXQK\ETBa\244\234?bJ\178\207n:\203\165\234\203\213\243\159M\241\194\200D\\V@\140\147\186\190\130\218\bwbe\132^\224\217\247N\241Zw\197\134\234\130\ENQ\183\145G\STX\ETX\SOH\NUL\SOH\163:080\ETB\ACK\ETXU\GS\DC1\EOT\DLE0\SO\130\ff04f8d134cd20\GS\ACK\ETXU\GS\SO\EOT\SYN\EOT\DC4mz3\128Z\139\f\179>\185\134\&1\213\250J\165\233\251\203x0\r\ACK\t*\134H\134\247\r\SOH\SOH\ENQ\ENQ\NUL\ETX\130\SOH\SOH\NUL#\"\175\190\138\&9\237|Z\135L\157\160\&7\243\138\243\175\241\228Ui\228\223\&1 \SYN\188\170\192\155-\186\&7\221\250\167\RS\142=\193\218h\224x\246L6\155\&8f\176r\217\&8<\195&\DC38A\149\247\157y\156\132x\224_\DC3 &m\208\139\b6\224\SUBLH\254\218\207\US2\EOT\SI\SIg\236*\187\244^^FS\198\203C\179\157f\v\SI\129%\148)\DC2\211\171\200\237\237y\ESC\132c\255\DLE\153\ENQ\186\153~(\132\255\USm\200~j\153h1\207\138\173\249)\236\223\173f5\180:H\ETX\144\158\212\ETB\151\b\133f\208\ENQ\178[\DC4\131\232\229}\NUL\fAPf\ETX2|FX\196\GS\154_F\EOT\SO\201\&2\177\223\186\166\ENQd\141\171\233\NULUHd\201S\128\EOT|\246\EM\140O\202<fL\\x\183h\246o\230\166\STXrj]\196Z^\132)\206\&8e|+,T\204\219\251;\158T8\DC3\ACK\197\DC1\190\162{Y\250")+ : [])+ : []))+ [ XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionAES128GCM)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionAES192GCM)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionAES256GCM)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionAES128)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionAES192)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionAES256)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.BlockEncryptionTripleDES)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.KeyTransportRSAOAEP)+ Nothing Nothing Nothing []+ , XEnc.EncryptionMethod + (Identified XEnc.KeyTransportRSAOAEPMGF1P)+ Nothing Nothing Nothing []+ ] + ]+ Nothing [])+ (SSODescriptor + [IndexedEndpoint + (Endpoint + (Identified BindingSOAP)+ (uri "https://accounts.osf.io/Shibboleth.sso/Artifact/SOAP")+ Nothing [] [])+ 1+ False]+ [ Endpoint + (Identified BindingSOAP)+ (uri "https://accounts.osf.io/Shibboleth.sso/SLO/SOAP")+ Nothing [] []+ , Endpoint + (Identified BindingHTTPRedirect)+ (uri "https://accounts.osf.io/Shibboleth.sso/SLO/Redirect")+ Nothing [] []+ , Endpoint + (Identified BindingHTTPPOST)+ (uri "https://accounts.osf.io/Shibboleth.sso/SLO/POST")+ Nothing [] []+ , Endpoint + (Identified BindingHTTPArtifact)+ (uri "https://accounts.osf.io/Shibboleth.sso/SLO/Artifact")+ Nothing [] []+ ]+ [] [])+ False False+ (IndexedEndpoint + (Endpoint + (Identified BindingHTTPPOST)+ (uri "https://accounts.osf.io/Shibboleth.sso/SAML2/POST")+ Nothing [] [])+ 1+ False+ :| IndexedEndpoint + (Endpoint + (Unidentified $ samlURN SAML20 ["bindings", "HTTP-POST-SimpleSign"])+ (uri "https://accounts.osf.io/Shibboleth.sso/SAML2/POST-SimpleSign")+ Nothing [] [])+ 2+ False+ : IndexedEndpoint + (Endpoint + (Identified BindingHTTPArtifact)+ (uri "https://accounts.osf.io/Shibboleth.sso/SAML2/Artifact")+ Nothing [] [])+ 3+ False+ : IndexedEndpoint + (Endpoint + (Identified BindingPAOS)+ (uri "https://accounts.osf.io/Shibboleth.sso/SAML2/ECP")+ Nothing [] [])+ 4+ False+ : IndexedEndpoint + (Endpoint + (Unidentified $ samlURN SAML10 ["profiles", "browser-post"])+ (uri "https://accounts.osf.io/Shibboleth.sso/SAML/POST")+ Nothing [] [])+ 5+ False+ : IndexedEndpoint + (Endpoint + (Unidentified $ samlURN SAML10 ["profiles", "artifact-01"])+ (uri "https://accounts.osf.io/Shibboleth.sso/SAML/Artifact")+ Nothing [] [])+ 6+ False+ : [])+ []+ :| []))+ Nothing [] []++ , testXML "test/Metadata/metadata-nyu.xml" $+ EntityDescriptor+ (uri "urn:mace:incommon:nyu.edu")+ Nothing+ Nothing Nothing [] Nothing+ (Extensions [])+ (Descriptors+ (IDPSSODescriptor+ (RoleDescriptor + Nothing Nothing Nothing+ [ uri "urn:mace:shibboleth:1.0"+ , samlURN SAML11 ["protocol"]+ , samlURN SAML20 ["protocol"]+ ]+ Nothing [] Nothing+ (Extensions [+ HXT.mkElement (HXT.mkQName "shibmd" "Scope" "urn:mace:shibboleth:metadata:1.0")+ [HXT.mkAttr (HXT.mkName "regexp") [HXT.mkText "false"]]+ [HXT.mkText "nyu.edu"]+ ])+ [ KeyDescriptor + KeyTypeSigning+ (DS.KeyInfo + Nothing+ (DS.X509Data + (DS.X509Certificate (either error id $ X509.decodeSignedObject+ "0\130\ACKM0\130\ENQ5\160\ETX\STX\SOH\STX\STX\SOH|0\r\ACK\t*\134H\134\247\r\SOH\SOH\ENQ\ENQ\NUL0V1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\FS0\SUB\ACK\ETXU\EOT\n\DC3\DC3InCommon Federation1)0'\ACK\ETXU\EOT\ETX\DC3 InCommon Certification Authority0\RS\ETB\r070116232303Z\ETB\r080116232303Z081\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\f0\n\ACK\ETXU\EOT\n\DC3\ETXNYU1\ESC0\EM\ACK\ETXU\EOT\ETX\DC3\DC2shibboleth.nyu.edu0\130\SOH\184\&0\130\SOH,\ACK\a*\134H\206\&8\EOT\SOH0\130\SOH\US\STX\129\129\NUL\253\DELS\129\GSu\DC2)R\223J\156.\236\228\231\246\DC1\183R<\239D\NUL\195\RS?\128\182Q&iE]@\"Q\251Y=\141X\250\191\197\245\186\&0\246\203\155Ul\215\129;\128\GS4o\242f`\183k\153P\165\164\159\159\232\EOT{\DLE\"\194O\187\169\215\254\183\198\ESC\248;W\231\198\168\166\NAK\SI\EOT\251\131\246\211\197\RS\195\STX5T\DC3Z\SYN\145\&2\246u\243\174+a\215*\239\242\"\ETX\EM\157\209H\SOH\199\STX\NAK\NUL\151`P\143\NAK#\v\204\178\146\185\130\162\235\132\v\240X\FS\245\STX\129\129\NUL\247\225\160\133\214\155=\222\203\188\171\\6\184W\185y\148\175\187\250:\234\130\249WL\v=\a\130gQYW\142\186\212YO\230q\a\DLE\129\128\180I\SYNq#\232L(\SYN\DC3\183\207\t2\140\200\166\225<\SYNz\139T|\141(\224\163\174\RS+\179\166u\145n\163\DEL\v\250!5b\241\251bz\SOH$;\204\164\241\190\168Q\144\137\168\131\223\225Z\229\159\ACK\146\139f^\128{U%d\SOHL;\254\207I*\ETX\129\133\NUL\STX\129\129\NUL\228b\190y]\216\187'\219M\226W\158\165x\141?\142s>\215\b\190\DC3\153\230\NUL\165\203\128\254\209\187\189\DC4J\151c\178\163\221\213\t\238\198\199\ETX\128\ETB*vj:\177w0\DEL\200\US\204s?4\CANB\243\253A:\190@=\tm\245\230\SOH\208\251\&5\136\153F\207\197\218y\189\250\RS\215\ACK{Ze\195D\222~\225 \233\182R\129>\198Aj\208\t\132\CAN\201:E\139\255==w$]\131\230\CAN]\193\163\130\STX\172\&0\130\STX\168\&0\SO\ACK\ETXU\GS\SI\SOH\SOH\255\EOT\EOT\ETX\STX\ENQ\160\&0\f\ACK\ETXU\GS\DC3\SOH\SOH\255\EOT\STX0\NUL0\GS\ACK\ETXU\GS%\EOT\SYN0\DC4\ACK\b+\ACK\SOH\ENQ\ENQ\a\ETX\SOH\ACK\b+\ACK\SOH\ENQ\ENQ\a\ETX\STX0\GS\ACK\ETXU\GS\SO\EOT\SYN\EOT\DC4\137\209\220M\178,{S\132\DEL\241qy\252F3\192\SYNz80~\ACK\ETXU\GS#\EOTw0u\128\DC4\147-\200a\CAN\173c\227\155e\179\157\221\141\147\186\231\202cE\161Z\164X0V1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\FS0\SUB\ACK\ETXU\EOT\n\DC3\DC3InCommon Federation1)0'\ACK\ETXU\EOT\ETX\DC3 InCommon Certification Authority\130\SOH\NUL0\129\186\ACK\b+\ACK\SOH\ENQ\ENQ\a\SOH\SOH\EOT\129\173\&0\129\170\&0\129\167\ACK\b+\ACK\SOH\ENQ\ENQ\a0\STX\134\129\154http://incommonca1.incommonfederation.org/bridge/certs/ca-certs.p7b\n\t\tCA Issuers - URI:http://incommonca2.incommonfederation.org/bridge/certs/ca-certs.p7b0\129\141\ACK\ETXU\GS\US\EOT\129\133\&0\129\130\&0?\160=\160;\134\&9http://incommoncrl1.incommonfederation.org/crl/eecrls.crl0?\160=\160;\134\&9http://incommoncrl2.incommonfederation.org/crl/eecrls.crl0^\ACK\ETXU\GS \EOTW0U0S\ACK\v+\ACK\SOH\EOT\SOH\174#\SOH\EOT\SOH\SOH0D0B\ACK\b+\ACK\SOH\ENQ\ENQ\a\STX\SOH\SYN6http://incommonca.incommonfederation.org/practices.pdf0\GS\ACK\ETXU\GS\DC1\EOT\SYN0\DC4\130\DC2shibboleth.nyu.edu0\r\ACK\t*\134H\134\247\r\SOH\SOH\ENQ\ENQ\NUL\ETX\130\SOH\SOH\NUL\173N\237\243\NAK\n\232\242\135\t?\216\223\156\151\187-\f/\v\239\SYN\210\DEL\192\196\153\228\161\167@Y{\203\234\197\148\252Z\DC3\131\143)\165\238\128\224\177fg\196\153\"\175\251\&5\234:I\251\212\146\DC4\254;\217\128\174x\217'\165\192\187\247\a\209\240\255(\DLE$R\EM\EOTBYmK-OO`)\162\191\237\139m\bW\172\156\\\147\220\224\ENQA\135\181\255\152\170Y\235\142\130Zq\239\162#\212\168\240\"\206<d\199D\230\162\198\139\251b\NAK\ETX\207\213waW\167\231\t,\152(\\\196\131y;6\157\248\253\211\232M\DC3\145\132\246\200Q\DEL\169I\150\151U\226u\\\239\139\186\DC3Dp\160'\191\239\165+\186OU\205\200\139J\"p\DC2?\228\184S\223\131\221\236\212\174p\215\FS\172\150k]\197\135z\GS\177\&14j\149H\238Z\169\152\206\166\129y;\242\169\223\211W\214+\244\243(\203Pk\172\215E%+\144q")+ :| [])+ :| []))+ [] + , KeyDescriptor + KeyTypeSigning+ (DS.KeyInfo + Nothing+ (DS.X509Data + (DS.X509Certificate (either error id $ X509.decodeSignedObject+ "0\130\EOT\232\&0\130\ETX\208\160\ETX\STX\SOH\STX\STX\t\NUL\141\149\236\&9\165\128\180\&50\r\ACK\t*\134H\134\247\r\SOH\SOH\ENQ\ENQ\NUL0\129\168\&1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC10\SI\ACK\ETXU\EOT\b\DC3\bNew York1\DC10\SI\ACK\ETXU\EOT\a\DC3\bNew York1\FS0\SUB\ACK\ETXU\EOT\n\DC3\DC3New York University1\f0\n\ACK\ETXU\EOT\v\DC3\ETXITS1\"0 \ACK\ETXU\EOT\ETX\DC3\EMurn:mace:incommon:nyu.edu1#0!\ACK\t*\134H\134\247\r\SOH\t\SOH\SYN\DC4idm.services@nyu.edu0\RS\ETB\r120810213516Z\ETB\r220808213516Z0\129\168\&1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC10\SI\ACK\ETXU\EOT\b\DC3\bNew York1\DC10\SI\ACK\ETXU\EOT\a\DC3\bNew York1\FS0\SUB\ACK\ETXU\EOT\n\DC3\DC3New York University1\f0\n\ACK\ETXU\EOT\v\DC3\ETXITS1\"0 \ACK\ETXU\EOT\ETX\DC3\EMurn:mace:incommon:nyu.edu1#0!\ACK\t*\134H\134\247\r\SOH\t\SOH\SYN\DC4idm.services@nyu.edu0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\223o\134\232\180\242Sp\195\194,7\167\204z\150\NUL\226Ezu\ACK\DLE\STX\247\221\DC3\179\ETX\175;o\189\197\131\189\133\158Xv>\158\199\&6\SYN\193\223\231\DLE\142\196\SO\SO+c\NAK\213\NAKB\ENQ\252\DLE\ETX\247\196\198@\CAN\165\&63\253\EM\EM\249\198\162H\249\162%4\243H\162\235\&3\179\DLE5\STX\146\233Q\228%\186\167\233\160v\128\152^\205\219\246\184\176\155U\249\166\168\191\225\150\194(\255\138\233\&8\146\EOT\225\150\198\208\172\ACK\158\230XWI\149e\DC3\233\173\213E\218\CAN\153E\DLE\DLE\226_\173<\147\240%\180vM\ETX\EM:\SYN{h\247\181\172FO\207D\221P:\195\145\164\181\STX\185\179\EMe\DLE\ENQP\219w\226\&1\218.\f\vk\226\NUL\205\188\228\176\181\133.\SI\b\DEL>\153\DEL\148\ETX0\141O\157\191O)6\226Q\245\218\197\141\131\&4\140\170\">\193\189\SOH\209\&0\144T\134A\189\b\a\\-\243\236\NULy0\151 \163+\STX\ETX\SOH\NUL\SOH\163\130\SOH\DC10\130\SOH\r0\GS\ACK\ETXU\GS\SO\EOT\SYN\EOT\DC4\149\228.\172J\129\198\195\135\170\DC2\253\210\DLE\162X\194\136\191\GS0\129\221\ACK\ETXU\GS#\EOT\129\213\&0\129\210\128\DC4\149\228.\172J\129\198\195\135\170\DC2\253\210\DLE\162X\194\136\191\GS\161\129\174\164\129\171\&0\129\168\&1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC10\SI\ACK\ETXU\EOT\b\DC3\bNew York1\DC10\SI\ACK\ETXU\EOT\a\DC3\bNew York1\FS0\SUB\ACK\ETXU\EOT\n\DC3\DC3New York University1\f0\n\ACK\ETXU\EOT\v\DC3\ETXITS1\"0 \ACK\ETXU\EOT\ETX\DC3\EMurn:mace:incommon:nyu.edu1#0!\ACK\t*\134H\134\247\r\SOH\t\SOH\SYN\DC4idm.services@nyu.edu\130\t\NUL\141\149\236\&9\165\128\180\&50\f\ACK\ETXU\GS\DC3\EOT\ENQ0\ETX\SOH\SOH\255\&0\r\ACK\t*\134H\134\247\r\SOH\SOH\ENQ\ENQ\NUL\ETX\130\SOH\SOH\NUL\194\239\SO\EM\206O\234\130\147a\153\231\180\221LF:,e\SYN\DEL\148i\142\144\DC1\216\193 \237\206\222m\180\132E\139\167\131\218\249\&0o\177)l\NAKd!\213i_\158\ACK_\128\175\204\141\156\164v\r>ll\DC1O\210Lq\150\224\247\ETB\174\206s\130\DC1\233\238L\194m\SO\184\170\187\&4\229\&7\169\172\217\233\158\150\NUL^~$\SO\164\&0\191\134\197\247\RS\189&\137\&8\NAK\171\172[\222dqR\147\DC2DP\DLE\170\t&W\141\&7}\SI\164w\193\\\177,\181\142\&4\185\\\188\ETB\136\GS\244\\\219\128\213Q4\RSU\172\226^\ETXWQ\191\219\130\\\212z\251\137L\162d\199\217\149\153\EOT\186;\187<\234\230\198\213\&4E\157\&4\SI(\172\183;\135\131i%\236yS\ENQ\251N\179\193C\196\205\236\170+re'.\181\t\175\&1\238\141\v/\247\157!r\FS$\ENQ=\251\186\233\188\156\178\198\ACKG\238\CAN\190\216\194e\NAK\SUB\179\153\DC2")+ :| [])+ :| []))+ [] + ]+ Nothing [])+ (SSODescriptor + [] [] [] [])+ False+ (Endpoint + (Unidentified $ uri "urn:mace:shibboleth:1.0:profiles:AuthnRequest")+ (uri "https://shibboleth.nyu.edu/idp/profile/Shibboleth/SSO")+ Nothing [] []+ :| Endpoint + (Identified BindingHTTPRedirect)+ (uri "https://shibboleth.nyu.edu/idp/profile/SAML2/Redirect/SSO")+ Nothing [] []+ : Endpoint + (Identified BindingHTTPPOST)+ (uri "https://shibboleth.nyu.edu/idp/profile/SAML2/POST/SSO")+ Nothing [] []+ : [])+ []+ []+ []+ []+ :| []))+ (Just $ Organization+ []+ (Extensions [])+ (Localized "en" "New York University" :| [])+ (Localized "en" "New York University" :| [])+ (Localized "en" (uri "http://www.nyu.edu/") :| []))+ [ ContactPerson+ ContactTypeTechnical+ []+ (Extensions [])+ Nothing+ (Just "Yavor Yanakiev")+ Nothing+ [uri "yy27@nyu.edu"]+ []+ , ContactPerson+ ContactTypeTechnical+ []+ (Extensions [])+ Nothing+ (Just "Tracy Edappara")+ Nothing+ [uri "tte3@nyu.edu"]+ []+ , ContactPerson+ ContactTypeAdministrative+ []+ (Extensions [])+ Nothing+ (Just "Gary Chapman")+ Nothing+ [uri "gwc1@nyu.edu"]+ []+ ]+ []+ ]
+ test/XML.hs view
@@ -0,0 +1,29 @@+module XML+ ( testXML+ , uri+ , pickleElem+ ) where++import Data.Maybe (fromJust)+import Network.URI (URI, parseURIReference)+import qualified Test.HUnit as U++import qualified Text.XML.HXT.Core as HXT+import qualified Text.XML.HXT.HTTP as HXT (withHTTP)+import qualified Text.XML.HXT.DOM.XmlNode as DOM++parseXML :: HXT.XmlPickler a => String -> IO [Either String a]+parseXML u = fmap (map (HXT.unpickleDoc' HXT.xpickle)) $+ HXT.runX $+ HXT.readDocument [HXT.withCheckNamespaces HXT.yes, HXT.withHTTP [], HXT.withRemoveWS HXT.yes] u+ HXT.>>> HXT.processBottomUp (HXT.processAttrl (HXT.none `HXT.when` HXT.isNamespaceDeclAttr))++testXML :: (Eq a, HXT.XmlPickler a, Show a) => String -> a -> U.Test+testXML u a = U.TestCase $+ U.assertEqual u [Right a] =<< parseXML u++uri :: String -> URI+uri = fromJust . parseURIReference++pickleElem :: HXT.PU a -> a -> HXT.XmlTree+pickleElem p = head . DOM.getChildren . HXT.pickleDoc p
+ test/XML/Canonical.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE OverloadedStrings #-}+module XML.Canonical (tests) where++import qualified Data.ByteString as BS+import qualified Test.HUnit as U+import qualified Text.XML.HXT.Core as HXT++import SAML2.XML.Canonical++canonicalizeXML :: CanonicalizationAlgorithm -> String -> Bool -> IO [BS.ByteString]+canonicalizeXML c f ent = HXT.runX $+ (HXT.readDocument [HXT.withCheckNamespaces HXT.yes, HXT.withValidate HXT.no, HXT.withCanonicalize HXT.no, HXT.withSubstDTDEntities ent] f+ HXT.>>> HXT.arrIO (canonicalize c Nothing Nothing))++testC14N :: CanonicalizationAlgorithm -> String -> Bool -> BS.ByteString -> U.Test+testC14N c f ent s = U.TestCase $+ U.assertEqual (show c ++ ' ' : f) [s] =<< canonicalizeXML c f ent++tests :: U.Test+tests = U.test+ [ testC14N (CanonicalXML10 False) "test/XML/noncanonical1.xml" False+ "<?xml-stylesheet href=\"doc.xsl\"\n type=\"text/xsl\" ?>\n<doc>Hello, world!</doc>\n<?pi-without-data?>"+ , testC14N (CanonicalXML10 True) "test/XML/noncanonical1.xml" False+ "<?xml-stylesheet href=\"doc.xsl\"\n type=\"text/xsl\" ?>\n<doc>Hello, world!<!-- Comment 1 --></doc>\n<?pi-without-data?>\n<!-- Comment 2 -->\n<!-- Comment 3 -->"+ , testC14N (CanonicalXML10 False) "test/XML/noncanonical2.xml" False+ "<doc>\n <clean> </clean>\n <dirty> A B </dirty>\n <mixed>\n A\n <clean> </clean>\n B\n <dirty> A B </dirty>\n C\n </mixed>\n</doc>"+ , testC14N (CanonicalXML10 False) "test/XML/noncanonical3.xml" False+ "<doc>\n <e1></e1>\n <e2></e2>\n <e3 id=\"elem3\" name=\"elem3\"></e3>\n <e4 id=\"elem4\" name=\"elem4\"></e4>\n <e5 xmlns=\"http://example.org\" xmlns:a=\"http://www.w3.org\" xmlns:b=\"http://www.ietf.org\" attr=\"I'm\" attr2=\"all\" b:attr=\"sorted\" a:attr=\"out\"></e5>\n <e6 xmlns:a=\"http://www.w3.org\">\n <e7 xmlns=\"http://www.ietf.org\">\n <e8 xmlns=\"\">\n <e9 xmlns:a=\"http://www.ietf.org\" attr=\"default\"></e9>\n </e8>\n </e7>\n </e6>\n</doc>"+ , testC14N (CanonicalXML10 False) "test/XML/noncanonical4.xml" False+ "<doc>\n <text>First line
\nSecond line</text>\n <value>2</value>\n <compute>value>\"0\" && value<\"10\" ?\"valid\":\"error\"</compute>\n <compute expr=\"value>"0" && value<"10" ?"valid":"error"\">valid</compute>\n <norm attr=\" ' 
	 ' \"></norm>\n <normNames attr=\"A 
	 B\"></normNames>\n <normId id=\"' 
	 '\"></normId>\n</doc>"+ , testC14N (CanonicalXML10 False) "test/XML/noncanonical5.xml" True+ "<doc attrExtEnt=\"entExt\">\n Hello, world!\n</doc>"+ , testC14N (CanonicalXML10 False) "test/XML/noncanonical6.xml" False+ "<doc>\194\169</doc>"+ -- , testC14N (CanonicalXML10 False) "test/XML/noncanonical7.xml"+ ]
+ test/XML/Encryption.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE OverloadedStrings #-}+module XML.Encryption (tests) where++import Data.List.NonEmpty (NonEmpty(..))+import qualified Test.HUnit as U+import qualified Text.XML.HXT.Arrow.Pickle.Xml as XP+import qualified Text.XML.HXT.DOM.QualifiedName as HXT+import qualified Text.XML.HXT.DOM.XmlNode as HXT++import SAML2.XML+import SAML2.XML.Signature+import SAML2.XML.Encryption++import XML++tests :: U.Test+tests = U.test+ [ testXML "test/XML/encryption-example.xml" $+ EncryptedData $ EncryptedType (Just "eg1")+ Nothing+ Nothing+ Nothing+ (Just $ EncryptionMethod+ (Identified KeyTransportRSAOAEPMGF1P)+ (Just 256)+ (Just "\246U\174\221")+ (Just $ DigestMethod+ (Identified DigestSHA1)+ [])+ [])+ (Just $ KeyInfo Nothing+ (KeyName "Joseph"+ :| RetrievalMethod+ (uri "http://exmample.org/Reagle/PublicKey")+ (Just $ uri "http://www.w3.org/2001/04/xmlenc#EncryptedKey")+ Nothing+ : KeyInfoElement (pickleElem XP.xpickle $ EncryptedKey+ (EncryptedType Nothing+ Nothing+ Nothing+ Nothing+ (Just $ EncryptionMethod+ (Identified BlockEncryptionTripleDES)+ Nothing+ Nothing+ Nothing+ [])+ Nothing+ (CipherValue "\169\153>6G\ACK\129j\186>%qxP\194l\156\208\216\157")+ Nothing)+ Nothing+ [ KeyReference+ (uri "http://exmample.org/foo2")+ []+ , DataReference+ (uri "http://exmample.org/foo1")+ []+ ]+ Nothing)+ : KeyInfoElement (pickleElem XP.xpickle $ AgreementMethod+ (Unidentified (uri "example:Agreement/Algorithm"))+ (Just "foo")+ (Just $ DigestMethod+ (Identified DigestSHA1)+ [])+ (Just $ KeyInfo Nothing $+ KeyInfoKeyValue (RSAKeyValue+ 124965805205214+ 124965805205214)+ :| [])+ (Just $ KeyInfo Nothing $+ KeyInfoKeyValue (RSAKeyValue+ 124965805205214+ 124965805205214)+ :| []))+ : []))+ (CipherReference+ (uri "http://example.org/pgpkeys/reagle.b64")+ (Transform+ (Identified TransformBase64)+ Nothing+ []+ :| []))+ (Just $ EncryptionProperties Nothing $+ EncryptionProperty (Just "ep2")+ (Just $ uri "#eg1")+ [HXT.mkElement (HXT.mkNsName "p" "http://www.w3.org/1999/xhtml")+ []+ [HXT.mkText "This XML document tests the schema for ambigous content."]]+ :| [])++ ]
+ test/XML/Signature.hs view
@@ -0,0 +1,154 @@+{-# LANGUAGE OverloadedStrings #-}+module XML.Signature (tests) where++import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.X509 as X509+import qualified Test.HUnit as U+import qualified Text.XML.HXT.DOM.QualifiedName as HXT+import qualified Text.XML.HXT.DOM.XmlNode as HXT++import SAML2.XML+import SAML2.XML.Signature+import SAML2.XML.Canonical++import XML++tests :: U.Test+tests = U.test+ [ testXML "test/XML/signature-example.xml" $+ Signature (Just "MyFirstSignature")+ (SignedInfo Nothing+ (CanonicalizationMethod+ (Identified (CanonicalXML10 False))+ Nothing+ [])+ (SignatureMethod+ (Identified SignatureDSA_SHA1)+ Nothing+ [])+ (Reference Nothing+ (Just $ uri "http://www.w3.org/TR/xml-stylesheet/")+ Nothing+ (Just $ Transforms $+ Transform+ (Identified TransformBase64)+ Nothing+ []+ :| [])+ (DigestMethod+ (Identified DigestSHA1)+ [])+ "\143\169p\199z\239\DLE\243\180\188\171L\186\158\rm\229n\242y"+ :| Reference Nothing+ (Just $ uri "http://www.w3.org/TR/REC-xml-names/")+ Nothing+ (Just $ Transforms $+ Transform+ (Identified TransformBase64)+ Nothing+ []+ :| [])+ (DigestMethod+ (Identified DigestSHA1)+ [])+ "R\181\203\f\176H\181\174\172\146\133y\252\SI\DLE\223\193\132\195\142"+ : []))+ (SignatureValue Nothing+ "0-\STX\DC4Z\213.\212e\144\199\&7\r\170'\224\SUB\170\158HB:Q\SUB\STX\NAK\NUL\147\202G\214$M+\234\181#\235\"\176\&4\243\217\&1D\NUL\177")+ (Just $ KeyInfo Nothing $+ KeyInfoKeyValue (DSAKeyValue+ (Just (4227,4467856506880))+ (Just 0)+ 0+ Nothing+ Nothing)+ :| [])+ [Object Nothing+ Nothing+ Nothing+ [ObjectSignatureProperties $ SignatureProperties Nothing+ (SignatureProperty Nothing+ (uri "#MyFirstSignature")+ (HXT.mkElement (HXT.mkQName "ts" "timestamp" "http://www.example.org/rfc/rfcxxxx.txt")+ []+ [HXT.mkText "\n this is a test of the mixed content model"]+ :| [])+ :| [])]]++ , testXML "http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/signature-example-rsa.xml" $+ Signature Nothing+ (SignedInfo Nothing+ (CanonicalizationMethod + (Identified (CanonicalXML10 False))+ Nothing+ [])+ (SignatureMethod + (Identified SignatureRSA_SHA1)+ Nothing+ [])+ (Reference Nothing+ (Just $ uri "http://www.w3.org/TR/xml-stylesheet")+ Nothing+ Nothing+ (DigestMethod + (Identified DigestSHA1)+ [])+ "\235Cof\251]L\US\187RyK\167\241\246\226\158\225\225\187"+ :| []))+ (SignatureValue Nothing+ "\142\228\185F\DC2|\243\138\168\NAK\US\US\149U\221\254\182\235H5F\159\141\STXj\152\SOH\238\167\144\137?\171\175C^\144D:\EOTxT\ETX\199S\223\224BL\NAK\DLE#GA\142Y\165\246\\3\DLE\213\239K\205\243D@\163\205v\204E5-U\152\143dm\169\168\163\231/f\DC4\220\SIz\227\149\STX\141\&5\212\250\&8\ENQ\186k\251\147\191!\137\224\ACK\SI3\\\139\227\208\244\164f\155\255\226q>\201i\140\237\130\222")+ (Just $ KeyInfo Nothing $+ KeyInfoKeyValue (RSAKeyValue+ 129320787110389946406925163824095500161767249273623083115363317842079233133623467127773858023148958966585889333894288698085674111884585270272937137414571531865090153762072690670922714784242933462808045060688046441910524319219807614054721975863956765214954333806674482022007523948700289932875920496009317903247+ 65537)+ :| X509Data + (X509SubjectName "\n CN=Merlin Hughes,O=Baltimore Technologies\\, Ltd.,ST=Dublin,C=IE\n "+ :| X509IssuerSerial + "\n CN=Test RSA CA,O=Baltimore Technologies\\, Ltd.,ST=Dublin,C=IE\n "+ 970849928+ : X509Certificate (either error id $ X509.decodeSignedObject+ "0\130\STXx0\130\SOH\225\160\ETX\STX\SOH\STX\STX\EOT9\221\254\136\&0\r\ACK\t*\134H\134\247\r\SOH\SOH\EOT\ENQ\NUL0[1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXIE1\SI0\r\ACK\ETXU\EOT\b\DC3\ACKDublin1%0#\ACK\ETXU\EOT\n\DC3\FSBaltimore Technologies, Ltd.1\DC40\DC2\ACK\ETXU\EOT\ETX\DC3\vTest RSA CA0\RS\ETB\r001006163207Z\ETB\r011006163204Z0]1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXIE1\SI0\r\ACK\ETXU\EOT\b\DC3\ACKDublin1%0#\ACK\ETXU\EOT\n\DC3\FSBaltimore Technologies, Ltd.1\SYN0\DC4\ACK\ETXU\EOT\ETX\DC3\rMerlin Hughes0\129\159\&0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\129\141\NUL0\129\137\STX\129\129\NUL\184(\174\146\152\SOh\233\171\171W\207Q1\247\b\ENQ\241\184Y\143\142\201\146\226\&9\211+\SUB\239\211\rI)\197\237'c7jF\149\213\223\228j\187\201\150g\154\163m#7/k\251\251\202\194&\227\&3\190\197\251v\200\145\227\EOT\SUB`\bRq\\\136\153\DC1\250(N\237\190\FSN\230'\DC2/1\RS\219\184\136\201\250\CAN\224\193\160L\234\NAK\ACK\GSw\202x\190\182A\178\251\&8\226t\235K\202\178\202\SYN\218\235\143\STX\ETX\SOH\NUL\SOH\163G0E0\RS\ACK\ETXU\GS\DC1\EOT\ETB0\NAK\129\DC3merlin@baltimore.ie0\SO\ACK\ETXU\GS\SI\SOH\SOH\255\EOT\EOT\ETX\STX\a\128\&0\DC3\ACK\ETXU\GS#\EOT\f0\n\128\bI\224\173\146\NAK\130\237\&70\r\ACK\t*\134H\134\247\r\SOH\SOH\EOT\ENQ\NUL\ETX\129\129\NULrn\224\149j\253i\215+j*\169\242\214\169\235\&9\188s\173}\DEL\217\132(\197\200\&3!\206\201H\241\169\186\218\ACK^w\ACK} \134H\194SQ2\241\ETX@\GS\179v\207\222\DLE\EM\200\SOH\ETX\229\255$K\b\179\159fv\192\240\245\235lS\249\138\v7\169\ENQ\146p\NAK#eX\226\147\190<\GSM\172QVNk\221\FS\210be\218\242l\253\FS2-n\255\184 \US\v\242?\147\220F\175\183\231z\130\SYN")+ : [])+ : [])+ []++ , testXML "http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/signature-example-dsa.xml" $+ Signature Nothing+ (SignedInfo Nothing+ (CanonicalizationMethod + (Identified (CanonicalXML10 False))+ Nothing+ [])+ (SignatureMethod+ (Identified SignatureDSA_SHA1)+ Nothing+ [])+ (Reference Nothing+ (Just $ uri "http://www.w3.org/TR/xml-stylesheet")+ Nothing+ Nothing+ (DigestMethod+ (Identified DigestSHA1)+ [])+ "\235Cof\251]L\US\187RyK\167\241\246\226\158\225\225\187"+ :| []))+ (SignatureValue Nothing+ "\169@\ETX\f\193\217\147'\155\189\ETBK\179\238\131\191\180o\128\194\209\149F\131\a\132=\202\DELW\160\144;\245\173\188\243g\223N")+ (Just $ KeyInfo Nothing $+ KeyInfoKeyValue (DSAKeyValue + (Just (153189639877411708224318232157362603672344144516811966787300053511761023886224186320604641484819218412604307502181416378142369715493548254902826913797675909798516465379299499179683104783603344189053156355893057246996756239330536459114483648506158907550755189559705056134296533730500485771224694549017281352213, 1393672757286116725466646726891466679477132949611))+ (Just 35729760834794135337622213068423837828001418115741637960451705317746718553830652249599411519579460551833355506946575671195284376652749132624400687252979877843978133909425395446287975790304889073526084815938167334643701873370635770822329121239717107729346766305695485960113338821879352438123181471401687525574)+ 80624726256040348115552042320696813500187275370942441977258669395023235020055564647117594451929708788598704081077890850726227289270230377442285367559774800853404089092381420228663316324808605521697655145608801533888071381819208887705771753016938104409283940243801509765453542091716518238707344493641683483917+ Nothing+ Nothing)+ :| X509Data+ (X509SubjectName "\n CN=Merlin Hughes,O=Baltimore Technologies\\, Ltd.,ST=Dublin,C=IE\n "+ :| X509IssuerSerial + "\n CN=Test DSA CA,O=Baltimore Technologies\\, Ltd.,ST=Dublin,C=IE\n "+ 970849936+ : X509Certificate (either error id $ X509.decodeSignedObject+ "0\130\ETX70\130\STX\245\160\ETX\STX\SOH\STX\STX\EOT9\221\254\144\&0\t\ACK\a*\134H\206\&8\EOT\ETX0[1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXIE1\SI0\r\ACK\ETXU\EOT\b\DC3\ACKDublin1%0#\ACK\ETXU\EOT\n\DC3\FSBaltimore Technologies, Ltd.1\DC40\DC2\ACK\ETXU\EOT\ETX\DC3\vTest DSA CA0\RS\ETB\r001006163215Z\ETB\r011006163214Z0]1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXIE1\SI0\r\ACK\ETXU\EOT\b\DC3\ACKDublin1%0#\ACK\ETXU\EOT\n\DC3\FSBaltimore Technologies, Ltd.1\SYN0\DC4\ACK\ETXU\EOT\ETX\DC3\rMerlin Hughes0\130\SOH\182\&0\130\SOH+\ACK\a*\134H\206\&8\EOT\SOH0\130\SOH\RS\STX\129\129\NUL\218&7\195N\182\176\&0w\252\&2%-c\158\ESC\223\184R\153\131gGr\169\EM=t\185MC\170\154\\\142\237:\184\221\"\EM\186\159\247\142\195\142@B\219\152J\158\172\164+}q\a\r\EOT\b\246\216\171\242\130\247\201 \133\215\246\196\144\174\232\US\SUB\168\159y+\216\221U\249I\221c\251=\fI\159\231\230\&3m\243\203\161\216-uH\n\151\234z\215M\143\245G\t(Y\DC3v\221&!:V\134\210\NAK\STX\NAK\NUL\244\RSr\164\182=\164\195\166\183\DLE\158L1\224\193\211Exk\STX\129\128\&2\225\128\150\167\129\213\172~\191#\182\248\235.n8e\238\145\241.\238;D\129\254\252\206v\SO1\DC2\ETX\210\140J\188\&3\177\140|\200\212vZ\138\SOH\202\177\&4\183\167\238\209Y\220+\181\n\242\137N\226\222\240\166\253\179\224\SOHP=\NAKB(\\(\210\168'\229\162\136\144\128\134\&2Z\209\203\205Z\189\189\187\192g\SYN\162\216q\222#\207\&2\209W\182\128\234+BH\181\162=G\204\220\214k\ENQ\132\205F(\198\ETX\129\132\NUL\STX\129\128r\208<`lk\182x \255\&2\149\190\161\SOy\249\240\153X\133\206\215'<\SYN\SI\148\155/\135\172\138#\136\131\155\175\US\158\158\f\139tk'\166\217\ETX(\ENQ\173B\DLE/\DC2\227W\227\137\182\STX@\DC2\218\&4\196\v>1\232n\171P\228HQ)?z\ETX\204$\206\178\179\162KP\240A\238(!\190\243VO\253\151\182\143\180\147\a[B\213\148\199pd\209M4\154*1\196\246e\240\143\173\251\216\189\r\163G0E0\RS\ACK\ETXU\GS\DC1\EOT\ETB0\NAK\129\DC3merlin@baltimore.ie0\SO\ACK\ETXU\GS\SI\SOH\SOH\255\EOT\EOT\ETX\STX\a\128\&0\DC3\ACK\ETXU\GS#\EOT\f0\n\128\bBY@m\n\193\SYN\207\&0\t\ACK\a*\134H\206\&8\EOT\ETX\ETX1\NUL0.\STX\NAK\NUL\174,\145a\ENQb\n\224\129\162@\242\246\NUL\193(\224\215o\138\STX\NAK\NUL\192t\232\239\ax\180Cp\244\176\n>IP\255\190\US\US_")+ : [])+ : [])+ []+ ]
+ test/XML/encryption-example.xml view
@@ -0,0 +1,53 @@+<?xml version="1.0" encoding="UTF-8"?>+<EncryptedData Id="eg1" xmlns="http://www.w3.org/2001/04/xmlenc#"+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">+ <EncryptionMethod+ Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">+ <KeySize>256</KeySize>+ <OAEPparams>9lWu3Q==</OAEPparams>+ <ds:DigestMethod+ Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>+ </EncryptionMethod>+ <ds:KeyInfo>+ <ds:KeyName>Joseph</ds:KeyName>+ <ds:RetrievalMethod URI="http://exmample.org/Reagle/PublicKey"+ Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"/>+ <EncryptedKey>+ <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>+ <CipherData>+ <CipherValue>qZk+NkcGgWq6PiVxeFDCbJzQ2J0=</CipherValue>+ </CipherData>+ <ReferenceList>+ <KeyReference URI="http://exmample.org/foo2"/>+ <DataReference URI="http://exmample.org/foo1"/>+ </ReferenceList>+ </EncryptedKey>+ <AgreementMethod Algorithm="example:Agreement/Algorithm">+ <KA-Nonce>Zm9v</KA-Nonce>+ <ds:DigestMethod+ Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>+ <OriginatorKeyInfo>+ <ds:KeyValue><ds:RSAKeyValue> + <ds:Modulus>cafebabe</ds:Modulus><ds:Exponent>cafebabe</ds:Exponent>+ </ds:RSAKeyValue></ds:KeyValue>+ </OriginatorKeyInfo>+ <RecipientKeyInfo>+ <ds:KeyValue><ds:RSAKeyValue> + <ds:Modulus>cafebabe</ds:Modulus><ds:Exponent>cafebabe</ds:Exponent>+ </ds:RSAKeyValue></ds:KeyValue>+ </RecipientKeyInfo> + </AgreementMethod>+ </ds:KeyInfo>+ <CipherData>+ <CipherReference URI="http://example.org/pgpkeys/reagle.b64">+ <Transforms>+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64"/>+ </Transforms>+ </CipherReference>+ </CipherData>+ <EncryptionProperties>+ <EncryptionProperty Target="#eg1" Id="ep2">+ <p xmlns="http://www.w3.org/1999/xhtml">This XML document tests the schema for ambigous content.</p>+ </EncryptionProperty></EncryptionProperties>+</EncryptedData>
+ test/XML/signature-example.xml view
@@ -0,0 +1,41 @@+<?xml version='1.0'?>+<Signature Id="MyFirstSignature" xmlns="http://www.w3.org/2000/09/xmldsig#">+ <SignedInfo> + <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">+ </CanonicalizationMethod>+ <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1">+ </SignatureMethod>+ <Reference URI="http://www.w3.org/TR/xml-stylesheet/"> + <Transforms> + <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64"/>+ </Transforms> + <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"> + </DigestMethod>+ <DigestValue>j6lwx3rvEPO0vKtMup4NbeVu8nk=</DigestValue> + </Reference> + <Reference URI="http://www.w3.org/TR/REC-xml-names/"> + <Transforms> + <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64"/> + </Transforms> + <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"> + </DigestMethod>+ <DigestValue>UrXLDLBIta6skoV5/A8Q38GEw44=</DigestValue> + </Reference> + </SignedInfo> + <SignatureValue>MC0CFFrVLtRlkMc3Daon4BqqnkhCOlEaAhUAk8pH1iRNK+q1I+sisDTz2TFEALE=</SignatureValue> + <KeyInfo>+ <KeyValue>+ <DSAKeyValue>+ <P>ABCD</P><Q>BBBBAAAA</Q><G></G><Y></Y>+ </DSAKeyValue> + </KeyValue> + </KeyInfo>+ <Object>+ <SignatureProperties>+ <SignatureProperty Target="#MyFirstSignature">+ <ts:timestamp xmlns:ts="http://www.example.org/rfc/rfcxxxx.txt">+ this is a test of the mixed content model</ts:timestamp>+ </SignatureProperty>+ </SignatureProperties>+ </Object>+</Signature>