packages feed

cryptostore 0.1.0.0 → 0.2.0.0

raw patch · 62 files changed

+2216/−821 lines, 62 filesdep +x509-validationdep ~x509PVP ok

version bump matches the API change (PVP)

Dependencies added: x509-validation

Dependency ranges changed: x509

API changes (from Hackage documentation)

- Crypto.Store.CMS: [adContentInfo] :: AuthenticatedData -> ContentInfo
- Crypto.Store.CMS: [ddContentInfo] :: DigestedData -> ContentInfo
- Crypto.Store.CMS: [sdContentInfo] :: SignedData -> ContentInfo
+ Crypto.Store.CMS: Attached :: a -> Encap a
+ Crypto.Store.CMS: Detached :: Encap a
+ Crypto.Store.CMS: Ed25519 :: SignatureAlg
+ Crypto.Store.CMS: Ed448 :: SignatureAlg
+ Crypto.Store.CMS: [SHAKE128] :: KnownNat n => Proxy n -> DigestProxy (SHAKE128 n)
+ Crypto.Store.CMS: [SHAKE128_256] :: DigestProxy (SHAKE128 256)
+ Crypto.Store.CMS: [SHAKE256] :: KnownNat n => Proxy n -> DigestProxy (SHAKE256 n)
+ Crypto.Store.CMS: [SHAKE256_512] :: DigestProxy (SHAKE256 512)
+ Crypto.Store.CMS: [adContentType] :: AuthenticatedData content -> ContentType
+ Crypto.Store.CMS: [adEncapsulatedContent] :: AuthenticatedData content -> content
+ Crypto.Store.CMS: [ddContentType] :: DigestedData content -> ContentType
+ Crypto.Store.CMS: [ddEncapsulatedContent] :: DigestedData content -> content
+ Crypto.Store.CMS: [sdContentType] :: SignedData content -> ContentType
+ Crypto.Store.CMS: [sdEncapsulatedContent] :: SignedData content -> content
+ Crypto.Store.CMS: class Encapsulates struct
+ Crypto.Store.CMS: data Encap a
+ Crypto.Store.CMS: fromAttached :: (MonadFail m, Encapsulates struct) => struct (Encap a) -> m (struct a)
+ Crypto.Store.CMS: fromDetached :: (MonadFail m, Encapsulates struct) => b -> struct (Encap a) -> m (struct b)
+ Crypto.Store.CMS: fromEncap :: b -> (a -> b) -> Encap a -> b
+ Crypto.Store.CMS: isAttached :: Encapsulates struct => struct (Encap a) -> Bool
+ Crypto.Store.CMS: isDetached :: Encapsulates struct => struct (Encap a) -> Bool
+ Crypto.Store.CMS: toAttachedCI :: Encapsulates struct => struct ByteString -> ContentInfo
+ Crypto.Store.CMS: toDetachedCI :: Encapsulates struct => struct ByteString -> (ByteString, ContentInfo)
+ Crypto.Store.CMS: type EncapsulatedContent = ByteString
+ Crypto.Store.Error: DigestMismatch :: StoreError
+ Crypto.Store.Error: SignatureNotVerified :: StoreError
+ Crypto.Store.PKCS12: fromNamedCredential :: String -> Maybe EncryptionScheme -> EncryptionScheme -> Password -> (CertificateChain, PrivKey) -> Either StoreError PKCS12
+ Crypto.Store.PKCS12: toNamedCredential :: String -> PKCS12 -> OptProtected (Maybe (CertificateChain, PrivKey))
+ Crypto.Store.PKCS8: instance Crypto.Store.ASN1.Generate.ASN1Elem e => Crypto.Store.CMS.Util.ProduceASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Curve25519.SecretKey)
+ Crypto.Store.PKCS8: instance Crypto.Store.ASN1.Generate.ASN1Elem e => Crypto.Store.CMS.Util.ProduceASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Curve448.SecretKey)
+ Crypto.Store.PKCS8: instance Crypto.Store.ASN1.Generate.ASN1Elem e => Crypto.Store.CMS.Util.ProduceASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Ed25519.SecretKey)
+ Crypto.Store.PKCS8: instance Crypto.Store.ASN1.Generate.ASN1Elem e => Crypto.Store.CMS.Util.ProduceASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Ed448.SecretKey)
+ Crypto.Store.PKCS8: instance GHC.Base.Monoid e => Crypto.Store.CMS.Util.ParseASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Curve25519.SecretKey)
+ Crypto.Store.PKCS8: instance GHC.Base.Monoid e => Crypto.Store.CMS.Util.ParseASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Curve448.SecretKey)
+ Crypto.Store.PKCS8: instance GHC.Base.Monoid e => Crypto.Store.CMS.Util.ParseASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Ed25519.SecretKey)
+ Crypto.Store.PKCS8: instance GHC.Base.Monoid e => Crypto.Store.CMS.Util.ParseASN1Object e (Crypto.Store.PKCS8.Modern Crypto.PubKey.Ed448.SecretKey)
- Crypto.Store.CMS: AuthEnvelopedData :: OriginatorInfo -> [RecipientInfo] -> ContentType -> ASN1ObjectExact AuthContentEncryptionParams -> EncryptedContent -> [Attribute] -> MessageAuthenticationCode -> [Attribute] -> AuthEnvelopedData
+ Crypto.Store.CMS: AuthEnvelopedData :: OriginatorInfo -> [RecipientInfo] -> ContentType -> ASN1ObjectExact AuthContentEncryptionParams -> content -> [Attribute] -> MessageAuthenticationCode -> [Attribute] -> AuthEnvelopedData content
- Crypto.Store.CMS: AuthEnvelopedDataCI :: AuthEnvelopedData -> ContentInfo
+ Crypto.Store.CMS: AuthEnvelopedDataCI :: AuthEnvelopedData (Encap EncryptedContent) -> ContentInfo
- Crypto.Store.CMS: AuthenticatedData :: OriginatorInfo -> [RecipientInfo] -> MACAlgorithm -> Maybe DigestAlgorithm -> ContentInfo -> [Attribute] -> MessageAuthenticationCode -> [Attribute] -> AuthenticatedData
+ Crypto.Store.CMS: AuthenticatedData :: OriginatorInfo -> [RecipientInfo] -> MACAlgorithm -> Maybe DigestAlgorithm -> ContentType -> content -> [Attribute] -> MessageAuthenticationCode -> [Attribute] -> AuthenticatedData content
- Crypto.Store.CMS: AuthenticatedDataCI :: AuthenticatedData -> ContentInfo
+ Crypto.Store.CMS: AuthenticatedDataCI :: AuthenticatedData (Encap EncapsulatedContent) -> ContentInfo
- Crypto.Store.CMS: CBC :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.CMS: CBC :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.CMS: CCM :: (ContentEncryptionCipher c) -> AuthContentEncryptionAlg
+ Crypto.Store.CMS: CCM :: ContentEncryptionCipher c -> AuthContentEncryptionAlg
- Crypto.Store.CMS: CFB :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.CMS: CFB :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.CMS: CTR :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.CMS: CTR :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.CMS: DigestAlgorithm :: (DigestProxy hashAlg) -> DigestAlgorithm
+ Crypto.Store.CMS: DigestAlgorithm :: DigestProxy hashAlg -> DigestAlgorithm
- Crypto.Store.CMS: DigestedData :: DigestProxy hashAlg -> ContentInfo -> Digest hashAlg -> DigestedData
+ Crypto.Store.CMS: DigestedData :: DigestProxy hashAlg -> ContentType -> content -> Digest hashAlg -> DigestedData content
- Crypto.Store.CMS: DigestedDataCI :: DigestedData -> ContentInfo
+ Crypto.Store.CMS: DigestedDataCI :: DigestedData (Encap EncapsulatedContent) -> ContentInfo
- Crypto.Store.CMS: ECB :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.CMS: ECB :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.CMS: EncryptedData :: ContentType -> ContentEncryptionParams -> EncryptedContent -> [Attribute] -> EncryptedData
+ Crypto.Store.CMS: EncryptedData :: ContentType -> ContentEncryptionParams -> content -> [Attribute] -> EncryptedData content
- Crypto.Store.CMS: EncryptedDataCI :: EncryptedData -> ContentInfo
+ Crypto.Store.CMS: EncryptedDataCI :: EncryptedData (Encap EncryptedContent) -> ContentInfo
- Crypto.Store.CMS: EnvelopedData :: OriginatorInfo -> [RecipientInfo] -> ContentType -> ContentEncryptionParams -> EncryptedContent -> [Attribute] -> EnvelopedData
+ Crypto.Store.CMS: EnvelopedData :: OriginatorInfo -> [RecipientInfo] -> ContentType -> ContentEncryptionParams -> content -> [Attribute] -> EnvelopedData content
- Crypto.Store.CMS: EnvelopedDataCI :: EnvelopedData -> ContentInfo
+ Crypto.Store.CMS: EnvelopedDataCI :: EnvelopedData (Encap EncryptedContent) -> ContentInfo
- Crypto.Store.CMS: GCM :: (ContentEncryptionCipher c) -> AuthContentEncryptionAlg
+ Crypto.Store.CMS: GCM :: ContentEncryptionCipher c -> AuthContentEncryptionAlg
- Crypto.Store.CMS: HMAC :: (DigestProxy hashAlg) -> MACAlgorithm
+ Crypto.Store.CMS: HMAC :: DigestProxy hashAlg -> MACAlgorithm
- Crypto.Store.CMS: SignedData :: [DigestAlgorithm] -> ContentInfo -> [CertificateChoice] -> [RevocationInfoChoice] -> [SignerInfo] -> SignedData
+ Crypto.Store.CMS: SignedData :: [DigestAlgorithm] -> ContentType -> content -> [CertificateChoice] -> [RevocationInfoChoice] -> [SignerInfo] -> SignedData content
- Crypto.Store.CMS: SignedDataCI :: SignedData -> ContentInfo
+ Crypto.Store.CMS: SignedDataCI :: SignedData (Encap EncapsulatedContent) -> ContentInfo
- Crypto.Store.CMS: [adAuthAttrs] :: AuthenticatedData -> [Attribute]
+ Crypto.Store.CMS: [adAuthAttrs] :: AuthenticatedData content -> [Attribute]
- Crypto.Store.CMS: [adDigestAlgorithm] :: AuthenticatedData -> Maybe DigestAlgorithm
+ Crypto.Store.CMS: [adDigestAlgorithm] :: AuthenticatedData content -> Maybe DigestAlgorithm
- Crypto.Store.CMS: [adMACAlgorithm] :: AuthenticatedData -> MACAlgorithm
+ Crypto.Store.CMS: [adMACAlgorithm] :: AuthenticatedData content -> MACAlgorithm
- Crypto.Store.CMS: [adMAC] :: AuthenticatedData -> MessageAuthenticationCode
+ Crypto.Store.CMS: [adMAC] :: AuthenticatedData content -> MessageAuthenticationCode
- Crypto.Store.CMS: [adOriginatorInfo] :: AuthenticatedData -> OriginatorInfo
+ Crypto.Store.CMS: [adOriginatorInfo] :: AuthenticatedData content -> OriginatorInfo
- Crypto.Store.CMS: [adRecipientInfos] :: AuthenticatedData -> [RecipientInfo]
+ Crypto.Store.CMS: [adRecipientInfos] :: AuthenticatedData content -> [RecipientInfo]
- Crypto.Store.CMS: [adUnauthAttrs] :: AuthenticatedData -> [Attribute]
+ Crypto.Store.CMS: [adUnauthAttrs] :: AuthenticatedData content -> [Attribute]
- Crypto.Store.CMS: [aeAuthAttrs] :: AuthEnvelopedData -> [Attribute]
+ Crypto.Store.CMS: [aeAuthAttrs] :: AuthEnvelopedData content -> [Attribute]
- Crypto.Store.CMS: [aeContentEncryptionParams] :: AuthEnvelopedData -> ASN1ObjectExact AuthContentEncryptionParams
+ Crypto.Store.CMS: [aeContentEncryptionParams] :: AuthEnvelopedData content -> ASN1ObjectExact AuthContentEncryptionParams
- Crypto.Store.CMS: [aeContentType] :: AuthEnvelopedData -> ContentType
+ Crypto.Store.CMS: [aeContentType] :: AuthEnvelopedData content -> ContentType
- Crypto.Store.CMS: [aeEncryptedContent] :: AuthEnvelopedData -> EncryptedContent
+ Crypto.Store.CMS: [aeEncryptedContent] :: AuthEnvelopedData content -> content
- Crypto.Store.CMS: [aeMAC] :: AuthEnvelopedData -> MessageAuthenticationCode
+ Crypto.Store.CMS: [aeMAC] :: AuthEnvelopedData content -> MessageAuthenticationCode
- Crypto.Store.CMS: [aeOriginatorInfo] :: AuthEnvelopedData -> OriginatorInfo
+ Crypto.Store.CMS: [aeOriginatorInfo] :: AuthEnvelopedData content -> OriginatorInfo
- Crypto.Store.CMS: [aeRecipientInfos] :: AuthEnvelopedData -> [RecipientInfo]
+ Crypto.Store.CMS: [aeRecipientInfos] :: AuthEnvelopedData content -> [RecipientInfo]
- Crypto.Store.CMS: [aeUnauthAttrs] :: AuthEnvelopedData -> [Attribute]
+ Crypto.Store.CMS: [aeUnauthAttrs] :: AuthEnvelopedData content -> [Attribute]
- Crypto.Store.CMS: [ddDigestAlgorithm] :: DigestedData -> DigestProxy hashAlg
+ Crypto.Store.CMS: [ddDigestAlgorithm] :: DigestedData content -> DigestProxy hashAlg
- Crypto.Store.CMS: [ddDigest] :: DigestedData -> Digest hashAlg
+ Crypto.Store.CMS: [ddDigest] :: DigestedData content -> Digest hashAlg
- Crypto.Store.CMS: [edContentEncryptionParams] :: EncryptedData -> ContentEncryptionParams
+ Crypto.Store.CMS: [edContentEncryptionParams] :: EncryptedData content -> ContentEncryptionParams
- Crypto.Store.CMS: [edContentType] :: EncryptedData -> ContentType
+ Crypto.Store.CMS: [edContentType] :: EncryptedData content -> ContentType
- Crypto.Store.CMS: [edEncryptedContent] :: EncryptedData -> EncryptedContent
+ Crypto.Store.CMS: [edEncryptedContent] :: EncryptedData content -> content
- Crypto.Store.CMS: [edUnprotectedAttrs] :: EncryptedData -> [Attribute]
+ Crypto.Store.CMS: [edUnprotectedAttrs] :: EncryptedData content -> [Attribute]
- Crypto.Store.CMS: [evContentEncryptionParams] :: EnvelopedData -> ContentEncryptionParams
+ Crypto.Store.CMS: [evContentEncryptionParams] :: EnvelopedData content -> ContentEncryptionParams
- Crypto.Store.CMS: [evContentType] :: EnvelopedData -> ContentType
+ Crypto.Store.CMS: [evContentType] :: EnvelopedData content -> ContentType
- Crypto.Store.CMS: [evEncryptedContent] :: EnvelopedData -> EncryptedContent
+ Crypto.Store.CMS: [evEncryptedContent] :: EnvelopedData content -> content
- Crypto.Store.CMS: [evOriginatorInfo] :: EnvelopedData -> OriginatorInfo
+ Crypto.Store.CMS: [evOriginatorInfo] :: EnvelopedData content -> OriginatorInfo
- Crypto.Store.CMS: [evRecipientInfos] :: EnvelopedData -> [RecipientInfo]
+ Crypto.Store.CMS: [evRecipientInfos] :: EnvelopedData content -> [RecipientInfo]
- Crypto.Store.CMS: [evUnprotectedAttrs] :: EnvelopedData -> [Attribute]
+ Crypto.Store.CMS: [evUnprotectedAttrs] :: EnvelopedData content -> [Attribute]
- Crypto.Store.CMS: [sdCRLs] :: SignedData -> [RevocationInfoChoice]
+ Crypto.Store.CMS: [sdCRLs] :: SignedData content -> [RevocationInfoChoice]
- Crypto.Store.CMS: [sdCertificates] :: SignedData -> [CertificateChoice]
+ Crypto.Store.CMS: [sdCertificates] :: SignedData content -> [CertificateChoice]
- Crypto.Store.CMS: [sdDigestAlgorithms] :: SignedData -> [DigestAlgorithm]
+ Crypto.Store.CMS: [sdDigestAlgorithms] :: SignedData content -> [DigestAlgorithm]
- Crypto.Store.CMS: [sdSignerInfos] :: SignedData -> [SignerInfo]
+ Crypto.Store.CMS: [sdSignerInfos] :: SignedData content -> [SignerInfo]
- Crypto.Store.CMS: authEnvelopData :: Applicative f => OriginatorInfo -> ContentEncryptionKey -> AuthContentEncryptionParams -> [ProducerOfRI f] -> [Attribute] -> [Attribute] -> ContentInfo -> f (Either StoreError ContentInfo)
+ Crypto.Store.CMS: authEnvelopData :: Applicative f => OriginatorInfo -> ContentEncryptionKey -> AuthContentEncryptionParams -> [ProducerOfRI f] -> [Attribute] -> [Attribute] -> ContentInfo -> f (Either StoreError (AuthEnvelopedData EncryptedContent))
- Crypto.Store.CMS: data AuthEnvelopedData
+ Crypto.Store.CMS: data AuthEnvelopedData content
- Crypto.Store.CMS: data AuthenticatedData
+ Crypto.Store.CMS: data AuthenticatedData content
- Crypto.Store.CMS: data DigestedData
+ Crypto.Store.CMS: data DigestedData content
- Crypto.Store.CMS: data EncryptedData
+ Crypto.Store.CMS: data EncryptedData content
- Crypto.Store.CMS: data EnvelopedData
+ Crypto.Store.CMS: data EnvelopedData content
- Crypto.Store.CMS: data SignedData
+ Crypto.Store.CMS: data SignedData content
- Crypto.Store.CMS: decryptData :: ContentEncryptionKey -> EncryptedData -> Either StoreError ContentInfo
+ Crypto.Store.CMS: decryptData :: ContentEncryptionKey -> EncryptedData EncryptedContent -> Either StoreError ContentInfo
- Crypto.Store.CMS: digestData :: DigestAlgorithm -> ContentInfo -> ContentInfo
+ Crypto.Store.CMS: digestData :: DigestAlgorithm -> ContentInfo -> DigestedData EncapsulatedContent
- Crypto.Store.CMS: digestVerify :: DigestedData -> Maybe ContentInfo
+ Crypto.Store.CMS: digestVerify :: DigestedData EncapsulatedContent -> Either StoreError ContentInfo
- Crypto.Store.CMS: encryptData :: ContentEncryptionKey -> ContentEncryptionParams -> [Attribute] -> ContentInfo -> Either StoreError ContentInfo
+ Crypto.Store.CMS: encryptData :: ContentEncryptionKey -> ContentEncryptionParams -> [Attribute] -> ContentInfo -> Either StoreError (EncryptedData EncryptedContent)
- Crypto.Store.CMS: envelopData :: Applicative f => OriginatorInfo -> ContentEncryptionKey -> ContentEncryptionParams -> [ProducerOfRI f] -> [Attribute] -> ContentInfo -> f (Either StoreError ContentInfo)
+ Crypto.Store.CMS: envelopData :: Applicative f => OriginatorInfo -> ContentEncryptionKey -> ContentEncryptionParams -> [ProducerOfRI f] -> [Attribute] -> ContentInfo -> f (Either StoreError (EnvelopedData EncryptedContent))
- Crypto.Store.CMS: generateAuthenticatedData :: Applicative f => OriginatorInfo -> AuthenticationKey -> MACAlgorithm -> Maybe DigestAlgorithm -> [ProducerOfRI f] -> [Attribute] -> [Attribute] -> ContentInfo -> f (Either StoreError ContentInfo)
+ Crypto.Store.CMS: generateAuthenticatedData :: Applicative f => OriginatorInfo -> AuthenticationKey -> MACAlgorithm -> Maybe DigestAlgorithm -> [ProducerOfRI f] -> [Attribute] -> [Attribute] -> ContentInfo -> f (Either StoreError (AuthenticatedData EncapsulatedContent))
- Crypto.Store.CMS: openAuthEnvelopedData :: Monad m => ConsumerOfRI m -> AuthEnvelopedData -> m (Either StoreError ContentInfo)
+ Crypto.Store.CMS: openAuthEnvelopedData :: Monad m => ConsumerOfRI m -> AuthEnvelopedData EncryptedContent -> m (Either StoreError ContentInfo)
- Crypto.Store.CMS: openEnvelopedData :: Monad m => ConsumerOfRI m -> EnvelopedData -> m (Either StoreError ContentInfo)
+ Crypto.Store.CMS: openEnvelopedData :: Monad m => ConsumerOfRI m -> EnvelopedData EncryptedContent -> m (Either StoreError ContentInfo)
- Crypto.Store.CMS: signData :: Applicative f => [ProducerOfSI f] -> ContentInfo -> f (Either StoreError ContentInfo)
+ Crypto.Store.CMS: signData :: Applicative f => [ProducerOfSI f] -> ContentInfo -> f (Either StoreError (SignedData EncapsulatedContent))
- Crypto.Store.CMS: verifyAuthenticatedData :: Monad m => ConsumerOfRI m -> AuthenticatedData -> m (Either StoreError ContentInfo)
+ Crypto.Store.CMS: verifyAuthenticatedData :: Monad m => ConsumerOfRI m -> AuthenticatedData EncapsulatedContent -> m (Either StoreError ContentInfo)
- Crypto.Store.CMS: verifySignedData :: Monad m => ConsumerOfSI m -> SignedData -> m (Maybe ContentInfo)
+ Crypto.Store.CMS: verifySignedData :: Monad m => ConsumerOfSI m -> SignedData EncapsulatedContent -> m (Either StoreError ContentInfo)
- Crypto.Store.PKCS12: CRLBag :: (Bag CRLInfo) -> SafeInfo
+ Crypto.Store.PKCS12: CRLBag :: Bag CRLInfo -> SafeInfo
- Crypto.Store.PKCS12: CertBag :: (Bag CertInfo) -> SafeInfo
+ Crypto.Store.PKCS12: CertBag :: Bag CertInfo -> SafeInfo
- Crypto.Store.PKCS12: KeyBag :: (FormattedKey PrivKey) -> SafeInfo
+ Crypto.Store.PKCS12: KeyBag :: FormattedKey PrivKey -> SafeInfo
- Crypto.Store.PKCS5: CBC :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.PKCS5: CBC :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.PKCS5: CFB :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.PKCS5: CFB :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.PKCS5: CTR :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.PKCS5: CTR :: ContentEncryptionCipher c -> ContentEncryptionAlg
- Crypto.Store.PKCS5: ECB :: (ContentEncryptionCipher c) -> ContentEncryptionAlg
+ Crypto.Store.PKCS5: ECB :: ContentEncryptionCipher c -> ContentEncryptionAlg

Files

ChangeLog.md view
@@ -1,5 +1,36 @@ # Revision history for cryptostore -## 0.1.0.0 -- 2018-09-23+## 0.2.0.0 - 2019-03-24++* Added functions `toNamedCredential` and `fromNamedCredential` to handle+  PKCS#12 elements with an alias (friendly name).++* Functions `fromCredential` and `fromNamedCredential` now generate PKCS#12+  elements with the `localKeyId` attribute.++* Function `toCredential` is now able to locate the leaf certificate and issuers+  more reliably.++* Algorithms X25519, X448, Ed25519 and Ed448 are now supported.++* CMS functions `digestVerify` and `verifySignedData` now return an `Either`+  instead of a `Maybe`.  Errors `DigestMismatch` and `SignatureNotVerified` are+  added to report failures.++* CMS types `SignedData`, `DigestedData` and `AuthenticatedData` now retain the+  encapsulated content in encoded form (with type alias `EncapsulatedContent`)+  instead of a decoded and parsed `ContentInfo`.  The `ContentInfo` is parsed+  and provided only when successfully unwrapping the encapsulated type.++* The CMS interface is transformed to support detached content.  CMS types now+  have a type parameter to distinguish between a direct reference to the+  encapsulated or encrypted content, and the `Encap` indirection which denotes+  an attached or detached content.  Functions building CMS types do not return+  the `ContentInfo` directly anymore, but an intermediate type to be fed into+  `toAttachedCI` or `toDetachedCI`.  Reverse transformation is possible with+  utility functions `fromAttached` and `fromDetached` when unwrapping a+  `ContentInfo`.++## 0.1.0.0 - 2018-09-23  * First version. Released on an unsuspecting world.
README.md view
@@ -137,8 +137,6 @@  The API also provides functions to generate/extract a pair containing a private key and a certificate chain.  This pair is the type alias `Credential` in `tls`.-Currently the functions assume that the PKCS #12 file contains no other data-than the credential.  ```haskell > :set -XOverloadedStrings@@ -170,6 +168,9 @@ > writeP12File "/path/to/newfile.p12" iParams "myintegrityassword" pkcs12' ``` +Variants `toNamedCredential` and `fromNamedCredential` are also available when+PKCS #12 elements need an alias (friendly name).+ ## Cryptographic Message Syntax  The API to read and write CMS content is available in `Crypto.Store.CMS`.  The@@ -187,7 +188,6 @@  Notable omissions: -* detached content * streaming * compressed data * and S/MIME external format (only PEM is supported, i.e. the textual encoding@@ -217,8 +217,10 @@ > keParams <- generateEncryptionParams (CBC AES128) > let pri = forPasswordRecipient "mypassword" kdf (PWRIKEK keParams) --- Generate the enveloped structure for this single recipient-> Right envelopedCI <- envelopData mempty ceKey ceParams [pri] [] info+-- Generate the enveloped structure for this single recipient.  Encrypted+-- content is kept attached in the structure.+> Right envelopedData <- envelopData mempty ceKey ceParams [pri] [] info+> let envelopedCI = toAttachedCI envelopedData > writeCMSFile "/path/to/enveloped.pem" [envelopedCI] ``` @@ -230,7 +232,8 @@  -- Then this recipient just has to read the file and recover enveloped -- content using the password-> [EnvelopedDataCI envelopedData] <- readCMSFile "/path/to/enveloped.pem"+> [EnvelopedDataCI envelopedEncapData] <- readCMSFile "/path/to/enveloped.pem"+> envelopedData <- fromAttached envelopedEncapData > openEnvelopedData (withRecipientPassword "mypassword") envelopedData Right (DataCI "Hi, what will you need from the cryptostore?") ```
cryptostore.cabal view
@@ -1,5 +1,5 @@ name:                cryptostore-version:             0.1.0.0+version:             0.2.0.0 synopsis:            Serialization of cryptographic data types description:         Haskell implementation of PKCS \#8, PKCS \#12 and CMS                      (Cryptographic Message Syntax).@@ -35,7 +35,9 @@                      , Crypto.Store.ASN1.Parse                      , Crypto.Store.CMS.Algorithms                      , Crypto.Store.CMS.Attribute+                     , Crypto.Store.CMS.Authenticated                      , Crypto.Store.CMS.AuthEnveloped+                     , Crypto.Store.CMS.Digested                      , Crypto.Store.CMS.Encrypted                      , Crypto.Store.CMS.Enveloped                      , Crypto.Store.CMS.Info@@ -59,7 +61,8 @@                      , asn1-types >= 0.3.1 && < 0.4                      , asn1-encoding >= 0.9 && < 0.10                      , hourglass >= 0.2-                     , x509 >= 1.7.3+                     , x509 >= 1.7.5+                     , x509-validation >= 1.5   default-language:    Haskell2010   ghc-options:         -Wall 
src/Crypto/Store/ASN1/Parse.hs view
@@ -43,7 +43,7 @@ import Control.Applicative import Control.Arrow (first) import Control.Monad (MonadPlus(..), liftM2)-import Control.Monad.Fail+import Control.Monad.Fail as Fail  data State e = State [(ASN1, e)] !e @@ -70,7 +70,7 @@         case runP m1 s of             Left err      -> Left err             Right (a, s2) -> runP (m2 a) s2-    fail        = throwParseError+    fail        = Fail.fail instance MonadFail (ParseASN1 e) where     fail = throwParseError instance MonadPlus (ParseASN1 e) where
src/Crypto/Store/CMS.hs view
@@ -19,6 +19,8 @@ -- * <https://tools.ietf.org/html/rfc5084 RFC 5084>: Using AES-CCM and AES-GCM Authenticated Encryption in the Cryptographic Message Syntax (CMS) -- * <https://tools.ietf.org/html/rfc6476 RFC 6476>: Using Message Authentication Code (MAC) Encryption in the Cryptographic Message Syntax (CMS) -- * <https://tools.ietf.org/html/rfc8103 RFC 8103>: Using ChaCha20-Poly1305 Authenticated Encryption in the Cryptographic Message Syntax (CMS)+-- * <https://tools.ietf.org/html/rfc8418 RFC 8418>: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)+-- * <https://tools.ietf.org/html/rfc8419 RFC 8419>: Use of Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the Cryptographic Message Syntax (CMS) {-# LANGUAGE RecordWildCards #-} module Crypto.Store.CMS     ( ContentType(..)@@ -26,9 +28,20 @@     , getContentType     -- * Reading and writing PEM files     , module Crypto.Store.CMS.PEM+    -- * Content encapsulation+    , Encap(..)+    , fromEncap+    , Encapsulates+    , isAttached+    , fromAttached+    , toAttachedCI+    , isDetached+    , fromDetached+    , toDetachedCI     -- * Signed data     , SignatureValue     , SignatureAlg(..)+    , EncapsulatedContent     , SignedData(..)     , ProducerOfSI     , ConsumerOfSI@@ -75,7 +88,7 @@     , forKeyRecipient     , withRecipientKey      -- ** Password recipients-   , PasswordRecipientInfo(..)+    , PasswordRecipientInfo(..)     , forPasswordRecipient     , withRecipientPassword     -- * Digested data@@ -141,14 +154,20 @@     , ASN1ObjectExact     ) where +import Data.ASN1.BinaryEncoding+import Data.ASN1.Encoding+import Data.ByteString (ByteString) import Data.Maybe (isJust) import Data.List (nub, unzip3)  import Crypto.Hash +import Crypto.Store.ASN1.Parse import Crypto.Store.CMS.Algorithms import Crypto.Store.CMS.Attribute+import Crypto.Store.CMS.Authenticated import Crypto.Store.CMS.AuthEnveloped+import Crypto.Store.CMS.Digested import Crypto.Store.CMS.Encrypted import Crypto.Store.CMS.Enveloped import Crypto.Store.CMS.OriginatorInfo@@ -158,25 +177,27 @@ import Crypto.Store.CMS.Type import Crypto.Store.CMS.Util import Crypto.Store.Error+import Crypto.Store.Util   -- DigestedData  -- | Add a digested-data layer on the specified content info.-digestData :: DigestAlgorithm -> ContentInfo -> ContentInfo-digestData (DigestAlgorithm alg) ci = DigestedDataCI dd+digestData :: DigestAlgorithm -> ContentInfo -> DigestedData EncapsulatedContent+digestData (DigestAlgorithm alg) ci = dd   where dd = DigestedData                  { ddDigestAlgorithm = alg-                 , ddContentInfo     = ci-                 , ddDigest          = hash (encapsulate ci)+                 , ddContentType = getContentType ci+                 , ddEncapsulatedContent = encapsulate ci+                 , ddDigest = hash (encapsulate ci)                  }  -- | Return the inner content info but only if the digest is valid.-digestVerify :: DigestedData -> Maybe ContentInfo+digestVerify :: DigestedData EncapsulatedContent -> Either StoreError ContentInfo digestVerify DigestedData{..} =-    if ddDigest == hash (encapsulate ddContentInfo)-        then Just ddContentInfo-        else Nothing+    if ddDigest == hash ddEncapsulatedContent+        then decapsulate ddContentType ddEncapsulatedContent+        else Left DigestMismatch   -- EncryptedData@@ -189,9 +210,9 @@             -> ContentEncryptionParams             -> [Attribute]             -> ContentInfo-            -> Either StoreError ContentInfo+            -> Either StoreError (EncryptedData EncryptedContent) encryptData key params attrs ci =-    EncryptedDataCI . build <$> contentEncrypt key params (encapsulate ci)+    build <$> contentEncrypt key params (encapsulate ci)   where     build ec = EncryptedData                    { edContentType = getContentType ci@@ -202,7 +223,7 @@  -- | Decrypt an encrypted content info using the specified key. decryptData :: ContentEncryptionKey-            -> EncryptedData+            -> EncryptedData EncryptedContent             -> Either StoreError ContentInfo decryptData key EncryptedData{..} = do     decrypted <- contentDecrypt key edContentEncryptionParams edEncryptedContent@@ -223,12 +244,12 @@             -> [ProducerOfRI f]             -> [Attribute]             -> ContentInfo-            -> f (Either StoreError ContentInfo)+            -> f (Either StoreError (EnvelopedData EncryptedContent)) envelopData oinfo key params envFns attrs ci =     f <$> (sequence <$> traverse ($ key) envFns)   where     ebs = contentEncrypt key params (encapsulate ci)-    f ris = EnvelopedDataCI <$> (build <$> ebs <*> ris)+    f ris = build <$> ebs <*> ris     build bs ris = EnvelopedData                        { evOriginatorInfo = oinfo                        , evRecipientInfos = ris@@ -242,7 +263,7 @@ -- function. openEnvelopedData :: Monad m                   => ConsumerOfRI m-                  -> EnvelopedData+                  -> EnvelopedData EncryptedContent                   -> m (Either StoreError ContentInfo) openEnvelopedData devFn EnvelopedData{..} = do     r <- riAttempts (map (fmap (>>= decr) . devFn) evRecipientInfos)@@ -273,7 +294,7 @@                           -> [Attribute]                           -> [Attribute]                           -> ContentInfo-                          -> f (Either StoreError ContentInfo)+                          -> f (Either StoreError (AuthenticatedData EncapsulatedContent)) generateAuthenticatedData oinfo key macAlg digAlg envFns aAttrs uAttrs ci =     f <$> (sequence <$> traverse ($ key) envFns)   where@@ -289,13 +310,14 @@                 in (l, encodeAuthAttrs l)      ebs   = mac macAlg key input-    f ris = AuthenticatedDataCI <$> (build ebs <$> ris)+    f ris = build ebs <$> ris     build authTag ris = AuthenticatedData                             { adOriginatorInfo = oinfo                             , adRecipientInfos = ris                             , adMACAlgorithm = macAlg                             , adDigestAlgorithm = digAlg-                            , adContentInfo = ci+                            , adContentType = getContentType ci+                            , adEncapsulatedContent = encapsulate ci                             , adAuthAttrs = aAttrs'                             , adMAC = authTag                             , adUnauthAttrs = uAttrs@@ -306,13 +328,13 @@ -- could be verified. verifyAuthenticatedData :: Monad m                         => ConsumerOfRI m-                        -> AuthenticatedData+                        -> AuthenticatedData EncapsulatedContent                         -> m (Either StoreError ContentInfo) verifyAuthenticatedData devFn AuthenticatedData{..} =     riAttempts (map (fmap (>>= unwrap) . devFn) adRecipientInfos)   where-    msg = encapsulate adContentInfo-    ct  = getContentType adContentInfo+    msg = adEncapsulatedContent+    ct  = adContentType      noAttr    = null adAuthAttrs     mdMatch   = case adDigestAlgorithm of@@ -327,7 +349,7 @@         | isJust adDigestAlgorithm && noAttr  = Left (InvalidInput "Missing auth attributes")         | not noAttr && not attrMatch         = Left (InvalidInput "Invalid auth attributes")         | adMAC /= mac adMACAlgorithm k input = Left BadContentMAC-        | otherwise                           = Right adContentInfo+        | otherwise                           = decapsulate adContentType adEncapsulatedContent   -- AuthEnvelopedData@@ -347,14 +369,14 @@                 -> [Attribute]                 -> [Attribute]                 -> ContentInfo-                -> f (Either StoreError ContentInfo)+                -> f (Either StoreError (AuthEnvelopedData EncryptedContent)) authEnvelopData oinfo key params envFns aAttrs uAttrs ci =     f <$> (sequence <$> traverse ($ key) envFns)   where     raw = encodeASN1Object params     aad = encodeAuthAttrs aAttrs     ebs = authContentEncrypt key params raw aad (encapsulate ci)-    f ris = AuthEnvelopedDataCI <$> (build <$> ebs <*> ris)+    f ris = build <$> ebs <*> ris     build (authTag, bs) ris = AuthEnvelopedData                        { aeOriginatorInfo = oinfo                        , aeRecipientInfos = ris@@ -370,7 +392,7 @@ -- 'ConsumerOfRI' function. openAuthEnvelopedData :: Monad m                       => ConsumerOfRI m-                      -> AuthEnvelopedData+                      -> AuthEnvelopedData EncryptedContent                       -> m (Either StoreError ContentInfo) openAuthEnvelopedData devFn AuthEnvelopedData{..} = do     r <- riAttempts (map (fmap (>>= decr) . devFn) aeRecipientInfos)@@ -389,18 +411,19 @@ -- processed by one or several 'ProducerOfSI' functions to create signer info -- elements. signData :: Applicative f-         => [ProducerOfSI f] -> ContentInfo -> f (Either StoreError ContentInfo)+         => [ProducerOfSI f] -> ContentInfo -> f (Either StoreError (SignedData EncapsulatedContent)) signData sigFns ci =     f <$> (sequence <$> traverse (\fn -> fn ct msg) sigFns)   where     msg = encapsulate ci     ct  = getContentType ci-    f   = fmap (SignedDataCI . build . unzip3)+    f   = fmap (build . unzip3)      build (sis, certLists, crlLists) =         SignedData             { sdDigestAlgorithms = nub (map siDigestAlgorithm sis)-            , sdContentInfo = ci+            , sdContentType = getContentType ci+            , sdEncapsulatedContent = encapsulate ci             , sdCertificates = concat certLists             , sdCRLs = concat crlLists             , sdSignerInfos = sis@@ -410,14 +433,15 @@ -- Verification of at least one signer info must be successful in order to -- return the inner content info. verifySignedData :: Monad m-                 => ConsumerOfSI m -> SignedData -> m (Maybe ContentInfo)+                 => ConsumerOfSI m -> SignedData EncapsulatedContent -> m (Either StoreError ContentInfo) verifySignedData verFn SignedData{..} =     f <$> siAttemps valid sdSignerInfos   where-    msg      = encapsulate sdContentInfo-    ct       = getContentType sdContentInfo+    msg      = sdEncapsulatedContent+    ct       = sdContentType     valid si = verFn ct msg si sdCertificates sdCRLs-    f bool   = if bool then Just sdContentInfo else Nothing+    f bool   = if bool then decapsulate sdContentType sdEncapsulatedContent+                       else Left SignatureNotVerified   -- Utilities@@ -437,3 +461,27 @@ siAttemps _ []     = pure False siAttemps f (x:xs) = f x >>= orTail   where orTail bool = if bool then return True else siAttemps f xs++decode :: ParseASN1 [ASN1Event] a -> ByteString -> Either StoreError a+decode parser bs = vals >>= mapLeft ParseFailure . runParseASN1_ parser+  where vals = mapLeft DecodingError (decodeASN1Repr' BER bs)++-- | Encode the information for encapsulation in another content info.+encapsulate :: ContentInfo -> ByteString+encapsulate (DataCI bs)              = bs+encapsulate (SignedDataCI ed)        = encodeASN1Object ed+encapsulate (EnvelopedDataCI ed)     = encodeASN1Object ed+encapsulate (DigestedDataCI dd)      = encodeASN1Object dd+encapsulate (EncryptedDataCI ed)     = encodeASN1Object ed+encapsulate (AuthenticatedDataCI ad) = encodeASN1Object ad+encapsulate (AuthEnvelopedDataCI ae) = encodeASN1Object ae++-- | Decode the information from encapsulated content.+decapsulate :: ContentType -> ByteString -> Either StoreError ContentInfo+decapsulate DataType bs              = pure (DataCI bs)+decapsulate SignedDataType bs        = SignedDataCI <$> decode parse bs+decapsulate EnvelopedDataType bs     = EnvelopedDataCI <$> decode parse bs+decapsulate DigestedDataType bs      = DigestedDataCI <$> decode parse bs+decapsulate EncryptedDataType bs     = EncryptedDataCI <$> decode parse bs+decapsulate AuthenticatedDataType bs = AuthenticatedDataCI <$> decode parse bs+decapsulate AuthEnvelopedDataType bs = AuthEnvelopedDataCI <$> decode parse bs
src/Crypto/Store/CMS/Algorithms.hs view
@@ -6,6 +6,7 @@ -- Portability : unknown -- -- Cryptographic Message Syntax algorithms+{-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}@@ -60,7 +61,9 @@     , transportEncrypt     , transportDecrypt     , KeyAgreementParams(..)+    , ECDHPair     , ecdhGenerate+    , ecdhPublic     , ecdhEncrypt     , ecdhDecrypt     , MaskGenerationFunc(..)@@ -86,10 +89,13 @@ import qualified Data.ByteArray as B import           Data.ByteString (ByteString) import           Data.Maybe (fromMaybe)+import           Data.Proxy import           Data.Word import qualified Data.X509 as X509 import           Data.X509.EC +import GHC.TypeLits+ import qualified Crypto.Cipher.AES as Cipher import qualified Crypto.Cipher.CAST5 as Cipher import qualified Crypto.Cipher.Camellia as Cipher@@ -98,6 +104,7 @@ import qualified Crypto.Cipher.TripleDES as Cipher import           Crypto.Cipher.Types import           Crypto.Data.Padding+import           Crypto.ECC (Curve_X25519, Curve_X448, ecdh) import           Crypto.Error import qualified Crypto.Hash as Hash import qualified Crypto.KDF.PBKDF2 as PBKDF2@@ -105,10 +112,14 @@ import qualified Crypto.MAC.HMAC as HMAC import qualified Crypto.MAC.Poly1305 as Poly1305 import           Crypto.Number.Serialize+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448 as X448 import qualified Crypto.PubKey.DSA as DSA import qualified Crypto.PubKey.ECC.DH as ECDH import qualified Crypto.PubKey.ECC.ECDSA as ECDSA import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Crypto.PubKey.Ed448 as Ed448 import qualified Crypto.PubKey.MaskGenFunction as MGF import qualified Crypto.PubKey.RSA.PSS as RSAPSS import qualified Crypto.PubKey.RSA.OAEP as RSAOAEP@@ -126,6 +137,7 @@ import qualified Crypto.Store.KeyWrap.AES as AES_KW import qualified Crypto.Store.KeyWrap.TripleDES as TripleDES_KW import qualified Crypto.Store.KeyWrap.RC2 as RC2_KW+import           Crypto.Store.PKCS8.EC import           Crypto.Store.Util  @@ -150,6 +162,14 @@     SHA384 :: DigestProxy Hash.SHA384     -- | SHA-512     SHA512 :: DigestProxy Hash.SHA512+    -- | SHAKE128 (256 bits)+    SHAKE128_256 :: DigestProxy (Hash.SHAKE128 256)+    -- | SHAKE256 (512 bits)+    SHAKE256_512 :: DigestProxy (Hash.SHAKE256 512)+    -- | SHAKE128 (variable size)+    SHAKE128 :: KnownNat n => Proxy n -> DigestProxy (Hash.SHAKE128 n)+    -- | SHAKE256 (variable size)+    SHAKE256 :: KnownNat n => Proxy n -> DigestProxy (Hash.SHAKE256 n)  deriving instance Show (DigestProxy hashAlg) deriving instance Eq (DigestProxy hashAlg)@@ -171,43 +191,95 @@     DigestAlgorithm SHA256       == DigestAlgorithm SHA256       = True     DigestAlgorithm SHA384       == DigestAlgorithm SHA384       = True     DigestAlgorithm SHA512       == DigestAlgorithm SHA512       = True+    DigestAlgorithm SHAKE128_256 == DigestAlgorithm SHAKE128_256 = True+    DigestAlgorithm SHAKE256_512 == DigestAlgorithm SHAKE256_512 = True+    DigestAlgorithm (SHAKE128 a) == DigestAlgorithm (SHAKE128 b) = natVal a == natVal b+    DigestAlgorithm (SHAKE256 a) == DigestAlgorithm (SHAKE256 b) = natVal a == natVal b     _                            == _                            = False -instance Enumerable DigestAlgorithm where-    values = [ DigestAlgorithm MD2-             , DigestAlgorithm MD4-             , DigestAlgorithm MD5-             , DigestAlgorithm SHA1-             , DigestAlgorithm SHA224-             , DigestAlgorithm SHA256-             , DigestAlgorithm SHA384-             , DigestAlgorithm SHA512+data DigestType+    = Type_MD2+    | Type_MD4+    | Type_MD5+    | Type_SHA1+    | Type_SHA224+    | Type_SHA256+    | Type_SHA384+    | Type_SHA512+    | Type_SHAKE128_256+    | Type_SHAKE256_512+    | Type_SHAKE128_Len+    | Type_SHAKE256_Len++instance Enumerable DigestType where+    values = [ Type_MD2+             , Type_MD4+             , Type_MD5+             , Type_SHA1+             , Type_SHA224+             , Type_SHA256+             , Type_SHA384+             , Type_SHA512+             , Type_SHAKE128_256+             , Type_SHAKE256_512+             , Type_SHAKE128_Len+             , Type_SHAKE256_Len              ] -instance OIDable DigestAlgorithm where-    getObjectID (DigestAlgorithm MD2)    = [1,2,840,113549,2,2]-    getObjectID (DigestAlgorithm MD4)    = [1,2,840,113549,2,4]-    getObjectID (DigestAlgorithm MD5)    = [1,2,840,113549,2,5]-    getObjectID (DigestAlgorithm SHA1)   = [1,3,14,3,2,26]-    getObjectID (DigestAlgorithm SHA224) = [2,16,840,1,101,3,4,2,4]-    getObjectID (DigestAlgorithm SHA256) = [2,16,840,1,101,3,4,2,1]-    getObjectID (DigestAlgorithm SHA384) = [2,16,840,1,101,3,4,2,2]-    getObjectID (DigestAlgorithm SHA512) = [2,16,840,1,101,3,4,2,3]+instance OIDable DigestType where+    getObjectID Type_MD2          = [1,2,840,113549,2,2]+    getObjectID Type_MD4          = [1,2,840,113549,2,4]+    getObjectID Type_MD5          = [1,2,840,113549,2,5]+    getObjectID Type_SHA1         = [1,3,14,3,2,26]+    getObjectID Type_SHA224       = [2,16,840,1,101,3,4,2,4]+    getObjectID Type_SHA256       = [2,16,840,1,101,3,4,2,1]+    getObjectID Type_SHA384       = [2,16,840,1,101,3,4,2,2]+    getObjectID Type_SHA512       = [2,16,840,1,101,3,4,2,3]+    getObjectID Type_SHAKE128_256 = [2,16,840,1,101,3,4,2,11]+    getObjectID Type_SHAKE256_512 = [2,16,840,1,101,3,4,2,12]+    getObjectID Type_SHAKE128_Len = [2,16,840,1,101,3,4,2,17]+    getObjectID Type_SHAKE256_Len = [2,16,840,1,101,3,4,2,18] -instance OIDNameable DigestAlgorithm where+instance OIDNameable DigestType where     fromObjectID oid = unOIDNW <$> fromObjectID oid  instance AlgorithmId DigestAlgorithm where-    type AlgorithmType DigestAlgorithm = DigestAlgorithm+    type AlgorithmType DigestAlgorithm = DigestType     algorithmName _  = "digest algorithm"-    algorithmType    = id -    -- MD5 has NULL parameter, other algorithms have no parameter-    parameterASN1S (DigestAlgorithm MD5) = gNull-    parameterASN1S _                     = id+    algorithmType (DigestAlgorithm MD2)          = Type_MD2+    algorithmType (DigestAlgorithm MD4)          = Type_MD4+    algorithmType (DigestAlgorithm MD5)          = Type_MD5+    algorithmType (DigestAlgorithm SHA1)         = Type_SHA1+    algorithmType (DigestAlgorithm SHA224)       = Type_SHA224+    algorithmType (DigestAlgorithm SHA256)       = Type_SHA256+    algorithmType (DigestAlgorithm SHA384)       = Type_SHA384+    algorithmType (DigestAlgorithm SHA512)       = Type_SHA512+    algorithmType (DigestAlgorithm SHAKE128_256) = Type_SHAKE128_256+    algorithmType (DigestAlgorithm SHAKE256_512) = Type_SHAKE256_512+    algorithmType (DigestAlgorithm (SHAKE128 _)) = Type_SHAKE128_Len+    algorithmType (DigestAlgorithm (SHAKE256 _)) = Type_SHAKE256_Len -    parseParameter p = getNextMaybe nullOrNothing >> return p+    -- MD5 has NULL parameter, SHAKE128 and SHAKE256 have the bitsize as+    -- parameter, other algorithms have no parameter+    parameterASN1S (DigestAlgorithm MD5)          = gNull+    parameterASN1S (DigestAlgorithm (SHAKE128 n)) = gIntVal (natVal n)+    parameterASN1S (DigestAlgorithm (SHAKE256 n)) = gIntVal (natVal n)+    parameterASN1S _                              = id +    parseParameter Type_MD2          = parseDigestParam (DigestAlgorithm MD2)+    parseParameter Type_MD4          = parseDigestParam (DigestAlgorithm MD4)+    parseParameter Type_MD5          = parseDigestParam (DigestAlgorithm MD5)+    parseParameter Type_SHA1         = parseDigestParam (DigestAlgorithm SHA1)+    parseParameter Type_SHA224       = parseDigestParam (DigestAlgorithm SHA224)+    parseParameter Type_SHA256       = parseDigestParam (DigestAlgorithm SHA256)+    parseParameter Type_SHA384       = parseDigestParam (DigestAlgorithm SHA384)+    parseParameter Type_SHA512       = parseDigestParam (DigestAlgorithm SHA512)+    parseParameter Type_SHAKE128_256 = parseDigestParam (DigestAlgorithm SHAKE128_256)+    parseParameter Type_SHAKE256_512 = parseDigestParam (DigestAlgorithm SHAKE256_512)+    parseParameter Type_SHAKE128_Len = parseBitLen 256 (DigestAlgorithm $ SHAKE128 p256)+    parseParameter Type_SHAKE256_Len = parseBitLen 512 (DigestAlgorithm $ SHAKE256 p512)+ -- | Compute the digest of a message. digest :: ByteArrayAccess message => DigestAlgorithm -> message -> ByteString digest (DigestAlgorithm hashAlg) message = B.convert (doHash hashAlg message)@@ -219,7 +291,25 @@ hashFromProxy :: proxy a -> a hashFromProxy _ = undefined +parseDigestParam :: Monoid e => DigestAlgorithm -> ParseASN1 e DigestAlgorithm+parseDigestParam p = getNextMaybe nullOrNothing >> return p +parseBitLen :: Monoid e => Integer -> a -> ParseASN1 e a+parseBitLen expected res = do+    IntVal n <- getNext+    if n == expected+        then return res+        else throwParseError ("Invalid bit length: " ++ show n)+        -- FIXME: should allow any input size but cryptonite requires+        -- divisibility by 8.  For now we restrict to 256/512 bits only.++p256 :: Proxy 256+p256 = Proxy++p512 :: Proxy 512+p512 = Proxy++ -- Cipher-like things  -- | Algorithms that are based on a secret key.  This includes ciphers but also@@ -1497,57 +1587,97 @@                       (gOctetString $ toWord32 outBits)          in encodeASN1S $ asn1Container Sequence (ki . eui . spi) +-- | Key pair for ECDH.+data ECDHPair+    = PairEC ECC.Curve ECC.PrivateNumber ECC.Point+    | PairX25519 X25519.SecretKey X25519.PublicKey+    | PairX448 X448.SecretKey X448.PublicKey+ -- | Generate an ephemeral ECDH key.-ecdhGenerate :: MonadRandom m => X509.PubKey -> m (Either StoreError (ECC.Curve, ECC.PrivateNumber, X509.SerializedPoint))+ecdhGenerate :: MonadRandom m => X509.PubKey -> m (Either StoreError ECDHPair) ecdhGenerate (X509.PubKeyEC pub) =     case ecPubKeyCurveName pub of         Nothing -> return $ Left NamedCurveRequired         Just n  -> do             let curve = ECC.getCurveByName n             priv <- ECDH.generatePrivate curve-            return $ Right (curve, priv, X509.pubkeyEC_pub pub)+            return $ case unserializePoint curve (X509.pubkeyEC_pub pub) of+                Nothing -> Left (InvalidInput "Invalid serialized point")+                Just pt -> Right (PairEC curve priv pt)+ecdhGenerate (X509.PubKeyX25519 pub) = do+    priv <- X25519.generateSecretKey+    return $ Right (PairX25519 priv pub)+ecdhGenerate (X509.PubKeyX448 pub) = do+    priv <- X448.generateSecretKey+    return $ Right (PairX448 priv pub) ecdhGenerate _ = return $ Left UnexpectedPublicKeyType +-- | Return the serialized public key corresponding to the ECDH private key.+ecdhPublic :: ECDHPair -> ByteString+ecdhPublic (PairEC curve d _)  = unSerialize (getSerializedPoint curve d)+  where unSerialize (X509.SerializedPoint pt) = pt+ecdhPublic (PairX25519 priv _) = B.convert (X25519.toPublic priv)+ecdhPublic (PairX448 priv _)   = B.convert (X448.toPublic priv)+ -- | Encrypt the specified content with an ECDH key pair and key-agreement -- algorithm. ecdhEncrypt :: (MonadRandom m, ByteArray ba)-            => KeyAgreementParams -> Maybe ByteString -> ECC.Curve -> ECC.PrivateNumber -> X509.SerializedPoint -> ba -> m (Either StoreError ba)-ecdhEncrypt (StdDH dig kep) ukm curve d pt bs =-    case unserializePoint curve pt of-        Nothing  -> return $ Left (InvalidInput "Invalid serialized point")-        Just pub -> do-            let s = ECDH.getShared curve d pub-                k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes-            keyEncrypt k kep bs-ecdhEncrypt (CofactorDH dig kep) ukm curve d pt bs =-    case unserializePoint curve pt of-        Nothing  -> return $ Left (InvalidInput "Invalid serialized point")-        Just pub -> do-            let h = ECC.ecc_h (ECC.common_curve curve)-                s = ECDH.getShared curve (h * d) pub-                k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes-            keyEncrypt k kep bs+            => KeyAgreementParams -> Maybe ByteString -> ECDHPair -> ba -> m (Either StoreError ba)+ecdhEncrypt (StdDH dig kep) ukm (PairEC curve d pub) bs = do+    let s = ECDH.getShared curve d pub+        k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes+    keyEncrypt k kep bs+ecdhEncrypt (StdDH dig kep) ukm (PairX25519 priv pub) bs =+    case fromCryptoFailable (ecdh x25519 priv pub) of+        Left e  -> return (Left e)+        Right s ->+            let k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes+             in keyEncrypt k kep bs+ecdhEncrypt (StdDH dig kep) ukm (PairX448 priv pub) bs =+    case fromCryptoFailable (ecdh x448 priv pub) of+        Left e  -> return (Left e)+        Right s ->+            let k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes+             in keyEncrypt k kep bs+ecdhEncrypt (CofactorDH dig kep) ukm (PairEC curve d pub) bs = do+    let h = ECC.ecc_h (ECC.common_curve curve)+        s = ECDH.getShared curve (h * d) pub+        k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes+    keyEncrypt k kep bs+ecdhEncrypt (CofactorDH _ _) _ (PairX25519 _ _) _ =+    return $ Left (InvalidInput "X25519 is not supported for cofactor DH")+ecdhEncrypt (CofactorDH _ _) _ (PairX448 _ _) _ =+    return $ Left (InvalidInput "X448 is not supported for cofactor DH")  -- | Decrypt the specified content with an ECDH key pair and key-agreement -- algorithm. ecdhDecrypt :: ByteArray ba-            => KeyAgreementParams -> Maybe ByteString -> X509.PrivKeyEC -> X509.SerializedPoint -> ba -> Either StoreError ba-ecdhDecrypt (StdDH dig kep) ukm priv pt bs =+            => KeyAgreementParams -> Maybe ByteString -> X509.PrivKey -> ByteString -> ba -> Either StoreError ba+ecdhDecrypt (StdDH dig kep) ukm (X509.PrivKeyEC priv) pt bs =     case ecPrivKeyCurve priv of         Nothing    -> Left UnsupportedEllipticCurve         Just curve ->-            case unserializePoint curve pt of+            case unserializePoint curve (X509.SerializedPoint pt) of                 Nothing  -> Left (InvalidInput "Invalid serialized point")                 Just pub -> do                     let d = X509.privkeyEC_priv priv                         s = ECDH.getShared curve d pub                         k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes                     keyDecrypt k kep bs-ecdhDecrypt (CofactorDH dig kep) ukm priv pt bs =+ecdhDecrypt (StdDH dig kep) ukm (X509.PrivKeyX25519 priv) pt bs = do+    s <- fromCryptoFailable (X25519.publicKey pt >>= ecdh x25519 priv)+    let k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes+    keyDecrypt k kep bs+ecdhDecrypt (StdDH dig kep) ukm (X509.PrivKeyX448 priv) pt bs = do+    s <- fromCryptoFailable (X448.publicKey pt >>= ecdh x448 priv)+    let k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes+    keyDecrypt k kep bs+ecdhDecrypt (StdDH _ _) _ _ _ _ = Left UnexpectedPrivateKeyType+ecdhDecrypt (CofactorDH dig kep) ukm (X509.PrivKeyEC priv) pt bs =     case ecPrivKeyCurve priv of         Nothing    -> Left UnsupportedEllipticCurve         Just curve ->-            case unserializePoint curve pt of+            case unserializePoint curve (X509.SerializedPoint pt) of                 Nothing  -> Left (InvalidInput "Invalid serialized point")                 Just pub -> do                     let h = ECC.ecc_h (ECC.common_curve curve)@@ -1555,8 +1685,15 @@                         s = ECDH.getShared curve (h * d) pub                         k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes                     keyDecrypt k kep bs+ecdhDecrypt (CofactorDH _ _) _ _ _ _ = Left UnexpectedPrivateKeyType +x25519 :: Proxy Curve_X25519+x25519 = Proxy +x448 :: Proxy Curve_X448+x448 = Proxy++ -- Utilities  getCipher :: (BlockCipher cipher, ByteArray key)@@ -1713,6 +1850,8 @@                    | TypeRSAPSS                    | TypeDSA DigestAlgorithm                    | TypeECDSA DigestAlgorithm+                   | TypeEd25519+                   | TypeEd448     deriving (Show,Eq)  instance Enumerable SignatureType where@@ -1737,6 +1876,9 @@              , TypeECDSA (DigestAlgorithm SHA256)              , TypeECDSA (DigestAlgorithm SHA384)              , TypeECDSA (DigestAlgorithm SHA512)++             , TypeEd25519+             , TypeEd448              ]  instance OIDable SignatureType where@@ -1762,6 +1904,9 @@     getObjectID (TypeECDSA (DigestAlgorithm SHA384)) = [1,2,840,10045,4,3,3]     getObjectID (TypeECDSA (DigestAlgorithm SHA512)) = [1,2,840,10045,4,3,4] +    getObjectID TypeEd25519                          = [1,3,101,112]+    getObjectID TypeEd448                            = [1,3,101,113]+     getObjectID ty = error ("Unsupported SignatureType: " ++ show ty)  instance OIDNameable SignatureType where@@ -1773,6 +1918,8 @@                   | RSAPSS PSSParams                   | DSA DigestAlgorithm                   | ECDSA DigestAlgorithm+                  | Ed25519+                  | Ed448     deriving (Show,Eq)  instance AlgorithmId SignatureAlg where@@ -1784,35 +1931,42 @@     algorithmType (RSAPSS _)  = TypeRSAPSS     algorithmType (DSA alg)   = TypeDSA alg     algorithmType (ECDSA alg) = TypeECDSA alg+    algorithmType Ed25519     = TypeEd25519+    algorithmType Ed448       = TypeEd448      parameterASN1S RSAAnyHash = gNull     parameterASN1S (RSA _)    = gNull     parameterASN1S (RSAPSS p) = asn1s p     parameterASN1S (DSA _)    = id     parameterASN1S (ECDSA _)  = id+    parameterASN1S Ed25519    = id+    parameterASN1S Ed448      = id      parseParameter TypeRSAAnyHash   = getNextMaybe nullOrNothing >> return RSAAnyHash     parseParameter (TypeRSA alg)    = getNextMaybe nullOrNothing >> return (RSA alg)     parseParameter TypeRSAPSS       = RSAPSS <$> parse     parseParameter (TypeDSA alg)    = return (DSA alg)     parseParameter (TypeECDSA alg)  = return (ECDSA alg)+    parseParameter TypeEd25519      = return Ed25519+    parseParameter TypeEd448        = return Ed448 --- | Sign a message using the specified algorithm and private key.-signatureGenerate :: MonadRandom m => SignatureAlg -> X509.PrivKey -> ByteString -> m (Either StoreError SignatureValue)-signatureGenerate RSAAnyHash _ _ =+-- | Sign a message using the specified algorithm and private key.  The+-- corresponding public key is also required for some algorithms.+signatureGenerate :: MonadRandom m => SignatureAlg -> X509.PrivKey -> X509.PubKey -> ByteString -> m (Either StoreError SignatureValue)+signatureGenerate RSAAnyHash _ _ _ =     error "signatureGenerate: should call signatureResolveHash first"-signatureGenerate (RSA alg)   (X509.PrivKeyRSA priv) msg =+signatureGenerate (RSA alg)   (X509.PrivKeyRSA priv) (X509.PubKeyRSA _) msg =     let err = return . Left $ InvalidParameter ("Invalid hash algorithm for RSA: " ++ show alg)      in withHashAlgorithmASN1 alg err $ \hashAlg ->             mapLeft RSAError <$> RSA.signSafer (Just hashAlg) priv msg-signatureGenerate (RSAPSS p)  (X509.PrivKeyRSA priv) msg =+signatureGenerate (RSAPSS p)  (X509.PrivKeyRSA priv) (X509.PubKeyRSA _) msg =     withPSSParams p $ \params ->         mapLeft RSAError <$> RSAPSS.signSafer params priv msg-signatureGenerate (DSA alg)   (X509.PrivKeyDSA priv) msg =+signatureGenerate (DSA alg)   (X509.PrivKeyDSA priv) (X509.PubKeyDSA _) msg =     case alg of         DigestAlgorithm t ->             Right . dsaFromSignature <$> DSA.sign priv (hashFromProxy t) msg-signatureGenerate (ECDSA alg) (X509.PrivKeyEC priv)  msg =+signatureGenerate (ECDSA alg) (X509.PrivKeyEC priv)  (X509.PubKeyEC _)  msg =     case alg of         DigestAlgorithm t ->             case ecdsaToPrivateKey priv of@@ -1820,7 +1974,11 @@                 Just p  ->                     let h = hashFromProxy t                      in Right . ecdsaFromSignature <$> ECDSA.sign p h msg-signatureGenerate _ _ _ = return (Left UnexpectedPrivateKeyType)+signatureGenerate Ed25519 (X509.PrivKeyEd25519 priv) (X509.PubKeyEd25519 pub) msg =+    return . Right . B.convert $ Ed25519.sign priv pub msg+signatureGenerate Ed448 (X509.PrivKeyEd448 priv) (X509.PubKeyEd448 pub) msg =+    return . Right . B.convert $ Ed448.sign priv pub msg+signatureGenerate _ _ _ _ = return (Left UnexpectedPrivateKeyType)  -- | Verify a message signature using the specified algorithm and public key. signatureVerify :: SignatureAlg -> X509.PubKey -> ByteString -> SignatureValue -> Bool@@ -1840,6 +1998,14 @@     s <- ecdsaToSignature sig     case alg of         DigestAlgorithm t -> return $ ECDSA.verify (hashFromProxy t) p s msg+signatureVerify Ed25519 (X509.PubKeyEd25519 pub) msg sig =+    case Ed25519.signature sig of+        CryptoFailed _ -> False+        CryptoPassed s -> Ed25519.verify pub msg s+signatureVerify Ed448 (X509.PubKeyEd448 pub) msg sig =+    case Ed448.signature sig of+        CryptoFailed _ -> False+        CryptoPassed s -> Ed448.verify pub msg s signatureVerify _                 _                    _   _   = False  withHashAlgorithmASN1 :: DigestAlgorithm@@ -1858,12 +2024,15 @@ -- | Return on which digest algorithm the specified signature algorithm is -- based, as well as a substitution algorithm for when a default digest -- algorithm is required.-signatureResolveHash :: DigestAlgorithm -> SignatureAlg -> (DigestAlgorithm, SignatureAlg)-signatureResolveHash d RSAAnyHash     = (d, RSA d)-signatureResolveHash _ alg@(RSA d)    = (d, alg)-signatureResolveHash _ alg@(RSAPSS p) = (pssHashAlgorithm p, alg)-signatureResolveHash _ alg@(DSA d)    = (d, alg)-signatureResolveHash _ alg@(ECDSA d)  = (d, alg)+signatureResolveHash :: Bool -> DigestAlgorithm -> SignatureAlg -> (DigestAlgorithm, SignatureAlg)+signatureResolveHash _     d RSAAnyHash     = (d, RSA d)+signatureResolveHash _     _ alg@(RSA d)    = (d, alg)+signatureResolveHash _     _ alg@(RSAPSS p) = (pssHashAlgorithm p, alg)+signatureResolveHash _     _ alg@(DSA d)    = (d, alg)+signatureResolveHash _     _ alg@(ECDSA d)  = (d, alg)+signatureResolveHash _     _ alg@Ed25519    = (DigestAlgorithm SHA512, alg)+signatureResolveHash True  _ alg@Ed448      = (DigestAlgorithm SHAKE256_512, alg)+signatureResolveHash False _ alg@Ed448      = (DigestAlgorithm (SHAKE256 p512), alg)  -- | Check that a signature algorithm is based on the specified digest algorithm -- and return a substitution algorithm for when a default digest algorithm is@@ -1882,6 +2051,13 @@ signatureCheckHash expected alg@(ECDSA found)     | expected == found = Just alg     | otherwise         = Nothing+signatureCheckHash expected alg@Ed25519+    | expected == DigestAlgorithm SHA512 = Just alg+    | otherwise                          = Nothing+signatureCheckHash expected alg@Ed448+    | expected == DigestAlgorithm SHAKE256_512    = Just alg+    | expected == DigestAlgorithm (SHAKE256 p512) = Just alg+    | otherwise                                   = Nothing  dsaToSignature :: ByteString -> Maybe DSA.Signature dsaToSignature b = tryDecodeAndParse b $ onNextContainer Sequence $ do
src/Crypto/Store/CMS/AuthEnveloped.hs view
@@ -34,7 +34,7 @@ import Crypto.Store.CMS.Util  -- | Authenticated-enveloped content information.-data AuthEnvelopedData = AuthEnvelopedData+data AuthEnvelopedData content = AuthEnvelopedData     { aeOriginatorInfo :: OriginatorInfo       -- ^ Optional information about the originator     , aeRecipientInfos :: [RecipientInfo]@@ -43,7 +43,7 @@       -- ^ Inner content type     , aeContentEncryptionParams :: ASN1ObjectExact AuthContentEncryptionParams       -- ^ Encryption algorithm-    , aeEncryptedContent :: EncryptedContent+    , aeEncryptedContent :: content       -- ^ Encrypted content info     , aeAuthAttrs :: [Attribute]       -- ^ Optional authenticated attributes@@ -54,7 +54,7 @@     }     deriving (Show,Eq) -instance ProduceASN1Object ASN1P AuthEnvelopedData where+instance ProduceASN1Object ASN1P (AuthEnvelopedData (Encap EncryptedContent)) where     asn1s AuthEnvelopedData{..} =         asn1Container Sequence (ver . oi . ris . eci . aa . tag . ua)       where@@ -69,7 +69,7 @@         oi | aeOriginatorInfo == mempty = id            | otherwise = originatorInfoASN1S (Container Context 0) aeOriginatorInfo -instance ParseASN1Object [ASN1Event] AuthEnvelopedData where+instance ParseASN1Object [ASN1Event] (AuthEnvelopedData (Encap EncryptedContent)) where     parse =         onNextContainer Sequence $ do             IntVal v <- getNext
+ src/Crypto/Store/CMS/Authenticated.hs view
@@ -0,0 +1,99 @@+-- |+-- Module      : Crypto.Store.CMS.Authenticated+-- License     : BSD-style+-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>+-- Stability   : experimental+-- Portability : unknown+--+--+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RecordWildCards #-}+module Crypto.Store.CMS.Authenticated+    ( AuthenticatedData(..)+    ) where++import Control.Applicative+import Control.Monad++import           Data.ASN1.Types+import qualified Data.ByteArray as B++import Crypto.Cipher.Types++import Crypto.Store.ASN1.Generate+import Crypto.Store.ASN1.Parse+import Crypto.Store.CMS.Algorithms+import Crypto.Store.CMS.Attribute+import Crypto.Store.CMS.Enveloped+import Crypto.Store.CMS.OriginatorInfo+import Crypto.Store.CMS.Signed+import Crypto.Store.CMS.Type+import Crypto.Store.CMS.Util++-- | Authenticated content information.+data AuthenticatedData content = AuthenticatedData+    { adOriginatorInfo :: OriginatorInfo+      -- ^ Optional information about the originator+    , adRecipientInfos :: [RecipientInfo]+      -- ^ Information for recipients, allowing to authenticate the content+    , adMACAlgorithm :: MACAlgorithm+      -- ^ MAC algorithm+    , adDigestAlgorithm :: Maybe DigestAlgorithm+      -- ^ Optional digest algorithm+    , adContentType :: ContentType+      -- ^ Inner content type+    , adEncapsulatedContent :: content+      -- ^ Encapsulated content+    , adAuthAttrs :: [Attribute]+      -- ^ Optional authenticated attributes+    , adMAC :: MessageAuthenticationCode+      -- ^ Message authentication code+    , adUnauthAttrs :: [Attribute]+      -- ^ Optional unauthenticated attributes+    }+    deriving (Show,Eq)++instance ProduceASN1Object ASN1P (AuthenticatedData (Encap EncapsulatedContent)) where+    asn1s AuthenticatedData{..} =+        asn1Container Sequence (ver . oi . ris . alg . dig . ci . aa . tag . ua)+      where+        ver = gIntVal v+        ris = asn1Container Set (asn1s adRecipientInfos)+        alg = algorithmASN1S Sequence adMACAlgorithm+        dig = algorithmMaybeASN1S (Container Context 1) adDigestAlgorithm+        ci  = encapsulatedContentInfoASN1S adContentType adEncapsulatedContent+        aa  = attributesASN1S(Container Context 2) adAuthAttrs+        tag = gOctetString (B.convert adMAC)+        ua  = attributesASN1S (Container Context 3) adUnauthAttrs++        oi | adOriginatorInfo == mempty = id+           | otherwise = originatorInfoASN1S (Container Context 0) adOriginatorInfo++        v | hasChoiceOther adOriginatorInfo = 3+          | otherwise                       = 0++instance ParseASN1Object [ASN1Event] (AuthenticatedData (Encap EncapsulatedContent)) where+    parse =+        onNextContainer Sequence $ do+            IntVal v <- getNext+            when (v `notElem` [0, 1, 3]) $+                throwParseError ("AuthenticatedData: parsed invalid version: " ++ show v)+            oi <- parseOriginatorInfo (Container Context 0) <|> return mempty+            ris <- onNextContainer Set parse+            alg <- parseAlgorithm Sequence+            dig <- parseAlgorithmMaybe (Container Context 1)+            (ct, bs) <- parseEncapsulatedContentInfo+            aAttrs <- parseAttributes (Container Context 2)+            OctetString tag <- getNext+            uAttrs <- parseAttributes (Container Context 3)+            return AuthenticatedData { adOriginatorInfo = oi+                                     , adRecipientInfos = ris+                                     , adMACAlgorithm = alg+                                     , adDigestAlgorithm = dig+                                     , adContentType = ct+                                     , adEncapsulatedContent = bs+                                     , adAuthAttrs = aAttrs+                                     , adMAC = AuthTag $ B.convert tag+                                     , adUnauthAttrs = uAttrs+                                     }
+ src/Crypto/Store/CMS/Digested.hs view
@@ -0,0 +1,82 @@+-- |+-- Module      : Crypto.Store.CMS.Digested+-- License     : BSD-style+-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>+-- Stability   : experimental+-- Portability : unknown+--+--+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RecordWildCards #-}+module Crypto.Store.CMS.Digested+    ( DigestedData(..)+    ) where++import Control.Monad++import           Data.ASN1.Types+import qualified Data.ByteArray as B++import Crypto.Hash hiding (MD5)++import Crypto.Store.ASN1.Generate+import Crypto.Store.ASN1.Parse+import Crypto.Store.CMS.Algorithms+import Crypto.Store.CMS.Signed+import Crypto.Store.CMS.Type+import Crypto.Store.CMS.Util++-- | Digested content information.+data DigestedData content = forall hashAlg. HashAlgorithm hashAlg => DigestedData+    { ddDigestAlgorithm :: DigestProxy hashAlg     -- ^ Digest algorithm+    , ddContentType :: ContentType                 -- ^ Inner content type+    , ddEncapsulatedContent :: content             -- ^ Encapsulated content+    , ddDigest :: Digest hashAlg                   -- ^ Digest value+    }++instance Show content => Show (DigestedData content) where+    showsPrec d DigestedData{..} = showParen (d > 10) $+        showString "DigestedData "+            . showString "{ ddDigestAlgorithm = " . shows ddDigestAlgorithm+            . showString ", ddContentType = " . shows ddContentType+            . showString ", ddEncapsulatedContent = " . shows ddEncapsulatedContent+            . showString ", ddDigest = " . shows ddDigest+            . showString " }"++instance Eq content => Eq (DigestedData content) where+    DigestedData a1 t1 e1 d1 == DigestedData a2 t2 e2 d2 =+        DigestAlgorithm a1 == DigestAlgorithm a2 && d1 `B.eq` d2 && t1 == t2 && e1 == e2++instance ASN1Elem e => ProduceASN1Object e (DigestedData (Encap EncapsulatedContent)) where+    asn1s DigestedData{..} =+        asn1Container Sequence (ver . alg . ci . dig)+      where+        v = if ddContentType == DataType then 0 else 2+        d = DigestAlgorithm ddDigestAlgorithm++        ver = gIntVal v+        alg = algorithmASN1S Sequence d+        ci  = encapsulatedContentInfoASN1S ddContentType ddEncapsulatedContent+        dig = gOctetString (B.convert ddDigest)++instance Monoid e => ParseASN1Object e (DigestedData (Encap EncapsulatedContent)) where+    parse =+        onNextContainer Sequence $ do+            IntVal v <- getNext+            when (v /= 0 && v /= 2) $+                throwParseError ("DigestedData: parsed invalid version: " ++ show v)+            alg <- parseAlgorithm Sequence+            (ct, bs) <- parseEncapsulatedContentInfo+            OctetString digValue <- getNext+            case alg of+                DigestAlgorithm digAlg ->+                    case digestFromByteString digValue of+                        Nothing -> throwParseError "DigestedData: parsed invalid digest"+                        Just d  ->+                            return DigestedData { ddDigestAlgorithm = digAlg+                                                , ddContentType = ct+                                                , ddEncapsulatedContent = bs+                                                , ddDigest = d+                                                }
src/Crypto/Store/CMS/Encrypted.hs view
@@ -38,19 +38,19 @@ type EncryptedContent = B.ByteString  -- | Encrypted content information.-data EncryptedData = EncryptedData+data EncryptedData content = EncryptedData     { edContentType :: ContentType       -- ^ Inner content type     , edContentEncryptionParams :: ContentEncryptionParams       -- ^ Encryption algorithm-    , edEncryptedContent :: EncryptedContent+    , edEncryptedContent :: content       -- ^ Encrypted content info     , edUnprotectedAttrs :: [Attribute]       -- ^ Optional unprotected attributes     }     deriving (Show,Eq) -instance ASN1Elem e => ProduceASN1Object e EncryptedData where+instance ASN1Elem e => ProduceASN1Object e (EncryptedData (Encap EncryptedContent)) where     asn1s EncryptedData{..} =         asn1Container Sequence (ver . eci . ua)       where@@ -59,7 +59,7 @@                   (edContentType, edContentEncryptionParams, edEncryptedContent)         ua  = attributesASN1S (Container Context 1) edUnprotectedAttrs -instance Monoid e => ParseASN1Object e EncryptedData where+instance Monoid e => ParseASN1Object e (EncryptedData (Encap EncryptedContent)) where     parse =         onNextContainer Sequence $ do             IntVal v <- getNext@@ -75,21 +75,27 @@  -- | Generate ASN.1 for EncryptedContentInfo. encryptedContentInfoASN1S :: (ASN1Elem e, ProduceASN1Object e alg)-                          => (ContentType, alg, B.ByteString) -> ASN1Stream e+                          => (ContentType, alg, Encap EncryptedContent) -> ASN1Stream e encryptedContentInfoASN1S (ct, alg, ec) =     asn1Container Sequence (ct' . alg' . ec')   where     ct'  = gOID (getObjectID ct)     alg' = asn1s alg-    ec'  = asn1Container (Container Context 0) (gOctetString ec)+    ec'  = encapsulatedASN1S (Container Context 0) ec +encapsulatedASN1S :: ASN1Elem e+                  => ASN1ConstructionType -> Encap EncryptedContent -> ASN1Stream e+encapsulatedASN1S _   Detached     = id+encapsulatedASN1S ty (Attached bs) = asn1Container ty (gOctetString bs)+ -- | Parse EncryptedContentInfo from ASN.1. parseEncryptedContentInfo :: ParseASN1Object e alg-                          => ParseASN1 e (ContentType, alg, B.ByteString)+                          => ParseASN1 e (ContentType, alg, Encap EncryptedContent) parseEncryptedContentInfo = onNextContainer Sequence $ do     OID oid <- getNext     alg <- parse-    ec <- parseEncryptedContent+    b <- hasNext+    ec <- if b then Attached <$> parseEncryptedContent else return Detached     withObjectID "content type" oid $ \ct -> return (ct, alg, ec)   where     parseEncryptedContent = parseWrapped <|> parsePrimitive
src/Crypto/Store/CMS/Enveloped.hs view
@@ -67,7 +67,6 @@ import Crypto.Store.CMS.Type import Crypto.Store.CMS.Util import Crypto.Store.Error-import Crypto.Store.PKCS8.EC  -- | Encrypted key. type EncryptedKey = ByteString@@ -424,7 +423,7 @@ isPwriOri (PasswordRI _) = True  -- | Enveloped content information.-data EnvelopedData = EnvelopedData+data EnvelopedData content = EnvelopedData     { evOriginatorInfo :: OriginatorInfo       -- ^ Optional information about the originator     , evRecipientInfos :: [RecipientInfo]@@ -433,14 +432,14 @@       -- ^ Inner content type     , evContentEncryptionParams :: ContentEncryptionParams       -- ^ Encryption algorithm-    , evEncryptedContent :: EncryptedContent+    , evEncryptedContent :: content       -- ^ Encrypted content info     , evUnprotectedAttrs :: [Attribute]       -- ^ Optional unprotected attributes     }     deriving (Show,Eq) -instance ProduceASN1Object ASN1P EnvelopedData where+instance ProduceASN1Object ASN1P (EnvelopedData (Encap EncryptedContent)) where     asn1s EnvelopedData{..} =         asn1Container Sequence (ver . oi . ris . eci . ua)       where@@ -460,7 +459,7 @@           | all isVersion0 evRecipientInfos = 0           | otherwise                       = 2 -instance ParseASN1Object [ASN1Event] EnvelopedData where+instance ParseASN1Object [ASN1Event] (EnvelopedData (Encap EncryptedContent)) where     parse =         onNextContainer Sequence $ do             IntVal v <- getNext@@ -524,10 +523,10 @@ forKeyAgreeRecipient cert params inkey = do     ephemeral <- ecdhGenerate (certPubKey obj)     case ephemeral of-        Right (curve, d, sp) -> do-            let SerializedPoint pt = getSerializedPoint curve d+        Right pair -> do+            let pt = ecdhPublic pair                 aPub = OriginatorPublicKeyEC [] (toBitArray pt 0)-            ek <- ecdhEncrypt params Nothing curve d sp inkey+            ek <- ecdhEncrypt params Nothing pair inkey             return (KARI . build aPub <$> ek)         Left err -> return $ Left err   where@@ -553,16 +552,15 @@ -- This function can be used as parameter to -- 'Crypto.Store.CMS.openEnvelopedData'. withRecipientKeyAgree :: MonadRandom m => PrivKey -> SignedCertificate -> ConsumerOfRI m-withRecipientKeyAgree (PrivKeyEC priv) cert (KARI KARecipientInfo{..}) =+withRecipientKeyAgree priv cert (KARI KARecipientInfo{..}) =     case kaOriginator of         OriginatorPublic (OriginatorPublicKeyEC _ ba) ->             case findRecipientEncryptedKey cert kaRecipientEncryptedKeys of                 Nothing -> pure (Left RecipientKeyNotFound)                 Just ek ->-                    let pub = SerializedPoint (bitArrayGetData ba)+                    let pub = bitArrayGetData ba                      in pure (ecdhDecrypt kaKeyAgreementParams kaUkm priv pub ek)         _ -> pure (Left UnsupportedOriginatorFormat)-withRecipientKeyAgree _ _ (KARI _) = pure (Left UnexpectedPrivateKeyType) withRecipientKeyAgree _ _ _        = pure (Left RecipientTypeMismatch)  -- | Generate a Key Encryption Key recipient from a key encryption key and
src/Crypto/Store/CMS/Info.hs view
@@ -6,49 +6,38 @@ -- Portability : unknown -- -- CMS content information.-{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeFamilies #-} module Crypto.Store.CMS.Info     ( ContentInfo(..)     , getContentType-    , SignedData(..)-    , DigestedData(..)-    , AuthenticatedData(..)-    , encapsulate-    , decapsulate+    , Encapsulates+    , isAttached+    , fromAttached+    , toAttachedCI+    , isDetached+    , fromDetached+    , toDetachedCI     ) where -import Control.Applicative-import Control.Monad--import           Data.ASN1.BinaryEncoding-import           Data.ASN1.Encoding-import           Data.ASN1.Types-import           Data.ByteString (ByteString)-import qualified Data.ByteArray as B-import           Data.Maybe (fromMaybe)+import Control.Monad.Fail (MonadFail) -import Crypto.Cipher.Types-import Crypto.Hash hiding (MD5)+import Data.ASN1.Types+import Data.ByteString (ByteString)+import Data.Functor.Identity+import Data.Maybe (isJust, isNothing)  import Crypto.Store.ASN1.Generate import Crypto.Store.ASN1.Parse-import Crypto.Store.CMS.Algorithms-import Crypto.Store.CMS.Attribute+import Crypto.Store.CMS.Authenticated import Crypto.Store.CMS.AuthEnveloped+import Crypto.Store.CMS.Digested import Crypto.Store.CMS.Encrypted import Crypto.Store.CMS.Enveloped-import Crypto.Store.CMS.OriginatorInfo import Crypto.Store.CMS.Signed import Crypto.Store.CMS.Type import Crypto.Store.CMS.Util-import Crypto.Store.Error-import Crypto.Store.Util  -- | Get the type of a content info. getContentType :: ContentInfo -> ContentType@@ -64,13 +53,20 @@ -- ContentInfo  -- | CMS content information.-data ContentInfo = DataCI ByteString                     -- ^ Arbitrary octet string-                 | SignedDataCI SignedData               -- ^ Signed content info-                 | EnvelopedDataCI EnvelopedData         -- ^ Enveloped content info-                 | DigestedDataCI DigestedData           -- ^ Content info with associated digest-                 | EncryptedDataCI EncryptedData         -- ^ Encrypted content info-                 | AuthenticatedDataCI AuthenticatedData -- ^ Authenticatedcontent info-                 | AuthEnvelopedDataCI AuthEnvelopedData -- ^ Authenticated-enveloped content info+data ContentInfo = DataCI ByteString+                   -- ^ Arbitrary octet string+                 | SignedDataCI (SignedData (Encap EncapsulatedContent))+                   -- ^ Signed content info+                 | EnvelopedDataCI (EnvelopedData (Encap EncryptedContent))+                   -- ^ Enveloped content info+                 | DigestedDataCI (DigestedData (Encap EncapsulatedContent))+                   -- ^ Content info with associated digest+                 | EncryptedDataCI (EncryptedData (Encap EncryptedContent))+                   -- ^ Encrypted content info+                 | AuthenticatedDataCI (AuthenticatedData (Encap EncapsulatedContent))+                   -- ^ Authenticatedcontent info+                 | AuthEnvelopedDataCI (AuthEnvelopedData (Encap EncryptedContent))+                   -- ^ Authenticated-enveloped content info                  deriving (Show,Eq)  instance ProduceASN1Object ASN1P ContentInfo where@@ -115,239 +111,89 @@         OctetString bs -> return bs         _              -> throwParseError "Data: parsed unexpected content" -isData :: ContentInfo -> Bool-isData (DataCI _) = True-isData _          = False ---- SignedData---- | Signed content information.-data SignedData = SignedData-    { sdDigestAlgorithms :: [DigestAlgorithm] -- ^ Digest algorithms-    , sdContentInfo :: ContentInfo            -- ^ Inner content info-    , sdCertificates :: [CertificateChoice]   -- ^ The collection of certificates-    , sdCRLs  :: [RevocationInfoChoice]       -- ^ The collection of CRLs-    , sdSignerInfos :: [SignerInfo]           -- ^ Per-signer information-    }-    deriving (Show,Eq)--instance ProduceASN1Object ASN1P SignedData where-    asn1s SignedData{..} =-        asn1Container Sequence (ver . dig . ci . certs . crls . sis)-      where-        ver = gIntVal v-        dig = asn1Container Set (digestTypesASN1S sdDigestAlgorithms)-        ci  = encapsulatedContentInfoASN1S sdContentInfo-        certs = gen 0 sdCertificates-        crls  = gen 1 sdCRLs-        sis = asn1Container Set (asn1s sdSignerInfos)--        gen tag list-            | null list = id-            | otherwise = asn1Container (Container Context tag) (asn1s list)--        v | hasChoiceOther sdCertificates = 5-          | hasChoiceOther sdCRLs         = 5-          | any isVersion3 sdSignerInfos  = 3-          | isData sdContentInfo          = 1-          | otherwise                     = 3---instance ParseASN1Object [ASN1Event] SignedData where-    parse =-        onNextContainer Sequence $ do-            IntVal v <- getNext-            when (v > 5) $-                throwParseError ("SignedData: parsed invalid version: " ++ show v)-            dig <- onNextContainer Set parseDigestTypes-            inner <- parseEncapsulatedContentInfo-            certs <- parseOptList 0-            crls  <- parseOptList 1-            sis <- onNextContainer Set parse-            return SignedData { sdDigestAlgorithms = dig-                              , sdContentInfo = inner-                              , sdCertificates = certs-                              , sdCRLs = crls-                              , sdSignerInfos = sis-                              }-      where-        parseOptList tag =-            fromMaybe [] <$> onNextContainerMaybe (Container Context tag) parse--digestTypesASN1S :: ASN1Elem e => [DigestAlgorithm] -> ASN1Stream e-digestTypesASN1S list cont = foldr (algorithmASN1S Sequence) cont list--parseDigestTypes :: Monoid e => ParseASN1 e [DigestAlgorithm]-parseDigestTypes = getMany (parseAlgorithm Sequence)----- DigestedData---- | Digested content information.-data DigestedData = forall hashAlg. HashAlgorithm hashAlg => DigestedData-    { ddDigestAlgorithm :: DigestProxy hashAlg     -- ^ Digest algorithm-    , ddContentInfo :: ContentInfo                 -- ^ Inner content info-    , ddDigest :: Digest hashAlg                   -- ^ Digest value-    }--instance Show DigestedData where-    showsPrec d DigestedData{..} = showParen (d > 10) $-        showString "DigestedData "-            . showString "{ ddDigestAlgorithm = " . shows ddDigestAlgorithm-            . showString ", ddContentInfo = " . shows ddContentInfo-            . showString ", ddDigest = " . shows ddDigest-            . showString " }"--instance Eq DigestedData where-    DigestedData a1 i1 d1 == DigestedData a2 i2 d2 =-        DigestAlgorithm a1 == DigestAlgorithm a2 && d1 `B.eq` d2 && i1 == i2--instance ASN1Elem e => ProduceASN1Object e DigestedData where-    asn1s DigestedData{..} =-        asn1Container Sequence (ver . alg . ci . dig)-      where-        v = if isData ddContentInfo then 0 else 2-        d = DigestAlgorithm ddDigestAlgorithm--        ver = gIntVal v-        alg = algorithmASN1S Sequence d-        ci  = encapsulatedContentInfoASN1S ddContentInfo-        dig = gOctetString (B.convert ddDigest)--instance Monoid e => ParseASN1Object e DigestedData where-    parse =-        onNextContainer Sequence $ do-            IntVal v <- getNext-            when (v /= 0 && v /= 2) $-                throwParseError ("DigestedData: parsed invalid version: " ++ show v)-            alg <- parseAlgorithm Sequence-            inner <- parseEncapsulatedContentInfo-            OctetString bs <- getNext-            case alg of-                DigestAlgorithm digAlg ->-                    case digestFromByteString bs of-                        Nothing -> throwParseError "DigestedData: parsed invalid digest"-                        Just d  ->-                            return DigestedData { ddDigestAlgorithm = digAlg-                                                , ddContentInfo = inner-                                                , ddDigest = d-                                                }+-- Encapsulation +-- | Class of data structures with inner content that may be stored externally.+-- This class has instances for each CMS content type containing other+-- encapsulated or encrypted content info.+--+-- Functions 'fromAttached' and 'fromDetached' are used to introspect+-- encapsulation state (attached or detached), and recover a data structure with+-- actionable content.+--+-- Functions 'toAttachedCI' and 'toDetachedCI' are needed to decide about the+-- outer encapsulation state and build a 'ContentInfo'.+class Encapsulates struct where+    lens :: Functor f => (a -> f b) -> struct a -> f (struct b)+    toCI :: struct (Encap ByteString) -> ContentInfo --- Authenticated data+instance Encapsulates SignedData where+    lens f s = let g a = s { sdEncapsulatedContent = a }+                in fmap g (f $ sdEncapsulatedContent s)+    toCI = SignedDataCI --- | Authenticated content information.-data AuthenticatedData = AuthenticatedData-    { adOriginatorInfo :: OriginatorInfo-      -- ^ Optional information about the originator-    , adRecipientInfos :: [RecipientInfo]-      -- ^ Information for recipients, allowing to authenticate the content-    , adMACAlgorithm :: MACAlgorithm-      -- ^ MAC algorithm-    , adDigestAlgorithm :: Maybe DigestAlgorithm-      -- ^ Optional digest algorithm-    , adContentInfo :: ContentInfo-      -- ^ Inner content info-    , adAuthAttrs :: [Attribute]-      -- ^ Optional authenticated attributes-    , adMAC :: MessageAuthenticationCode-      -- ^ Message authentication code-    , adUnauthAttrs :: [Attribute]-      -- ^ Optional unauthenticated attributes-    }-    deriving (Show,Eq)+instance Encapsulates EnvelopedData where+    lens f s = let g a = s { evEncryptedContent = a }+                in fmap g (f $ evEncryptedContent s)+    toCI = EnvelopedDataCI -instance ProduceASN1Object ASN1P AuthenticatedData where-    asn1s AuthenticatedData{..} =-        asn1Container Sequence (ver . oi . ris . alg . dig . ci . aa . tag . ua)-      where-        ver = gIntVal v-        ris = asn1Container Set (asn1s adRecipientInfos)-        alg = algorithmASN1S Sequence adMACAlgorithm-        dig = algorithmMaybeASN1S (Container Context 1) adDigestAlgorithm-        ci  = encapsulatedContentInfoASN1S adContentInfo-        aa  = attributesASN1S(Container Context 2) adAuthAttrs-        tag = gOctetString (B.convert adMAC)-        ua  = attributesASN1S (Container Context 3) adUnauthAttrs+instance Encapsulates DigestedData where+    lens f s = let g a = s { ddEncapsulatedContent = a }+                in fmap g (f $ ddEncapsulatedContent s)+    toCI = DigestedDataCI -        oi | adOriginatorInfo == mempty = id-           | otherwise = originatorInfoASN1S (Container Context 0) adOriginatorInfo+instance Encapsulates EncryptedData where+    lens f s = let g a = s { edEncryptedContent = a }+                in fmap g (f $ edEncryptedContent s)+    toCI = EncryptedDataCI -        v | hasChoiceOther adOriginatorInfo = 3-          | otherwise                       = 0+instance Encapsulates AuthenticatedData where+    lens f s = let g a = s { adEncapsulatedContent = a }+                in fmap g (f $ adEncapsulatedContent s)+    toCI = AuthenticatedDataCI -instance ParseASN1Object [ASN1Event] AuthenticatedData where-    parse =-        onNextContainer Sequence $ do-            IntVal v <- getNext-            when (v `notElem` [0, 1, 3]) $-                throwParseError ("AuthenticatedData: parsed invalid version: " ++ show v)-            oi <- parseOriginatorInfo (Container Context 0) <|> return mempty-            ris <- onNextContainer Set parse-            alg <- parseAlgorithm Sequence-            dig <- parseAlgorithmMaybe (Container Context 1)-            inner <- parseEncapsulatedContentInfo-            aAttrs <- parseAttributes (Container Context 2)-            OctetString tag <- getNext-            uAttrs <- parseAttributes (Container Context 3)-            return AuthenticatedData { adOriginatorInfo = oi-                                     , adRecipientInfos = ris-                                     , adMACAlgorithm = alg-                                     , adDigestAlgorithm = dig-                                     , adContentInfo = inner-                                     , adAuthAttrs = aAttrs-                                     , adMAC = AuthTag $ B.convert tag-                                     , adUnauthAttrs = uAttrs-                                     }+instance Encapsulates AuthEnvelopedData where+    lens f s = let g a = s { aeEncryptedContent = a }+                in fmap g (f $ aeEncryptedContent s)+    toCI = AuthEnvelopedDataCI +-- | Return 'True' when the encapsulated content is attached.+isAttached :: Encapsulates struct => struct (Encap a) -> Bool+isAttached = isJust . fromAttached --- Utilities+-- | Unwrap the encapsulation, assuming the inner content is inside the data+-- structure.  The monadic computation fails if the content was detached.+fromAttached :: (MonadFail m, Encapsulates struct) => struct (Encap a) -> m (struct a)+fromAttached = lens (fromEncap err return)+  where err = fail "fromAttached: detached" -decode :: ParseASN1 [ASN1Event] a -> ByteString -> Either StoreError a-decode parser bs = vals >>= mapLeft ParseFailure . runParseASN1_ parser-  where vals = mapLeft DecodingError (decodeASN1Repr' BER bs)+-- | Keep the content inside the data structure.+toAttached :: Encapsulates struct => struct a -> struct (Encap a)+toAttached = runIdentity . lens (Identity . Attached) --- | Encode the information for encapsulation in another content info.-encapsulate :: ContentInfo -> ByteString-encapsulate (DataCI bs)              = bs-encapsulate (SignedDataCI ed)        = encodeASN1Object ed-encapsulate (EnvelopedDataCI ed)     = encodeASN1Object ed-encapsulate (DigestedDataCI dd)      = encodeASN1Object dd-encapsulate (EncryptedDataCI ed)     = encodeASN1Object ed-encapsulate (AuthenticatedDataCI ad) = encodeASN1Object ad-encapsulate (AuthEnvelopedDataCI ae) = encodeASN1Object ae+-- | Transform the data structure into a content info, keeping the encapsulated+-- content attached.  May be applied to structures with 'EncapsulatedContent' or+-- 'EncryptedContent'.+toAttachedCI :: Encapsulates struct => struct ByteString -> ContentInfo+toAttachedCI = toCI . toAttached --- | Decode the information from encapsulated content.-decapsulate :: ContentType -> ByteString -> Either StoreError ContentInfo-decapsulate DataType bs              = pure (DataCI bs)-decapsulate SignedDataType bs        = SignedDataCI <$> decode parse bs-decapsulate EnvelopedDataType bs     = EnvelopedDataCI <$> decode parse bs-decapsulate DigestedDataType bs      = DigestedDataCI <$> decode parse bs-decapsulate EncryptedDataType bs     = EncryptedDataCI <$> decode parse bs-decapsulate AuthenticatedDataType bs = AuthenticatedDataCI <$> decode parse bs-decapsulate AuthEnvelopedDataType bs = AuthEnvelopedDataCI <$> decode parse bs+-- | Return 'True' when the encapsulated content is detached.+isDetached :: Encapsulates struct => struct (Encap a) -> Bool+isDetached = isNothing . fromAttached -encapsulatedContentInfoASN1S :: ASN1Elem e => ContentInfo -> ASN1Stream e-encapsulatedContentInfoASN1S ci = asn1Container Sequence (oid . cont)-  where oid = gOID $ getObjectID $ getContentType ci-        cont = asn1Container (Container Context 0) inner-        inner = gOctetString (encapsulate ci)+-- | Recover the original data structure from a detached encapsulation and the+-- external content.  The monadic computation fails if the content was attached.+fromDetached :: (MonadFail m, Encapsulates struct) => b -> struct (Encap a) -> m (struct b)+fromDetached c = lens (fromEncap (return c) err)+  where err _ = fail "fromDetached: attached" -parseEncapsulatedContentInfo :: Monoid e => ParseASN1 e ContentInfo-parseEncapsulatedContentInfo =-    onNextContainer Sequence $ do-        OID oid <- getNext-        withObjectID "content type" oid $ \ct ->-            onNextContainer (Container Context 0) (parseInner ct)-  where-    parseInner ct = do-        bs <- parseContentSingle <|> parseContentChunks-        case decapsulate ct bs of-            Left err -> throwParseError-                ("Unable to decode and parse encapsulated ASN.1: " ++ show err)-            Right ci -> return ci+-- | Remove the content from the data structure to store it externally.+toDetached :: Encapsulates struct => struct a -> (a, struct (Encap a))+toDetached = let f a = (a, Detached) in lens f -    parseContentSingle = do { OctetString bs <- getNext; return bs }-    parseContentChunks = onNextContainer (Container Universal 4) $-        B.concat <$> getMany parseContentSingle+-- | Transform the data structure into a content info, detaching the+-- encapsulated content.  May be applied to structures with+-- 'EncapsulatedContent' or 'EncryptedContent'.+toDetachedCI :: Encapsulates struct => struct ByteString -> (ByteString, ContentInfo)+toDetachedCI = fmap toCI . toDetached
src/Crypto/Store/CMS/OriginatorInfo.hs view
@@ -54,8 +54,7 @@  instance Monoid OriginatorInfo where     mempty = OriginatorInfo [] []-    mappend (OriginatorInfo a b) (OriginatorInfo c d) =-        OriginatorInfo (mappend a c) (mappend b d)+    mappend = (<>)  instance HasChoiceOther OriginatorInfo where     hasChoiceOther OriginatorInfo{..} =
src/Crypto/Store/CMS/Signed.hs view
@@ -10,26 +10,30 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-} module Crypto.Store.CMS.Signed-    ( SignerInfo(..)+    ( EncapsulatedContent+    , SignedData(..)+    , SignerInfo(..)     , SignerIdentifier(..)     , IssuerAndSerialNumber(..)-    , isVersion3     , ProducerOfSI     , ConsumerOfSI     , certSigner     , withPublicKey     , withSignerKey     , withSignerCertificate+    , encapsulatedContentInfoASN1S+    , parseEncapsulatedContentInfo     ) where  import Control.Applicative import Control.Monad -import Data.ASN1.Types-import Data.ByteString (ByteString)-import Data.List-import Data.Maybe-import Data.X509+import           Data.ASN1.Types+import           Data.ByteString (ByteString)+import qualified Data.ByteString as B+import           Data.List+import           Data.Maybe+import           Data.X509  import Crypto.Random (MonadRandom) @@ -44,6 +48,9 @@ import Crypto.Store.CMS.Util import Crypto.Store.Error +-- | Encapsulated content.+type EncapsulatedContent = ByteString+ -- | Information related to a signer of a 'Crypto.Store.CMS.SignedData'.  An -- element contains the signature material that was produced. data SignerInfo = SignerInfo@@ -173,9 +180,11 @@     cert = head chain     obj  = signedObject (getSigned cert)     isn  = IssuerAndSerialNumber (certIssuerDN obj) (certSerial obj)+    pub  = certPubKey obj -    (dig, alg') = signatureResolveHash def alg+    (dig, alg') = signatureResolveHash noAttr def alg +    noAttr          = null sAttrs     (sAttrs, input) =         case sAttrsM of             Nothing    -> ([], msg)@@ -183,7 +192,7 @@                 let l = setContentTypeAttr ct $ setMessageDigestAttr md attrs                  in (l, encodeAuthAttrs l) -    generate  = signatureGenerate alg' priv input+    generate  = signatureGenerate alg' priv pub input     build sig =         let si = SignerInfo { siSignerId = SignerIASN isn                             , siDigestAlgorithm = dig@@ -238,3 +247,92 @@      asX509 (CertificateCertificate c) = Just c     asX509 _                          = Nothing++-- | Signed content information.+data SignedData content = SignedData+    { sdDigestAlgorithms :: [DigestAlgorithm]      -- ^ Digest algorithms+    , sdContentType :: ContentType                 -- ^ Inner content type+    , sdEncapsulatedContent :: content             -- ^ Encapsulated content+    , sdCertificates :: [CertificateChoice]        -- ^ The collection of certificates+    , sdCRLs  :: [RevocationInfoChoice]            -- ^ The collection of CRLs+    , sdSignerInfos :: [SignerInfo]                -- ^ Per-signer information+    }+    deriving (Show,Eq)++instance ProduceASN1Object ASN1P (SignedData (Encap EncapsulatedContent)) where+    asn1s SignedData{..} =+        asn1Container Sequence (ver . dig . ci . certs . crls . sis)+      where+        ver = gIntVal v+        dig = asn1Container Set (digestTypesASN1S sdDigestAlgorithms)+        ci  = encapsulatedContentInfoASN1S sdContentType sdEncapsulatedContent+        certs = gen 0 sdCertificates+        crls  = gen 1 sdCRLs+        sis = asn1Container Set (asn1s sdSignerInfos)++        gen tag list+            | null list = id+            | otherwise = asn1Container (Container Context tag) (asn1s list)++        v | hasChoiceOther sdCertificates = 5+          | hasChoiceOther sdCRLs         = 5+          | any isVersion3 sdSignerInfos  = 3+          | sdContentType == DataType     = 1+          | otherwise                     = 3+++instance ParseASN1Object [ASN1Event] (SignedData (Encap EncapsulatedContent)) where+    parse =+        onNextContainer Sequence $ do+            IntVal v <- getNext+            when (v > 5) $+                throwParseError ("SignedData: parsed invalid version: " ++ show v)+            dig <- onNextContainer Set parseDigestTypes+            (ct, bs) <- parseEncapsulatedContentInfo+            certs <- parseOptList 0+            crls  <- parseOptList 1+            sis <- onNextContainer Set parse+            return SignedData { sdDigestAlgorithms = dig+                              , sdContentType = ct+                              , sdEncapsulatedContent = bs+                              , sdCertificates = certs+                              , sdCRLs = crls+                              , sdSignerInfos = sis+                              }+      where+        parseOptList tag =+            fromMaybe [] <$> onNextContainerMaybe (Container Context tag) parse++-- | Generate ASN.1 for EncapsulatedContentInfo.+encapsulatedContentInfoASN1S :: ASN1Elem e => ContentType -> Encap EncapsulatedContent -> ASN1Stream e+encapsulatedContentInfoASN1S ct ec = asn1Container Sequence (oid . cont)+  where oid = gOID (getObjectID ct)+        cont = encapsulatedASN1S (Container Context 0) ec++encapsulatedASN1S :: ASN1Elem e+                  => ASN1ConstructionType -> Encap B.ByteString -> ASN1Stream e+encapsulatedASN1S _   Detached     = id+encapsulatedASN1S ty (Attached bs) = asn1Container ty (gOctetString bs)++-- | Parse EncapsulatedContentInfo from ASN.1.+parseEncapsulatedContentInfo :: Monoid e => ParseASN1 e (ContentType, Encap EncapsulatedContent)+parseEncapsulatedContentInfo =+    onNextContainer Sequence $ do+        OID oid <- getNext+        withObjectID "content type" oid $ \ct ->+            wrap ct <$> onNextContainerMaybe (Container Context 0) parseInner+  where+    wrap ct Nothing  = (ct, Detached)+    wrap ct (Just c) = (ct, Attached c)++    parseInner = parseContentSingle <|> parseContentChunks++    parseContentSingle = do { OctetString bs <- getNext; return bs }+    parseContentChunks = onNextContainer (Container Universal 4) $+        B.concat <$> getMany parseContentSingle++digestTypesASN1S :: ASN1Elem e => [DigestAlgorithm] -> ASN1Stream e+digestTypesASN1S list cont = foldr (algorithmASN1S Sequence) cont list++parseDigestTypes :: Monoid e => ParseASN1 e [DigestAlgorithm]+parseDigestTypes = getMany (parseAlgorithm Sequence)
src/Crypto/Store/CMS/Type.hs view
@@ -11,6 +11,8 @@ {-# LANGUAGE TypeFamilies #-} module Crypto.Store.CMS.Type     ( ContentType(..)+    , Encap(..)+    , fromEncap     ) where  import Data.ASN1.OID@@ -48,3 +50,40 @@  instance OIDNameable ContentType where     fromObjectID oid = unOIDNW <$> fromObjectID oid++-- | Denotes the state of encapsulated content in a CMS data structure.  This+-- type is isomorphic to 'Maybe'.+data Encap a+    = Detached    -- content is stored externally to the structure+    | Attached a  -- content is stored inside the CMS struture+    deriving (Show,Eq)++instance Functor Encap where+    fmap _ Detached = Detached+    fmap f (Attached c) = Attached (f c)++instance Applicative Encap where+    pure = Attached++    Attached f <*> e = fmap f e+    Detached   <*> _ = Detached++instance Foldable Encap where+    foldMap = fromEncap mempty++    foldr _ d Detached     = d+    foldr f d (Attached c) = f c d++    foldl _ d Detached     = d+    foldl f d (Attached c) = f d c++instance Traversable Encap where+    traverse _ Detached     = pure Detached+    traverse f (Attached c) = Attached <$> f c++-- | Fold over an 'Encap' value.  This is similar to function 'maybe'.  If the+-- content is detached, the first argument is returned.  Otherwise the second+-- argument is applied to the content.+fromEncap :: b -> (a -> b) -> Encap a -> b+fromEncap d _ Detached     = d+fromEncap _ f (Attached c) = f c
src/Crypto/Store/Error.hs view
@@ -32,6 +32,10 @@       -- ^ MAC verification failed, incorrect key or password?     | BadChecksum       -- ^ Checksum verification failed, incorrect key or password?+    | DigestMismatch+      -- ^ Digest verification failed+    | SignatureNotVerified+      -- ^ Signature verification failed     | InvalidInput String       -- ^ Some condition is not met about input to algorithm     | InvalidPassword String
src/Crypto/Store/PKCS12.hs view
@@ -53,7 +53,9 @@     , setLocalKeyId     -- * Credentials     , fromCredential+    , fromNamedCredential     , toCredential+    , toNamedCredential     -- * Password-based protection     , Password     , OptProtected(..)@@ -68,9 +70,11 @@ import           Data.ASN1.Encoding import qualified Data.ByteArray as B import qualified Data.ByteString as BS-import           Data.Maybe (fromMaybe)+import           Data.List (partition)+import           Data.Maybe (fromMaybe, mapMaybe) import           Data.Semigroup import qualified Data.X509 as X509+import qualified Data.X509.Validation as X509  import Crypto.Cipher.Types @@ -280,7 +284,7 @@         cont = asn1Container (Container Context 0) inner         inner = asn1Container Sequence (gIntVal 0 . eci)         eci = encryptedContentInfoASN1S-                  (DataType, encryptionAlgorithm, encryptedData)+                  (DataType, encryptionAlgorithm, Attached encryptedData)  instance Monoid e => ParseASN1Object e ASElement where     parse = onNextContainer Sequence $ do@@ -296,7 +300,7 @@         parseUnencrypted = parseOctetStringObject "PKCS12"         parseEncrypted = onNextContainer Sequence $ do             IntVal 0 <- getNext-            (DataType, eScheme, ed) <- parseEncryptedContentInfo+            (DataType, eScheme, Attached ed) <- parseEncryptedContentInfo             return PKCS5 { encryptionAlgorithm = eScheme, encryptedData = ed }  @@ -454,23 +458,43 @@ instance Monoid e => ParseASN1Object e SafeContents where     parse = SafeContents <$> onNextContainer Sequence parse --- | Return all private keys contained in the safe contents.-getSafeKeys :: SafeContents -> [OptProtected X509.PrivKey]-getSafeKeys (SafeContents scs) = loop scs+filterBags :: ([Attribute] -> Bool) -> SafeContents -> SafeContents+filterBags p (SafeContents scs) = SafeContents (mapMaybe f scs)   where+    f (Bag (SafeContentsBag inner) attrs) =+        Just (Bag (SafeContentsBag $ filterBags p inner) attrs)+    f bag | p (bagAttributes bag)         = Just bag+          | otherwise                     = Nothing++filterByFriendlyName :: String -> SafeContents -> SafeContents+filterByFriendlyName name = filterBags ((== Just name) . getFriendlyName)++filterByLocalKeyId :: BS.ByteString -> SafeContents -> SafeContents+filterByLocalKeyId d = filterBags ((== Just d) . getLocalKeyId)++getSafeKeysId :: SafeContents -> [OptProtected (Id X509.PrivKey)]+getSafeKeysId (SafeContents scs) = loop scs+  where     loop []           = []     loop (bag : bags) =         case bagInfo bag of-            KeyBag (FormattedKey _ k) -> Unprotected k : loop bags-            PKCS8ShroudedKeyBag k     -> Protected (unshroud k) : loop bags-            SafeContentsBag inner     -> getSafeKeys inner ++ loop bags+            KeyBag (FormattedKey _ k) -> Unprotected (mkId k bag) : loop bags+            PKCS8ShroudedKeyBag k     -> Protected (unshroud k bag) : loop bags+            SafeContentsBag inner     -> getSafeKeysId inner ++ loop bags             _                         -> loop bags -    unshroud shrouded pwd = do+    unshroud shrouded bag pwd = do         bs <- decrypt shrouded pwd         FormattedKey _ k <- decode bs-        return k+        return (mkId k bag) +-- | Return all private keys contained in the safe contents.+getSafeKeys :: SafeContents -> [OptProtected X509.PrivKey]+getSafeKeys = map (fmap unId) . getSafeKeysId++getAllSafeKeysId :: [SafeContents] -> OptProtected [Id X509.PrivKey]+getAllSafeKeysId = applySamePassword . concatMap getSafeKeysId+ -- | Return all private keys contained in the safe content list.  All shrouded -- private keys must derive from the same password. --@@ -481,32 +505,38 @@ getAllSafeKeys :: [SafeContents] -> OptProtected [X509.PrivKey] getAllSafeKeys = applySamePassword . concatMap getSafeKeys --- | Return all X.509 certificates contained in the safe contents.-getSafeX509Certs :: SafeContents -> [X509.SignedCertificate]-getSafeX509Certs (SafeContents scs) = loop scs+getSafeX509CertsId :: SafeContents -> [Id X509.SignedCertificate]+getSafeX509CertsId (SafeContents scs) = loop scs   where     loop []           = []     loop (bag : bags) =         case bagInfo bag of-            CertBag (Bag (CertX509 c) _) -> c : loop bags-            SafeContentsBag inner        -> getSafeX509Certs inner ++ loop bags+            CertBag (Bag (CertX509 c) _) -> mkId c bag : loop bags+            SafeContentsBag inner        -> getSafeX509CertsId inner ++ loop bags             _                            -> loop bags +-- | Return all X.509 certificates contained in the safe contents.+getSafeX509Certs :: SafeContents -> [X509.SignedCertificate]+getSafeX509Certs = map unId . getSafeX509CertsId+ -- | Return all X.509 certificates contained in the safe content list. getAllSafeX509Certs :: [SafeContents] -> [X509.SignedCertificate] getAllSafeX509Certs = concatMap getSafeX509Certs --- | Return all X.509 CRLs contained in the safe contents.-getSafeX509CRLs :: SafeContents -> [X509.SignedCRL]-getSafeX509CRLs (SafeContents scs) = loop scs+getSafeX509CRLsId :: SafeContents -> [Id X509.SignedCRL]+getSafeX509CRLsId (SafeContents scs) = loop scs   where     loop []           = []     loop (bag : bags) =         case bagInfo bag of-            CRLBag (Bag (CRLX509 c) _) -> c : loop bags-            SafeContentsBag inner      -> getSafeX509CRLs inner ++ loop bags+            CRLBag (Bag (CRLX509 c) _) -> mkId c bag : loop bags+            SafeContentsBag inner      -> getSafeX509CRLsId inner ++ loop bags             _                          -> loop bags +-- | Return all X.509 CRLs contained in the safe contents.+getSafeX509CRLs :: SafeContents -> [X509.SignedCRL]+getSafeX509CRLs = map unId . getSafeX509CRLsId+ -- | Return all X.509 CRLs contained in the safe content list. getAllSafeX509CRLs :: [SafeContents] -> [X509.SignedCRL] getAllSafeX509CRLs = concatMap getSafeX509CRLs@@ -515,14 +545,31 @@ -- Conversion to/from credentials  getInnerCredential :: [SafeContents] -> SamePassword (Maybe (X509.CertificateChain, X509.PrivKey))-getInnerCredential l = SamePassword (fn <$> getAllSafeKeys l)+getInnerCredential l = SamePassword (fn <$> getAllSafeKeysId l)   where-    certs = getAllSafeX509Certs l--    fn []               = Nothing-    fn [k] | null certs = Nothing-           | otherwise  = Just (X509.CertificateChain certs, k)-    fn _                = Nothing+    certs     = getAllSafeX509Certs l+    fn idKeys = do+        iKey <- single idKeys+        let k = unId iKey+        case idKeyId iKey of+            Just d  -> do+                -- locate a single certificate with same ID as private key+                -- and follow the issuers to get all certificates in the chain+                let filtered = map (filterByLocalKeyId d) l+                leaf <- single (getAllSafeX509Certs filtered)+                pure (buildCertificateChain leaf certs, k)+            Nothing ->+                case idName iKey of+                    Just name -> do+                        -- same but using friendly name of private key+                        let filtered = map (filterByFriendlyName name) l+                        leaf <- single (getAllSafeX509Certs filtered)+                        pure (buildCertificateChain leaf certs, k)+                    Nothing   -> do+                        -- no identifier available, so we simply return all+                        -- certificates with input order+                        guard (not $ null certs)+                        pure (X509.CertificateChain certs, k)  -- | Extract the private key and certificate chain from a 'PKCS12' value.  A -- credential is returned when the structure contains exactly one private key@@ -531,6 +578,23 @@ toCredential p12 =     unSamePassword (SamePassword (unPKCS12 p12) >>= getInnerCredential) +getInnerCredentialNamed :: String -> [SafeContents] -> SamePassword (Maybe (X509.CertificateChain, X509.PrivKey))+getInnerCredentialNamed name l = SamePassword (fn <$> getAllSafeKeys filtered)+  where+    certs    = getAllSafeX509Certs l+    filtered = map (filterByFriendlyName name) l+    fn keys  = do+        k <- single keys+        leaf <- single (getAllSafeX509Certs filtered)+        pure (buildCertificateChain leaf certs, k)++-- | Extract a private key and certificate chain with the specified friendly+-- name from a 'PKCS12' value.  A credential is returned when the structure+-- contains exactly one private key and one X.509 certificate with the name.+toNamedCredential :: String -> PKCS12 -> OptProtected (Maybe (X509.CertificateChain, X509.PrivKey))+toNamedCredential name p12 = unSamePassword $+    SamePassword (unPKCS12 p12) >>= getInnerCredentialNamed name+ -- | Build a 'PKCS12' value containing a private key and certificate chain. -- Distinct encryption is applied for both.  Encrypting the certificate chain is -- optional.@@ -542,7 +606,30 @@                -> Password                -> (X509.CertificateChain, X509.PrivKey)                -> Either StoreError PKCS12-fromCredential algChain algKey pwd (X509.CertificateChain certs, key)+fromCredential = fromCredential' id++-- | Build a 'PKCS12' value containing a private key and certificate chain+-- identified with the specified friendly name.  Distinct encryption is applied+-- for private key and certificates.  Encrypting the certificate chain is+-- optional.+--+-- Note: advice is to always generate fresh and independent 'EncryptionScheme'+-- values so that the salt is not reused twice in the encryption process.+fromNamedCredential :: String+                    -> Maybe EncryptionScheme -- for certificates+                    -> EncryptionScheme       -- for private key+                    -> Password+                    -> (X509.CertificateChain, X509.PrivKey)+                    -> Either StoreError PKCS12+fromNamedCredential name = fromCredential' (setFriendlyName name)++fromCredential' :: ([Attribute] -> [Attribute])+                -> Maybe EncryptionScheme -- for certificates+                -> EncryptionScheme       -- for private key+                -> Password+                -> (X509.CertificateChain, X509.PrivKey)+                -> Either StoreError PKCS12+fromCredential' trans algChain algKey pwd (X509.CertificateChain certs, key)     | null certs = Left (InvalidInput "Empty certificate chain")     | otherwise  = (<>) <$> pkcs12Chain <*> pkcs12Key   where@@ -552,14 +639,17 @@             Just alg -> encrypted alg pwd scChain             Nothing  -> Right (unencrypted scChain) -    scChain     = SafeContents (map toCertBag certs)-    toCertBag c = Bag (CertBag (Bag (CertX509 c) [])) []+    scChain       = SafeContents (zipWith toCertBag certAttrs certs)+    certAttrs     = attrs : repeat []+    toCertBag a c = Bag (CertBag (Bag (CertX509 c) [])) a      scKeyOrError = wrap <$> encrypt algKey pwd encodedKey -    wrap shrouded = SafeContents [Bag (PKCS8ShroudedKeyBag shrouded) []]+    wrap shrouded = SafeContents [Bag (PKCS8ShroudedKeyBag shrouded) attrs]     encodedKey    = encodeASN1Object (FormattedKey PKCS8Format key) +    X509.Fingerprint keyId = X509.getFingerprint (head certs) X509.HashSHA1+    attrs = trans (setLocalKeyId keyId [])  -- Standard attributes @@ -633,6 +723,20 @@ applySamePassword :: [OptProtected a] -> OptProtected [a] applySamePassword = unSamePassword . traverse SamePassword +single :: [a] -> Maybe a+single [x] = Just x+single _   = Nothing++data Id a = Id+    { unId    :: a+    , idKeyId :: Maybe BS.ByteString+    , idName  :: Maybe String+    }++mkId :: a -> Bag info -> Id a+mkId val bag = val `seq` Id val (getLocalKeyId attrs) (getFriendlyName attrs)+  where attrs = bagAttributes bag+ decode :: ParseASN1Object [ASN1Event] obj => BS.ByteString -> Either StoreError obj decode bs =     case decodeASN1Repr' BER bs of@@ -650,3 +754,20 @@     case decode bs of         Left e  -> throwParseError (name ++ ": " ++ show e)         Right c -> return c++buildCertificateChain :: X509.SignedCertificate -> [X509.SignedCertificate]+                      -> X509.CertificateChain+buildCertificateChain leaf authorities =+    X509.CertificateChain (leaf : findAuthorities leaf authorities)+  where+    findAuthorities cert others+        | subject cert == issuer cert = []+        | otherwise                   =+            case partition (\c -> subject c == issuer cert) others of+                ([c], others') -> c : findAuthorities c others'+                _              -> []++    signedCert = X509.signedObject . X509.getSigned++    subject c = X509.certSubjectDN (signedCert c)+    issuer c  = X509.certIssuerDN (signedCert c)
src/Crypto/Store/PKCS5/PBES1.hs view
@@ -267,6 +267,7 @@ getV (DigestAlgorithm SHA256) = 64 getV (DigestAlgorithm SHA384) = 128 getV (DigestAlgorithm SHA512) = 128+getV t                        = error ("pkcs12Derive: unsupported hash: " ++ show t)  hashFromProxy :: proxy a -> a hashFromProxy _ = undefined
src/Crypto/Store/PKCS8.hs view
@@ -51,12 +51,18 @@ import Data.ASN1.BinaryEncoding import Data.ASN1.BitArray import Data.ASN1.Encoding+import Data.ByteArray (ByteArrayAccess, convert) import Data.Maybe import qualified Data.X509 as X509 import qualified Data.ByteString as B+import           Crypto.Error import           Crypto.Number.Serialize (i2osp, i2ospOf_, os2ip)+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448 as X448 import qualified Crypto.PubKey.DSA as DSA import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Crypto.PubKey.Ed448 as Ed448 import qualified Crypto.PubKey.RSA as RSA  import Crypto.Store.ASN1.Generate@@ -129,12 +135,20 @@     rsa       = X509.PrivKeyRSA . unFormat <$> parse     dsa       = X509.PrivKeyDSA . DSA.toPrivateKey . unFormat <$> parse     ecdsa     = X509.PrivKeyEC . unFormat <$> parse+    x25519    = X509.PrivKeyX25519 <$> parseModern+    x448      = X509.PrivKeyX448 <$> parseModern+    ed25519   = X509.PrivKeyEd25519 <$> parseModern+    ed448     = X509.PrivKeyEd448 <$> parseModern     encrypted = inner . decrypt <$> parse      getParser "PRIVATE KEY"           = Unprotected <$> allTypes     getParser "RSA PRIVATE KEY"       = Unprotected <$> rsa     getParser "DSA PRIVATE KEY"       = Unprotected <$> dsa     getParser "EC PRIVATE KEY"        = Unprotected <$> ecdsa+    getParser "X25519 PRIVATE KEY"    = Unprotected <$> x25519+    getParser "X448 PRIVATE KEY"      = Unprotected <$> x448+    getParser "ED25519 PRIVATE KEY"   = Unprotected <$> ed25519+    getParser "ED448 PRIVATE KEY"     = Unprotected <$> ed448     getParser "ENCRYPTED PRIVATE KEY" = Protected   <$> encrypted     getParser _                       = empty @@ -198,8 +212,13 @@         X509.PrivKeyRSA k -> ("RSA", traditional k)         X509.PrivKeyDSA k -> ("DSA", traditional (dsaPrivToPair k))         X509.PrivKeyEC  k -> ("EC",  traditional k)+        X509.PrivKeyX25519  k -> ("X25519",  tradModern k)+        X509.PrivKeyX448    k -> ("X448",    tradModern k)+        X509.PrivKeyEd25519 k -> ("ED25519", tradModern k)+        X509.PrivKeyEd448   k -> ("ED448",   tradModern k)   where     traditional a = asn1s (Traditional a)+    tradModern a  = asn1s (Modern [] a)  keyToModernPEM :: X509.PrivKey -> PEM keyToModernPEM privKey = mkPEM "PRIVATE KEY" (encodeASN1S asn1)@@ -211,6 +230,10 @@         X509.PrivKeyRSA k -> modern k         X509.PrivKeyDSA k -> modern (dsaPrivToPair k)         X509.PrivKeyEC  k -> modern k+        X509.PrivKeyX25519  k -> modern k+        X509.PrivKeyX448    k -> modern k+        X509.PrivKeyEd25519 k -> modern k+        X509.PrivKeyEd448   k -> modern k   where     modern a = asn1s (Modern attrs a) @@ -263,7 +286,7 @@     asn1s (FormattedKey PKCS8Format k)       = asn1s (Modern [] k)  instance (Monoid e, ParseASN1Object e (Traditional a), ParseASN1Object e (Modern a)) => ParseASN1Object e (FormattedKey a) where-    parse = (traditional <$> parseTraditional) <|> (modern <$> parseModern)+    parse = (modern <$> parseModern) <|> (traditional <$> parseTraditional)       where         traditional = FormattedKey TraditionalFormat         modern      = FormattedKey PKCS8Format@@ -292,11 +315,15 @@     asn1s (Modern attrs privKey) = modernPrivKeyASN1S attrs privKey  instance Monoid e => ParseASN1Object e (Modern X509.PrivKey) where-    parse = rsa <|> dsa <|> ecdsa+    parse = rsa <|> dsa <|> ecdsa <|> x25519 <|> x448 <|> ed25519 <|> ed448       where         rsa   = fmap X509.PrivKeyRSA  <$> parse         dsa   = fmap (X509.PrivKeyDSA . DSA.toPrivateKey) <$> parse         ecdsa = fmap X509.PrivKeyEC <$> parse+        x25519  = fmap X509.PrivKeyX25519 <$> parse+        x448    = fmap X509.PrivKeyX448 <$> parse+        ed25519 = fmap X509.PrivKeyEd25519 <$> parse+        ed448   = fmap X509.PrivKeyEd448 <$> parse  skipVersion :: Monoid e => ParseASN1 e () skipVersion = do@@ -349,11 +376,7 @@         IntVal pexp1 <- getNext         IntVal pexp2 <- getNext         IntVal pcoef <- getNext-        let calculate_modulus m i =-                if (2 ^ (i * 8)) > m-                    then i-                    else calculate_modulus m (i+1)-            pubKey  = RSA.PublicKey { RSA.public_size = calculate_modulus n 1+        let pubKey  = RSA.PublicKey { RSA.public_size = numBytes n                                     , RSA.public_n    = n                                     , RSA.public_e    = e                                     }@@ -598,3 +621,89 @@             then do BitString seed <- getNext                     return (os2ip $ bitArrayGetData seed)             else return 0+++-- X25519, X448, Ed25519, Ed448++instance ASN1Elem e => ProduceASN1Object e (Modern X25519.SecretKey) where+    asn1s (Modern attrs privKey) = asn1Container Sequence (v . alg . bs . att)+      where+        v     = gIntVal 0+        alg   = asn1Container Sequence (gOID [1,3,101,110])+        bs    = innerEddsaASN1S privKey+        att   = attributesASN1S (Container Context 0) attrs++instance Monoid e => ParseASN1Object e (Modern X25519.SecretKey) where+    parse = onNextContainer Sequence $ do+        skipVersion+        onNextContainer Sequence $ do { OID [1,3,101,110] <- getNext; return () }+        (attrs, bs) <- parseAttrKeys+        Modern attrs <$> parseInnerEddsa "X25519" X25519.secretKey bs++instance ASN1Elem e => ProduceASN1Object e (Modern X448.SecretKey) where+    asn1s (Modern attrs privKey) = asn1Container Sequence (v . alg . bs . att)+      where+        v     = gIntVal 0+        alg   = asn1Container Sequence (gOID [1,3,101,111])+        bs    = innerEddsaASN1S privKey+        att   = attributesASN1S (Container Context 0) attrs++instance Monoid e => ParseASN1Object e (Modern X448.SecretKey) where+    parse = onNextContainer Sequence $ do+        skipVersion+        onNextContainer Sequence $ do { OID [1,3,101,111] <- getNext; return () }+        (attrs, bs) <- parseAttrKeys+        Modern attrs <$> parseInnerEddsa "X448" X448.secretKey bs++instance ASN1Elem e => ProduceASN1Object e (Modern Ed25519.SecretKey) where+    asn1s (Modern attrs privKey) = asn1Container Sequence (v . alg . bs . att)+      where+        v     = gIntVal 0+        alg   = asn1Container Sequence (gOID [1,3,101,112])+        bs    = innerEddsaASN1S privKey+        att   = attributesASN1S (Container Context 0) attrs++instance Monoid e => ParseASN1Object e (Modern Ed25519.SecretKey) where+    parse = onNextContainer Sequence $ do+        skipVersion+        onNextContainer Sequence $ do { OID [1,3,101,112] <- getNext; return () }+        (attrs, bs) <- parseAttrKeys+        Modern attrs <$> parseInnerEddsa "Ed25519" Ed25519.secretKey bs++instance ASN1Elem e => ProduceASN1Object e (Modern Ed448.SecretKey) where+    asn1s (Modern attrs privKey) = asn1Container Sequence (v . alg . bs . att)+      where+        v     = gIntVal 0+        alg   = asn1Container Sequence (gOID [1,3,101,113])+        bs    = innerEddsaASN1S privKey+        att   = attributesASN1S (Container Context 0) attrs++instance Monoid e => ParseASN1Object e (Modern Ed448.SecretKey) where+    parse = onNextContainer Sequence $ do+        skipVersion+        onNextContainer Sequence $ do { OID [1,3,101,113] <- getNext; return () }+        (attrs, bs) <- parseAttrKeys+        Modern attrs <$> parseInnerEddsa "Ed448" Ed448.secretKey bs++innerEddsaASN1S :: (ASN1Elem e, ByteArrayAccess key) => key -> ASN1Stream e+innerEddsaASN1S key = gOctetString (encodeASN1S inner)+  where inner = gOctetString (convert key)++parseInnerEddsa :: Monoid e+                => String+                -> (B.ByteString -> CryptoFailable key)+                -> B.ByteString+                -> ParseASN1 e key+parseInnerEddsa name buildKey input =+    case either strError (runParseASN1 parser) (decodeASN1' BER input) of+        Left err -> throwParseError ("PKCS8: error parsing inner " ++ name ++ ": " ++ err)+        Right privKey -> return privKey+  where+    innerMsg = "PKCS8: error decoding inner " ++ name ++ ": "+    strError = Left . (innerMsg ++) . show+    parser   = do+        OctetString bs <- getNext+        case buildKey bs of+            CryptoPassed privKey -> return privKey+            CryptoFailed _       ->+                throwParseError ("PKCS8: parsed invalid " ++ name ++ " secret key")
src/Crypto/Store/PKCS8/EC.hs view
@@ -22,7 +22,7 @@ import Data.X509 import Data.X509.EC -import Crypto.Number.Basic (numBits)+import Crypto.Number.Basic (numBits, numBytes) import Crypto.Number.Serialize (i2ospOf_) import Crypto.PubKey.ECC.Prim import Crypto.PubKey.ECC.Types@@ -32,10 +32,6 @@ -- | Number of bytes necessary to serialize n bits. bitsToBytes :: Int -> Int bitsToBytes n = (n + 7) `div` 8---- | Number of bytes necessary to serialize an integer.-numBytes :: Integer -> Int-numBytes = bitsToBytes . numBits  -- | Number of bytes to serialize a field element. curveSizeBytes :: Curve -> Int
src/Crypto/Store/Util.hs view
@@ -7,6 +7,7 @@ -- -- {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE MagicHash #-} module Crypto.Store.Util     ( (&&!)@@ -30,8 +31,12 @@ infixr 3 &&!  -- | Reverse a bytearray.-reverseBytes :: (ByteArrayAccess bin, ByteArray bout) => bin -> bout+reverseBytes :: ByteArray ba => ba -> ba+#if MIN_VERSION_memory(0,14,18)+reverseBytes = B.reverse+#else reverseBytes = B.pack . reverse . B.unpack+#endif  -- | Test if all bytes in a bytearray are equal to the value specified.  Runs in -- constant time.
src/Crypto/Store/X509.hs view
@@ -40,6 +40,7 @@ import Data.Proxy import qualified Data.X509 as X509 import qualified Data.ByteString as B+import           Crypto.Number.Basic (numBytes) import qualified Crypto.PubKey.RSA as RSA  import Crypto.Store.ASN1.Generate@@ -167,12 +168,8 @@     parse = onNextContainer Sequence $ do         IntVal modulus <- getNext         IntVal pubexp <- getNext-        let pub = RSA.PublicKey { RSA.public_size = calculate_modulus modulus 1+        let pub = RSA.PublicKey { RSA.public_size = numBytes modulus                                 , RSA.public_n    = modulus                                 , RSA.public_e    = pubexp                                 }         return (RSAPublicKey pub)-      where-        calculate_modulus n i-            | (2 ^ (i * 8)) > n = i-            | otherwise         = calculate_modulus n (i + 1)
tests/CMS/Instances.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DataKinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- | Orphan instances. module CMS.Instances@@ -11,6 +12,7 @@ import           Data.ASN1.Types import qualified Data.ByteArray as B import           Data.ByteString (ByteString)+import           Data.Proxy import           Data.X509  import Test.Tasty.QuickCheck@@ -34,25 +36,28 @@         if n == 0             then DataCI <$> arbitraryMessage             else oneof [ DataCI <$> arbitraryMessage-                       , arbitrarySignedData-                       , arbitraryEnvelopedData-                       , arbitraryDigestedData-                       , arbitraryEncryptedData-                       , arbitraryAuthenticatedData-                       , arbitraryAuthEnvelopedData+                       , arbitraryMode <*> arbitrarySignedData+                       , arbitraryMode <*> arbitraryEnvelopedData+                       , arbitraryMode <*> arbitraryDigestedData+                       , arbitraryMode <*> arbitraryEncryptedData+                       , arbitraryMode <*> arbitraryAuthenticatedData+                       , arbitraryMode <*> arbitraryAuthEnvelopedData                        ]       where         arbitraryMessage :: Gen ByteString         arbitraryMessage = resize 2048 (B.pack <$> arbitrary) -        arbitrarySignedData :: Gen ContentInfo+        arbitraryMode :: Encapsulates struct => Gen (struct ByteString -> ContentInfo)+        arbitraryMode = elements [ toAttachedCI, snd . toDetachedCI ]++        arbitrarySignedData :: Gen (SignedData EncapsulatedContent)         arbitrarySignedData = do             alg   <- arbitrary             (sigFns, _) <- arbitrarySigVer alg             inner <- scale (subtract $ length sigFns) arbitrary             signData sigFns inner >>= failIfError -        arbitraryEnvelopedData :: Gen ContentInfo+        arbitraryEnvelopedData :: Gen (EnvelopedData EncryptedContent)         arbitraryEnvelopedData = do             oinfo <- arbitrary             (alg, key, attrs) <- getCommon@@ -60,19 +65,19 @@             inner <- scale (subtract $ length envFns) arbitrary             envelopData oinfo key alg envFns attrs inner >>= failIfError -        arbitraryDigestedData :: Gen ContentInfo+        arbitraryDigestedData :: Gen (DigestedData EncapsulatedContent)         arbitraryDigestedData = do             inner <- scale pred arbitrary             dt <- arbitrary             return $ digestData dt inner -        arbitraryEncryptedData :: Gen ContentInfo+        arbitraryEncryptedData :: Gen (EncryptedData EncryptedContent)         arbitraryEncryptedData = do             (alg, key, attrs) <- getCommon             inner <- scale pred arbitrary             failIfError $ encryptData key alg attrs inner -        arbitraryAuthenticatedData :: Gen ContentInfo+        arbitraryAuthenticatedData :: Gen (AuthenticatedData EncapsulatedContent)         arbitraryAuthenticatedData = do             (oinfo, alg, key, envFns, aAttrs, uAttrs) <- getCommonAuth             dig <- arbitrary@@ -80,7 +85,7 @@             generateAuthenticatedData oinfo key alg dig envFns aAttrs uAttrs inner                 >>= failIfError -        arbitraryAuthEnvelopedData :: Gen ContentInfo+        arbitraryAuthEnvelopedData :: Gen (AuthEnvelopedData EncryptedContent)         arbitraryAuthEnvelopedData = do             (oinfo, alg, key, envFns, aAttrs, uAttrs) <- getCommonAuth             inner <- scale (subtract $ length envFns) arbitrary@@ -118,16 +123,26 @@ arbitraryAttributes :: Gen [Attribute] arbitraryAttributes = resize 3 arbitrary +p256 :: Proxy 256+p256 = Proxy++p512 :: Proxy 512+p512 = Proxy+ instance Arbitrary DigestAlgorithm where-    arbitrary = elements-        [ DigestAlgorithm MD2-        , DigestAlgorithm MD4-        , DigestAlgorithm MD5-        , DigestAlgorithm SHA1-        , DigestAlgorithm SHA224-        , DigestAlgorithm SHA256-        , DigestAlgorithm SHA384-        , DigestAlgorithm SHA512+    arbitrary = oneof+        [ pure $ DigestAlgorithm MD2+        , pure $ DigestAlgorithm MD4+        , pure $ DigestAlgorithm MD5+        , pure $ DigestAlgorithm SHA1+        , pure $ DigestAlgorithm SHA224+        , pure $ DigestAlgorithm SHA256+        , pure $ DigestAlgorithm SHA384+        , pure $ DigestAlgorithm SHA512+        , pure $ DigestAlgorithm SHAKE128_256+        , pure $ DigestAlgorithm SHAKE256_512+        , pure $ DigestAlgorithm (SHAKE128 p256)+        , pure $ DigestAlgorithm (SHAKE256 p512)         ]  arbitraryIntegrityDigest :: Gen DigestAlgorithm@@ -184,6 +199,9 @@         , pure $ ECDSA (DigestAlgorithm SHA256)         , pure $ ECDSA (DigestAlgorithm SHA384)         , pure $ ECDSA (DigestAlgorithm SHA512)++        , pure Ed25519+        , pure Ed448         ]  arbitraryKeyPair :: SignatureAlg -> Gen (PubKey, PrivKey)@@ -202,6 +220,12 @@ arbitraryKeyPair (ECDSA _) = do     (pub, priv) <- arbitraryNamedEC     return (PubKeyEC pub, PrivKeyEC priv)+arbitraryKeyPair Ed25519 = do+    (pub, priv) <- arbitraryEd25519+    return (PubKeyEd25519 pub, PrivKeyEd25519 priv)+arbitraryKeyPair Ed448 = do+    (pub, priv) <- arbitraryEd448+    return (PubKeyEd448 pub, PrivKeyEd448 priv)  arbitrarySigVer :: SignatureAlg -> Gen ([ProducerOfSI Gen], ConsumerOfSI Gen) arbitrarySigVer alg = sized $ \n -> do@@ -352,11 +376,13 @@         kid <- arbitrarySmall         KeyIdentifier kid Nothing <$> arbitrary -arbitraryAgreeParams :: KeyEncryptionParams -> Gen KeyAgreementParams-arbitraryAgreeParams alg = oneof-    [ flip StdDH alg <$> arbitraryDigest-    , flip CofactorDH alg <$> arbitraryDigest-    ]+arbitraryAgreeParams :: Bool -> KeyEncryptionParams -> Gen KeyAgreementParams+arbitraryAgreeParams allowCofactorDH alg+    | allowCofactorDH = oneof+        [ flip StdDH alg <$> arbitraryDigest+        , flip CofactorDH alg <$> arbitraryDigest+        ]+    | otherwise = flip StdDH alg <$> arbitraryDigest   where     arbitraryDigest =         elements@@ -387,11 +413,14 @@         return (envFn, devFn)      arbitraryKA = do-        (pub, priv) <- arbitraryNamedEC-        cert <- arbitrarySignedCertificate (PubKeyEC pub)-        kap  <- arbitraryAlg >>= arbitraryAgreeParams+        (cert, priv) <- arbitraryDHParams+        let allowCofactorDH =+                case priv of+                    PrivKeyEC _ -> True+                    _           -> False+        kap <- arbitraryAlg >>= arbitraryAgreeParams allowCofactorDH         let envFn = forKeyAgreeRecipient cert kap-            devFn = withRecipientKeyAgree (PrivKeyEC priv) cert+            devFn = withRecipientKeyAgree priv cert         return (envFn, devFn)      arbitraryKEK = do@@ -430,6 +459,26 @@                                  , return AES256_WRAP_PAD                                  , RC2_WRAP <$> choose (1, 1024)                                  ]++    arbitraryDHParams = oneof [ arbitraryCredNamedEC+                              , arbitraryCredX25519+                              , arbitraryCredX448+                              ]++    arbitraryCredNamedEC = do+        (pub, priv) <- arbitraryNamedEC+        cert <- arbitrarySignedCertificate (PubKeyEC pub)+        return (cert, PrivKeyEC priv)++    arbitraryCredX25519 = do+        (pub, priv) <- arbitraryX25519+        cert <- arbitrarySignedCertificate (PubKeyX25519 pub)+        return (cert, PrivKeyX25519 priv)++    arbitraryCredX448 = do+        (pub, priv) <- arbitraryX448+        cert <- arbitrarySignedCertificate (PubKeyX448 pub)+        return (cert, PrivKeyX448 priv)      -- key wrapping in PWRIKEK is incompatible with CTR mode so we must never     -- generate this combination
tests/CMS/Tests.hs view
@@ -49,8 +49,17 @@         count = 1  signedDataTests :: TestTree-signedDataTests =-    testCaseSteps "SignedData" $ \step -> do+signedDataTests = signedDataAnyTests "SignedData" path getAttached+  where path  = testFile "cms-signed-data.pem"++signedDataDetachedTests :: TestTree+signedDataDetachedTests =+    signedDataAnyTests "SignedDataDetached" path (getDetached message)+  where path  = testFile "cms-signed-data-detached.pem"++signedDataAnyTests :: TestName -> FilePath -> (SignedData (Encap EncapsulatedContent) -> IO (SignedData EncapsulatedContent)) -> TestTree+signedDataAnyTests caseName path getInner =+    testCaseSteps caseName $ \step -> do         cms <- readCMSFile path         assertEqual "unexpected parse count" (length names) (length cms) @@ -59,11 +68,11 @@              step ("verifying " ++ name)             assertBool "unexpected type" (hasType SignedDataType ci)-            let SignedDataCI sd = ci+            let SignedDataCI sdEncap = ci+            sd <- getInner sdEncap             result <- verifySignedData withSignerKey sd-            assertJust result (verifyInnerMessage message)-  where path  = testFile "cms-signed-data.pem"-        names = [ "RSA"+            assertRight result (verifyInnerMessage message)+  where names = [ "RSA"                 , "DSA"                 , "EC (named curve)"                 , "EC (explicit prime curve)"@@ -86,8 +95,7 @@                 let (name, key) = keys !! index                  step ("testing " ++ name)-                assertBool "unexpected type" (hasType EnvelopedDataType ci)-                let EnvelopedDataCI ev = ci+                ev <- getEnveloppedAttached ci                 result <- openEnvelopedData (f key) ev                 assertRight result (verifyInnerMessage message)         testKT caseName path keys = testCaseSteps caseName $ \step -> do@@ -100,8 +108,7 @@             let pairs = [ (c, m) | c <- map fst keys1, m <- modes ]             forM_ (zip pairs cms) $ \((c, m), ci) -> do                 step ("testing " ++ c ++ " with " ++ m)-                assertBool "unexpected type" (hasType EnvelopedDataType ci)-                let EnvelopedDataCI ev = ci+                ev <- getEnveloppedAttached ci                 result <- openEnvelopedData (withRecipientKeyTrans priv) ev                 assertRight result (verifyInnerMessage message)         testKA caseName path keys = testCaseSteps caseName $ \step -> do@@ -116,10 +123,13 @@             let pairs = [ (c, h) | c <- map fst keys, h <- mds ]             forM_ (zip pairs cms) $ \((c, h), ci) -> do                 step ("testing " ++ c ++ " with " ++ h)-                assertBool "unexpected type" (hasType EnvelopedDataType ci)-                let EnvelopedDataCI ev = ci+                ev <- getEnveloppedAttached ci                 result <- openEnvelopedData (withRecipientKeyAgree priv cert) ev                 assertRight result (verifyInnerMessage message)+        getEnveloppedAttached ci = do+            assertBool "unexpected type" (hasType EnvelopedDataType ci)+            let EnvelopedDataCI evEncap = ci+            getAttached evEncap         path1 = testFile "cms-enveloped-kekri-data.pem"         path2 = testFile "cms-enveloped-pwri-data.pem"         path3 = testFile "cms-enveloped-ktri-data.pem"@@ -160,12 +170,14 @@              step ("verifying " ++ name)             assertBool "unexpected type" (hasType DigestedDataType ci)-            let DigestedDataCI dd = ci-                result = digestVerify dd-            assertJust result (verifyInnerMessage message)+            let DigestedDataCI ddEncap = ci+            dd <- getAttached ddEncap+            let result = digestVerify dd+            assertRight result (verifyInnerMessage message)              step ("digesting " ++ name)-            let ci' = digestData alg (DataCI message)+            let dd' = digestData alg (DataCI message)+                ci' = toAttachedCI dd'             ci @?= ci'   where path  = testFile "cms-digested-data.pem"         algs  = [ ("MD5",    DigestAlgorithm MD5)@@ -187,13 +199,15 @@              step ("decrypting " ++ name)             assertBool "unexpected type" (hasType EncryptedDataType ci)-            let EncryptedDataCI ed = ci-                result = decryptData key ed+            let EncryptedDataCI edEncap = ci+            ed <- getAttached edEncap+            let result = decryptData key ed             assertRight result (verifyInnerMessage message)              step ("encrypting " ++ name)             let params = edContentEncryptionParams ed-                ci'    = encryptData key params [] (DataCI message)+                ed'    = encryptData key params [] (DataCI message)+                ci'    = toAttachedCI <$> ed'             Right ci @?= ci'   where path  = testFile "cms-encrypted-data.pem"         keys  = [ ("DES_CBC",              testKey  8)@@ -223,13 +237,15 @@         forM_ (zip [0..] cms) $ \(index, ci) -> do             step ("testing vector " ++ show (index :: Int))             assertBool "unexpected type" (hasType AuthEnvelopedDataType ci)-            let AuthEnvelopedDataCI ae = ci+            let AuthEnvelopedDataCI aeEncap = ci+            ae <- getAttached aeEncap             result <- openAuthEnvelopedData (withRecipientPassword pwd) ae             assertRight result (verifyInnerMessage msg)              step ("testing encoded vector " ++ show index)             let [Just ci'] = pemToContentInfo [] (contentInfoToPEM ci)-                AuthEnvelopedDataCI ae' = ci'+                AuthEnvelopedDataCI aeEncap' = ci'+            ae' <- getAttached aeEncap'             result' <- openAuthEnvelopedData (withRecipientPassword pwd) ae'             assertRight result' (verifyInnerMessage msg)   where path  = testFile "cms-auth-enveloped-data-rfc6476.pem"@@ -246,32 +262,32 @@         collect alg $ do             (sigFns, verFn) <- scale succ (arbitrarySigVer alg)             r <- signData sigFns ci-            let Right (SignedDataCI sd) = r+            let Right sd = r             r' <- verifySignedData verFn sd-            return (Just ci === r')+            return (Right ci === r')     , testProperty "enveloping" $ \alg ci ->         collect alg $ do             (oinfo, key, envFns, devFn, attrs) <- getCommon alg             r <- envelopData oinfo key alg envFns attrs ci-            let Right (EnvelopedDataCI ev) = r+            let Right ev = r             r' <- openEnvelopedData devFn ev             return (Right ci === r')     , testProperty "digesting" $ \alg ci ->         collect alg $-            let DigestedDataCI dd = digestData alg ci-             in Just ci === digestVerify dd+            let dd = digestData alg ci+             in Right ci === digestVerify dd     , testProperty "encrypting" $ \alg ci ->         collect alg $ do             key <- generateKey alg             attrs <- arbitraryAttributes-            let Right (EncryptedDataCI ed) = encryptData key alg attrs ci+            let Right ed = encryptData key alg attrs ci             return (Right ci === decryptData key ed)     , testProperty "authenticating" $ \alg dig ci ->         collect alg $ do             (oinfo, key, envFns, devFn, uAttrs) <- getCommon alg             aAttrs <- if isNothing dig then pure [] else arbitraryAttributes             r <- generateAuthenticatedData oinfo key alg dig envFns aAttrs uAttrs ci-            let Right (AuthenticatedDataCI ad) = r+            let Right ad = r             r' <- verifyAuthenticatedData devFn ad             return (Right ci === r')     , testProperty "enveloping with authentication" $ \alg ci ->@@ -279,7 +295,7 @@             (oinfo, key, envFns, devFn, uAttrs) <- getCommon alg             aAttrs <- arbitraryAttributes             r <- authEnvelopData oinfo key alg envFns aAttrs uAttrs ci-            let Right (AuthEnvelopedDataCI ae) = r+            let Right ae = r             r' <- openAuthEnvelopedData devFn ae             return (Right ci === r')     ]@@ -303,6 +319,7 @@     testGroup "CMS"         [ dataTests         , signedDataTests+        , signedDataDetachedTests         , envelopedDataTests         , digestedDataTests         , encryptedDataTests
tests/PKCS12/Instances.hs view
@@ -2,6 +2,7 @@ -- | Orphan instances. module PKCS12.Instances     ( arbitraryPassword+    , arbitraryAlias     , arbitraryIntegrityParams     , arbitraryPKCS12     ) where@@ -20,6 +21,10 @@  arbitrarySmall :: Gen ByteString arbitrarySmall = resize 10 (B.pack <$> arbitrary)++arbitraryAlias :: Gen String+arbitraryAlias = resize 16 asciiChar+  where asciiChar = listOf $ choose ('\x20','\x7f')  arbitraryIntegrityParams :: Gen IntegrityParams arbitraryIntegrityParams = (,) <$> arbitraryIntegrityDigest <*> arbitrary
tests/PKCS12/Tests.hs view
@@ -81,19 +81,28 @@         let r = readP12FileFromMemory <$> writeP12FileToMemory params pI c         return $ Right (Right (Right c)) === (fmap (recover pI) <$> r)     , localOption (QuickCheckTests 20) $ testProperty "converting credentials" $+        \pChain pKey privKey ->+            testCredConv privKey toCredential (fromCredential pChain pKey)+    , localOption (QuickCheckTests 20) $ testProperty "converting named credentials" $         \pChain pKey privKey -> do-            pwd <- arbitraryPassword-            chain <- arbitrary >>= arbitraryCertificateChain-            let cred = (chain, privKey)-                pkcs12 = fromCredential pChain pKey pwd cred-                r = pkcs12 >>= recover pwd . toCredential-            return $ Right (Just cred) === r+            name <- arbitraryAlias+            testCredConv privKey+                (toNamedCredential name)+                (fromNamedCredential name pChain pKey)     ]+  where+    testCredConv privKey to from = do+        pwd <- arbitraryPassword+        chain <- arbitrary >>= arbitraryCertificateChain+        chain' <- shuffleCertificateChain chain+        let cred = (chain, privKey)+            r = from pwd (chain', privKey) >>= recover pwd . to+        return $ Right (Just cred) === r  pkcs12Tests :: TestTree pkcs12Tests =     testGroup "PKCS12"         [ testType "RSA"                        "rsa"-        , testType "EC (named curve)"           "ecdsa-p256"+        , testType "Ed25519"                    "ed25519"         , propertyTests         ]
tests/PKCS8/Instances.hs view
@@ -1,9 +1,12 @@+{-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- | Orphan instances. module PKCS8.Instances     ( arbitraryPassword     ) where +import Data.X509+ import Test.Tasty.QuickCheck  import Crypto.Store.CMS@@ -35,5 +38,13 @@ instance Arbitrary PrivateKeyFormat where     arbitrary = elements [ TraditionalFormat, PKCS8Format ] -instance Arbitrary a => Arbitrary (FormattedKey a) where-    arbitrary = FormattedKey <$> arbitrary <*> arbitrary+instance Arbitrary (FormattedKey PrivKey) where+    arbitrary = do+        key <- arbitrary+        fmt <- case key of+                   PrivKeyX25519  _ -> return PKCS8Format+                   PrivKeyX448    _ -> return PKCS8Format+                   PrivKeyEd25519 _ -> return PKCS8Format+                   PrivKeyEd448   _ -> return PKCS8Format+                   _                -> arbitrary+        return (FormattedKey fmt key)
tests/PKCS8/Tests.hs view
@@ -97,5 +97,9 @@         , testType "DSA"                        "dsa"         , testType "EC (named curve)"           "ecdsa-p256"         , testType "EC (explicit prime curve)"  "ecdsa-epc"+        , testType "X25519"                     "x25519"+        , testType "X448"                       "x448"+        , testType "Ed25519"                    "ed25519"+        , testType "Ed448"                      "ed448"         , propertyTests         ]
tests/Util.hs view
@@ -3,14 +3,20 @@ module Util     ( assertJust     , assertRight+    , getAttached+    , getDetached     , testFile     , TestKey(..)     , TestIV(..)     ) where +import Control.Monad (when)+ import Data.ByteString (ByteString, pack)+import Data.Maybe (isNothing)  import Crypto.Cipher.Types+import Crypto.Store.CMS  import Test.Tasty.HUnit import Test.Tasty.QuickCheck@@ -23,6 +29,20 @@ assertRight (Right b)  f = f b assertRight (Left val) _ =     assertFailure ("expecting Right but got: Left " ++ show val)++getAttached :: Encapsulates struct => struct (Encap a) -> IO (struct a)+getAttached e = do+    let m = fromAttached e+    when (isNothing m) $+        assertFailure "expecting attached but got detached content"+    let Just r = m in return r++getDetached :: Encapsulates struct => a -> struct (Encap a) -> IO (struct a)+getDetached c e = do+    let m = fromDetached c e+    when (isNothing m) $+        assertFailure "expecting detached but got attached content"+    let Just r = m in return r  testFile :: String -> FilePath testFile name = "tests/files/" ++ name
tests/X509/Instances.hs view
@@ -6,22 +6,34 @@     , arbitraryLargeRSA     , arbitraryDSA     , arbitraryNamedEC+    , arbitraryX25519+    , arbitraryX448+    , arbitraryEd25519+    , arbitraryEd448     , arbitrarySignedCertificate     , arbitraryCertificateChain+    , shuffleCertificateChain     ) where +import           Control.Monad (zipWithM)+ import           Data.ASN1.Types import qualified Data.ByteArray as B import           Data.Hourglass+import           Data.List (nub) import           Data.X509  import Test.Tasty.QuickCheck  import           Crypto.Number.Serialize (i2ospOf_)+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448 as X448 import qualified Crypto.PubKey.DSA as DSA import qualified Crypto.PubKey.ECC.ECDSA as ECDSA import qualified Crypto.PubKey.ECC.Generate as ECC import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Crypto.PubKey.Ed448 as Ed448 import qualified Crypto.PubKey.RSA as RSA import           Crypto.Random @@ -49,6 +61,10 @@                       , PubKeyDSA . fst <$> arbitraryDSA                       , PubKeyEC . fst  <$> arbitraryNamedEC                       --, PubKeyEC . fst  <$> arbitraryExplicitPrimeCurve+                      , PubKeyX25519 . fst <$> arbitraryX25519+                      , PubKeyX448 . fst   <$> arbitraryX448+                      , PubKeyEd25519 . fst <$> arbitraryEd25519+                      , PubKeyEd448 . fst   <$> arbitraryEd448                       ]  instance Arbitrary PrivKey where@@ -56,6 +72,10 @@                       , PrivKeyDSA . snd <$> arbitraryDSA                       , PrivKeyEC . snd  <$> arbitraryNamedEC                       , PrivKeyEC . snd  <$> arbitraryExplicitPrimeCurve+                      , PrivKeyX25519 . snd <$> arbitraryX25519+                      , PrivKeyX448 . snd <$> arbitraryX448+                      , PrivKeyEd25519 . snd <$> arbitraryEd25519+                      , PrivKeyEd448 . snd <$> arbitraryEd448                       ]  arbitraryRSA :: Gen (RSA.PublicKey, RSA.PrivateKey)@@ -193,6 +213,26 @@ curveSizeBytes :: ECC.Curve -> Int curveSizeBytes curve = (ECC.curveSizeBits curve + 7) `div` 8 +arbitraryX25519 :: Gen (X25519.PublicKey, X25519.SecretKey)+arbitraryX25519 = do+    priv <- X25519.generateSecretKey+    return (X25519.toPublic priv, priv)++arbitraryX448 :: Gen (X448.PublicKey, X448.SecretKey)+arbitraryX448 = do+    priv <- X448.generateSecretKey+    return (X448.toPublic priv, priv)++arbitraryEd25519 :: Gen (Ed25519.PublicKey, Ed25519.SecretKey)+arbitraryEd25519 = do+    priv <- Ed25519.generateSecretKey+    return (Ed25519.toPublic priv, priv)++arbitraryEd448 :: Gen (Ed448.PublicKey, Ed448.SecretKey)+arbitraryEd448 = do+    priv <- Ed448.generateSecretKey+    return (Ed448.toPublic priv, priv)+ instance Arbitrary SignatureALG where     arbitrary = elements         [ SignatureALG HashSHA1   PubKeyALG_RSA@@ -211,6 +251,9 @@         , SignatureALG HashSHA256 PubKeyALG_EC         , SignatureALG HashSHA384 PubKeyALG_EC         , SignatureALG HashSHA512 PubKeyALG_EC++        , SignatureALG_IntrinsicHash PubKeyALG_Ed25519+        , SignatureALG_IntrinsicHash PubKeyALG_Ed448         ]  instance Arbitrary DateTime where@@ -218,17 +261,26 @@         let arbitraryElapsed = Elapsed . Seconds <$> choose (1, 100000000)          in timeConvert <$> arbitraryElapsed -arbitraryCertificate :: PubKey -> Gen Certificate-arbitraryCertificate pubKey =+arbitraryCertificateWithDNs :: PubKey+                            -> DistinguishedName -- issuer+                            -> DistinguishedName -- subject+                            -> Gen Certificate+arbitraryCertificateWithDNs pubKey issuerDN subjectDN =     Certificate <$> pure 2                 <*> arbitrary                 <*> arbitrary-                <*> arbitraryDN+                <*> pure issuerDN                 <*> arbitrary-                <*> arbitraryDN+                <*> pure subjectDN                 <*> pure pubKey                 <*> pure (Extensions Nothing) +arbitraryCertificate :: PubKey -> Gen Certificate+arbitraryCertificate pubKey = do+    issuerDN  <- arbitraryDN+    subjectDN <- arbitraryDN+    arbitraryCertificateWithDNs pubKey issuerDN subjectDN+ instance Arbitrary Certificate where     arbitrary = arbitrary >>= arbitraryCertificate @@ -262,6 +314,25 @@  arbitraryCertificateChain :: PubKey -> Gen CertificateChain arbitraryCertificateChain pubKey = do-    leaf <- arbitrarySignedCertificate pubKey-    others <- resize 3 $ listOf (arbitrary >>= arbitrarySignedCertificate)-    return $ CertificateChain (leaf:others)+    rootDN <- arbitraryDN+    otherDNs <- nub <$> resize 3 (listOf (arbitraryDN `suchThat` (/= rootDN)))+    if null otherDNs+        then do+            root <- generateCert pubKey (rootDN, rootDN)+            return $ CertificateChain [root]+        else do+            rootKey <- arbitrary+            root <- generateCert rootKey (rootDN, rootDN)+            let dnPairs = zip (rootDN : otherDNs) otherDNs -- (issuer, subject)+            keys <- vectorOf (length otherDNs - 1) arbitrary+            certs <- zipWithM generateCert (keys ++ [pubKey]) dnPairs+            return $ CertificateChain $ reverse (root : certs)+  where+    generateCert key (issuerDN, subjectDN) =+        arbitraryCertificateWithDNs key issuerDN subjectDN+            >>= arbitrarySignedExact++shuffleCertificateChain :: CertificateChain -> Gen CertificateChain+shuffleCertificateChain (CertificateChain []) = fail "empty certificate chain"+shuffleCertificateChain (CertificateChain (leaf : auths)) =+    CertificateChain . (leaf :) <$> shuffle auths
tests/X509/Tests.hs view
@@ -66,5 +66,9 @@         , keyTests "DSA"                        "dsa"        1         , keyTests "EC (named curve)"           "ecdsa-p256" 1 --        , keyTests "EC (explicit prime curve)"  "ecdsa-epc"  1+        , keyTests "X25519"                     "x25519"     1+        , keyTests "X448"                       "x448"       1+        , keyTests "Ed25519"                    "ed25519"    1+        , keyTests "Ed448"                      "ed448"      1         , propertyTests         ]
+ tests/files/cms-signed-data-detached.pem view
@@ -0,0 +1,120 @@+-----BEGIN CMS-----+MIIECwYJKoZIhvcNAQcCoIID/DCCA/gCAQExDTALBglghkgBZQMEAgEwCwYJKoZI+hvcNAQcBoIICFDCCAhAwggF5oAMCAQICCQD3DetZLCk3mzANBgkqhkiG9w0BAQsF+ADAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE4MDcwMTA5+MzUxNFoXDTE4MDczMTA5MzUxNFowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt+cGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtBG2kKWfYT1jUXmb+DlrkD3Wfw0rKDjTdjjSswM2HggMtEbiutc+cxnoW+DaeP1SzSdY7NXlaqvzftCPL+vzJNaRM4HpG/XLqIdub5hzisbrwN1/Q77zmUvr1ka7NaXKZDuuepORJsNh5lHkp2+2VR0O0pQ3zLNjX8IVAGkdpoE5iMCAwEAAaNQME4wHQYDVR0OBBYEFPyU0yv/vEPO+p1kMDzLvtT6U+gYOMB8GA1UdIwQYMBaAFPyU0yv/vEPOp1kMDzLvtT6U+gYOMAwG+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAn2yZT/WNupO34PxVdWU65X9q+mCE4AtZKLbRa0iSkkEY961oqDiMVWiaN0sPVeV1GoNCz9J8lLKaEkR8b0F4KlJ0K+44V+5P0Hvb1UnKV2SsNxkmNbxwjO8Q+9aNjd4IqxAnSyQ45yMN4K5x1COJFXQETJ+ApFWhLZxgA0hqnotQGcxggG9MIIBuQIBATAuMCExHzAdBgkqhkiG9w0BCQEWEHRl+c3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzALBglghkgBZQMEAgGggeQwGAYJKoZI+hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMzE2MDkwMDM4+WjAvBgkqhkiG9w0BCQQxIgQgu747Zx6FPf4woOYFlDZvJPAvMeok/0ZRdD/WDHPN+aCIweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUDBAEWMAsG+CWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYIKoZIhvcN+AwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcNAQEBBQAEgYCz+vhp05pvQNr25On3965Z+JXk6auQV9tl1bxI39jxIPxykJgoVKIC5b6FIMnzpPzZA+I66bK6Tx7dY5PTRcYImjkKgFpubNObwhFaNGeOyKh+W+o7Kic/Zy2nlWwGVMQ3ad+fCDfp0iR46yzoL94pWXrt+2DSQ7QzMRWvKy0SvNdWw==+-----END CMS-----+-----BEGIN CMS-----+MIIEegYJKoZIhvcNAQcCoIIEazCCBGcCAQExDTALBglghkgBZQMEAgEwCwYJKoZI+hvcNAQcBoIIC1jCCAtIwggKPoAMCAQICCQCEbw8q6JYKTTALBglghkgBZQMEAwIw+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTAeFw0xODA3MDEwOTM1+MTRaFw0xODA3MzEwOTM1MTRaMCExHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBs+ZS5jb20wggG2MIIBKwYHKoZIzjgEATCCAR4CgYEAu1pOSXz76spaDcSOsEx8vzAB++xC1LNppXIxeP/mQHim7ADlf5uNPAavPt1Ky7kkJHbdxj9sWXUG+BvgC1CSNoEkO+0b4A2OTkYdfgCjk8CSjNK4LgsLZcNDW+MMa4G7AooGAxTL+WbvMxMKmWly9eDODd+VVN2M7S2UCKPlIkoDjcCFQDcpQZHCPm2jIkDaMNf3WJggezQxwKBgDKLgJAp/VlG+YYflUdnJzcAWS04vg+FQaRIQhocrzaUJQ/ZfPCYtjCozt3gL9KNwFghXBklo082s+Qwin52NgohB2fBKIOsvBPX1VU8oXmtQ4vIN1L6kmPZkeZvEkOJdVojHBJehU1CDh+UDT+PEH3joy2s3N9AC1SE1UplbHVBsE5A4GEAAKBgH/LWxi51YZsuzDiIkXdqPAB++jiJqfccumYpG4m+nIbCaBDCMXxxKmfr/uQBcYFIbdAU4bK4WL7joHjCjXeA/DDz+zjD3wqFSKax7xFKbBmAK0eC0UdwVlnx9qeJ9Ypduk58+CBWqQkyFJCIlf0aJFlKE+y8BEvuPC6Hlu2nTc9SHeo1AwTjAdBgNVHQ4EFgQUTAOzcwRJKUUZaP/WO6LOlOLC+YwEwHwYDVR0jBBgwFoAUTAOzcwRJKUUZaP/WO6LOlOLCYwEwDAYDVR0TBAUwAwEB+/zALBglghkgBZQMEAwIDMAAwLQIVAKC36+0syuqwTPBd7MLJ7NBJS10XAhQRKGb0+d0Q8f19UOAr5ROp10HSxdTGCAWowggFmAgEBMC4wITEfMB0GCSqGSIb3DQEJARYQ+dGVzdEBleGFtcGxlLmNvbQIJAIRvDyrolgpNMAsGCWCGSAFlAwQCAaCB5DAYBgkq+hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xOTAzMTYwOTAw+MzhaMC8GCSqGSIb3DQEJBDEiBCC7vjtnHoU9/jCg5gWUNm8k8C8x6iT/RlF0P9YM+c81oIjB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjALBglghkgBZQMEARYw+CwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIAgDANBggqhkiG+9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDALBglghkgBZQMEAwIEMDAu+AhUAqTVaVNHUEv4tM84y/QLPWSr0cP0CFQCvlZDKBaa0qlBWE2NyJ574gyXqkQ==+-----END CMS-----+-----BEGIN CMS-----+MIIDRwYJKoZIhvcNAQcCoIIDODCCAzQCAQExDTALBglghkgBZQMEAgEwCwYJKoZI+hvcNAQcBoIIBjTCCAYkwggEvoAMCAQICCQCX+mbx0uA+ADAKBggqhkjOPQQDAjAh+MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE4MDcwMTA5MzUx+NFoXDTE4MDczMTA5MzUxNFowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl+LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHYqvg1E3p4xXBUK+72zfa3M+LmJIkfZDsYqRsZwRXJ7TzRxsbeVgBDVy3Zj6Y4ZSB/dK7jxs54KTECoDcXLvEASj+UDBOMB0GA1UdDgQWBBRT2nMGMWO1FT6OJajOnO9gp1hHVzAfBgNVHSMEGDAWgBRT+2nMGMWO1FT6OJajOnO9gp1hHVzAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gA+MEUCIAvA93qcJRkeyRffr9IKzYQXkZE30qnCBzlEfmfUClagAiEA0GQKzKiwGTv6+uV1pwjprqyI7h0dObEESk3KMQRa+5zcxggGAMIIBfAIBATAuMCExHzAdBgkqhkiG+9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20CCQCX+mbx0uA+ADALBglghkgBZQMEAgGg+geQwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkw+MzE2MDkwMDM4WjAvBgkqhkiG9w0BCQQxIgQgu747Zx6FPf4woOYFlDZvJPAvMeok+/0ZRdD/WDHPNaCIweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZI+AWUDBAEWMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAw+DQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwCgYIKoZIzj0E+AwIERzBFAiEA0zBWegmlEC9Zp6Nql04cSiCD0ZdlFzecaGoF+xmctUICID+cT8s9+dF/iJdrrTKh4+UmuAXO+PMfVRXwjP1TFBq4z+-----END CMS-----+-----BEGIN CMS-----+MIIEPQYJKoZIhvcNAQcCoIIELjCCBCoCAQExDTALBglghkgBZQMEAgEwCwYJKoZI+hvcNAQcBoIICgjCCAn4wggIjoAMCAQICCQCgEjyokIqTbTAKBggqhkjOPQQDAjAh+MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE4MDcwMTA5MzUx+NFoXDTE4MDczMTA5MzUxNFowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl+LmNvbTCCAUswggEDBgcqhkjOPQIBMIH3AgEBMCwGByqGSM49AQECIQD/////AAAA+AQAAAAAAAAAAAAAAAP///////////////zBbBCD/////AAAAAQAAAAAAAAAAAAAA+AP///////////////AQgWsY12Ko6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsD+FQDEnTYIhucEk2pmeOETnSa3gZ9+kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLesz+oPShOUXYmMKWT+NC4v4af5uO5+tKfA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////+AAAAAP//////////vOb6racXnoTzucrC/GMlUQIBAQNCAASRj2SKQoEgjz6+KYQx+zT4ayoZgTTCBYWY3EiaJl5ok0cqIU7SBaPdN+qTVczCMjUvrflk8wDmKmgdrVauL+H/l0o1AwTjAdBgNVHQ4EFgQUgvJMdTkQVKStsiGY0/eY/NB/nHswHwYDVR0jBBgw+FoAUgvJMdTkQVKStsiGY0/eY/NB/nHswDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQD+AgNJADBGAiEA2aHN4T0QP85J2+vz2Rmpk+DEJzwb8Fv9DGj4gwcNiScCIQDwUiXY+FcmbsiZc8oQxVcMKHXelMQUbkPq12rSnAOJNaTGCAYEwggF9AgEBMC4wITEfMB0G+CSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAKASPKiQipNtMAsGCWCGSAFl+AwQCAaCB5DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEP+Fw0xOTAzMTYwOTAwMzhaMC8GCSqGSIb3DQEJBDEiBCC7vjtnHoU9/jCg5gWUNm8k+8C8x6iT/RlF0P9YMc81oIjB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjAL+BglghkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMC+AgIAgDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDAKBggq+hkjOPQQDAgRIMEYCIQDtHZXf8lH3LgaqQR6AiCXWWn1rs+76ilGdbZtWy3ZVtgIh+APRXOwsrj/zkjQ4gSvCgi0swPjceDvNqayJ+zc0c5ecz+-----END CMS-----+-----BEGIN CMS-----+MIIEOwYJKoZIhvcNAQcCoIIELDCCBCgCAQExDTALBglghkgBZQMEAgEwCwYJKoZI+hvcNAQcBoIICFDCCAhAwggF5oAMCAQICCQD3DetZLCk3mzANBgkqhkiG9w0BAQsF+ADAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE4MDcwMTA5+MzUxNFoXDTE4MDczMTA5MzUxNFowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt+cGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtBG2kKWfYT1jUXmb+DlrkD3Wfw0rKDjTdjjSswM2HggMtEbiutc+cxnoW+DaeP1SzSdY7NXlaqvzftCPL+vzJNaRM4HpG/XLqIdub5hzisbrwN1/Q77zmUvr1ka7NaXKZDuuepORJsNh5lHkp2+2VR0O0pQ3zLNjX8IVAGkdpoE5iMCAwEAAaNQME4wHQYDVR0OBBYEFPyU0yv/vEPO+p1kMDzLvtT6U+gYOMB8GA1UdIwQYMBaAFPyU0yv/vEPOp1kMDzLvtT6U+gYOMAwG+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAn2yZT/WNupO34PxVdWU65X9q+mCE4AtZKLbRa0iSkkEY961oqDiMVWiaN0sPVeV1GoNCz9J8lLKaEkR8b0F4KlJ0K+44V+5P0Hvb1UnKV2SsNxkmNbxwjO8Q+9aNjd4IqxAnSyQ45yMN4K5x1COJFXQETJ+ApFWhLZxgA0hqnotQGcxggHtMIIB6QIBATAuMCExHzAdBgkqhkiG9w0BCQEWEHRl+c3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzALBglghkgBZQMEAgGggeQwGAYJKoZI+hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMzE2MDkwMDM4+WjAvBgkqhkiG9w0BCQQxIgQgu747Zx6FPf4woOYFlDZvJPAvMeok/0ZRdD/WDHPN+aCIweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUDBAEWMAsG+CWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYIKoZIhvcN+AwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwPQYJKoZIhvcNAQEKMDCgDTAL+BglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCAV4EgYBS+HLzcxWJqmnUPO4IpwKEf5Xsh359SPjFzcJUMQcAI/NswyRKN85WRqYrWuXbqBRX/+d1dO7o4hZEcU9y8VvWo6sZCba+teSvUG8qYmu13KE/UXYVW1ZIWbNWRsDhxDmlaL+PGIwcx501+iCuwogqFYvGzualDuMc+/ErbSQ6ZVN5Q==+-----END CMS-----
− tests/files/ecdsa-p256-pkcs12.pem
@@ -1,305 +0,0 @@------BEGIN PKCS12------MIID6AIBAzCCA+EGCSqGSIb3DQEHAaCCA9IEggPOMIIDyjCCAncGCSqGSIb3DQEH-BqCCAmgwggJkAgEAMIICXQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIFH0q-sY0hx1QCAggAgIICMPbvE6ijIbACCeQG+LUnydn7HbMHXK0C9fl3+gT6I3j01OiG-b9RAobZtnwLV1Zbvgw/hUMG3YMKJZ00ydG+C54Cvn/Ta7O4NJfrxJXq2IPL/0RZW-GtnbIMm71NL5c4wmbbn3nPXWXbcpTS2TC7bnLc6N+ePjKybtynqdFKcRQxiiLqtl-yzlh7nym+kJV5KSdaCOSUArU699Ymk99OAkYrJhceIJo6v3A0oxxTfYnoJjuxVIM-sVo3GTfK+kFORJ3Spxia5vCPoN301hfOaieCrd1RpCaB6Q1Y50nJbZI/bjQ0kjEY-O+7Cnw5XYl2AHHCTzcQ4mwzDDD75r7E4R7tA/XuTbmC6mFB3TyBFtsUtXZtCRFWD-vrao0ae9sY7GRe//GTI587ZQAXRaLgmbEnUGMkMubsrh/xwPSEDtpzvPtZW81Qf2-krkO0rwb7FJTzajoklpsgFgVw/VMKNElAO8YSNJ+JX/sagldvfjNHUIQMSgUignW-dQzSPBRwdOfdjqwgaYVwcNJ0JpgdMZpomNgJmEB10GJblaKXyzbRSH7AuO8Mrr7+-lrriXEinopnJCAb6YO+Wzc1bsSeVLu2ofeHblbMWb3yofLCuTtvJXR06Xs+ulIjx-J/sGL/P/JPt8LsnPIrGd67dHy3ChmTtBPNGjDoOzZ+EOOQmLTnsVHR6LDABTaXvR-+U+MHjc9QIpWVxaE/osyLhBPOHIDqyIGAX1WqHa4aPUzNxwgXn6TMgIfoUZGMIIB-SwYJKoZIhvcNAQcBoIIBPASCATgwggE0MIIBMAYLKoZIhvcNAQwKAQKggbQwgbEw-HAYKKoZIhvcNAQwBAzAOBAj7Rnc8QaBBAgICCAAEgZA1tAy4JyoPS9EXxO2e7Vow-MtDsVwyJzuitmD44ICjPxrJgmyKBFtGDLXBfadLI/VuD9oqlI8lP176hTYwwM+72-/hwKGXmJfP1s2r7sH28sVmbJO2dYjQXtaanyQ5aDGaFMbcMiuPK4y0vB98TyguD9-S+mWoT/MVE9F20xh82gxQycHhpdT664UxeA6YBJru4sxajAjBgkqhkiG9w0BCRUx-FgQUSzL+96khVinAc/v3rkVpgmeGLu4wQwYJKoZIhvcNAQkUMTYeNABQAEsAQwBT-ADEAMgAgACgAZQBjAGQAcwBhAC0AcAAyADUANgApACAALQBuAG8AbQBhAGM=------END PKCS12-----------BEGIN PKCS12------MIIELwIBAzCCA/UGCSqGSIb3DQEHAaCCA+YEggPiMIID3jCCAn8GCSqGSIb3DQEH-BqCCAnAwggJsAgEAMIICZQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIzybx-opbqZnkCAggAgIICOJEdutTDfLmiKQEJCzoT33Z6rEqdA2STzm1qL2Ir9RMTl2wL-zKqqMT9ORFErYaYuIjatj4uSxn//JumW7/fD1d1fJeY+0KajhGpMbp7hmU/wn4mL-nyUCcSqI+2VoXjQlmH2yRFTgfSBU91+4caap4HN/wGefTZsRG6EwyXkHS7JV9Ocu-qm1SYBRjxIcMt/4JSMNYVwuSDffFEIE8U9FjnuJKTl49GFdcKwBRJVUyTnYOlPse-YtFw7Wt/FESxG1IxGV9bU+Xf/SR1AfVlRdqofoCNVavwd8GFtWEfUdXlHjqo2ufN-fTLTzxQSGNakGknJHDxQFgUUAkrB+KyWBf+YItGfb5DPp4R3TJJvBH6lNTq1w8j4-aSncKElU/Wo75ULUmZqKra2B7gnIZ9JdTxijRaHothgwd6zDRVlNPSct+xDg4mwO-zFtjg4AEyCcK/NjbUsq17qbKXDyLkfwA6P3xQc3gepgj6xG+102WOMu0ZD25ucNa-Te0By0PjkrSrP2kW7PFnBWZFI7abNP00JRvQ0gLuj3ax8JW7Qie6DEOXOPqeeljK-gPguFb9GAX3o6lZed4K9dIMXB4btyl8mA6PIIPr23hHnyRcqno/Eq1FQQUwmN+XE-o7s22Tc/y/DQ4fgiMZkFZhW7DweRPrx4flqRmp8QIaG05wGgMW9q+u7s5Djew9kA-UkHmTAoHbSWal2XXyqkDfbKQp1QdC5fTxOmdFZdQ+xdh/jCknwvKVaDcp0Xs5ZWz-ZICgq4AwggFXBgkqhkiG9w0BBwGgggFIBIIBRDCCAUAwggE8BgsqhkiG9w0BDAoB-AqCBtDCBsTAcBgoqhkiG9w0BDAEDMA4ECNIHMplVd+ksAgIIAASBkBjKaNoqEBSQ-vnDxd3cf49x2w5su0kKScrVMXRjKS0EdhvkEZ/Ybmg9ro4VHvkmTmWv54eY+no3r-UbvVJ2Btct5LQdg6WgvB1/v1wJGuOH/Y8f9toZ+FjsLsOSkpzOP21iVX8pLJOTvK-F4dGPyyLGx8xtZZOeA8eevzFlrf99uwlzCkv4lG4ZS3FSKgCdb5DUjF2MCMGCSqG-SIb3DQEJFTEWBBRLMv73qSFWKcBz+/euRWmCZ4Yu7jBPBgkqhkiG9w0BCRQxQh5A-AFAASwBDAFMAMQAyACAAKABlAGMAZABzAGEALQBwADIANQA2ACkAIAAtAG0AYQBj-AGEAbABnACAAcwBoAGEAMTAxMCEwCQYFKw4DAhoFAAQUtk2zS8R+SstIRhAjP6LT-PfQ0BEUECOhUkmBy8gJ0AgIIAA==------END PKCS12-----------BEGIN PKCS12------MIIESwIBAzCCBAEGCSqGSIb3DQEHAaCCA/IEggPuMIID6jCCAocGCSqGSIb3DQEH-BqCCAngwggJ0AgEAMIICbQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIC5r4-uzdR0uECAggAgIICQHbHiFhHuPenOSHe/jkUiyUJE2ItJlxxYGWJylZI41Sz5Hlg-O75NCrJq4WFytBD7qeFt4bdJ5u3L4TmusDhZfjgi1iW2dtPmp+rwksikWITK4eHU-d8HJOx+PqtvtN+UI9rTObfVxg8SPQ76vR3NLaQCaLtqNwGmAWSzOlivDdXCoumez-VG7m7CX3luLSqhG2aag8EhV15dBc2eIj5Vd6zSRlybV06/MEdasAoeiARAFK4c+e-mC7gK1sxvECWnFwCVrYyWEn1EDLvGYjpUnLfHmTu+fV2LFCTxWmV7gXM0tODaYDM-hsHtMw9zlSAhVa2iBkL37mjPeSM9q6Un6NWw645+8DyTbWxGkrrpm1vGc/xJYp0G-5t05jzrFY/AQ9USVELw0PzBFwQiGsNxAHkR8z6k9wxfIES9IpdFAbNBzWmurlEtK-5OXP17hEmQlSc4EaOivN17g0oJxRcTXglHN6zwiP8iE5OfJehHgGWynytJ9wnN+W-fkQrX3NNVBxXT7jPMgVHkWBWytZeex6Wy4wUb2uKSYd3I3XEMGAFzg/3bcfHoTBN-FRwPCFX4DyrSkLsFDv4uAwtNkZjRzRbV9mW9hW3chkcNvH7HfS8plsm19LhayqHC-LnaM45zOmU1R7zPL269HUq4IZ/qEGspwDoN6wTRhbiXZMaYQ7BA1XoNVsTBCzijT-FxIO7Oi+IJNffXtSW6AuRtq7I99p5iSVrB1QTZr1pVD9KAsCzbGGMnUrei+LFScz-zuqYmWbsms/WtCqX9TCCAVsGCSqGSIb3DQEHAaCCAUwEggFIMIIBRDCCAUAGCyqG-SIb3DQEMCgECoIG0MIGxMBwGCiqGSIb3DQEMAQMwDgQI+WOkpHQZfYgCAggABIGQ-K6ntubGW+rZ1/c7EqUNvRVnoyxJRp/VG+vEQPf/VoyZpAvYGZPzmVseP2nSTZ9zk-ooElFmVR7tMVi86/wPhwyuotGdpou2s49lx4prH1mjdQY6W+Ybgj5HdI/vdo6Bsr-rjOewC1j4WG6Gz6lTQr+ADA/21vn/f2ooxHGf3Uh53yPe+MfCV8RYg6c5fhG5CaI-MXowIwYJKoZIhvcNAQkVMRYEFEsy/vepIVYpwHP7965FaYJnhi7uMFMGCSqGSIb3-DQEJFDFGHkQAUABLAEMAUwAxADIAIAAoAGUAYwBkAHMAYQAtAHAAMgA1ADYAKQAg-AC0AbQBhAGMAYQBsAGcAIABzAGgAYQAyADUANjBBMDEwDQYJYIZIAWUDBAIBBQAE-IOKPMwzYN46vB1aOSoIKQSfDo1A3CJH0PZleWonCGwdwBAjCHBkcsCYB1wICCAA=------END PKCS12-----------BEGIN PKCS12------MIIEWwIBAzCCBAEGCSqGSIb3DQEHAaCCA/IEggPuMIID6jCCAocGCSqGSIb3DQEH-BqCCAngwggJ0AgEAMIICbQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIn89b-Z3GPMpUCAggAgIICQGKf889YFGdAEDzO/krxYBRVQaf6j0ZOWH5G/VyKDbyXl8hm-9A4LQtfQa+q+LO8DhIea9AfA6aK7WKuAz5vnQIA4DpFF9r1qmGOFQsIbzB9Jo/ik-wRIp8bylPIdwfbyaUqH+0ITun1NjgdZCpGkV1Onay0uUPZq6St22T6Kv5g/b/x9f-MwaUwikXQVNNfOR+8wrW0djK0MOp9b8zZnmTvUDURK6nJEaDMeQgP0CFcb4F9Xv2-8r5v56zSU6edePQuxS5uALWSZQg+7TqQlD4d5nwT0+wkIU4QnDMHcZJGwblyunsy-d5RTJADCrky0V4BTz8l7/avwK66CkNdw2k2dbN3uJhaIjSZuoWkHmD/iAYS7fAQe-fHwBElEqOPS+KLsBtdOtwyhWnbqIFd1cL3p+ndZn5UEA07PWSHfpg+SejMK8RRff-HSBGVgdgvbXKH6PKrz/NoJVb7BO8xKJjIdVjC4GOVNlazhFdUNXryMgEC9n/tg+U-EZJNcMq74jR9tinCnE6sU1IAP8CpOtrMTpCXnnMIBl3JQCgfem7ahLDfY+3YIp8B-1Q0DnDG441e1iTcOBaxcOPWaTN8Lyc3SDDgE5D9YWr+Ez3s5OALtb0jy1AS6ODRJ-v1wgunlHqpapH9iA+ArOa4VTXp60MrUqhCdGkMv/EmiDRJ7P4Se/f2Tl7epCPCl3-/SEy/4EMX0hhpPGAAASaWhhDtTUsuNrX+G71NeJiyDFh/k55Wu3djblBkMszVsgY-P26MwEnl451bVHGzQzCCAVsGCSqGSIb3DQEHAaCCAUwEggFIMIIBRDCCAUAGCyqG-SIb3DQEMCgECoIG0MIGxMBwGCiqGSIb3DQEMAQMwDgQIlX6PffUsFMMCAggABIGQ-JI4+RRpUEffRaz3qRdXoM1w6rHCxyefKTxfRp/jsDvsHkBEOeWFE5cKm+3BXEm0T-RMGJlxB52ceYvVA9/Bo0StUKuxYw5dsbftBhjSsH/5i6i8DGVOCFycD0C3B1hn3e-8Xt+kQSCrZcc6vVd8gjiPvHhynPPnn2lli0NlE5j0DH+as3sxjtfEdPeQunKSFE1-MXowIwYJKoZIhvcNAQkVMRYEFEsy/vepIVYpwHP7965FaYJnhi7uMFMGCSqGSIb3-DQEJFDFGHkQAUABLAEMAUwAxADIAIAAoAGUAYwBkAHMAYQAtAHAAMgA1ADYAKQAg-AC0AbQBhAGMAYQBsAGcAIABzAGgAYQAzADgANDBRMEEwDQYJYIZIAWUDBAICBQAE-MC8cVO84hC0SnVOrqDYNQMWor1IyVaU/CaepQiEq949CrUONcjVM4BfgxoVse2WB-uAQIkqDilpIjT5oCAggA------END PKCS12-----------BEGIN PKCS12------MIID/QIBAzCCA8MGCSqGSIb3DQEHAaCCA7QEggOwMIIDrDCCAksGCSqGSIb3DQEH-AaCCAjwEggI4MIICNDCCAjAGCyqGSIb3DQEMCgEDoIIBpTCCAaEGCiqGSIb3DQEJ-FgGgggGRBIIBjTCCAYkwggEvoAMCAQICCQCX+mbx0uA+ADAKBggqhkjOPQQDAjAh-MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE4MDcwMTA5MzUx-NFoXDTE4MDczMTA5MzUxNFowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl-LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHYqvg1E3p4xXBUK+72zfa3M-LmJIkfZDsYqRsZwRXJ7TzRxsbeVgBDVy3Zj6Y4ZSB/dK7jxs54KTECoDcXLvEASj-UDBOMB0GA1UdDgQWBBRT2nMGMWO1FT6OJajOnO9gp1hHVzAfBgNVHSMEGDAWgBRT-2nMGMWO1FT6OJajOnO9gp1hHVzAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gA-MEUCIAvA93qcJRkeyRffr9IKzYQXkZE30qnCBzlEfmfUClagAiEA0GQKzKiwGTv6-uV1pwjprqyI7h0dObEESk3KMQRa+5zcxeDAjBgkqhkiG9w0BCRUxFgQUSzL+96kh-VinAc/v3rkVpgmeGLu4wUQYJKoZIhvcNAQkUMUQeQgBQAEsAQwBTADEAMgAgACgA-ZQBjAGQAcwBhAC0AcAAyADUANgApACAALQBjAGUAcgB0AHAAYgBlACAATgBPAE4A-RTCCAVkGCSqGSIb3DQEHAaCCAUoEggFGMIIBQjCCAT4GCyqGSIb3DQEMCgECoIG0-MIGxMBwGCiqGSIb3DQEMAQMwDgQIQrFaHl+yq4oCAggABIGQQw+qnMDWAzFgExmo-n/TFr+viTkGMvUHp1ku0i/84uJ5qZh8M8VxNz+kHMdqpICLo1QQNjzUfxNZGcdrG-9d4cUPTBJolOnAUgIJsB92V7gbrsfou+sTWe3Tcg71hVQIgGgrJAKuKFIyTByviX-m4Dm0TzfxmNJwK3RU8YuLnJkm3QHmGUYPJ3IJqFWNGYVXiNbMXgwIwYJKoZIhvcN-AQkVMRYEFEsy/vepIVYpwHP7965FaYJnhi7uMFEGCSqGSIb3DQEJFDFEHkIAUABL-AEMAUwAxADIAIAAoAGUAYwBkAHMAYQAtAHAAMgA1ADYAKQAgAC0AYwBlAHIAdABw-AGIAZQAgAE4ATwBOAEUwMTAhMAkGBSsOAwIaBQAEFAKLUf6d7uvD54jrsI9j+DpK-2CroBAhjf4XZU/977QICCAA=------END PKCS12-----------BEGIN PKCS12------MIIEkwIBAzCCBFkGCSqGSIb3DQEHAaCCBEoEggRGMIIEQjCCAtIGCSqGSIb3DQEH-BqCCAsMwggK/AgEAMIICuAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG-SIb3DQEFDDAcBAiggrBG/lAmGgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME-AQIEEHMNnXuYwnMbjOSFmm5xEl2AggJQRvqqPaLjdtyoaWULLjqu1g0bPuZuwsV7-yBRI3/2sTrFfqBasHMl2nQOA3udFVtlCq9lUuaI/vyDRTqlM2xKqjzvoeFVCoXEB-qGBfYMPova4443zYYczv+WJLi56Y0z/rOwon9tZfQWWCPrRhhp1w/CqXLeuK06R1-/+AvNUGK9ihu3/DZ9wHTGHcfs7ciLe4U9YubiVHXwLzhMxZMvXgfs8ogXcDlckrv-xKuOl7Fww92TKni8IpYU1PWpeD5Qb5IYveaEGF7ELw4USHfYG38G7pBnj1Yy4+KT-+SC9rx5ZCbuELGtoWTJlcvI54kkLZ6sn+ZqxVVxOZql71f1nUqLiJmt5V8r6kgEt-+ARauqbrNMcPm4kPJmTADCLYLtYyriQyqbvb/X0bXgAevND/mrl3XTrqj1SwXRxP-+380fXmNYlz28QFgM1Z16RjxLfGzo/mMQx/vT27oGbwOkar8+wqcR9acZnjuVDEr-Fq3b0ski6mn6gM73H2F4xzv30UD+BUxgg/YJbcgLiCNBNMy5cr06j4ebvDKGWbgj-qAZBShbgOzpYMhUpX+KfN0eq7k6CHn9ukn3s6A/blak6oy7Lf+pQfg/ZxubAgA1I-q6Z19FH3QS6WYOaMvXLXKNe6T4YLtnu154iV430GMCEwhhFVL82ieuCKKGWXJey1-yQZAxr4m05rouZ6ul8rd1bxTsh6AZlZ5ylIi6pk+h7HD1cZxYthvPnrkWrAI849e-WYUu5VBykQUMTt6fUUltQiDTez9FtC1RN2+9qFrJacnxFm1PEPD0QjCCAWgGCSqG-SIb3DQEHAaCCAVkEggFVMIIBUTCCAU0GCyqGSIb3DQEMCgECoIG0MIGxMBwGCiqG-SIb3DQEMAQMwDgQI5D7IqrU5mUICAggABIGQh+/XDU/wsMCf7Gh0SY+9okbbdgLO-Hj3PlneCOMeJMJoIK2upJUU0MHety7Xeu8Xv4jMcspCTTqbIBwJtkMdZo7M7JlZ2-LZ1DgG6xRXYLTYUvWK5aTXZqwISEriTyWNetpBrfuCK48tvo/FP4WmmDHWwVUcTB-5g/Rsvdcu/c8chKWD+m4jtaXG1fO7kW9qcK9MYGGMCMGCSqGSIb3DQEJFTEWBBRL-Mv73qSFWKcBz+/euRWmCZ4Yu7jBfBgkqhkiG9w0BCRQxUh5QAFAASwBDAFMAMQAy-ACAAKABlAGMAZABzAGEALQBwADIANQA2ACkAIAAtAGMAZQByAHQAcABiAGUAIABh-AGUAcwAtADEAMgA4AC0AYwBiAGMwMTAhMAkGBSsOAwIaBQAEFJOaeethrlOXj6Bd-WTDuuxNrbxOTBAjuBPHa1rrDLwICCAA=------END PKCS12-----------BEGIN PKCS12------MIIEagIBAzCCBDAGCSqGSIb3DQEHAaCCBCEEggQdMIIEGTCCAp8GCSqGSIb3DQEH-BqCCApAwggKMAgEAMIIChQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQUwDgQIout0-wIR7cikCAggAgIICWC7qqjpGtFkZwlykv0FzU3eNU0c0L8Et3yaH1frNkhpnX7U0-oksJ55AsKNCYHxjCGfBi4/pFLJjzmIm7jw1EUNpw7Tz6/8xwov9JS7VhHLgIJ+Ak-SGUOEcrGBEtCWcwpovXlkqoawvmM304m81b2wSjNeOD+gD6q1nx0s6SOoy6ZyUK1-bSGnK/mdR4WEs2KeQ/MZK01UR5XI+E4qX9tmF9yL3IU0yokqWY91XKBR+vS4+pRd-YxVjKZYoMtuO+QdczAQSa4F36Lf/IXW7N45HeCnijCVWz/9r7S2KBjlDQ1duWF5s-nHucl6VTX5vr41hcTqWH+DQCi2w0y2zbAyqwz8xyronh2OF0mus25rKBuXaxo6BQ-C4msotkVk7K1E8d9Mo9HbrbjTr6q7q2XRTsD+YKFa8TcDFUTZ5gYsmpUAjevR3Hc-S5Db0nlLyMXJAasBFvuMfNKoCqWkgm2abrYAd6ROcIK9Mf7Vvs/Q++ED6PDDsew4-7TIleg7lVJ/VvK1Bf/r7w6ISR3R8G5rpjYX1Z5UKBx9JGdRsE92qfI3cpXy7jTo7-EDlvIXK1EE0iVOAbDRphQw7z682nzdqAuwwSUvLYIlekxjx+Trtig7xLOrnl0ARI-ivqCwqCWf+BeD6fC6jqyC75s97PRO1CwyrwjermHqZ29LAbzlsp3su4UgE/q+8zT-C1HeK6uSnOvsAXCMGyLI9N6w+IK0vMhYY2v5KRqJE9UTdJ/99t71rWooBkZfemF6-Kk7A8Q0a3x7bTyN1QgOlO9cIIeHkSYn65ObsWankSYm4Nff+LzCCAXIGCSqGSIb3-DQEHAaCCAWMEggFfMIIBWzCCAVcGCyqGSIb3DQEMCgECoIG0MIGxMBwGCiqGSIb3-DQEMAQMwDgQIQ0XzzqZ/BJkCAggABIGQH/cfMsPknp6dnjzVV9kj1rxmqLvkxB30-jCqwxsaOVJmRdPc+lRU3KTOeltejSHgPP8K/v2F8VqQZA0yXniLoNsL4Rlx1GLp7-qXbxZ/oD+3+ESLJyTjB74uVpi9L56ojJpvjYtZfHzlMBZtEpZBYO6CJh9dRVJt1i-ODacdYaAmvTZ+Gh6mT87AKugwDuLiQhWMYGQMCMGCSqGSIb3DQEJFTEWBBRLMv73-qSFWKcBz+/euRWmCZ4Yu7jBpBgkqhkiG9w0BCRQxXB5aAFAASwBDAFMAMQAyACAA-KABlAGMAZABzAGEALQBwADIANQA2ACkAIAAtAGMAZQByAHQAcABiAGUAIABQAEIA-RQAtAFMASABBADEALQBSAEMAMgAtADEAMgA4MDEwITAJBgUrDgMCGgUABBRXtjhP-ScsUZNfJPD+XGatgpre9TgQI6/JvBnA5OYECAggA------END PKCS12-----------BEGIN PKCS12------MIIEYAIBAzCCBCYGCSqGSIb3DQEHAaCCBBcEggQTMIIEDzCCApcGCSqGSIb3DQEH-BqCCAogwggKEAgEAMIICfQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI+UgP-cFrlQ6MCAggAgIICUJKytD8kl8Tjhl80ilyB/NxrxFUoFLx3ySy5PVMNEzpOM2Oc-bHDvAVle2ehL1ZR8c812a4EkNeKAMaRi9ymiRRrN+FRqmjfVqt+lPQidwXkpnS/b-Nd3nZvKQVsp3sQzeNNkBMobt1eUxA6mzp92/MZSQPvAS0tU92aNDj/PqiQzDA17e-qEn2Ns/u8JSzvk2d2fRWYeAdgibFebFxyhI6rdJdenE9RNf7JgALJiGZghM5f7NM-734cz3v1eP+CrxwFq0igXQW/qDqky2xe0p1k0uFwVjfs0SfajtBBSKXEBA88bTnf-+NCNiX7pzpp/eu8re3oRvx2o17FDxdezLj1Ez4zp1yM+O35abSpAIRW+pmSukqLy-1t6zfETxlSOwKGEGuX81rZzag4OT0zlmayuXijQfdn2Uv1dYoMvJRbyfIOG5Wbg4-6rWXIIy5AG6+nzzYWjMBh2e7Rts1rFrhx0hTklBpLJLeQfTnlJrwQN0SIdUIN9gg-4oOFRqrS0BUdYYi8qhizOoLeju1AUZFDQ0ShBC0kXAAYwsKhxvBANYQDi1+h0MY+-EAXJLGoSKgXqRB3F0jsXFlcGg3bPTfY8gAs9QBGYv3jfcy4A6fxNbXvUFOTRfoUg-ZGDXZLysvqarb45E36Mr3BtbvOCKLG5hmazAPteUpdcKepgAM8WBoWWIfoPcOh29-XRlIx+x93kjrQjDoo2EH8SmfWrh5vYIsDD2K9fozg844hZqq763SDXVYQ+BmTJWu-4XHaEu6wCyGX6AeE7flDl6Bc/1th2yfw8KQsF2MwggFwBgkqhkiG9w0BBwGgggFh-BIIBXTCCAVkwggFVBgsqhkiG9w0BDAoBAqCBtDCBsTAcBgoqhkiG9w0BDAEDMA4E-CNNFERdaKdyRAgIIAASBkAcC2AyLZzIegZfk/G8eA0EMOyjaneHbbqDficRNg0Ok-PkQXZEWNddUdgBggNwo9Y4rGUooHpxnbDEsmgh/iEvnr0UoqZ72uLGOidNnjqfy8-hOE0jotsl7LI/hIlH24Jniz4jwOoYhE94iEM1JfD4PvRNrbnBfLUcJ5zGCCptdXq-EVloYf236K3tHSXRADes+DGBjjAjBgkqhkiG9w0BCRUxFgQUSzL+96khVinAc/v3-rkVpgmeGLu4wZwYJKoZIhvcNAQkUMVoeWABQAEsAQwBTADEAMgAgACgAZQBjAGQA-cwBhAC0AcAAyADUANgApACAALQBjAGUAcgB0AHAAYgBlACAAUABCAEUALQBTAEgA-QQAxAC0AUgBDADIALQA0ADAwMTAhMAkGBSsOAwIaBQAEFB+JS38oqVLgJ/LV3sE+-rTlusdnvBAhD3Yt2Q836RAICCAA=------END PKCS12-----------BEGIN PKCS12------MIIEBQIBAzCCA8sGCSqGSIb3DQEHAaCCA7wEggO4MIIDtDCCAn8GCSqGSIb3DQEH-BqCCAnAwggJsAgEAMIICZQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI36cy-Nl2ETYkCAggAgIICOJDnrzg0U67gXWn/F3HiUgpK5ZhdaTzoSYoZPEVgCXlx04fa-WADXrFywcbF2HTU2wiasHws+9DHqRRsCKSzd7DcWiniSREXenhhl6ZjVUL1/kDX9-uVh8vpetzyIv5eO3LDvJw3TSbBBptH4vO1FCJSXNYA7vQCnKVBOxSpli4TW7PUjZ-TcQMofIHeFT11HCtlZ4cdV6NqePvc9m0UkyU8bxi/2ynqIyf4YtQiIBa5NsxXWOt-ZaKw2o7dKK7Hr8STnuLaSdJOkY7qh2TBeFfXNe9rjG7bUM0cjO+ELdHoNM3aJ1l2-T5Zx6kLIn99CFLUB89yt6Z4MideotQQA8K04uwh23vd3FDwj9G31u2Cm/sEgeO/g-5dataSR/Eo4+cDTLE8qlNjTBJdjlz2qVVPr/x0eMbMAc/WnDmvtSZEE7dzQfawtw-TubaChURw6zdKKwNOFesNvfDyIN98lsLuOhAWgNwj+gcsvA/MFBnodPTLiUEEMu5-R5oIjXmI2JYTcaPMrQX3dCgGCrOW3ddq2at1d+Lbnhvv2jPMCpesBjMPwv555GNj-O7tculdYrKYh/0WiqRE8AF9M/vlPomNIk0o6SXTGew/7VRFSi4O5zeOmSrxbiu2x-MORhd/fycw0eB/OIKQUYOxBGsFLMc3HNwGSFtVqRJTO6v9F0e+QtHlcD2VWTKqR4-w+wexcarA6N0qlDOCBGRAS3vJ5d7YQJTfV/n/4SnLDmWIhsrvmwwNiie+Eea6ABm-JpzknGQwggEtBgkqhkiG9w0BBwGgggEeBIIBGjCCARYwggESBgsqhkiG9w0BDAoB-AaCBijCBhwIBADATBgcqhkjOPQIBBggqhkjOPQMBBwRtMGsCAQEEIN1BcbroS4Rr-SL98OeRm0Hzo0POI2k/SkGxKn2EObv3/oUQDQgAEdiq+DUTenjFcFQr7vbN9rcwu-YkiR9kOxipGxnBFcntPNHGxt5WAENXLdmPpjhlIH90ruPGzngpMQKgNxcu8QBDF2-MCMGCSqGSIb3DQEJFTEWBBRLMv73qSFWKcBz+/euRWmCZ4Yu7jBPBgkqhkiG9w0B-CRQxQh5AAFAASwBDAFMAMQAyACAAKABlAGMAZABzAGEALQBwADIANQA2ACkAIAAt-AGsAZQB5AHAAYgBlACAATgBPAE4ARTAxMCEwCQYFKw4DAhoFAAQUjqGpn9xugean-d4IruG5yTl2yAl4ECEFTBPWXAettAgIIAA==------END PKCS12-----------BEGIN PKCS12------MIIEiQIBAzCCBE8GCSqGSIb3DQEHAaCCBEAEggQ8MIIEODCCAo8GCSqGSIb3DQEH-BqCCAoAwggJ8AgEAMIICdQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQImZIF-k9BCNzQCAggAgIICSKgLbeRZWeciVOjIMDaAOkLV0MP2cVad59E767+6nlAsNflO-qidcopd5snW9RUQ+eprSx9DAS2Rhqax1H3QWskpGDIVdD91LOHUhDUqx2akwDD2C-sOPlooaaTIoVZ9zbTpl3JDdJ7NHdRyHoWtbCu1xEvK+akcqK2fLvI8xwDSwU/DRE-RU7ELR7GX/jW+WuOvBdBGC8RyetVlC93sUjW7Cfl1QOTbJ28A/UCgD9zANXmMRez-nEiRw4iG30UX71EBVUJdNvLREqSF2l463G/dTphr4ak5IoLACHuQlVYfOWYCevXN-4R9LDY1pB56XedHkPATcrXAwxy/gapdIdYdVbUAIWUQzxXxtmZgeoBboVG68Ceq2-5zsM/Ktl3jnnAwFPx2EuuLHV+5yaNOGZFkz4NsFc0lSMlI24tf6J6GIXo4Crupw/-dywPn4dX68ILjdSiF9xf8lB4xysVEHfG8CWJvCg5KLNPgvRszCxbCDoSnZX4VnSy-Ra7Dg7x4ZWQyYCgt6wLoqEuiFTxNhtOI5UMxZDK08UhKdKOPWAEZnuZgnUC2Nnpd-6Bj/Gtd4N+SjqATYCACQDTIP9uDj7iMsTBmIwK3WjPF3hflSsh6ekIF1J/t8uG3/-lIDfhZnwlkj3Tli8pub5A/kIyPHyhYxUZaY+7QTwQB1Fws4v9K+nj6QCduBuV6or-MObYTnrlzz/sqKxohlfaiZroiJmYicjgSHiziBo/fE/miUgSWANin5quolzB/HY5-eZ0k04F9Im8xLFCA/PIEzt+SV3XNMIIBoQYJKoZIhvcNAQcBoIIBkgSCAY4wggGK-MIIBhgYLKoZIhvcNAQwKAQKgge8wgewwVwYJKoZIhvcNAQUNMEowKQYJKoZIhvcN-AQUMMBwECIpOphpdh/ohAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBAgQQ-Juna4/Yf0krRZ6mE2g9HKQSBkF8wVESzEUBn1Dw4eEU+2Hu5/Zl2pk082BCqAd+W-ig4eOZ1w2E0/TWSxYf3mzoLmwCrqn0GSmPfpK8D8ulP1MU6fr89YI1QPL5PhV4WU-NHI2Og5dRagkT8wcptOM4CVHivgbrceTMWwrp/SWZsBkSxBQEl+RkCwo7mx2KUYP-NosSC1cwEDgEmYa85ok6Kbk++DGBhDAjBgkqhkiG9w0BCRUxFgQUSzL+96khVinA-c/v3rkVpgmeGLu4wXQYJKoZIhvcNAQkUMVAeTgBQAEsAQwBTADEAMgAgACgAZQBj-AGQAcwBhAC0AcAAyADUANgApACAALQBrAGUAeQBwAGIAZQAgAGEAZQBzAC0AMQAy-ADgALQBjAGIAYzAxMCEwCQYFKw4DAhoFAAQULemygmsvFuzhulUVO5CzQbDYIzYE-CFkpGLVxDD7KAgIIAA==------END PKCS12-----------BEGIN PKCS12------MIIEYAIBAzCCBCYGCSqGSIb3DQEHAaCCBBcEggQTMIIEDzCCApcGCSqGSIb3DQEH-BqCCAogwggKEAgEAMIICfQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIrVmM-qkskLmMCAggAgIICUGUvn0KkHcJIGDZGDtYe5gbUX8HB5Ug3j4GF/tOT4losFD7A-YEQVsZnW1ywS5eemKuw/1oCsQ7YaJf0WFUTk3J26C9yQqsnQWxvPVGs8qOqHQoi9-C9L1KTRpAvWj6envRFkk9skr5YVNm6FqShPc/LbWBf5JJCw/G4yR3zvPZtpkKaaz-BMb7JWVRbyXuA6mbr7QaeymY8Zg602TNB3Awk8F9c/tiM6dk3/SSdZFL1aH7Fiog-W9qDJ+3MoLKwwZcBjZxknuimNWQgjsXihcog6idwaako5a8Vzzh+9Yr2u4Cd4jWU-TPY5DkspEpU0Se9lmjn4cKdX3AdNQNSPbn1UpBagirbM8/lfUkkbVGezUnTusy5Q-Yc7rTTJ6WDWpilP89ZMTIEnriLvF92GhqQvb6Q6IJ1prD/iuJG8RZEZ7Rdd/phJP-55dadyVlQb2mNOQb2zR6ikqn8F1WX+cEW609Xv/fsURblUFndhP+2DsxBbmdbrpP-rZoboP3S5eyK53dSoH0jE+dDwLmTCRaBHRBdTHEaN7/qNefK+TaSJ7/ZhkT6QrZx-JwM+7T0gWLTY19QSEbQDW+nmw/mT7YF9Y4AJJRjKWONZDlzz2JOuft+6gDqfivkz-oJt0sNklvCv+fwxIxQJINIEshhiqTmCiYb//LDlskKZw/X71FE2UeekmA5zdCXhW-ezaBLXifFYKHG3njl3IUxLSfZXz0SVmCXZb8Zvn0pOfAz5WQqIXak9nIgSMzh1cG-OQGVeNxiKE7M1rd+Iwx7S/y8QXIVhAUeXCyOjY0wggFwBgkqhkiG9w0BBwGgggFh-BIIBXTCCAVkwggFVBgsqhkiG9w0BDAoBAqCBtDCBsTAcBgoqhkiG9w0BDAEFMA4E-CHluHyg/OSArAgIIAASBkAL+Y4024MWAZN/tETNU9kDN4pn7kRwdvF/tE5qWJblk-BDoYOzUydhQgQNTxQloKxJwwQ/aYG/fmCZy9n7XF4wvSXjrIIqQ1fTiwsg5hpO/I-NfEBjAQMDQe2PZZn0v6Z2drzagtKvP+5dcKR9Rm4hdhlT3+sTQFPMuqE/naW0RUu-VgyufgDvrdo3eGkNV0cNcDGBjjAjBgkqhkiG9w0BCRUxFgQUSzL+96khVinAc/v3-rkVpgmeGLu4wZwYJKoZIhvcNAQkUMVoeWABQAEsAQwBTADEAMgAgACgAZQBjAGQA-cwBhAC0AcAAyADUANgApACAALQBrAGUAeQBwAGIAZQAgAFAAQgBFAC0AUwBIAEEA-MQAtAFIAQwAyAC0AMQAyADgwMTAhMAkGBSsOAwIaBQAEFM1Mf/02bHn5IeF/chYj-fTf91X+wBAhxy4p/PLmCegICCAA=------END PKCS12-----------BEGIN PKCS12------MIIEXgIBAzCCBCQGCSqGSIb3DQEHAaCCBBUEggQRMIIEDTCCApcGCSqGSIb3DQEH-BqCCAogwggKEAgEAMIICfQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIgDZr-yESnGsUCAggAgIICUI0Xi/tNudGKSqEhCSc2kdmly1P1UspJQiROTByJAGEGpiw6-MbKJ6HhfxJ2vJvtlz+1iA2iYKA6LnFUGaU57aBFZMjbPjTs4K7gILwHpMCtv+DPx-u4zROBGqYw5ufsvuEFQSaSeWZSwRYMPM8yVfQTZopdHSHGIhxbKFdtRMlnVOVyNh-p+IGo0yOsELkRIxrmhdawmNsjo3ua5pSRb8nitrKIxc+LatWpp4wbwb/Z5KwaMOf-1Xz1zKDNL4BlZVI9c3bPWM/sYWoLRxl4LF8xQK6G1lk+s9RR9n9i2r8BDZ2c2LRI-X7KVfUhi+BWlkRzjwkO8w0pL5acoc3hYg/rvm1TCh7b2sSE1JYP5xZziOl2TSaWh-1FNrAASb84F5XDKnA8DL+jSRevMERfP+uuWX26R3bO6D/Y/qDegq63VRSdFjSVDn-zxwUxrb93GLSbLBJ4vW14R2Dl5WrwLzCXvP3jTTjfDVg74Av9ODE9QqEw5eABJx1-uCUM8zhXx8NVzsRQ8U9aUOhJXK+HXVbrN4gRMZ5fqKtHeELG9mjV4guZ60cd8u4X-fcdzyOMQzedbGhoxHl3vmcSbJb+DUgzTF+Sw85V1nqV7oPRgJgx11ib74cdAfs4f-GSlnEY/wdP20unPGGTS5k9I7GoUFLM2bPXeue983JIEFrZXefdSXUyY8ug2UFxMl-Ta/7aFFTmz5SBfqXUuZX48FOL1GlUvl+8o5ISbTGNvt8jwunxWJcCJCWMgQ2s82k-PJpzqJlz8sE+4xz8qdTW1MR1HELw+v6bkRPDHH0wggFuBgkqhkiG9w0BBwGgggFf-BIIBWzCCAVcwggFTBgsqhkiG9w0BDAoBAqCBtDCBsTAcBgoqhkiG9w0BDAEGMA4E-CFdmR0tc6KJ+AgIIAASBkFi0RoWkd1KWd22KzsQ0u5Hl/3W8n1GgVKe1aAVx/YVo-W+/PNJPpFZ3r9X1rx39aWMnDVa6c5wsMQ5wJ2vVzobrdEMZkvxYRXoAeyoxRRHR3-01SFdSgqE3i6D/25ashBZWgyjHiwFd1xpK/uac9watUdcJ1JvFP5ZZ38T85E6IPF-hnXk/GYbvedO9Wtklvbt7DGBjDAjBgkqhkiG9w0BCRUxFgQUSzL+96khVinAc/v3-rkVpgmeGLu4wZQYJKoZIhvcNAQkUMVgeVgBQAEsAQwBTADEAMgAgACgAZQBjAGQA-cwBhAC0AcAAyADUANgApACAALQBrAGUAeQBwAGIAZQAgAFAAQgBFAC0AUwBIAEEA-MQAtAFIAQwAyAC0ANAAwMDEwITAJBgUrDgMCGgUABBTtLc+uuG76aVNLcrEgZ+1M-peI07AQIkVLG3UDA77sCAggA------END PKCS12-----
+ tests/files/ed25519-encrypted-pbes1.pem view
@@ -0,0 +1,32 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFcwGwYJKoZIhvcNAQUDMA4ECLtv2JchVem9AgIIAAQ4cVbXStmw+OANljhnJppr+YqzWhH0zLXu+SohCnQ5cb+R0AsffwhT118Fedfi6w2b9RAL8EhvLk3Q=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFcwGwYJKoZIhvcNAQUKMA4ECAHGvbJ4ns9TAgIIAAQ4GXgJkD9MzX1OzBP/Joo3+5ORepS0PLaDUk6yYU7jfD9ov3JZXnxxAyQsgfwIJcJHE0lDm8IL5qUA=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFAwHAYKKoZIhvcNAQwBATAOBAhN+aPvlVgjrgICCAAEMEd+AOxYLmDMzouXzEo/+Z4XCVMZrBGGrQIYSL3d7r/sqMHklH+SC0JtdrVHzJyCuhQ==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFAwHAYKKoZIhvcNAQwBAjAOBAjCex3tcisjiAICCAAEMAgOe/PJs/m84I+QR2TY+ojYndqNX4lWnQs6rl5YK94QB0V5rDZWN9YvmZGmX+/JwkA==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBAzAOBAgmU1ToGVo9cQICCAAEODaxyq/B/HNpbpsDCL/w+fYohLS5QA3s5OIKHo9uELz2GrTS7aI7idAEPY+iFo+eFD6Ql5gy7GVkb+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBBDAOBAgiVSOpDt2NGQICCAAEOLpMO1GOWTMQQ6MN0F+u+LyTzAruvs3lbad5wd0EyGE10m8c+2vXSsl6DMEkaXrjRovsCebZwVzK8+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBBTAOBAjdfsUUZ9k3AwICCAAEOERTz7/lepT7BrKWJ1L++IilhhSxYjjeHzCNYGoz8+WIRhcykLlZHnAcVTNXWkhNjMf8MEUmfh9My+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBBjAOBAhwossvpdyt4AICCAAEOBQ2Yh3yt8lrCppVUJGv+W7cdL+6gH1H6dL+cGeOsVnpFUNwnEQS92OLQYENAO4B9vqYgOhnq3rDF+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/ed25519-encrypted-pbkdf2.pem view
@@ -0,0 +1,39 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGHMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAhYNXHDae7APAICCAAw+DAYIKoZIhvcNAgkFADARBgUrDgMCBwQII/ySuK0RdrIEOA/iQWXFUgSRi59Z/okt+ab+6coDwGEB6ywo+ysTpEwSTMICq6xWcp+BnACzs1FWrlYS/wWDxzeKy+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGKME4GCSqGSIb3DQEFDTBBMCkGCSqGSIb3DQEFDDAcBAiKSUa4hjdNcwICCAAw+DAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQICGrlfM+5tqAEOAtOZfvoEAPcFs3P+k/D46jpfdBroO7vU3Ki/6+2gRWrnjWWL4UyYoMkT8ZHVk4uOa/4mvnakkU16+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGLME8GCSqGSIb3DQEFDTBCMCkGCSqGSIb3DQEFDDAcBAhMNKAxJTPu/AICCAAw+DAYIKoZIhvcNAgkFADAVBgkqhkiG9n0HQgoECHiEUPBMhqlQBDjXy5F/zsQz/cVA+F2QfS7D6zLuLjwPsNGX0m3+070d3usNVdyogbViSEhcbvTmOtjp0DB0BNMPr0A==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGdMFkGCSqGSIb3DQEFDTBMMCkGCSqGSIb3DQEFDDAcBAgrm/IrFyklqwICCAAw+DAYIKoZIhvcNAgkFADAfBgsqgwiMmks9AQEBAgQQCsvDlOh1VOEXubKKcLMWugRA+GRNH/la87zQtE7qPP8SsTQgY2/f10rEHLMZ80ysfVq0oGmRfrN79QZETYLdsckWt+HmJyslMCCCswvM8o6rt1wg==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGSMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAhuP6SU4nsQrgICCAAC+ARAwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgE6BAhgU/sm47TbAgQ4wCSj+KK1+rp72Ucw107jbsrffscMFx8NoYYK2RzxnwDZzjiy23bBQlxlEZ94Y2BzNT1Hi+WHJBcls=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTMFcGCSqGSIb3DQEFDTBKMCwGCSqGSIb3DQEFDDAfBAiXc8D7B3BAMQICCAAC+AQUwDAYIKoZIhvcNAgkFADAaBggqhkiG9w0DAjAOAgIAoAQIQBrLaUdGxWoEOJvN+6DN4SgKuaFDap+JvqTmx2yKEEr0+0HbUMIVUOZXaiw1kVmLCR/jaoLUJGz6rjSGv+9RPCtrvw+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGSMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAjxi1l3WQPQPAICCAAC+AQgwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgF4BAgj4x/sdHkh0QQ4eEY9+UwHGaNE9AsBfjCUTjVRzvrJedZvbPTNIAjipMYW3uYzVcg5I+GE/4V7yhKUzBipS+RLxOGtk=+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/ed25519-encrypted-scrypt.pem view
@@ -0,0 +1,18 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAgale+OTVw8jgICQAAC+AQgCAQEwHQYJYIZIAWUDBAECBBDYon9Yg2PXUt8HMaRZW75QBECKmy2N93fMD8+d+dpTnG3bXuLETOhn/SGCG35Pgzvp3gZoRR/tksLS0IBXdmbvjRGmMiWcMZ57Zcd1a+PJvyO9p6+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAjA3W23hPPCvwICQAAC+AQgCAQEwHQYJYIZIAWUDBAEWBBDIidXH5KqQ5BsAcSf+VpjxBEBuN9xS3T/q39Sc+gVXmgBTsfD9XI1owhsiG8r49GF6iXztnyJPMIYDMkjsnk4ypdffSTXlRpRUv8lIj+vGE7LUbr+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAgrfV0wImuOLAICQAAC+AQgCAQEwHQYJYIZIAWUDBAEqBBDglOp/wHw9J8ue05R8hroDBEBP2X9ptHWTKrb++WwyGW+s2MuVulF4In3RjYNld1ZYQeeQk4Vnqwq9t+2djgXsKL4hhwEfhk0wCH64b+z0EbHIzA+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/ed25519-pkcs12.pem view
@@ -0,0 +1,269 @@+-----BEGIN PKCS12-----+MIIDSgIBAzCCA0MGCSqGSIb3DQEHAaCCAzQEggMwMIIDLDCCAj8GCSqGSIb3DQEH+BqCCAjAwggIsAgEAMIICJQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIGFEN+2HUjTJMCAggAgIIB+MiKzSuuknXax1GE1S9jQ2WfuaNablKCYNUB6wAQJkqiwUjC+DhW1hAcz+4o8Ow1NmuGNiTR/9RKp9ZJmU/GFQCfKInNRL/4WzgQ1l5pKIYYKmKgH+YA5Ci1bnVA8SEXvQxto7K2maWrxvuSxLktHo7ajYfI+Ze5qiGYET9TqWXRiql9ld+oxbtTFDkSzNC4VxqU8HYkZFx/4ld6oKJZ71QaSZgf4t9+BkHNfOknOhAp39WKOhy+3UBmopdBUPpqJAdAPQmR5BUeRibIC+3RllvRW/FLOW/FwfyVoQESUu1u2nl4C/8z+t3i14EVcKuNbBvz0Qja+4fO+p6e9+q6clFVYCU6UvZQncKX8mRdCSGDZOla018L4+Lf4SauXnnyyGk8bEI6kqATtkPh/pK3nVBcjboX437+ZbGRj4H3viC7rBijaTizlx+gb8FhVaryk5bwlzBJQUrtcndd5WdjCYhZPMaB/xFxqSotWWzvxya3A0Z/SgBKj3I+6Dbv+hGIGACVfcT7k9W7pPC8QaESOCZJOqzckik4h9huEyyDQyvxKa18l+RXA9AQ+uop3gtG4WRiosSxzgNPh2e5RGVM40AY3ktrsuilZP9skDMF5viTin1C4p7rPxD3s+dzs5augp0VppJTA74czDilJs3f/u18PPQS2r92ckcM836SDbAjCB5gYJKoZIhvcN+AQcBoIHYBIHVMIHSMIHPBgsqhkiG9w0BDAoBAqBaMFgwHAYKKoZIhvcNAQwBAzAO+BAgHTXORITxbIwICCAAEOHln0Ez/AkpJiI7hvzh/t01Rsl0foaq6Mt28OJNcaLLH+CoyMtg2TTSDWlxrwcTc8A2Ug5mf5jzFPMWQwIwYJKoZIhvcNAQkVMRYEFBUJzG3C+AW0LFRvdr7FoWoQ3wIq6MD0GCSqGSIb3DQEJFDEwHi4AUABLAEMAUwAxADIAIAAo+AGUAZAAyADUANQAxADkAKQAgAC0AbgBvAG0AYQBj+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDkQIBAzCCA1cGCSqGSIb3DQEHAaCCA0gEggNEMIIDQDCCAkcGCSqGSIb3DQEH+BqCCAjgwggI0AgEAMIICLQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIyxVd+QGw+wjsCAggAgIICABwReIhjPnmxn4HZySDDd8+z43vHNIlplmHIKyKuWkdPZNHi+knA/nqebK2gdLx5hcauatxUVEQCaob/wzbxPfK86NDbf/2LsZqm8TiZbMz69sqxY+2M9QpjRY5WKPnbA8s0uhnsdm5UxCHF4E2omZA6yqPcGIioleCJ+d3b7U74v0uxn9+SjdAY6fw2mJo155sTZHaK0GucDmzz0h4N8RzDhURTwxMy9eXEaBy07rRfDJ1iFTB+YTWNOu++XcwvzWxV8h6EEEBbSE0kuLbnPwN4L8kcxZV/DhlW6QbiwVOJWL4dyQ84+FzxkdIC5lzhvw0lzOGM1WMI27fvFhHXzmRDTPJRZhsjLAZjeJwlw6CDY+04VNi5k+PzoC/mQgRjf8BDCWMyHcHyimrlm43OuIr4lW3qfLP2bUHue6f8ppZq6napC1V86P+bTsYjoDrS/gFwjpieA8JjTVV9yWu+IRsA22th/ensplyfwU/wadDOXJFLvJrP1tL+EavSjphDR1+VSXI3Xp8k/g+RJzxd2P0sTcUyqCTGDCJKplYCD8V/azfitlGfretp+SFHqVVV/1yaFAZu+fI1IFN3YiSqeJrjbGeus9hhLs5jsiFzol3udh9whmp09rYCS+6BPXi0m+izvcLgffoLwjidx2hvwSbRjTUTj9iJGlz/Bn16djzDgyTlJVOm1fMIHy+BgkqhkiG9w0BBwGggeQEgeEwgd4wgdsGCyqGSIb3DQEMCgECoFowWDAcBgoqhkiG+9w0BDAEDMA4ECLuL8XSr9Wt3AgIIAAQ4sIhPGPPiVe7pC3a9af5gSKLNIdbbdYXg+DvnsCB76bZQDkWXfe3+eKwU0RjX4Tf7sEKBSitUQdyMxcDAjBgkqhkiG9w0BCRUx+FgQUFQnMbcIBbQsVG92vsWhahDfAirowSQYJKoZIhvcNAQkUMTweOgBQAEsAQwBT+ADEAMgAgACgAZQBkADIANQA1ADEAOQApACAALQBtAGEAYwBhAGwAZwAgAHMAaABh+ADEwMTAhMAkGBSsOAwIaBQAEFHPqC5cmwg+m5BlOEYjZTtH8iD8wBAj8srMZ8dqO+YAICCAA=+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDrQIBAzCCA2MGCSqGSIb3DQEHAaCCA1QEggNQMIIDTDCCAk8GCSqGSIb3DQEH+BqCCAkAwggI8AgEAMIICNQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI+m6G+P6kpcWcCAggAgIICCL51B8K3PIhgUNA8wpwp4dad0LOS8IbVvrN522K3/urilvQl+ZuxvHgWhbg8vm47LYCZD+M8k3RHXSR5Rdd9qCOD44bVwFlM8Fq0yYg9hMjhGPgMk+SYKGErU2OppX3FJPAL397BlWTMpvpvH9t0nEnvAcEOEFHWytTN8/80Pl2j2lseGS+OEMCahKZ5yfLcNDl+SGA2hfi34he62i80LWWJye5UzPEKJgRU2KAIR1OMciGnpmN+E90ozaZbgrZqnwI3bYYpAGMPBWwgpp4CVvBpJxT5wkw82TaC9WXNutXQmBWri1cC+HXrkeqG7lO2E79AkJANVq0LSaVhcDb5lYoqif7oom9W7gA3SoFQdN8j6LE9XyHTc+wGCP4jspnA66fGECOIerziF7b4EfQEIFtmX6DSpE9WCjL9MoU8Ftxt1UzY6WzXSi+LrsSUb9R4sdkzjzkgw67r3UAyvO845qnXxNaUtCPPOq7pCSOwPNaecqSwNxQgxZg+xo0yyZbm3bjyrLK1suvhegk9TANTNjmyaYhQ/DyO07+V3ee01rMYVOre0hWliQRW+bfYh9cGLFKfQcMmwRv1bCUV0D8keOL3iTxQM7ReyIZeJDIqHfDckhWgbvpBs4dBX+GFEimDEsLfXjFPK8M+z3V9Syi5ywigwICyvXIat1NspD0xcTkp4BhKCVNct0m2Mo+7r2LA3IwgfYGCSqGSIb3DQEHAaCB6ASB5TCB4jCB3wYLKoZIhvcNAQwKAQKgWjBY+MBwGCiqGSIb3DQEMAQMwDgQI5KEgbc5PTYACAggABDhD49uTQNK70taieGdPG4+F+jDIhYIWX4BqI4Op+i5RrLmZLLh8IIwS9u54L2mU8iLixcUGJM7hwdjF0MCMGCSqG+SIb3DQEJFTEWBBQVCcxtwgFtCxUb3a+xaFqEN8CKujBNBgkqhkiG9w0BCRQxQB4++AFAASwBDAFMAMQAyACAAKABlAGQAMgA1ADUAMQA5ACkAIAAtAG0AYQBjAGEAbABn+ACAAcwBoAGEAMgA1ADYwQTAxMA0GCWCGSAFlAwQCAQUABCA9iAPHbYTZUVBGKEN8+m6ecISHakuyKxNOMdo9sjrPOWQQI1CwiN3JXU8MCAggA+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDvQIBAzCCA2MGCSqGSIb3DQEHAaCCA1QEggNQMIIDTDCCAk8GCSqGSIb3DQEH+BqCCAkAwggI8AgEAMIICNQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIhOBk+A4W2LFMCAggAgIICCLYRPF4zD4p8DxHKzg+OJXB2KIb7k/3LvCOt5XdpFuicw9Z9+oBVviniBOI9AnzYwIm8lKCtfEazSY7jfRqX3tbLNltN6uBr8EaQM6mu5glgHccEF+LG4A7CNgks85IwFovCj4zT1zE5PrT7B8xUmLlMCGup+dImlmBPvzUaNcqq4O3mg++m4NkCI0qW12LTczdy54mPiaNXaklqQHjHBSEGugtMexm9CWV8t2bXuZsZv161NKj+d9lu2EfVN+GutIervK5MOa0o91+VjdFWh4UW7GjWmlFfvCO92dg2BXnm+J+sSV45+PM3UyM9DhxjJjYpv8E4Qm0AH3ylx/WY+23AKa5LzCokfdrPOfYC4tQWDZhtxclvl+bZ2KDJ9TUaNoXB0pdaSYppgn+AfMTPacgtDRTVfWE7TbnyTT9GV67kNhZeeJnyuP+LwjjGT/F1HqjiQswz3+ssdIjsfXv2M3laCF30kWrsRj+nUx9aFbplovYhm+Bsimo+GBsk+JSV8aWDO5v9WInQ0nX1qdMj70edt3U/jU+8fXrdoGshyJlJL5vhTN0lrUKR+y+Rugp6DGu2Jh4C+8Plzt3rsck+VR1anY3sLqYs4Nm8NTmrlO5W6iVJI96i6Wkbi+kCkQ+8FZAw+a5NvNX9fY1BfFLGCGAcYBJsKXATMP4e5Wu2rpefYMv+gtLBVgVoDh+A5vGr0UwgfYGCSqGSIb3DQEHAaCB6ASB5TCB4jCB3wYLKoZIhvcNAQwKAQKgWjBY+MBwGCiqGSIb3DQEMAQMwDgQIEbbZB/ZOFvICAggABDgz1+uoucIRZUyWxQCnM2Z1+VrIDzkjSRnzfh63gtKAYax2kaYUzpGnZMY4DrYi1WQDTmnfTY7XE7zF0MCMGCSqG+SIb3DQEJFTEWBBQVCcxtwgFtCxUb3a+xaFqEN8CKujBNBgkqhkiG9w0BCRQxQB4++AFAASwBDAFMAMQAyACAAKABlAGQAMgA1ADUAMQA5ACkAIAAtAG0AYQBjAGEAbABn+ACAAcwBoAGEAMwA4ADQwUTBBMA0GCWCGSAFlAwQCAgUABDAzqjArAvU6yK/h2AkT++5m4+jU4enOBGOCCtPDy0+0jaM1EkyDTqYyxoxsQBDXwSS0ECLaGyXHiSBnYAgII+AA==+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDXwIBAzCCAyUGCSqGSIb3DQEHAaCCAxYEggMSMIIDDjCCAhMGCSqGSIb3DQEH+AaCCAgQEggIAMIIB/DCCAfgGCyqGSIb3DQEMCgEDoIIBczCCAW8GCiqGSIb3DQEJ+FgGgggFfBIIBWzCCAVcwggEJoAMCAQICFEInN18eR8QZFfy9Yb39ycd56f8PMAUG+AytlcDAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE4MDkw+MjE1NTA1M1oXDTE4MTAwMjE1NTA1M1owITEfMB0GCSqGSIb3DQEJARYQdGVzdEBl+eGFtcGxlLmNvbTAqMAUGAytlcAMhAPb4Tt742Tc9pS4/MMDFCjYzApuBncDQ5JWS+0QL4ejEUo1MwUTAdBgNVHQ4EFgQUV4Bp7nHkyDQxsCEBe5xqQauBKXgwHwYDVR0j+BBgwFoAUV4Bp7nHkyDQxsCEBe5xqQauBKXgwDwYDVR0TAQH/BAUwAwEB/zAFBgMr+ZXADQQB+hVuzD7hONYSSQAZ60fo3ZBjs0R/guNoOWIGBCgSxaqhs//Np31/mmiZF+bsT67I2lYUyh0ogms4Y2av5dJ40FMXIwIwYJKoZIhvcNAQkVMRYEFBUJzG3CAW0L+FRvdr7FoWoQ3wIq6MEsGCSqGSIb3DQEJFDE+HjwAUABLAEMAUwAxADIAIAAoAGUA+ZAAyADUANQAxADkAKQAgAC0AYwBlAHIAdABwAGIAZQAgAE4ATwBOAEUwgfQGCSqG+SIb3DQEHAaCB5gSB4zCB4DCB3QYLKoZIhvcNAQwKAQKgWjBYMBwGCiqGSIb3DQEM+AQMwDgQIX1+nSa1n/HwCAggABDjgLTq2CBAruDxaYp+1TvD9bU2VV5rzE8J4e20O+1lH1ERi0gHw7nLtjAsxBWVI20NOyalAB0cG4LTFyMCMGCSqGSIb3DQEJFTEWBBQV+CcxtwgFtCxUb3a+xaFqEN8CKujBLBgkqhkiG9w0BCRQxPh48AFAASwBDAFMAMQAy+ACAAKABlAGQAMgA1ADUAMQA5ACkAIAAtAGMAZQByAHQAcABiAGUAIABOAE8ATgBF+MDEwITAJBgUrDgMCGgUABBRTDkUp0GY2M6X+Hz5M0GBAj6ZDYAQI8L4NQjeGa+4C+AggA+-----END PKCS12-----+-----BEGIN PKCS12-----+MIID7gIBAzCCA7QGCSqGSIb3DQEHAaCCA6UEggOhMIIDnTCCApIGCSqGSIb3DQEH+BqCCAoMwggJ/AgEAMIICeAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG+SIb3DQEFDDAcBAjB8PuFLlFLeAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME+AQIEEBPci+1MGZrIWpKRWObpG4mAggIQ0CxiJz+08OHzg/h0QX5++RjcH3HeyNs++C84tjzJOcnRe1xRZ9ritRaPuFN+FrEXn73dKJmTogpXvGGlfxomoHyVuLRl1HBbf+PUz+rhj4NHoRfTVBwqaP3GNO7zOmipqQrMi7wZTtUtxaXgRzsEruHCIe99T+5MUQ+OCLRqhoHh7ukwZlAyYz/dtdnk3Q0GfQAmQjhf1VEsIMdE1YwcQBInA3B08uPFSnR+JG0J7Hs164wU/o7oust8uPxuGnmEJjcQ3DwCIHN+5cLiRmO5qnZc6lGKhJnHGEWI+BoziLgj993gkB/2Ir1RwWsze05bp6XPI6irRUyLOkC4WuSOFzTu2ck3fL+tVgmtK+wUSO93WwWspDNdQX5kR/gIl9WbAYWkCjAhk145dlXRTlJxpksp5taQF4dY3GHXs4+NrjCe5773t/xW1k1kioZL5mIjT2p9MUM2DT5wkbpd9KI76pIB2BEjiAo+cZTJ9e++Ck9JnUrck2gSYg4CloGabMXt9nsJ+V4UpcJm2eXAlswno4cxFmXKlT+GtqzzKQH1+sp7YAxrdqxATlwsyD52rEJo/rKOoFt6dyDcc8PLdXrErfoY1WAl+Z6p4iDp2VYqr+GMx0R5uArjhR+R7rmGqTfwDFgc6pp4HWBwGoQ2ZfWcpQBUNJPaZ4o4uEpd270XNR+mW+BQMXXbsRTUK9AT0iL7vCFVdyPQg4DMIIBAwYJKoZIhvcNAQcBoIH1BIHyMIHv+MIHsBgsqhkiG9w0BDAoBAqBaMFgwHAYKKoZIhvcNAQwBAzAOBAgfExpEdcn3jAIC+CAAEOOoKa1gzF3tuBnPxpeEqkeIrPQl+t4jyzFj11QC2QswhPTcc0NLIisoTllk0+m8VrMAhZzbz7xHcTMYGAMCMGCSqGSIb3DQEJFTEWBBQVCcxtwgFtCxUb3a+xaFqE+N8CKujBZBgkqhkiG9w0BCRQxTB5KAFAASwBDAFMAMQAyACAAKABlAGQAMgA1ADUA+MQA5ACkAIAAtAGMAZQByAHQAcABiAGUAIABhAGUAcwAtADEAMgA4AC0AYwBiAGMw+MTAhMAkGBSsOAwIaBQAEFF0dkSA5il/b6lVIdVS9t3Yh092sBAi2pnHLHUDLgAIC+CAA=+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDzQIBAzCCA5MGCSqGSIb3DQEHAaCCA4QEggOAMIIDfDCCAmcGCSqGSIb3DQEH+BqCCAlgwggJUAgEAMIICTQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQUwDgQIksEU+f1NeOYYCAggAgIICIJSOWj0qBbk5tlV/M5hekb8fYtjFVjbN287fAaWFEHty1EXo+6B1wKULhaIBJdlFDa8feE6icJOtYHwtiGKRAfo2HfAeu4IsHEY9Ejvbq/vT7QTH2+GvKWmE45mZTavedRnSLGBq9/JAOZMvVnIoW5DKji1e+piXP8sTAn7R6QWIELvf7l+LU/u1dWOZGWxWkztLAAU4E/PhAYlVMkOD0imZIYcF1wKhqpZUgzsq53AdOWBghz5+7Y54rULci1kZ9H0e/YvUGsB2ahmJtgO6vCLwi06+ohQ5ONzHPYOaZ+yKiBou+w4I+YeOLlk7zTDOFKAVgEHsg2XSMvFRVnoPDm/UxYzITKlmyu9WWptmQnVtlf0YKK6aE+lirn10ZUR4oO5AxlvNdJyuBOHFb5z0eEGXta+dKse381Y6lRAA2/nA7z5+3fosch+MeU0Jc7D/ZWWCmPOsLzPvWWOGYTxU7iba10iMhEvT55qCCMrYQAEJ45IwWItKCMY+aKiNZythBWiajke41tZYWgsVcl0PcDTlrmqaOcGCKJt797hH9QrfGlnpRZ65dIVY+LR8QSjZJl30doV6ga56Y2oktPvQCgs6MLcCU/QlAGTGmneMRnBwm0EgU/fQwg7yj+tIGwbkd4z8Fw4quRnAcvjrlmePM1vRo5cX49KGnq26WS+oMLWh6cGotVPRHZLzbA+gpLUBEv5HUYLS7vEbTp+TCn57OFrXgXXuxR1UmUwggENBgkqhkiG9w0BBwGggf8E+gfwwgfkwgfYGCyqGSIb3DQEMCgECoFowWDAcBgoqhkiG9w0BDAEDMA4ECFwonOIg++flzAgIIAAQ4kpdDC/If2Ro8CTME3LIf3OvZw38/CliPX6Yy4MLI7JSME5RQUrhY+/Saw4/L6B68mukeWZMBnOmExgYowIwYJKoZIhvcNAQkVMRYEFBUJzG3CAW0LFRvd+r7FoWoQ3wIq6MGMGCSqGSIb3DQEJFDFWHlQAUABLAEMAUwAxADIAIAAoAGUAZAAy+ADUANQAxADkAKQAgAC0AYwBlAHIAdABwAGIAZQAgAFAAQgBFAC0AUwBIAEEAMQAt+AFIAQwAyAC0AMQAyADgwMTAhMAkGBSsOAwIaBQAEFGs4YBeZuWInWICuG68IQgRV+jLS2BAh8zZrG9uqvuQICCAA=+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDwwIBAzCCA4kGCSqGSIb3DQEHAaCCA3oEggN2MIIDcjCCAl8GCSqGSIb3DQEH+BqCCAlAwggJMAgEAMIICRQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIk4KE+evBBgTMCAggAgIICGNsQ9DokOToB4WN9X0SQpj0yKDTMzWWiJWouEdbnH9rpsLHB+oxPJRSdvl6qYD9BDIIyQBb72/oJEyv9x4vevZ4Y+dr7TI5iam8cqf/jqCRDcJngJ+F1OeoGw/48yniY+wLLX9stA7mLVlGOqmkv5ubr4oS3B2DvMgouaiqOzBY970zepk+aI8v5bDciknvNxLcqwb8gef9qSi2lXjSS+da1r/Auv8wy1DWMb0wXppkr6iE4eRD+GBrJBCV0+0GkdlEQdZlUwF9L6Rk5Gh6T5sfqb6MfetbKdJ9o6ZZsgBiePVco0yrV+gPVCaDkZwoIH+iSixn0YOqVjzWrnARyWKdf3esXGloIQhQnrfuDIdHK8YBlXuN3z+EfSmfRh/+aWG2DW4s9wcAB1t3GMZewMZ+z+Wrie/sYgp2o/105Q8dUQqNDu4JBYH+1xjfqyjlWOtzEyJk1wrUefOWIFlHkzwZcGrY7kUSJNWY+ljm+ZZbvJWQE7mPXet/+yZFSXv42FwFwU0C/Lo8BFI2c0KC4CVuFNmfNWToJfa9nnEvInh2rra/XyuyeHHRe++Bc9CMCkKWnOMoC/p3jnjatqjsJdHhXVViJQcvxjJXwyJpRi3G2v1SWz6oWFx0eX+7K+rZWnrTT3QI9vSX6aCQXcgc9XNRtdObvQ1tToOt+qgvUBerqDjmvTp6eKsNw5a+Z5ZxH7oqjcWgnQIYfT2aEKMQ38RMMIIBCwYJKoZIhvcNAQcBoIH9BIH6MIH3MIH0+BgsqhkiG9w0BDAoBAqBaMFgwHAYKKoZIhvcNAQwBAzAOBAi7tsvABCrGugICCAAE+OL5EeTq4HVyV26RNPe2S3AKQyGxNzkwMJMBGyhIG1XPPLqumvDgJqrPcAD/QwRVj+MiJb51N45xC0MYGIMCMGCSqGSIb3DQEJFTEWBBQVCcxtwgFtCxUb3a+xaFqEN8CK+ujBhBgkqhkiG9w0BCRQxVB5SAFAASwBDAFMAMQAyACAAKABlAGQAMgA1ADUAMQA5+ACkAIAAtAGMAZQByAHQAcABiAGUAIABQAEIARQAtAFMASABBADEALQBSAEMAMgAt+ADQAMDAxMCEwCQYFKw4DAhoFAAQUzx421ERBMyF/yNxR0GS1DLlMANMECPiknVVT+vYXVAgIIAA==+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDZwIBAzCCAy0GCSqGSIb3DQEHAaCCAx4EggMaMIIDFjCCAkcGCSqGSIb3DQEH+BqCCAjgwggI0AgEAMIICLQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI70+G+ehLEN/oCAggAgIICAHqLWEksJuuLz0gv91JjhDKOXcFZFi0FPLkid4IvSGamAOBt+ABgWh2v4GydPxAnLdeggMYwL/mjudrWuymgUu4fjIvTLZi54duYzBr4B+iMShlo5+C/kc5qTqhWBqPfRi6b9q1ryWZQuJR5j101BLRU90axIUpT7g8UEWzmLtEZfbZj7m+5cXWI21H/3WmzrbhBT+MBL7PeSmAL7SB3z+jGpqjIAlgDSABWpINyKQIGTpwAQK9+v1y8nJ+VfE+V/MMz4nVBdzpiP8eczg7v4CeJq/AhLU1zYS/1wRs0Smgh4azn+Hpt+AoY7KfiashHgZnQOQ/YCKoUwDBoZg9A3tFiFHR6FNXXX0VChUzPEbfAKtzQ+J9vz+prl3uIBvOmNbPLK9QUOgCRfmMqpSzqx7WCBU1KHynS69KBtEQCrxphItLzLon5K3+uFjuavUkGab6WexWZC7yuVihFZbRunXjrqfP0AMG98f6iUhSLKmyzHqho1Xx/vdJ+ly9BFt0lo4ZV4dySYLy8fOSOw2GqvxFEVj+Giz0a4RCC2vbK5IjyTf95lsp3qiFC+fBwMhv39AtapGQVdVqMrA5P5TFplSZoz2dpnIlPwCbpyvpEe8LKc6oUpu5eKmCeU+RQ0zj4+J8KAzfVbzS1yMJe+umu95i5vfEUCs98C8RvDCnHsx87T/x7w2z8b2MIHI+BgkqhkiG9w0BBwGggboEgbcwgbQwgbEGCyqGSIb3DQEMCgEBoDAwLgIBADAFBgMr+ZXAEIgQg6so49BSrEJR5mdkomLkSagSPGcNJIanjlT+3V6/L/e8xcDAjBgkqhkiG+9w0BCRUxFgQUFQnMbcIBbQsVG92vsWhahDfAirowSQYJKoZIhvcNAQkUMTweOgBQ+AEsAQwBTADEAMgAgACgAZQBkADIANQA1ADEAOQApACAALQBrAGUAeQBwAGIAZQAg+AE4ATwBOAEUwMTAhMAkGBSsOAwIaBQAEFG+LXFJAkw20X9gqruZyFGH+jWmOBAgD+bdl51a1P3gICCAA=+-----END PKCS12-----+-----BEGIN PKCS12-----+MIID+QIBAzCCA78GCSqGSIb3DQEHAaCCA7AEggOsMIIDqDCCAlcGCSqGSIb3DQEH+BqCCAkgwggJEAgEAMIICPQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIow4v+Lsy+8FoCAggAgIICEA1VfaX02WNEBrOhNyi065Y2/GOi/rq48aMVrxk66tdu8J1A+scvnzo7ZKJAb0f93U+NUwP4BTvd93vYmAPa++3Mq+alBZvkT+OM+fmIbI8ZACvlr+6B0FjU4+GOLferGud5k7GopaRjVnNErXPBtfmCi3xqfrloRZNaJo1PQIrVXUpGfK+xB5iBQVIyTFZa9CGc0OFenGbegoWhR0NEa7M/M4zynaY+4HNEqv+yY8mm+aiim2E+r/Q3e5zHdr63k90ltnA+xpNyjeaLhBHixgD1njzPvHkhNXenss+GIMi54pmSz52N+gRX3K9ZwT4eSOQwh19fAJmK2TpCgGyffuoQMRWJfhrAsZdLoLzpOdaL9CR0FEFwy+mB1j41xMcKe2yoSOGi8A92Lbkk3xN0c57fECcLbwoaZZyegAqAYA94xr4M6yzvpq+7qVZKNaB4F2n4iGbfwRgendan7LjTfPhyesjuph71/ee4WVygOm1HEq1lqLS4LzX+FFwINo39oQTAIYxoXK7u8V5f/3Qj6DgiQkVMdvwn9qi8Sf6BG6AvJw4cjSbb8g7Z+RQ9lUaRtVx0vSjxd11fOp2etNTcvxDfM9du4lT2lKxJUP4ZHiwWveCGukXwaVs7J+BGzhCB9X9Lju9OdSjzWRPe/esHZ3uyyKif+uKR2st9pMq5QHwK53lkiWfCdnLKYA+U+bgNu+U+gZs3xPpNzCCAUkGCSqGSIb3DQEHAaCCAToEggE2MIIBMjCCAS4GCyqG+SIb3DQEMCgECoIGeMIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAj3+PgR467622wICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEELWZsHeLIm5n+za4zYKSmtdYEQNaOREFJzeUI9EuL/4xTKOTRWK4kmQ6rYPc8/xbtUTdARKblfWGd+POrYXUivZPNJEn38GCAWmNsIWGGOVc5kO8UxfjAjBgkqhkiG9w0BCRUxFgQUFQnM+bcIBbQsVG92vsWhahDfAirowVwYJKoZIhvcNAQkUMUoeSABQAEsAQwBTADEAMgAg+ACgAZQBkADIANQA1ADEAOQApACAALQBrAGUAeQBwAGIAZQAgAGEAZQBzAC0AMQAy+ADgALQBjAGIAYzAxMCEwCQYFKw4DAhoFAAQUi8/XCc5c6tAh7OhhCR2ZKE8sCdEE+CEeT/cMXItocAgIIAA==+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDwwIBAzCCA4kGCSqGSIb3DQEHAaCCA3oEggN2MIIDcjCCAl8GCSqGSIb3DQEH+BqCCAlAwggJMAgEAMIICRQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIRRt1+aGJ9pBoCAggAgIICGFNFd2VuEF5PLthNtYaAYLz2s4nHfzIM0J5BloKddcY7yeIB+mj6xMGPO0U2BKmRHk2lBtWkLRI1C7La+aLv/wqccnSq+gpXVfHGx++rOx6SDqjXK+53VdIrEGWIfYkJE3HqF/WCeuvot+qBK1JiRoJN1Sb8MSXHuoIjR21ic9vJzjGLI3+hyxmzzekk0TZ/7yfdoWc3MU76ZlBGMzVhbfHlz6Oi1GG7J+TMs6hnuO2BDJMSAAV+9C8xwU1B9RAiDsZXL3lCq8My2+u5BCNoYOcuPnvMlk1quZjuMuQJvFJH0ZfRnf97+zItYGcqNqxWbs3S61ma8T8jYZi1eL0hvD96n6Im+FRF63aAgeXWXLvzgGJ2+u58W++OrKEhpu+ipPtdwdWR46A3m8+jyctNolC8X/1goLs7EDDGfjQ77KIWaHxP5G3Ez7+v4dB7IlRIjHwoFwCd5TXiORrsQQWwtnSgr16st6cw2ObLjUeFKlAGBPYE1hP3rbR+6r3+nMsH8J58cfvqmpL0+ZnFvIVZcJtb5GG7FEoeVhQM3ZdIZjmXreF7cwe6K3s9+cnF2v3ikQ7rH835VUtWAPxPmh6e33jJLyF1LmmYMSv81R50jKqJdkqCI2dDUa1xG+ZgI6mJwtnwi4jSOCRs6icJR5XXbuyPOACACV1hrXEVTAXCnkvrB0vZ8FjTqbacS++1PI1v77ypPIn95aFn2fn7i8ojYf2MIIBCwYJKoZIhvcNAQcBoIH9BIH6MIH3MIH0+BgsqhkiG9w0BDAoBAqBaMFgwHAYKKoZIhvcNAQwBBTAOBAh05qDI2Ar72QICCAAE+OKOQzu+R5I48MSppj8vB6pLOR2z7g54qi3BqyZOeeDe2ACd6O3NfG/PYhBlaf0+3+Ve18P3K7uxS7MYGIMCMGCSqGSIb3DQEJFTEWBBQVCcxtwgFtCxUb3a+xaFqEN8CK+ujBhBgkqhkiG9w0BCRQxVB5SAFAASwBDAFMAMQAyACAAKABlAGQAMgA1ADUAMQA5+ACkAIAAtAGsAZQB5AHAAYgBlACAAUABCAEUALQBTAEgAQQAxAC0AUgBDADIALQAx+ADIAODAxMCEwCQYFKw4DAhoFAAQU3eBhLahYNnEAfOqstFWLRhkGW64ECIM22J8l+0UAiAgIIAA==+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIDwQIBAzCCA4cGCSqGSIb3DQEHAaCCA3gEggN0MIIDcDCCAl8GCSqGSIb3DQEH+BqCCAlAwggJMAgEAMIICRQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI8DlD+f/FkAioCAggAgIICGKfq8w8XKEbdolrBnthXWJAkm8mw8JWavB//y97b4r7HjtPw+0uUvfXbhIRnYNu76tac9zlaH66n1xLLqcUAEK3RdwPLN2qx0hs3vulUV16TVao+U+smU65HjazxXC/Oa3lfxAYhlxX44Nfe9p6uCDdf30G4yDKJNNSiuw5K83jpxrlIeH+UIGakE7XQ9gmROuOSAXJzmBOjw6rUbBtbUgP/J//yvP9ZeJkB3OFAYhohGo/9ych+L0wlk6Md6Vtq0nv/zYQP9PofXzfZUh+rZha3rxKioH7xAhnRNxvgh4mksqTyJM6k+OsXZ3rLvxehTKgSm5clEzr/T3qxjTusuNovTlt/avBnpnZIwS1qzTeq0zrd/m7H9+yM6NV7Cbl3LtHzQEQkrJGxVACDc6QcCrmHb7tJBO9dAHiZds7OXlI3QJ4D/aGAru+mlKD4ZFp0XurJOu/p6BocEyXo1D0yYEMVoo9/KGH8F4ugwyIQmc0XxrO5mmeRzQk+PH1Em4Im4DCKXX0wrlBEPmVuevlh3JmWQ0UEtmMxrhaiQ+Ufjr9TD0Hn7D5wZGVu+B5ymH68fPVe/HwksVHIC28uYM4EDMdaKs0BiPRxI5u2jBcWfSlow/jVsK86TcZ2J+4+p2X1949rD9Rz5YKXkaOGnkbkIWAyYDFZF5LW4+jNXxCoo+zloz/CIJmNk3Sn3F+v7Ao4VDmb8FANwwLzzYoZ6ZI/KmPMIIBCQYJKoZIhvcNAQcBoIH7BIH4MIH1MIHy+BgsqhkiG9w0BDAoBAqBaMFgwHAYKKoZIhvcNAQwBBjAOBAjrE5kdbf4WxwICCAAE+OL0cHLqeh5j6Mpts0fF20kwdBzS1/9yXPUtcD+CzwEvFaJCMBpt8s4inBnxN9/vi+D7hg66rk27phMYGGMCMGCSqGSIb3DQEJFTEWBBQVCcxtwgFtCxUb3a+xaFqEN8CK+ujBfBgkqhkiG9w0BCRQxUh5QAFAASwBDAFMAMQAyACAAKABlAGQAMgA1ADUAMQA5+ACkAIAAtAGsAZQB5AHAAYgBlACAAUABCAEUALQBTAEgAQQAxAC0AUgBDADIALQA0+ADAwMTAhMAkGBSsOAwIaBQAEFKL1V7ctReB15tch/MxfPTTEW0x3BAiQJg8kHcKT+2gICCAA=+-----END PKCS12-----
+ tests/files/ed25519-public.pem view
@@ -0,0 +1,3 @@+-----BEGIN PUBLIC KEY-----+MCowBQYDK2VwAyEA9vhO3vjZNz2lLj8wwMUKNjMCm4GdwNDklZLRAvh6MRQ=+-----END PUBLIC KEY-----
+ tests/files/ed25519-self-signed-cert.pem view
@@ -0,0 +1,10 @@+-----BEGIN CERTIFICATE-----+MIIBVzCCAQmgAwIBAgIUQic3Xx5HxBkV/L1hvf3Jx3np/w8wBQYDK2VwMCExHzAd+BgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMTgwOTAyMTU1MDUzWhcN+MTgxMDAyMTU1MDUzWjAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29t+MCowBQYDK2VwAyEA9vhO3vjZNz2lLj8wwMUKNjMCm4GdwNDklZLRAvh6MRSjUzBR+MB0GA1UdDgQWBBRXgGnuceTINDGwIQF7nGpBq4EpeDAfBgNVHSMEGDAWgBRXgGnu+ceTINDGwIQF7nGpBq4EpeDAPBgNVHRMBAf8EBTADAQH/MAUGAytlcANBAH6FW7MP+uE41hJJABnrR+jdkGOzRH+C42g5YgYEKBLFqqGz/82nfX+aaJkVuxPrsjaVhTKHS+iCazhjZq/l0njQU=+-----END CERTIFICATE-----
+ tests/files/ed25519-unencrypted-pkcs8.pem view
@@ -0,0 +1,3 @@+-----BEGIN PRIVATE KEY-----+MC4CAQAwBQYDK2VwBCIEIOrKOPQUqxCUeZnZKJi5EmoEjxnDSSGp45U/t1evy/3v+-----END PRIVATE KEY-----
+ tests/files/ed25519-unencrypted-trad.pem view
@@ -0,0 +1,3 @@+-----BEGIN ED25519 PRIVATE KEY-----+MC4CAQAwBQYDK2VwBCIEIOrKOPQUqxCUeZnZKJi5EmoEjxnDSSGp45U/t1evy/3v+-----END ED25519 PRIVATE KEY-----
+ tests/files/ed448-encrypted-pbes1.pem view
@@ -0,0 +1,40 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MG8wGwYJKoZIhvcNAQUDMA4ECFWox81pCznKAgIIAARQzXqhO+QND3jDKs9zv1Kj+MNU8LPm/PpazS6ec7qkxavm3P+5IFMk8eQl61ZlzgsZh3mLzERa3PI6bWlZLSe8V+7UHVIpw3KCQcQP42hfAQSTc=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MG8wGwYJKoZIhvcNAQUKMA4ECDioAMUqN3XrAgIIAARQL2hyA+GU61GYy+T1t9aE+ImSrEUBls7Xl3Ot4hOiLjOAP6jrZqTV05RD9QQwxZHbRDVq/HX/6A3WK0cWd5LXJ+u9UUhF0M4QTSJ2YvgzC8GZU=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MGkwHAYKKoZIhvcNAQwBATAOBAj+fxvGvZUmFgICCAAESR52DDqYruEbDKjnQYPb+4pqdQ98Tenh9F9vWcYvlPHKroHOSrPuW5p0ISNcTMjs6WcwfzrRVRLKMIUwUxyOc+j4+KPXziQpkTCeg=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MGkwHAYKKoZIhvcNAQwBAjAOBAifgbGu7z4HxAICCAAESWH3dIM8ezNr3m5cuklg+rOFbFNZqS9bNjvRVX2rQ4UsNl1PVNw3cxe9Mg6AYg7mXffibNOVZWwzbYi/8jHv3+f2atBdH64mI2ADg=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBAzAOBAjiQ6AN6cgfbAICCAAEUNi/xo5EN11/48aKiymw+3iiopFH549Yhzl7h/R8PMkR/73xSokPpShMUFiG9dfBHiJU+JrlCg7oINFK44+R0+YAN8vHdg6gXJHyxQuONZQZ7l+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBBDAOBAgG3+c8DYCcnwICCAAEUARVfrP73l/qnbQwBVpZ+RxBDrm3QT3Xn/VmclpSGcGBurOV8qKO2nYj7WzvY6KOxXVfGsrVuh/uOP7hn9l4L+A2KiGc0TM0sa9GGwg7nVWydN+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBBTAOBAjD/E31ow0hhwICCAAEUIEjJ4SJ9Aa5lUhe3a/q++1sf4bi9tFscXczGy19qKwja3zyisb7A4lFhFFojzN9Xf72qLQZlWerxG/E2xbaI+/toDPUlkEqareLJSm30dqltD+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBBjAOBAgGWbPgRlLvWAICCAAEUBBrF4fOikjttSlIQSyj+Uc84dgLgNVoxjSgEw5TFVD1hnHf6IhkDE/ke+sKKn897QzW/8UtLdHwdHBE1F04W+BOx5TskfCIfvbw2U6OAFQUK2+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/ed448-encrypted-pbkdf2.pem view
@@ -0,0 +1,42 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGfMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAgyO4eJgChTHgICCAAw+DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIjCW/gTtbGrQEUBOPwmDji8JmdtYKneqP+eHoOJVTC4h7EvPI8L69/qCWmdoQwb5FZQ4swhLYhEi6HUzaKF9rmF71SG0yj8x94+Ys5H6aRYf4yJ8mTYq/jNdYKf+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGiME4GCSqGSIb3DQEFDTBBMCkGCSqGSIb3DQEFDDAcBAgPnWL5vcrxMQICCAAw+DAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIk8VyO/qFnksEUDDZtPg1FOH8PvST+LKW9GEgmQVzqxYyQPUDVL0EjVbP5TS4Cs/bA/Kx4vX3z312PeY3I8JZtVoBLjWX/+sy3xgpqnGSmuNcvMwWaaWzcYD06F+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCkGCSqGSIb3DQEFDDAcBAi08Idt7k1mcQICCAAw+DAYIKoZIhvcNAgkFADAVBgkqhkiG9n0HQgoECHBod624v49oBFBLW/h5PDwAyvm9+NpmnMEgv1BHHfVpimwOGrHb29zrB8vxNA0h+8Lo0BcSZTpnL8jbN+IRLfBmyy8zx+EON4rhK8NS/auC1Wz/a03e4bCtMtGA==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGtMFkGCSqGSIb3DQEFDTBMMCkGCSqGSIb3DQEFDDAcBAiULPupkmkOnQICCAAw+DAYIKoZIhvcNAgkFADAfBgsqgwiMmks9AQEBAgQQUeP3K6y6H4/WAYH3t0rKXgRQ++Oo9san10yxV3ZjXi4VJcXiW/efe8T6aomjdLAfKx0hNtnf4YngHtBVyAHPLfI1x+9zWCBTzEt+agp4Umazk59dBhiIB7eiOoNhUZOFvHTtc=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGqMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAiA7abz6Y7GFgICCAAC+ARAwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgE6BAiq6CcB7kKwXARQKLMY+sHJoPrAEufRO5/iMNEvWbhrOTY1pNnZkryJTfnJSW5mnNYOdTfYFiItrd/ivH5+g+yMdFYFBRNDT27J1OBDSJHQQkrXmf+pb966ElVy8=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGrMFcGCSqGSIb3DQEFDTBKMCwGCSqGSIb3DQEFDDAfBAjOvvRIconlRAICCAAC+AQUwDAYIKoZIhvcNAgkFADAaBggqhkiG9w0DAjAOAgIAoAQIX+HtJbu7CmEEUAC0+aGZdMCNUA7aBhThDoH4DhK3F+u4TejId5xx00znWPd5lB/yXWJhaK6Rce/+k69oF+UUu9NYZnwURQ6SpoMeu7jLRvuasVNDZs9NKPm+MH+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGqMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAgGFmNY/ilxeAICCAAC+AQgwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgF4BAggk5ndlY33agRQSd7A+aeL5zykrjjIGNEM6DyzjN7ooiAd4qHHC70FkaNQ8RJneqXXxi1nNZDdXhaLa/raU+ifdM8M8566L7O/s6N5uKR8W8+ieDlmmwAIZDEGI=+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/ed448-encrypted-scrypt.pem view
@@ -0,0 +1,18 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAjsra6FzTgntAICQAAC+AQgCAQEwHQYJYIZIAWUDBAECBBBBzcngO3oAta7DS9ulmjcgBFB4gmQnHQmI+rbK+RFBjSrSrvHGeYzEGZO16iD0bQnPUTwStLE4y4rLE7AvD0/6cjjW9DnvPmPBW26ZT+IyWW/JZMykzfM0dk6ccNLg0MHibNcg==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAgbq3ygXPa8GgICQAAC+AQgCAQEwHQYJYIZIAWUDBAEWBBA3IcY5TlZ2FzxzTGljkaVTBFAEJRrLXzngWHUH+No+Hq9zGFJ6yBekd403KmlaHCdVPd1JrmgnYoPS7RYxhTMKQ518Zn218Vtf4Pdjl+XrN2MQ1O/eiQM1lp4lvw7gt4XImUFA==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhoIC6uL0dKvwICQAAC+AQgCAQEwHQYJYIZIAWUDBAEqBBA3aL14nbkyRn7MPVVqbgZFBFAY3ogxHUTT8ozS+tDmIogHDjazBBs98CIwdwTYrES20oZ41NCezpXlpPVFrzjqHm1rP6ZudloRPUHG/+xSEr1JeEKm5mBGTA1sjRJFyT5Chd2w==+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/ed448-public.pem view
@@ -0,0 +1,4 @@+-----BEGIN PUBLIC KEY-----+MEMwBQYDK2VxAzoAWopNPKAV3+c61CJqDrnMnRntbN8QSNHZwdKEXjxV0q522y9I+vhkuuwOJeZpMNqlLL8frTS1ds3SA+-----END PUBLIC KEY-----
+ tests/files/ed448-self-signed-cert.pem view
@@ -0,0 +1,11 @@+-----BEGIN CERTIFICATE-----+MIIBojCCASKgAwIBAgIUP+cIZ3jsepQ7zNuhZtdcyzaGHa8wBQYDK2VxMCExHzAd+BgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMTgwOTAyMTU1MDUzWhcN+MTgxMDAyMTU1MDUzWjAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29t+MEMwBQYDK2VxAzoAWopNPKAV3+c61CJqDrnMnRntbN8QSNHZwdKEXjxV0q522y9I+vhkuuwOJeZpMNqlLL8frTS1ds3SAo1MwUTAdBgNVHQ4EFgQUvzGR+t5kH8Ta2XUs+KP+uQfMQFUcwHwYDVR0jBBgwFoAUvzGR+t5kH8Ta2XUsKP+uQfMQFUcwDwYDVR0T+AQH/BAUwAwEB/zAFBgMrZXEDcwCrI8049W/mjhgaJm79n4Zwb1ryBg8aiFqmvszd+pFhGmhU1TWD+qPmBWOIdu0laToDvBiITaLAa94Ck0BZdNFsbqq1FRFXOzsLwV6kW+lLwG53nY7ITnC1MVytl8qjA0tsrnHqPwm5pp8rIzJudNk4S+AwA=+-----END CERTIFICATE-----
+ tests/files/ed448-unencrypted-pkcs8.pem view
@@ -0,0 +1,4 @@+-----BEGIN PRIVATE KEY-----+MEcCAQAwBQYDK2VxBDsEOceJ7Mc21YZq6/qusCHA5d3wARXuJxMKSRkvsyD4GEKq+hlubDZL0FQiS3OgjbeuKN+63xa+OVVz9XA==+-----END PRIVATE KEY-----
+ tests/files/ed448-unencrypted-trad.pem view
@@ -0,0 +1,4 @@+-----BEGIN ED448 PRIVATE KEY-----+MEcCAQAwBQYDK2VxBDsEOceJ7Mc21YZq6/qusCHA5d3wARXuJxMKSRkvsyD4GEKq+hlubDZL0FQiS3OgjbeuKN+63xa+OVVz9XA==+-----END ED448 PRIVATE KEY-----
+ tests/files/x25519-encrypted-pbes1.pem view
@@ -0,0 +1,32 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFcwGwYJKoZIhvcNAQUDMA4ECOlAaqWwdDweAgIIAAQ42XsuUzVmkZkOq+m4uBNW+6qzsaTJ7FPa4itB+tyv4BzPgFxAQTwzzJgSr9WkbYy6LKZIPporO/Ko=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFcwGwYJKoZIhvcNAQUKMA4ECH0TpXboY9MNAgIIAAQ4dojWsXYBrtraqLi1pBeF+uOyGGClKoj7XpGvXkNShtEnWTKjlNUebf02xQlcmxuksdXNffTMdsww=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFAwHAYKKoZIhvcNAQwBATAOBAjV52wnNhxkeAICCAAEMN5Tklnapb+agqTYnDle+pHKk61c0d0la81SFGz5pqFBPllX5zmnSWzzHFW7/B8cxLw==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFAwHAYKKoZIhvcNAQwBAjAOBAh7Iwy1tOrlZAICCAAEMK0FLzIhsz8+1ox7grnf++u+eD8k9LJ0X77X9GeNKyvIJl5lkkWaCblFwd2reMH3eug==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBAzAOBAgWrZKj/deGGQICCAAEOJOPMtVYLKsvzajTT5wG+hjhbX7RHWL9t9h1H1m/CAW/qJqALZNPQcQ8BiheRJv8LrgYr+Fzl4e6O+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBBDAOBAjPUjiYWb4CwwICCAAEOASPWNTFFZaViFQ+WQ4C+Upl6k5aTHq6KHpai/N6ABiwV8dcXAL5CXMzAxJWKW+ZdUgyydM7xAVn6+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBBTAOBAgbZrk3W2y+WgICCAAEONIxa0JMYEvV9curAwn5+5epAnnwv7vtqO/IT4mqkc0WLHPEZVhxL2EQWBGemGQWPxeRV5jK8RJvS+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MFgwHAYKKoZIhvcNAQwBBjAOBAgfr3469Ve4gAICCAAEOMHa3S4OYcEBCb+HHvJW+zpNlWtmW4/djMndGWpznIuguxK/7V+A3R7PgvA8K0FhpSPDR3bs/2QHW+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/x25519-encrypted-pbkdf2.pem view
@@ -0,0 +1,39 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGHMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAj6ZyFipSgetQICCAAw+DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIjgzX3kQ5dXgEOKlxrj9EtG1DNXBDgzDW+vnFReEN4zACwoxMQ41sAASIW1vCT1TuTIDXLxGM5CwfkyawnexrKlKmt+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGKME4GCSqGSIb3DQEFDTBBMCkGCSqGSIb3DQEFDDAcBAj4aVxRNkgd/QICCAAw+DAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIu/UnAKsbyacEOE3fiHQdWKYI5Inf+Wndw5d7UX0nRmCjwNQLK1UL+QXujoVmi+3hEZSRfYcqj5M4t7sJlzZ63w8Td+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGLME8GCSqGSIb3DQEFDTBCMCkGCSqGSIb3DQEFDDAcBAgJmEUBljH9VAICCAAw+DAYIKoZIhvcNAgkFADAVBgkqhkiG9n0HQgoECB9jZKjkRqv6BDg7458AElgxBUGx+9iPo02FNkzdUO23NB66AV0puFZAYz4NUY/wgJLA3HMwBERdSP74hBjh+GUT3sw==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGdMFkGCSqGSIb3DQEFDTBMMCkGCSqGSIb3DQEFDDAcBAjyCgD/RmiEIgICCAAw+DAYIKoZIhvcNAgkFADAfBgsqgwiMmks9AQEBAgQQFNpqaqXLMkVwoDSdtl+SEwRA+XXRprNwSjtJ/m6V3vFKq+VCHOrj7o2MSpt/pvjVnbHKaHgX9V3TP8e1KOiiXJoo1+PBv4M1beoCokVjhCECZWyg==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGSMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAiTO27y8UQ3LwICCAAC+ARAwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgE6BAibXxpEYv15IAQ4zEoc+lFpScRoeJ/6aaSI6f4NFG4N4cVFTXvi5YIDlbpwmz4xdIr2uU+jhiGknXDcE2Z+m+ERCITjQ=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTMFcGCSqGSIb3DQEFDTBKMCwGCSqGSIb3DQEFDDAfBAgxud22g8Bm+AICCAAC+AQUwDAYIKoZIhvcNAgkFADAaBggqhkiG9w0DAjAOAgIAoAQISGwHgT4bmFkEOC8m+drQbC9WA1ZvACMJWLXEL4JUgejLRDtfcvCC5kAhT2iy23RllpUftspZ4V/SzAePa+EFsn0jy8+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGSMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAiQqqDVfFc5OwICCAAC+AQgwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgF4BAgmnqFFlLCrwAQ4elCp+HTXrBbgtWoMW/hG5AUn2Vc9W3ycJ9tw9d4pxx3ZHV+FGiEMJsxqPwGVEDJvTgxy8+G55j3UE=+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/x25519-encrypted-scrypt.pem view
@@ -0,0 +1,18 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAjTIFxP0Hwq3wICQAAC+AQgCAQEwHQYJYIZIAWUDBAECBBDJmyEj4MkNJQMLM4t25IIuBECqSAJnq6gSp0Nc+OwqkgspkwyYGxHfZAJ/aw7BHSGTs+LQexB738lqY/B5Ri2udxqfE/kjKzyn2KTHJ+nqHQk8+J+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAi0kO+7kfp0NwICQAAC+AQgCAQEwHQYJYIZIAWUDBAEWBBBnSlCJdiXvxblUH3GgOP3iBECaLsAsyLbHLKcA+JCxrNQ3JrGL0TVQHPu/6tYFAWJoM4ZCIww1i5nXZUIigNDYERUJbm9od6E2a8apA+TH5JE4yf+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAjC70jTpl9lAQICQAAC+AQgCAQEwHQYJYIZIAWUDBAEqBBCKvOsxUwHu7QYwy/j409InBECFy0tXOpJYZD1V+S+x0XsC/nHHesRzKKbs86f//VcOQBOwbA044IcUNPiaSkuedtlAX84I1rtvtJGY6+b7fVHtJn+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/x25519-public.pem view
@@ -0,0 +1,3 @@+-----BEGIN PUBLIC KEY-----+MCowBQYDK2VuAyEAdNNs0T64alqPXbwrJt+rCdkerW6PgrQOUKHUlbPyahA=+-----END PUBLIC KEY-----
+ tests/files/x25519-self-signed-cert.pem view
@@ -0,0 +1,9 @@+-----BEGIN CERTIFICATE-----+MIIBOjCBpAIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0+QGV4YW1wbGUuY29tMB4XDTE4MDkwMjE1NTA1M1oXDTE4MTAwMjE1NTA1M1owITEf+MB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTAqMAUGAytlbgMhAHTTbNE++uGpaj128KybfqwnZHq1uj4K0DlCh1JWz8moQMA0GCSqGSIb3DQEBCwUAA4GBAE5F+wEzoWRJKgS6OYunevbkvDMOmAjhXg+P2zcuXSndAi6HLlHTsx0tdiTclrNqNkFcw+XaSBmGhpWy/qI4eM/lrWZV3+dfeNRBb6oqezjgbepC+J1akDNzseW1+Kr16ryQz9+STwnOqpQhrTQdBOaLlgofw3qqj88bWBsNciJWCX4+-----END CERTIFICATE-----
+ tests/files/x25519-unencrypted-pkcs8.pem view
@@ -0,0 +1,3 @@+-----BEGIN PRIVATE KEY-----+MC4CAQAwBQYDK2VuBCIEIIhgx84XP3vLVdFwZWT88BG/gGRXl6YYUeo0lWX2E8RY+-----END PRIVATE KEY-----
+ tests/files/x25519-unencrypted-trad.pem view
@@ -0,0 +1,3 @@+-----BEGIN X25519 PRIVATE KEY-----+MC4CAQAwBQYDK2VuBCIEIIhgx84XP3vLVdFwZWT88BG/gGRXl6YYUeo0lWX2E8RY+-----END X25519 PRIVATE KEY-----
+ tests/files/x448-encrypted-pbes1.pem view
@@ -0,0 +1,40 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MG8wGwYJKoZIhvcNAQUDMA4ECB4xXEg3WSinAgIIAARQM2CTxuxMnzkPDjXsonc3+lI7dqFNYNxBfgGoEur60JvnZyl7atO+lsCY08XYqLOAVwf/5aZC9Crkd0iHdtYZs+k8xCAG/KXBwdrrMiKgoRdXE=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MG8wGwYJKoZIhvcNAQUKMA4ECJFkCK4Fg7SgAgIIAARQ8CiLyhuksya2/pWvoemD+etLFhTSKFln9XfLXSyG70yCsPcH8kC5Xv3ppmNcgbYDC8iwxWwjH4b0QM/QuzaGK+F3itJG5sgWypa9Yt2rSwYUk=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MGgwHAYKKoZIhvcNAQwBATAOBAi75GJorMne3AICCAAESLScu+UVIvP1/FqkKBa1+lYTe0Z1nQWFs5bFdFsAAvw5RCSee2vcgFA5KhIqlPwdPgxmgAzIYgoSV0RqudMfq+OpABx2fMhpMhfA==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MGgwHAYKKoZIhvcNAQwBAjAOBAjxiQQdgW2rXwICCAAESHEAGY2q5vNhLbN7Swb1+UmDvXibgMjhTpunsmPouCahSf6Zvv8ahes53gvPg/j14hplD2d/MRDz5VRxYO7dP+v2mzZzuqhegssw==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBAzAOBAhQmGA2NmFbsQICCAAEUAxfvgmmGrViSF3kj82o+EbNw0fbLqoaa/SdNfV6KRJhZ8j/+6URQ0xKMw3sSCp/fwSon8h9I633VkGyxEtyF+KRYOakvY5kBOt+JYiZcbovFT+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBBDAOBAhziSywPrS4zQICCAAEUJaEyqSGPu9FZ9CLURVW+6ZRWj6TIaehX0cGGFCkTk3JqhpFFRNLte0SLGPkankzXKOV3D5mBUPF+UjvXSwaJ+FcyeszvN5xR6sNQkETZqpTD3+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBBTAOBAj1Ch4rgGz1ywICCAAEUDJ1FbgIb/MHcuoLexYq+dqMB7sGctwBmrFZRTpbbHGNvEUV6MFlBkximrCb9nnHjCKj2sZcfQp0R5Q1YODn8+gzEAEjVuh2yFCKlwumPknt+O+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MHAwHAYKKoZIhvcNAQwBBjAOBAhCm0/fGye6+gICCAAEUHqw28mTyCaI6k+ILt/o+3qleloo0f4bL9cJiOBYnUB/S1AElr/85ULA70bECsjsWjoWsRE8IxJ5a3FtWJaat+gaNyLYQlMtVBMoK6feFFT8aE+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/x448-encrypted-pbkdf2.pem view
@@ -0,0 +1,42 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGfMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAgJDIKD8sQsoQICCAAw+DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuYP9T2Y/nD4EUBXfNNfUENf19FHDdt9Z+b3s0OVeXeMbu+80TA2mtbk+wOucvB69hr8V/ZFW2HDiYJ2qMBMioOw197lTtVMIT+xS6gGSpl+tyPoWIoycGcnsq6+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGiME4GCSqGSIb3DQEFDTBBMCkGCSqGSIb3DQEFDDAcBAgu+SJlXxKQDAICCAAw+DAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIeyJz7DTK2+QEUIlmFpQp5DB+gwVp+6G6dhe10uhETbfSTN8+lmYiqb+0p91DnV+3kpaTrT6fuU3hsjTH1xx4a2oElc+T1+m8xZhvmmyued7UPBrQdy2RcNUToQ+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCkGCSqGSIb3DQEFDDAcBAiHVuSXubRGxwICCAAw+DAYIKoZIhvcNAgkFADAVBgkqhkiG9n0HQgoECJoupjD8iD9gBFAIaJQUeSI3WfZn+vJ5oM88JZrDdxVKlZlOL4ffXYu3PIUSlV0r3V08m/sPMY/7hCmtvyxBkw6ncB9su++0tC+fIu1+VVotbarL2eaZaxC217ew==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGtMFkGCSqGSIb3DQEFDTBMMCkGCSqGSIb3DQEFDDAcBAjkfqkzdtzUKQICCAAw+DAYIKoZIhvcNAgkFADAfBgsqgwiMmks9AQEBAgQQG/iQQd82wsC5mQN+bFY58ARQ+1QsQhP+TfWeZP9oztCd7s0Y1PnTM7WsMW2YtyS6Sn1Duyj9gx17cXLWvzQz9mS1Y+EoORulT1bTOzj585xSrNQLSqlnqTHcczNPKHCpU7EWE=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGqMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAg5ABTk7x6EhgICCAAC+ARAwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgE6BAiwam2gNHnwhgRQdpEG++fxlriH5rAAFneldLBFHSjBMSKJZNftQv0HFOQzwPhoiYKGXrcldx4bq4B1cbSJR+XFNvROPF/F3g0B/xn3YiU/5weo5B6Rsu1ojqTBA=+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGrMFcGCSqGSIb3DQEFDTBKMCwGCSqGSIb3DQEFDDAfBAiWX47sRP/MnQICCAAC+AQUwDAYIKoZIhvcNAgkFADAaBggqhkiG9w0DAjAOAgIAoAQIsvG8iVYWSzMEUBmO+u0pBmQkmBM3VI8A9cCRob5hAUojyd4NpodMJvpY4Da7mz20LgLyL5q2DsKgTfnzn+VTf+tP8vTv8i76Pr7aU89P784jI8nbtZgkcYizq++-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGqMFYGCSqGSIb3DQEFDTBJMCwGCSqGSIb3DQEFDDAfBAhkgv+PZmmr5gICCAAC+AQgwDAYIKoZIhvcNAgkFADAZBggqhkiG9w0DAjANAgF4BAjKasCGhxd/5ARQa6xv+vi/LtSLtwvrYHgWVGELLgkwqBJ6d2MM+ysEUeQILq5gbGcdnfGHtrakVtFir2ga6+lyrpv9Y0m5gLqgU0QPbm9XRqu1hdEKanozFs2H4=+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/x448-encrypted-scrypt.pem view
@@ -0,0 +1,18 @@+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAiW/EHVo6GeIQICQAAC+AQgCAQEwHQYJYIZIAWUDBAECBBAmafVnJcoBMlXmITIT+/hxBFCP8hh1dr4EGyMt+yqivyYBBAlCfjbAquHwPcys7kELHgwHG0PAgspAKkOLCqSgZVuomuS1Ya6bNzgeI+S2iW5nYwXL8QjIYYEXAxLx8ZQMVb0A==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhixLMqBNK2tAICQAAC+AQgCAQEwHQYJYIZIAWUDBAEWBBAA5uc3FQno7xKz2V8kxSCeBFACaBwzab3adlDg+bAIOfR1ytgNEI8+jLDxcPtEI6TjtmcLsT6NI3MaQfOnLy/gt/Uo5XbbiMJUGZ4Mm+ggEjLTPc7M2kLgGdKcLpnc8mWBCgyg==+-----END ENCRYPTED PRIVATE KEY-----+-----BEGIN ENCRYPTED PRIVATE KEY-----+MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhULg3jQtrIRwICQAAC+AQgCAQEwHQYJYIZIAWUDBAEqBBBk6w5yfaTk8et4FwnFTrfRBFBl+mFvAdi6X6Wm+44LkvMjiVCZ1xGDwLcZOyc5OnyNOHnW5tLBa7qvghaQljOyHS+na0z9uKTalbvRP+GaYKE3JRIsi9MzXOSXlZHLl6fDiVRw==+-----END ENCRYPTED PRIVATE KEY-----
+ tests/files/x448-public.pem view
@@ -0,0 +1,4 @@+-----BEGIN PUBLIC KEY-----+MEIwBQYDK2VvAzkA3cHetiJ/T/5Oo62IF1sDHfTBN9TUjxcTMhX3lHxw8W5ryxQ8+LWc2VD0RcfBn1BI3+QVEefPUd/c=+-----END PUBLIC KEY-----
+ tests/files/x448-self-signed-cert.pem view
@@ -0,0 +1,10 @@+-----BEGIN CERTIFICATE-----+MIIBUjCBvAIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0+QGV4YW1wbGUuY29tMB4XDTE4MDkwMjE1NTA1M1oXDTE4MTAwMjE1NTA1M1owITEf+MB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTBCMAUGAytlbwM5AN3B3rYi+f0/+TqOtiBdbAx30wTfU1I8XEzIV95R8cPFua8sUPC1nNlQ9EXHwZ9QSN/kFRHnz+1Hf3MA0GCSqGSIb3DQEBCwUAA4GBACwDI7X1kRJwPjDXTB1lkmf0U806ng0NuFuY+mXgt+PYiUhR3g76FC0chw2+WeeQ6WE4VkQsfdZz10bSWGtcaH4fCYVkQXF/BmNz8+7Mv0L9iK2438SBcK3WAYB+bC9CZw/gKObzDHxml4tlDaMmIsfYVqiHXuEAPI3wjw+qoHmxWqY+-----END CERTIFICATE-----
+ tests/files/x448-unencrypted-pkcs8.pem view
@@ -0,0 +1,4 @@+-----BEGIN PRIVATE KEY-----+MEYCAQAwBQYDK2VvBDoEOOC8GlnbqcOSJ+ISHV8HJTD3WGdC1sQdZ+0Gkx7sUvL0+Bm0KxY3cO9Rg9MQb3qkfVngRes0sb86Q+-----END PRIVATE KEY-----
+ tests/files/x448-unencrypted-trad.pem view
@@ -0,0 +1,4 @@+-----BEGIN X448 PRIVATE KEY-----+MEYCAQAwBQYDK2VvBDoEOOC8GlnbqcOSJ+ISHV8HJTD3WGdC1sQdZ+0Gkx7sUvL0+Bm0KxY3cO9Rg9MQb3qkfVngRes0sb86Q+-----END X448 PRIVATE KEY-----