packages feed

cryptostore 0.2.3.0 → 0.5.0.0

raw patch · 56 files changed

Files

ChangeLog.md view
@@ -1,5 +1,90 @@ # Revision history for cryptostore +## 0.5.0.0 - 2026-02-08++* Add support for key encapsulation to CMS, aka KEMRecipientInfo.  The only+  mechanism implemented for now is RSA-KEM.++* Decoding private keys in `Crypto.Store.PKCS8` is now more strict and makes+  sure that the optional public key matches the private key when present.++* Raise minimum bounds to crypton-x509-* >= 1.8.0 when flag `use_crypton`+  is enabled, and reflect the new transitive dependencies++* Fix encoding of key identifiers in CMS signer, originator and recipient info++## 0.4.0.0 - 2025-10-12++* Private keys are now represented as type `KeyPair` defined in module+  `Crypto.Store.PKCS8` instead of `PrivKey`.  This ensures that the+  corresponding public key is always available and that the library detects+  and reports data mistakes involving a wrong public key.  A new error+  `PublicPrivateKeyMismatch` is added for that purpose and returned by+  functions `certSigner`, `withRecipientKeyAgree`, `fromCredential` and+  `fromNamedCredential`.  Behavior of functions `toCredential` and+  `toNamedCredential` is now more strict, they return a key and leaf+  certificate that always match.  Functions to convert `KeyPair` to/from the+  usual type `PrivKey` are also exposed from module `Crypto.Store.PKCS8`.++* Added support of PBMAC1 for PKCS#12 integrity.  Type `IntegrityParams` used+  in functions `writeP12File` and `writeP12FileToMemory` is modified.+  A low-level function for PBMAC1 is also available in the PKCS5 module.++* CMS now supports SHA-3 algorithms in HMAC, ECDSA, or as digest algorithm++* Functions `pemToKey`, `pemToPubKey` and `pemToContentInfo` are modified to+  return error details when a PEM object cannot be read.  The old API signature+  that discarded error details is available with functions renamed+  `pemToKeyAccum`, `pemToPubKeyAccum` and `pemToContentInfoAccum`.++* Function `generateEncryptionParams` is removed and can be replaced with+  functions `ecbParams`, `generateCBCParams`, `generateCFBParams` or+  `generateCTRParams` according to the desired mode.++* Functions `generateAuthEnc128Params` and `generateAuthEnc256Params` are+  replaced with `authEnc128Params` and `authEnc256Params`.  The new API expects+  a `ContentEncryptionParams` argument instead of `ContentEncryptionAlg`.++* CMS encrypted data, enveloped data and authenticated-enveloped data now+  support optional HKDF-SHA256 derivation of the content-encryption key.  When+  generating encryption parameters, enable the key derivation feature with a+  call to functions `deriveEncryptionKey` or `authDeriveEncryptionKey`.  The+  feature prevents AEAD-to-CBC downgrade attack scenarios for `CCM` and `GCM`+  modes but requires support from the receiving end.++* Fixed encoding of some HMAC and PRF parameters++## 0.3.1.0 - 2024-05-05++* Strict validation of GCM/CCM authentication tag length++## 0.3.0.1 - 2023-06-25++* Add optional flag to use crypton instead of cryptonite++## 0.3.0.0 - 2023-01-14++* API change in PKCS5, PKCS8 and PKCS12 modules to handle better password-based+  encryption derived from an empty password.  All encryption/decryption+  functions now expect an opaque `ProtectionPassword` data type.  Conversion+  functions `toProtectionPassword` and `fromProtectionPassword` are provided.+  Additionnally in the PKCS12 module, the type `OptProtected` is replaced with+  `OptAuthenticated` when dealing with password integrity.  Similarly at that+  level, function `recover` is to be replaced with `recoverAuthenticated`.++* Added support for KMAC (Keccak Message Authentication Code) in CMS+  authenticated data, through constructors `KMAC_SHAKE128` and `KMAC_SHAKE256`.++* CMS key agreement now supports derivation with HKDF along with X9.63.  Data+  type `KeyAgreementParams` is modified to include a KDF instead of simply the+  digest algorithm.  HKDF has assigned OIDs only for standard DH and cannot be+  used with cofactor DH.++* Added CMS utility functions to deal with the `signingTime` attribute.++* Changed `withSignerCertificate` validation callback API to include the+  `signingTime` value when available.+ ## 0.2.3.0 - 2022-11-05  * Fix RC2 on big-endian architectures
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2018-2022, Olivier Chéron+Copyright (c) 2018-2026, Olivier Chéron  All rights reserved. 
README.md view
@@ -1,8 +1,8 @@ # cryptostore -[![Build Status](https://travis-ci.org/ocheron/cryptostore.png?branch=master)](https://travis-ci.org/ocheron/cryptostore)-[![BSD](https://b.repl.ca/v1/license-BSD-blue.png)](https://en.wikipedia.org/wiki/BSD_licenses)-[![Haskell](https://b.repl.ca/v1/language-haskell-lightgrey.png)](https://haskell.org/)+[![BSD](https://img.shields.io/badge/License-BSD-blue)](https://en.wikipedia.org/wiki/BSD_licenses)+[![Haskell](https://img.shields.io/badge/Language-Haskell-lightgrey)](https://haskell.org/)+[![Hackage](https://img.shields.io/hackage/v/cryptostore?label=Hackage&color=green)](https://hackage.haskell.org/package/cryptostore)  This package allows to read and write cryptographic objects to/from ASN.1. @@ -33,7 +33,7 @@ > :m Crypto.Store.PKCS8 > (key : _) <- readKeyFile "/path/to/privkey.pem" -- assuming single key > recover "mypassword" key-Right (PrivKeyRSA ...)+Right (keyPairFromPrivKey (PrivKeyRSA ...)) ```  Generating a private key and writing to disk, without encryption:@@ -41,7 +41,8 @@ ```haskell > :m Crypto.PubKey.RSA Crypto.Store.PKCS8 Data.X509 > privKey <- PrivKeyRSA . snd <$> generate (2048 `div` 8) 0x10001-> writeKeyFile PKCS8Format "/path/to/privkey.pem" [privKey]+> let keyPair = keyPairFromPrivKey privKey+> writeKeyFile PKCS8Format "/path/to/newkey.pem" [keyPair] ```  Generating a private key and writing to disk, with password-based encryption:@@ -50,17 +51,18 @@ > :set -XOverloadedStrings > :m Crypto.PubKey.RSA Crypto.Store.PKCS8 Data.X509 Crypto.Store.PKCS5 > privKey <- PrivKeyRSA . snd <$> generate (2048 `div` 8) 0x10001-> salt <- generateSalt 8-> let kdf = PBKDF2 salt 2048 Nothing PBKDF2_SHA256-> encParams <- generateEncryptionParams (CBC AES256)+> let keyPair = keyPairFromPrivKey privKey+> salt <- generateSalt 16+> let kdf = PBKDF2 salt 200000 Nothing PBKDF2_SHA256+> encParams <- generateCBCParams AES256 > let pbes = PBES2 (PBES2Parameter kdf encParams)-> writeEncryptedKeyFile "/path/to/privkey.pem" pbes "mypassword" privKey+> writeEncryptedKeyFile "/path/to/newkey.pem" pbes "mypassword" keyPair Right () ```  Parameters used in this example are AES-256-CBC as cipher, PBKDF2 as-key-derivation function, with an 8-byte salt, 2048 iterations and SHA-256 as-pseudorandom function.+key-derivation function, with a 16-byte salt, 200,000 iterations and SHA-256+as pseudorandom function.  ## Public Keys and Signed Objects @@ -92,18 +94,33 @@ API to read PKCS #12 files requires some password at each layer.  This API is available in module `Crypto.Store.PKCS12`. -Reading a binary PKCS #12 file using distinct integrity and privacy passwords:+Reading a binary PKCS #12 file using a single password doing both integrity+and privacy (usual case):  ```haskell > :set -XOverloadedStrings > :m Crypto.Store.PKCS12 > Right p12 <- readP12File "/path/to/file.p12"-> let Right pkcs12 = recover "myintegrityassword" p12+> let Right (password, pkcs12) = recoverAuthenticated "mypassword" p12+> let Right contents = recover password (unPKCS12 pkcs12)+> getAllSafeX509Certs contents+[SignedExact {getSigned = ...}]+> recover password (getAllSafeKeys contents)+Right [keyPairFromPrivKey (PrivKeyRSA ...)]+```++Reading a binary PKCS #12 file using distinct integrity and privacy passwords:++```haskell+> :set -XOverloadedStrings+> :m Crypto.Store.PKCS12+> Right p12 <- readP12File "/path/to/other.p12"+> let Right (_, pkcs12) = recoverAuthenticated "myintegritypassword" p12 > let Right contents = recover "myprivacypassword" (unPKCS12 pkcs12) > getAllSafeX509Certs contents [SignedExact {getSigned = ...}] > recover "myprivacypassword" (getAllSafeKeys contents)-Right [PrivKeyRSA ...]+Right [keyPairFromPrivKey (PrivKeyRSA ...)] ```  Generating a PKCS #12 file containing a private key:@@ -117,21 +134,22 @@  -- Put the key inside a bag > :m Crypto.Store.PKCS12 Crypto.Store.PKCS8 Crypto.Store.PKCS5 Crypto.Store.CMS+> let keyPair = keyPairFromPrivKey privKey > let attrs = setFriendlyName "Some Key" []->     keyBag = Bag (KeyBag $ FormattedKey PKCS8Format privKey) attrs+>     keyBag = Bag (KeyBag $ FormattedKey PKCS8Format keyPair) attrs >     contents = SafeContents [keyBag]  -- Encrypt the contents-> salt <- generateSalt 8-> let kdf = PBKDF2 salt 2048 Nothing PBKDF2_SHA256-> encParams <- generateEncryptionParams (CBC AES256)+> salt <- generateSalt 16+> let kdf = PBKDF2 salt 200000 Nothing PBKDF2_SHA256+> encParams <- generateCBCParams AES256 > let pbes = PBES2 (PBES2Parameter kdf encParams) >     Right pkcs12 = encrypted pbes "mypassword" contents  -- Save to PKCS #12 with integrity protection (same password)-> salt' <- generateSalt 8-> let iParams = (DigestAlgorithm SHA256, PBEParameter salt' 2048)-> writeP12File "/path/to/privkey.p12" iParams "mypassword" pkcs12+> salt' <- generateSalt 16+> let iParams = TraditionalIntegrity (DigestAlgorithm SHA256) (PBEParameter salt' 200000)+> writeP12File "/path/to/newkey.p12" iParams "mypassword" pkcs12 Right () ``` @@ -143,34 +161,68 @@ > :m Crypto.Store.PKCS12 Crypto.Store.PKCS8 Crypto.Store.PKCS5 Crypto.Store.CMS  -- Read PKCS #12 content as credential-> Right p12 <- readP12File "/path/to/file.p12"-> let Right pkcs12 = recover "myintegrityassword" p12+> Right p12 <- readP12File "/path/to/other.p12"+> let Right (_, pkcs12) = recoverAuthenticated "myintegritypassword" p12 > let Right (Just cred) = recover "myprivacypassword" (toCredential pkcs12) > cred (CertificateChain [...], PrivKeyRSA (...))  -- Scheme to reencrypt the key-> saltK <- generateSalt 8-> let kdfK = PBKDF2 saltK 2048 Nothing PBKDF2_SHA256-> encParamsK <- generateEncryptionParams (CBC AES256)+> saltK <- generateSalt 16+> let kdfK = PBKDF2 saltK 200000 Nothing PBKDF2_SHA256+> encParamsK <- generateCBCParams AES256 > let sKey = PBES2 (PBES2Parameter kdfK encParamsK)  -- Scheme to reencrypt the certificate chain > saltC <- generateSalt 8-> let kdfC = PBKDF2 saltC 1024 Nothing PBKDF2_SHA256-> encParamsC <- generateEncryptionParams (CBC AES128)+> let kdfC = PBKDF2 saltC 100000 Nothing PBKDF2_SHA256+> encParamsC <- generateCBCParams AES128 > let sCert = PBES2 (PBES2Parameter kdfC encParamsC)  -- Write the content back to a new file > let Right pkcs12' = fromCredential (Just sCert) sKey "myprivacypassword" cred-> salt <- generateSalt 8-> let iParams = (DigestAlgorithm SHA256, PBEParameter salt 2048)-> writeP12File "/path/to/newfile.p12" iParams "myintegrityassword" pkcs12'+> salt <- generateSalt 16+> let iParams = TraditionalIntegrity (DigestAlgorithm SHA256) (PBEParameter salt 200000)+> writeP12File "/path/to/newfile.p12" iParams "myintegritypassword" pkcs12' ```  Variants `toNamedCredential` and `fromNamedCredential` are also available when PKCS #12 elements need an alias (friendly name). +The library also supports integrity protection with PBMAC1 as defined in+[RFC 9579](https://tools.ietf.org/html/rfc9579). The following example shows+how to use PBKDF2 with SHA-256 HMAC and PRF:++```haskell+> :set -XOverloadedStrings++-- Generate a private key+> :m Crypto.PubKey.RSA Data.X509+> privKey <- PrivKeyRSA . snd <$> generate (2048 `div` 8) 0x10001++-- Put the key inside a bag+> :m Crypto.Store.PKCS12 Crypto.Store.PKCS8 Crypto.Store.PKCS5 Crypto.Store.CMS+> let keyPair = keyPairFromPrivKey privKey+> let attrs = setFriendlyName "Some Key" []+>     keyBag = Bag (KeyBag $ FormattedKey PKCS8Format keyPair) attrs+>     contents = SafeContents [keyBag]++-- Encrypt the contents+> salt <- generateSalt 16+> let kdf = PBKDF2 salt 200000 Nothing PBKDF2_SHA256+> encParams <- generateCBCParams AES256+> let pbes = PBES2 (PBES2Parameter kdf encParams)+>     Right pkcs12 = encrypted pbes "mypassword" contents++-- Save to PKCS #12 with PBMAC1 integrity protection (same password)+> salt' <- generateSalt 16+> let kdf' = PBKDF2 salt' 200000 (Just 32) PBKDF2_SHA256+> let authScheme = PBMAC1 $ PBMAC1Parameter kdf' (HMAC SHA256)+> let iParams = AuthSchemeIntegrity authScheme+> writeP12File "/path/to/newkey.p12" iParams "mypassword" pkcs12+Right ()+```+ ## Cryptographic Message Syntax  The API to read and write CMS content is available in `Crypto.Store.CMS`.  The@@ -208,15 +260,15 @@ > let info = DataCI "Hi, what will you need from the cryptostore?"  -- Content encryption will use AES-128-CBC-> ceParams <- generateEncryptionParams (CBC AES128)+> ceParams <- generateCBCParams AES128 > ceKey <- generateKey ceParams :: IO ContentEncryptionKey  -- Encrypt the Content Encryption Key with a Password Recipient Info, -- i.e. a KDF will derive the Key Encryption Key from a password -- that the recipient will need to know-> salt <- generateSalt 8-> let kdf = PBKDF2 salt 2048 Nothing PBKDF2_SHA256-> keParams <- generateEncryptionParams (CBC AES128)+> salt <- generateSalt 16+> let kdf = PBKDF2 salt 200000 Nothing PBKDF2_SHA256+> keParams <- generateCBCParams AES128 > let pri = forPasswordRecipient "mypassword" kdf (PWRIKEK keParams)  -- Generate the enveloped structure for this single recipient.  Encrypted@@ -256,7 +308,7 @@  -- Read signer certificate and private key > (key : _) <- readKeyFile "/path/to/privkey.pem" -- assuming single key-> let Right priv = recover "mypassword" key+> let Right pair = recover "mypassword" key > chain <- readSignedObject "/path/to/cert.pem" :: IO [SignedCertificate] > let cert = CertificateChain chain @@ -266,7 +318,7 @@  -- Generate the signed structure with a single signer.  Signed content is -- kept attached in the structure.-> let signer = certSigner (RSAPSS params) priv cert (Just []) []+> let signer = certSigner (RSAPSS params) pair cert (Just []) [] > Right signedData <- signData [signer] info > let signedCI = toAttachedCI signedData > writeCMSFile "/path/to/signed.pem" [signedCI]@@ -289,9 +341,67 @@ > :m Crypto.Store.CMS Data.Default.Class > [SignedDataCI signedEncapData] <- readCMSFile "/path/to/signed.pem" > signedData <- fromAttached signedEncapData-> let doValidation chain = null <$> validateNoFQHN store def noServiceID chain+> let doValidation _ chain = null <$> validateNoFQHN store def noServiceID chain > verifySignedData (withSignerCertificate doValidation) signedData Right (DataCI "Some trustworthy content")+```++### Authenticated-enveloped data++The following examples generate a CMS structure auth-enveloping some data to a+KEM recipient, then decrypt the data to recover the content.++#### Generating authenticated-enveloped data++```haskell+> :set -XOverloadedStrings+> :m Crypto.Store.CMS Data.X509 Crypto.Store.X509++-- Input content info+> let info = DataCI "Powered by Haskell"++-- Read receipient certificate+> [cert] <- readSignedObject "/path/to/cert.pem" :: IO [SignedCertificate]++-- Content encryption will use AES-128-GCM, and we protect against manipulation+-- of algorithm identifiers as defined in RFC 9709+> aceParams' <- generateGCMParams AES128 16+> let aceParams = authDeriveEncryptionKey aceParams'+> aceKey <- generateKey aceParams :: IO ContentEncryptionKey++-- Encrypt the Content Encryption Key with a KEM Recipient Info,+-- i.e. a KDF will derive the Key Encryption Key from a shared secret produced+-- by a Key Encapsulation Mechanism.  We are using RSA-KEM based on KDF3 with+-- SHA-256 to produce a 16-byte shared secret.  Further derivation of the KEK+-- uses HKDF with SHA-256.  The CEK is finally wrapped with AES-Wrap-128.+> let kem = KeyEncapsulationRSA (KDF3 (DigestAlgorithm SHA256)) 16+> let kdf = HKDF (DigestAlgorithm SHA256)+> let kri = forKeyEncapRecipient cert kdf AES128_WRAP kem++-- Generate the auth-enveloped structure for this single recipient.  Encrypted+-- content is kept attached in the structure.+> Right authEnvData <- authEnvelopData mempty aceKey aceParams [kri] [] [] info+> let authEnvCI = toAttachedCI authEnvData+> writeCMSFile "/path/to/authEnveloped.pem" [authEnvCI]+```++#### Opening the authenticated-enveloped data++```haskell+> :set -XOverloadedStrings+> :m Crypto.Store.CMS Data.X509 Crypto.Store.X509 Crypto.Store.PKCS8++-- Read receipient certificate and private key+> (key : _) <- readKeyFile "/path/to/privkey.pem" -- assuming single key+> let Right pair = recover "mypassword" key+> [cert] <- readSignedObject "/path/to/cert.pem" :: IO [SignedCertificate]++-- Then this recipient just has to read the file and recover enveloped+-- content using the private key and certificate+> [AuthEnvelopedDataCI authEnvEncapData] <- readCMSFile "/path/to/authEnveloped.pem"+> authEnvData <- fromAttached authEnvEncapData+> openAuthEnvelopedData (withRecipientKeyEncap pair cert) authEnvData+Right (DataCI "Powered by Haskell") ```  ## Algorithms and security
cryptostore.cabal view
@@ -1,5 +1,5 @@ name:                cryptostore-version:             0.2.3.0+version:             0.5.0.0 synopsis:            Serialization of cryptographic data types description:         Haskell implementation of PKCS \#8, PKCS \#12 and CMS                      (Cryptographic Message Syntax).@@ -9,16 +9,21 @@ maintainer:          olivier.cheron@gmail.com copyright:           Olivier Chéron category:            Cryptography, Codec-homepage:            https://github.com/ocheron/cryptostore-bug-reports:         https://github.com/ocheron/cryptostore/issues+homepage:            https://codeberg.org/ocheron/cryptostore+bug-reports:         https://codeberg.org/ocheron/cryptostore/issues build-type:          Simple extra-source-files:  README.md ChangeLog.md tests/files/*.pem cabal-version:       >=1.10  source-repository head   type:     git-  location: https://github.com/ocheron/cryptostore+  location: https://codeberg.org/ocheron/cryptostore +flag use_crypton+  description: Use crypton instead of cryptonite+  manual: True+  default: False+ library   hs-source-dirs:      src   exposed-modules:     Crypto.Store.CMS@@ -47,22 +52,33 @@                      , Crypto.Store.CMS.Type                      , Crypto.Store.CMS.Util                      , Crypto.Store.Cipher.RC2.Primitive+                     , Crypto.Store.Keys                      , Crypto.Store.PEM                      , Crypto.Store.PKCS5.PBES1                      , Crypto.Store.PKCS8.EC+                     , Crypto.Store.PubKey.RSA.KEM                      , Crypto.Store.Util   -- other-extensions:   build-depends:       base >= 4.9 && < 5                      , bytestring                      , basement                      , memory-                     , cryptonite >= 0.26-                     , pem >= 0.1 && < 0.3-                     , asn1-types >= 0.3.1 && < 0.4-                     , asn1-encoding >= 0.9 && < 0.10-                     , hourglass >= 0.2+  if flag(use_crypton)+    build-depends:     crypton+                     , crypton-asn1-encoding >= 0.10.0 && < 0.11+                     , crypton-asn1-types >= 0.4.1 && < 0.5+                     , crypton-pem >= 0.2.4 && <0.4+                     , crypton-x509 >= 1.8.0+                     , crypton-x509-validation >= 1.8.0+                     , time-hourglass+  else+    build-depends:     cryptonite >=0.26                      , x509 >= 1.7.5                      , x509-validation >= 1.5+                     , pem >= 0.1 && < 0.3+                     , asn1-types >= 0.3.1 && < 0.4+                     , asn1-encoding >= 0.9.6 && < 0.10+                     , hourglass >= 0.2.10   default-language:    Haskell2010   ghc-options:         -Wall @@ -85,15 +101,22 @@                      , X509.Tests   build-depends:       base >= 4.9 && < 5                      , bytestring-                     , asn1-types >= 0.3.1 && < 0.4-                     , cryptonite >= 0.25                      , memory                      , tasty                      , tasty-hunit                      , tasty-quickcheck+                     , cryptostore+  if flag(use_crypton)+    build-depends:     crypton+                     , crypton-asn1-types >= 0.4.1 && < 0.5+                     , crypton-pem >= 0.2.4 && <0.4+                     , crypton-x509+                     , time-hourglass+  else+    build-depends:     cryptonite >=0.25+                     , x509+                     , asn1-types >= 0.3.1 && < 0.4                      , hourglass                      , pem-                     , x509-                     , cryptostore   default-language:    Haskell2010   ghc-options:         -Wall
src/Crypto/Store/ASN1/Generate.hs view
@@ -107,7 +107,7 @@ gIntVal :: ASN1Elem e => Integer -> ASN1Stream e gIntVal = gOne . IntVal --- | Generate an 'OID' ASN.1 element.+-- | Generate an t'OID' ASN.1 element. gOID :: ASN1Elem e => OID -> ASN1Stream e gOID = gOne . OID 
src/Crypto/Store/ASN1/Parse.hs view
@@ -9,7 +9,7 @@ -- with the following additions: -- -- * Parsed stream is annotated, i.e. parser input is @('ASN1', e)@ instead of---   @'ASN1'@.  Main motivation is to allow to parse a sequence of 'ASN1Repr'+--   @'ASN1'@.  Main motivation is to allow to parse a sequence of @ASN1Repr@ --   and hold the exact binary content that has been parsed.  As consequence, --   no @getObject@ function is provided.  Function 'withAnnotations' runs --   a parser and returns all annotations consumed in a monoid concatenation.@@ -40,7 +40,9 @@     ) where  import Data.ASN1.Types-import Data.Monoid+#if !(MIN_VERSION_base(4,11,0))+import Data.Monoid ( (<>) )+#endif import Control.Applicative import Control.Arrow (first) import Control.Monad (MonadPlus(..), liftM2)
src/Crypto/Store/CMS.hs view
@@ -21,6 +21,11 @@ -- * <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)+-- * <https://tools.ietf.org/html/rfc8702 RFC 8702>: Use of the SHAKE One-Way Hash Functions in the Cryptographic Message Syntax (CMS)+-- * <https://tools.ietf.org/html/rfc9629 RFC 9629>: Using Key Encapsulation Mechanism (KEM) Algorithms in the Cryptographic Message Syntax (CMS)+-- * <https://tools.ietf.org/html/rfc9688 RFC 9688>: Use of the SHA3 One-Way Hash Functions in the Cryptographic Message Syntax (CMS)+-- * <https://tools.ietf.org/html/rfc9690 RFC 9690>: Use of the RSA-KEM Algorithm in the Cryptographic Message Syntax (CMS)+-- * <https://tools.ietf.org/html/rfc9709 RFC 9709>: Encryption Key Derivation in the Cryptographic Message Syntax (CMS) Using HKDF with SHA-256 {-# LANGUAGE RecordWildCards #-} module Crypto.Store.CMS     ( ContentType(..)@@ -60,6 +65,8 @@     , KeyEncryptionParams(..)     , KeyTransportParams(..)     , KeyAgreementParams(..)+    , KeyAgreementKDF(..)+    , KeyEncapsulationMechanism(..)     , RecipientInfo(..)     , EnvelopedData(..)     , ProducerOfRI@@ -91,6 +98,10 @@     , PasswordRecipientInfo(..)     , forPasswordRecipient     , withRecipientPassword+     -- ** Key Encapsulation recipients+    , KEMRecipientInfo(..)+    , forKeyEncapRecipient+    , withRecipientKeyEncap     -- * Digested data     , DigestProxy(..)     , DigestAlgorithm(..)@@ -104,8 +115,12 @@     , ContentEncryptionParams     , EncryptedContent     , EncryptedData(..)-    , generateEncryptionParams+    , ecbParams+    , generateCBCParams     , generateRC2EncryptionParams+    , generateCFBParams+    , generateCTRParams+    , deriveEncryptionKey     , getContentEncryptionAlg     , encryptData     , decryptData@@ -120,11 +135,12 @@     , AuthContentEncryptionAlg(..)     , AuthContentEncryptionParams     , AuthEnvelopedData(..)-    , generateAuthEnc128Params-    , generateAuthEnc256Params+    , authEnc128Params+    , authEnc256Params     , generateChaChaPoly1305Params     , generateCCMParams     , generateGCMParams+    , authDeriveEncryptionKey     , authEnvelopData     , openAuthEnvelopedData     -- * Key derivation@@ -132,6 +148,7 @@     , generateSalt     , KeyDerivationFunc(..)     , PBKDF2_PRF(..)+    , KeyDerivationFn(..)     -- * Secret-key algorithms     , HasKeySize(..)     , generateKey@@ -144,6 +161,10 @@     , findAttribute     , setAttribute     , filterAttributes+    -- * CMS standard attributes+    , getSigningTimeAttr+    , setSigningTimeAttr+    , setSigningTimeAttrCurrent     -- * Originator information     , OriginatorInfo(..)     , CertificateChoice(..)@@ -158,7 +179,7 @@ import Data.ASN1.Encoding import Data.ByteString (ByteString) import Data.Maybe (isJust)-import Data.List (nub, unzip3)+import Data.List (nub)  import Crypto.Hash @@ -248,7 +269,7 @@             -> ContentInfo             -> f (Either StoreError (EnvelopedData EncryptedContent)) envelopData oinfo key params envFns attrs ci =-    f <$> (sequence <$> traverse ($ key) envFns)+    f . sequence <$> traverse ($ key) envFns   where     ebs = contentEncrypt key params (encapsulate ci)     f ris = build <$> ebs <*> ris@@ -298,7 +319,7 @@                           -> ContentInfo                           -> f (Either StoreError (AuthenticatedData EncapsulatedContent)) generateAuthenticatedData oinfo key macAlg digAlg envFns aAttrs uAttrs ci =-    f <$> (sequence <$> traverse ($ key) envFns)+    f . sequence <$> traverse ($ key) envFns   where     msg = encapsulate ci     ct  = getContentType ci@@ -377,17 +398,17 @@                 -> ContentInfo                 -> f (Either StoreError (AuthEnvelopedData EncryptedContent)) authEnvelopData oinfo key params envFns aAttrs uAttrs ci =-    f <$> (sequence <$> traverse ($ key) envFns)+    f . sequence <$> traverse ($ key) envFns   where-    raw = encodeASN1Object params+    prm = derObjectExact params     aad = encodeAuthAttrs aAttrs-    ebs = authContentEncrypt key params raw aad (encapsulate ci)+    ebs = authContentEncrypt key prm aad (encapsulate ci)     f ris = build <$> ebs <*> ris     build (authTag, bs) ris = AuthEnvelopedData                        { aeOriginatorInfo = oinfo                        , aeRecipientInfos = ris                        , aeContentType = getContentType ci-                       , aeContentEncryptionParams = ASN1ObjectExact params raw+                       , aeContentEncryptionParams = prm                        , aeEncryptedContent = bs                        , aeAuthAttrs = aAttrs                        , aeMAC = authTag@@ -405,10 +426,8 @@     return (r >>= decapsulate ct)   where     ct       = aeContentType-    params   = exactObject aeContentEncryptionParams-    raw      = exactObjectRaw aeContentEncryptionParams     aad      = encodeAuthAttrs aeAuthAttrs-    decr k   = authContentDecrypt k params raw aad aeEncryptedContent aeMAC+    decr k   = authContentDecrypt k aeContentEncryptionParams aad aeEncryptedContent aeMAC   -- SignedData@@ -419,7 +438,7 @@ signData :: Applicative f          => [ProducerOfSI f] -> ContentInfo -> f (Either StoreError (SignedData EncapsulatedContent)) signData sigFns ci =-    f <$> (sequence <$> traverse (\fn -> fn ct msg) sigFns)+    f . sequence <$> traverse (\fn -> fn ct msg) sigFns   where     msg = encapsulate ci     ct  = getContentType ci
src/Crypto/Store/CMS/Algorithms.hs view
@@ -8,6 +8,7 @@ -- Cryptographic Message Syntax algorithms {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -16,6 +17,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} module Crypto.Store.CMS.Algorithms     ( DigestAlgorithm(..)@@ -24,7 +26,7 @@     , MessageAuthenticationCode     , MACAlgorithm(..)     , mac-    , HasStrength+    , HasStrength(..)     , securityAcceptable     , HasKeySize(..)     , getMaximumKeySize@@ -33,19 +35,24 @@     , ContentEncryptionCipher(..)     , ContentEncryptionAlg(..)     , ContentEncryptionParams(..)-    , generateEncryptionParams+    , ecbParams+    , generateCBCParams     , generateRC2EncryptionParams+    , generateCFBParams+    , generateCTRParams+    , deriveEncryptionKey     , getContentEncryptionAlg     , proxyBlockSize     , contentEncrypt     , contentDecrypt     , AuthContentEncryptionAlg(..)     , AuthContentEncryptionParams-    , generateAuthEnc128Params-    , generateAuthEnc256Params+    , authEnc128Params+    , authEnc256Params     , generateChaChaPoly1305Params     , generateCCMParams     , generateGCMParams+    , authDeriveEncryptionKey     , authContentEncrypt     , authContentDecrypt     , PBKDF2_PRF(..)@@ -54,7 +61,10 @@     , generateSalt     , KeyDerivationFunc(..)     , kdfKeyLength+    , kdfKeyLengthModify     , kdfDerive+    , KeyDerivationFn(..)+    , kdfApply     , KeyEncryptionParams(..)     , keyEncrypt     , keyDecrypt@@ -63,11 +73,15 @@     , transportEncrypt     , transportDecrypt     , KeyAgreementParams(..)+    , KeyAgreementKDF(..)     , ECDHPair     , ecdhGenerate     , ecdhPublic     , ecdhEncrypt     , ecdhDecrypt+    , KeyEncapsulationMechanism(..)+    , kemEncap+    , kemDecap     , MaskGenerationFunc(..)     , mgf     , SignatureValue@@ -90,7 +104,7 @@ import           Data.ByteArray (ByteArray, ByteArrayAccess) import qualified Data.ByteArray as B import           Data.ByteString (ByteString)-import           Data.Maybe (fromMaybe)+import           Data.Maybe (fromJust, fromMaybe) import           Data.Proxy import           Data.Word import qualified Data.X509 as X509@@ -109,9 +123,11 @@ import           Crypto.ECC (Curve_X25519, Curve_X448, ecdh) import           Crypto.Error import qualified Crypto.Hash as Hash+import qualified Crypto.KDF.HKDF as HKDF import qualified Crypto.KDF.PBKDF2 as PBKDF2 import qualified Crypto.KDF.Scrypt as Scrypt import qualified Crypto.MAC.HMAC as HMAC+import qualified Crypto.MAC.KMAC as KMAC import qualified Crypto.MAC.Poly1305 as Poly1305 import           Crypto.Number.Serialize import qualified Crypto.PubKey.Curve25519 as X25519@@ -139,7 +155,9 @@ 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.Keys import           Crypto.Store.PKCS8.EC+import qualified Crypto.Store.PubKey.RSA.KEM as RsaKem import           Crypto.Store.Util  @@ -164,6 +182,14 @@     SHA384 :: DigestProxy Hash.SHA384     -- | SHA-512     SHA512 :: DigestProxy Hash.SHA512+    -- | SHA3-224+    SHA3_224 :: DigestProxy Hash.SHA3_224+    -- | SHA3-256+    SHA3_256 :: DigestProxy Hash.SHA3_256+    -- | SHA3-384+    SHA3_384 :: DigestProxy Hash.SHA3_384+    -- | SHA3-512+    SHA3_512 :: DigestProxy Hash.SHA3_512     -- | SHAKE128 (256 bits)     SHAKE128_256 :: DigestProxy (Hash.SHAKE128 256)     -- | SHAKE256 (512 bits)@@ -173,7 +199,31 @@     -- | SHAKE256 (variable size)     SHAKE256 :: KnownNat n => Proxy n -> DigestProxy (Hash.SHAKE256 n) -deriving instance Show (DigestProxy hashAlg)++instance Show (DigestProxy hashAlg) where+    showsPrec _ MD2          = showString "MD2"+    showsPrec _ MD4          = showString "MD4"+    showsPrec _ MD5          = showString "MD5"+    showsPrec _ SHA1         = showString "SHA1"+    showsPrec _ SHA224       = showString "SHA224"+    showsPrec _ SHA256       = showString "SHA256"+    showsPrec _ SHA384       = showString "SHA384"+    showsPrec _ SHA512       = showString "SHA512"+    showsPrec _ SHA3_224     = showString "SHA3_224"+    showsPrec _ SHA3_256     = showString "SHA3_256"+    showsPrec _ SHA3_384     = showString "SHA3_384"+    showsPrec _ SHA3_512     = showString "SHA3_512"+    showsPrec _ SHAKE128_256 = showString "SHAKE128_256"+    showsPrec _ SHAKE256_512 = showString "SHAKE256_512"+    showsPrec d (SHAKE128 p) =+        showParen (d > 10) $ showString "SHAKE128 " . showNat 11 p+    showsPrec d (SHAKE256 p) =+        showParen (d > 10) $ showString "SHAKE256 " . showNat 11 p++showNat :: KnownNat n => Int -> Proxy n -> ShowS+showNat d n = showParen (d > 0) $+    shows n . showString " :: Proxy " . shows (natVal n)+ deriving instance Eq (DigestProxy hashAlg)  instance HasStrength (DigestProxy hashAlg) where@@ -185,6 +235,10 @@     getSecurityBits SHA256       = 128     getSecurityBits SHA384       = 192     getSecurityBits SHA512       = 256+    getSecurityBits SHA3_224     = 112+    getSecurityBits SHA3_256     = 128+    getSecurityBits SHA3_384     = 192+    getSecurityBits SHA3_512     = 256     getSecurityBits SHAKE128_256 = 128     getSecurityBits SHAKE256_512 = 256     getSecurityBits (SHAKE128 a) = shakeSecurityBits 128 a@@ -210,6 +264,10 @@     DigestAlgorithm SHA256       == DigestAlgorithm SHA256       = True     DigestAlgorithm SHA384       == DigestAlgorithm SHA384       = True     DigestAlgorithm SHA512       == DigestAlgorithm SHA512       = True+    DigestAlgorithm SHA3_224     == DigestAlgorithm SHA3_224     = True+    DigestAlgorithm SHA3_256     == DigestAlgorithm SHA3_256     = True+    DigestAlgorithm SHA3_384     == DigestAlgorithm SHA3_384     = True+    DigestAlgorithm SHA3_512     == DigestAlgorithm SHA3_512     = 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@@ -228,6 +286,10 @@     | Type_SHA256     | Type_SHA384     | Type_SHA512+    | Type_SHA3_224+    | Type_SHA3_256+    | Type_SHA3_384+    | Type_SHA3_512     | Type_SHAKE128_256     | Type_SHAKE256_512     | Type_SHAKE128_Len@@ -242,6 +304,10 @@              , Type_SHA256              , Type_SHA384              , Type_SHA512+             , Type_SHA3_224+             , Type_SHA3_256+             , Type_SHA3_384+             , Type_SHA3_512              , Type_SHAKE128_256              , Type_SHAKE256_512              , Type_SHAKE128_Len@@ -257,6 +323,10 @@     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_SHA3_224     = [2,16,840,1,101,3,4,2,7]+    getObjectID Type_SHA3_256     = [2,16,840,1,101,3,4,2,8]+    getObjectID Type_SHA3_384     = [2,16,840,1,101,3,4,2,9]+    getObjectID Type_SHA3_512     = [2,16,840,1,101,3,4,2,10]     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]@@ -277,6 +347,10 @@     algorithmType (DigestAlgorithm SHA256)       = Type_SHA256     algorithmType (DigestAlgorithm SHA384)       = Type_SHA384     algorithmType (DigestAlgorithm SHA512)       = Type_SHA512+    algorithmType (DigestAlgorithm SHA3_224)     = Type_SHA3_224+    algorithmType (DigestAlgorithm SHA3_256)     = Type_SHA3_256+    algorithmType (DigestAlgorithm SHA3_384)     = Type_SHA3_384+    algorithmType (DigestAlgorithm SHA3_512)     = Type_SHA3_512     algorithmType (DigestAlgorithm SHAKE128_256) = Type_SHAKE128_256     algorithmType (DigestAlgorithm SHAKE256_512) = Type_SHAKE256_512     algorithmType (DigestAlgorithm (SHAKE128 _)) = Type_SHAKE128_Len@@ -297,12 +371,16 @@     parseParameter Type_SHA256       = parseDigestParam (DigestAlgorithm SHA256)     parseParameter Type_SHA384       = parseDigestParam (DigestAlgorithm SHA384)     parseParameter Type_SHA512       = parseDigestParam (DigestAlgorithm SHA512)+    parseParameter Type_SHA3_224     = parseDigestParam (DigestAlgorithm SHA3_224)+    parseParameter Type_SHA3_256     = parseDigestParam (DigestAlgorithm SHA3_256)+    parseParameter Type_SHA3_384     = parseDigestParam (DigestAlgorithm SHA3_384)+    parseParameter Type_SHA3_512     = parseDigestParam (DigestAlgorithm SHA3_512)     parseParameter Type_SHAKE128_256 = parseDigestParam (DigestAlgorithm SHAKE128_256)     parseParameter Type_SHAKE256_512 = parseDigestParam (DigestAlgorithm SHAKE256_512)     parseParameter Type_SHAKE128_Len = parseBitLen $-        \(SomeNat p) -> DigestAlgorithm (SHAKE128 p)+        \(SomeNat p) -> return $ DigestAlgorithm (SHAKE128 p)     parseParameter Type_SHAKE256_Len = parseBitLen $-        \(SomeNat p) -> DigestAlgorithm (SHAKE256 p)+        \(SomeNat p) -> return $ DigestAlgorithm (SHAKE256 p)  -- | Compute the digest of a message. digest :: ByteArrayAccess message => DigestAlgorithm -> message -> ByteString@@ -318,13 +396,16 @@ parseDigestParam :: Monoid e => DigestAlgorithm -> ParseASN1 e DigestAlgorithm parseDigestParam p = getNextMaybe nullOrNothing >> return p -parseBitLen :: Monoid e => (SomeNat -> a) -> ParseASN1 e a-parseBitLen build = do+parseBitLen :: Monoid e => (SomeNat -> ParseASN1 e a) -> ParseASN1 e a+parseBitLen fn = do     IntVal n <- getNext     case someNatVal n of         Nothing -> throwParseError ("Invalid bit length: " ++ show n)-        Just sn -> return (build sn)+        Just sn -> fn sn +p256 :: Proxy 256+p256 = Proxy+ p512 :: Proxy 512 p512 = Proxy @@ -384,52 +465,129 @@ -- | Message authentication code.  Equality is time constant. type MessageAuthenticationCode = AuthTag +data MACType+    = forall hashAlg . Hash.HashAlgorithm hashAlg+        => TypeHMAC (DigestProxy hashAlg)+    | TypeKMAC_SHAKE128+    | TypeKMAC_SHAKE256++deriving instance Show MACType+ -- | Message Authentication Code (MAC) Algorithm. data MACAlgorithm     = forall hashAlg . Hash.HashAlgorithm hashAlg         => HMAC (DigestProxy hashAlg)+    | forall n . KnownNat n => KMAC_SHAKE128 (Proxy n) ByteString+    | forall n . KnownNat n => KMAC_SHAKE256 (Proxy n) ByteString -deriving instance Show MACAlgorithm+instance Show MACAlgorithm where+    showsPrec d (HMAC p) = showParen (d > 10) $+        showString "HMAC " . showsPrec 11 p+    showsPrec d (KMAC_SHAKE128 p s) = showParen (d > 10) $+        showString "KMAC_SHAKE128 " . showNat 11 p .+            showChar ' ' . showsPrec 11 s+    showsPrec d (KMAC_SHAKE256 p s) = showParen (d > 10) $+        showString "KMAC_SHAKE256 " . showNat 11 p .+            showChar ' ' . showsPrec 11 s  instance Eq MACAlgorithm where-    HMAC a1 == HMAC a2 = DigestAlgorithm a1 == DigestAlgorithm a2+    HMAC a1             == HMAC a2             =+        DigestAlgorithm a1 == DigestAlgorithm a2+    KMAC_SHAKE128 p1 s1 == KMAC_SHAKE128 p2 s2 =+        natVal p1 == natVal p2 && s1 == s2+    KMAC_SHAKE256 p1 s1 == KMAC_SHAKE256 p2 s2 =+        natVal p1 == natVal p2 && s1 == s2+    _                   == _                   = False  instance HasStrength MACAlgorithm where     getSecurityBits (HMAC a) = getSecurityBits (DigestAlgorithm a)+    getSecurityBits (KMAC_SHAKE128 p _) = shakeSecurityBits 128 p+    getSecurityBits (KMAC_SHAKE256 p _) = shakeSecurityBits 256 p -instance Enumerable MACAlgorithm where-    values = [ HMAC MD5-             , HMAC SHA1-             , HMAC SHA224-             , HMAC SHA256-             , HMAC SHA384-             , HMAC SHA512+instance Enumerable MACType where+    values = [ TypeHMAC MD5+             , TypeHMAC SHA1+             , TypeHMAC SHA224+             , TypeHMAC SHA256+             , TypeHMAC SHA384+             , TypeHMAC SHA512+             , TypeHMAC SHA3_224+             , TypeHMAC SHA3_256+             , TypeHMAC SHA3_384+             , TypeHMAC SHA3_512+             , TypeKMAC_SHAKE128+             , TypeKMAC_SHAKE256              ] -instance OIDable MACAlgorithm where-    getObjectID (HMAC MD5)    = [1,3,6,1,5,5,8,1,1]-    getObjectID (HMAC SHA1)   = [1,3,6,1,5,5,8,1,2]-    getObjectID (HMAC SHA224) = [1,2,840,113549,2,8]-    getObjectID (HMAC SHA256) = [1,2,840,113549,2,9]-    getObjectID (HMAC SHA384) = [1,2,840,113549,2,10]-    getObjectID (HMAC SHA512) = [1,2,840,113549,2,11]+instance OIDable MACType where+    getObjectID (TypeHMAC MD5)      = [1,2,840,113549,2,6]+    getObjectID (TypeHMAC SHA1)     = [1,2,840,113549,2,7]+    getObjectID (TypeHMAC SHA224)   = [1,2,840,113549,2,8]+    getObjectID (TypeHMAC SHA256)   = [1,2,840,113549,2,9]+    getObjectID (TypeHMAC SHA384)   = [1,2,840,113549,2,10]+    getObjectID (TypeHMAC SHA512)   = [1,2,840,113549,2,11]+    getObjectID (TypeHMAC SHA3_224) = [2,16,840,1,101,3,4,2,13]+    getObjectID (TypeHMAC SHA3_256) = [2,16,840,1,101,3,4,2,14]+    getObjectID (TypeHMAC SHA3_384) = [2,16,840,1,101,3,4,2,15]+    getObjectID (TypeHMAC SHA3_512) = [2,16,840,1,101,3,4,2,16]+    getObjectID TypeKMAC_SHAKE128   = [2,16,840,1,101,3,4,2,19]+    getObjectID TypeKMAC_SHAKE256   = [2,16,840,1,101,3,4,2,20]      getObjectID ty = error ("Unsupported MACAlgorithm: " ++ show ty) -instance OIDNameable MACAlgorithm where+instance OIDNameable MACType where     fromObjectID oid = unOIDNW <$> fromObjectID oid  instance AlgorithmId MACAlgorithm where-    type AlgorithmType MACAlgorithm = MACAlgorithm+    type AlgorithmType MACAlgorithm = MACType     algorithmName _  = "mac algorithm"-    algorithmType    = id-    parameterASN1S _ = id-    parseParameter p = getNextMaybe nullOrNothing >> return p +    algorithmType (HMAC alg)          = TypeHMAC alg+    algorithmType (KMAC_SHAKE128 _ _) = TypeKMAC_SHAKE128+    algorithmType (KMAC_SHAKE256 _ _) = TypeKMAC_SHAKE256++    -- SHA-224, SHA-256, SHA-384, SHA-512 have NULL parameter,+    -- other HMAC algorithms have no parameter+    parameterASN1S (HMAC SHA224)         = gNull+    parameterASN1S (HMAC SHA256)         = gNull+    parameterASN1S (HMAC SHA384)         = gNull+    parameterASN1S (HMAC SHA512)         = gNull+    parameterASN1S (HMAC _)              = id+    parameterASN1S (KMAC_SHAKE128 p str) = kmacASN1S 256 p str+    parameterASN1S (KMAC_SHAKE256 p str) = kmacASN1S 512 p str++    parseParameter (TypeHMAC alg)    = getNextMaybe nullOrNothing >> return (HMAC alg)+    parseParameter TypeKMAC_SHAKE128 = parseKMAC p256 KMAC_SHAKE128+    parseParameter TypeKMAC_SHAKE256 = parseKMAC p512 KMAC_SHAKE256++kmacASN1S :: (KnownNat n, ASN1Elem e)+          => Integer -> proxy n -> ByteString -> ASN1Stream e+kmacASN1S n p str+    | B.null str && n == i = id+    | otherwise = asn1Container Sequence (gIntVal i . gOctetString str)+  where i = natVal p++parseKMAC :: (Monoid e, KnownNat n')+          => Proxy n'+          -> (forall n . KnownNat n => Proxy n -> ByteString -> MACAlgorithm)+          -> ParseASN1 e MACAlgorithm+parseKMAC p' fn = do+    b <- hasNext+    if b then parseParams else return (fn p' B.empty)+  where+    parseParams = onNextContainer Sequence $ parseBitLen $ \(SomeNat p) ->+        fn p <$> parseOctetStringPrim+ instance HasKeySize MACAlgorithm where     getKeySizeSpecifier (HMAC a) = KeySizeFixed (digestSizeFromProxy a)-      where digestSizeFromProxy = Hash.hashDigestSize . hashFromProxy+    getKeySizeSpecifier (KMAC_SHAKE128 p _) =+        KeySizeFixed (digestSizeFromProxy (SHAKE128 p))+    getKeySizeSpecifier (KMAC_SHAKE256 p _) =+        KeySizeFixed (digestSizeFromProxy (SHAKE256 p)) +digestSizeFromProxy :: Hash.HashAlgorithm a => proxy a -> Int+digestSizeFromProxy = Hash.hashDigestSize . hashFromProxy+ -- | Invoke the MAC function. mac :: (ByteArrayAccess key, ByteArrayAccess message)      => MACAlgorithm -> key -> message -> MessageAuthenticationCode@@ -441,7 +599,19 @@         => proxy a -> k -> m -> HMAC.HMAC a     runHMAC _ = HMAC.hmac +mac (KMAC_SHAKE128 p str) = \key -> AuthTag . B.convert . run128 p str key+  where+    run128 :: (KnownNat n, ByteArrayAccess k, ByteArrayAccess m)+           => proxy n -> ByteString -> k -> m -> KMAC.KMAC (Hash.SHAKE128 n)+    run128 _ = KMAC.kmac +mac (KMAC_SHAKE256 p str) = \key -> AuthTag . B.convert . run256 p str key+  where+    run256 :: (KnownNat n, ByteArrayAccess k, ByteArrayAccess m)+           => proxy n -> ByteString -> k -> m -> KMAC.KMAC (Hash.SHAKE256 n)+    run256 _ = KMAC.kmac++ -- Content encryption  -- | CMS content encryption cipher.@@ -491,6 +661,9 @@       -- ^ Cipher Feedback     | forall c . BlockCipher c => CTR (ContentEncryptionCipher c)       -- ^ Counter+    | forall hashAlg . Hash.HashAlgorithm hashAlg+        => DeriveHKDF (DigestProxy hashAlg)+      -- ^ Derivation of content-encryption key with HKDF  instance Show ContentEncryptionAlg where     show (ECB c) = shows c "_ECB"@@ -498,6 +671,7 @@     show CBC_RC2 = "RC2_CBC"     show (CFB c) = shows c "_CFB"     show (CTR c) = shows c "_CTR"+    show (DeriveHKDF d) = "CMS_CEK_HKDF_" ++ show d  instance Enumerable ContentEncryptionAlg where     values = [ CBC DES@@ -522,6 +696,8 @@              , CFB Camellia128               , CTR Camellia128++             , DeriveHKDF SHA256              ]  instance OIDable ContentEncryptionAlg where@@ -548,6 +724,8 @@      getObjectID (CTR Camellia128)  = [0,3,4401,5,3,1,9,9] +    getObjectID (DeriveHKDF SHA256) = [1,2,840,113549,1,9,16,3,31]+     getObjectID ty = error ("Unsupported ContentEncryptionAlg: " ++ show ty)  instance OIDNameable ContentEncryptionAlg where@@ -556,7 +734,8 @@ -- | Content encryption algorithm with associated parameters (i.e. the -- initialization vector). ----- A value can be generated with 'generateEncryptionParams'.+-- A value can be built with functions 'ecbParams', 'generateCBCParams',+-- 'generateRC2EncryptionParams', 'generateCFBParams' and 'generateCTRParams'. data ContentEncryptionParams     = forall c . BlockCipher c => ParamsECB (ContentEncryptionCipher c)       -- ^ Electronic Codebook@@ -568,6 +747,9 @@       -- ^ Cipher Feedback     | forall c . BlockCipher c => ParamsCTR (ContentEncryptionCipher c) (IV c)       -- ^ Counter+    | forall hashAlg . Hash.HashAlgorithm hashAlg+        => ParamsDeriveHKDF (DigestProxy hashAlg) ContentEncryptionParams+      -- ^ Derivation of content-encryption key with HKDF  instance Show ContentEncryptionParams where     show = show . getContentEncryptionAlg@@ -578,6 +760,8 @@     ParamsCBCRC2 i1 iv1 == ParamsCBCRC2 i2 iv2 = i1 == i2 && iv1 `B.eq` iv2     ParamsCFB c1 iv1    == ParamsCFB c2 iv2    = cecI c1 == cecI c2 && iv1 `B.eq` iv2     ParamsCTR c1 iv1    == ParamsCTR c2 iv2    = cecI c1 == cecI c2 && iv1 `B.eq` iv2+    ParamsDeriveHKDF d1 a1 == ParamsDeriveHKDF d2 a2 =+        DigestAlgorithm d1 == DigestAlgorithm d2 && a1 == a2     _                   == _                   = False  instance HasKeySize ContentEncryptionParams where@@ -586,6 +770,7 @@     getKeySizeSpecifier (ParamsCBCRC2 i _) = KeySizeFixed $ (i + 7) `div` 8     getKeySizeSpecifier (ParamsCFB c _)    = getCipherKeySizeSpecifier c     getKeySizeSpecifier (ParamsCTR c _)    = getCipherKeySizeSpecifier c+    getKeySizeSpecifier (ParamsDeriveHKDF _ a) = getKeySizeSpecifier a  instance ASN1Elem e => ProduceASN1Object e ContentEncryptionParams where     asn1s param =@@ -605,6 +790,7 @@ ceParameterASN1S (ParamsCBCRC2 len iv) = rc2ParameterASN1S len iv ceParameterASN1S (ParamsCFB _ iv)      = gOctetString (B.convert iv) ceParameterASN1S (ParamsCTR _ iv)      = gOctetString (B.convert iv)+ceParameterASN1S (ParamsDeriveHKDF _ a) = asn1s a  parseCEParameter :: Monoid e                  => ContentEncryptionAlg -> ParseASN1 e ContentEncryptionParams@@ -613,6 +799,7 @@ parseCEParameter CBC_RC2 = parseRC2Parameter parseCEParameter (CFB c) = ParamsCFB c <$> (getNext >>= getIV) parseCEParameter (CTR c) = ParamsCTR c <$> (getNext >>= getIV)+parseCEParameter (DeriveHKDF d) = ParamsDeriveHKDF d <$> parse  getIV :: BlockCipher cipher => ASN1 -> ParseASN1 e (IV cipher) getIV (OctetString ivBs) =@@ -641,22 +828,41 @@ getContentEncryptionAlg (ParamsCBCRC2 _ _) = CBC_RC2 getContentEncryptionAlg (ParamsCFB c _)    = CFB c getContentEncryptionAlg (ParamsCTR c _)    = CTR c+getContentEncryptionAlg (ParamsDeriveHKDF d _) = DeriveHKDF d --- | Generate random parameters for the specified content encryption algorithm.-generateEncryptionParams :: MonadRandom m-                         => ContentEncryptionAlg -> m ContentEncryptionParams-generateEncryptionParams (ECB c) = return (ParamsECB c)-generateEncryptionParams (CBC c) = ParamsCBC c <$> ivGenerate undefined-generateEncryptionParams CBC_RC2 = ParamsCBCRC2 128 <$> ivGenerate undefined-generateEncryptionParams (CFB c) = ParamsCFB c <$> ivGenerate undefined-generateEncryptionParams (CTR c) = ParamsCTR c <$> ivGenerate undefined+-- | Get 'ECB' parameters for the specified cipher.+ecbParams :: BlockCipher c+          => ContentEncryptionCipher c -> ContentEncryptionParams+ecbParams = ParamsECB +-- | Generate random 'CBC' parameters for the specified cipher.+generateCBCParams :: (MonadRandom m, BlockCipher c)+                  => ContentEncryptionCipher c+                  -> m ContentEncryptionParams+generateCBCParams c = ParamsCBC c <$> ivGenerate undefined+ -- | Generate random RC2 parameters with the specified effective key length (in -- bits). generateRC2EncryptionParams :: MonadRandom m                             => Int -> m ContentEncryptionParams generateRC2EncryptionParams len = ParamsCBCRC2 len <$> ivGenerate undefined +-- | Generate random 'CFB' parameters for the specified cipher.+generateCFBParams :: (MonadRandom m, BlockCipher c)+                  => ContentEncryptionCipher c+                  -> m ContentEncryptionParams+generateCFBParams c = ParamsCFB c <$> ivGenerate undefined++-- | Generate random 'CTR' parameters for the specified cipher.+generateCTRParams :: (MonadRandom m, BlockCipher c)+                  => ContentEncryptionCipher c+                  -> m ContentEncryptionParams+generateCTRParams c = ParamsCTR c <$> ivGenerate undefined++-- | Use derivation of the content-encryption key with HKDF-SHA256+deriveEncryptionKey :: ContentEncryptionParams -> ContentEncryptionParams+deriveEncryptionKey = ParamsDeriveHKDF SHA256+ -- | Encrypt a bytearray with the specified content encryption key and -- algorithm. contentEncrypt :: (ByteArray cek, ByteArray ba)@@ -670,6 +876,7 @@         ParamsCBCRC2 len iv -> getRC2Cipher len key >>= (\c -> force $ cbcEncrypt c iv $ padded c bs)         ParamsCFB cipher iv -> getCipher cipher key >>= (\c -> force $ cfbEncrypt c iv $ padded c bs)         ParamsCTR cipher iv -> getCipher cipher key >>= (\c -> force $ ctrCombine c iv $ padded c bs)+        ParamsDeriveHKDF d a -> deriveHKDF d a key >>= \derived -> contentEncrypt derived a bs   where     force x  = x `seq` Right x     padded c = pad (PKCS7 $ blockSize c)@@ -687,6 +894,7 @@         ParamsCBCRC2 len iv -> getRC2Cipher len key >>= (\c -> unpadded c (cbcDecrypt c iv bs))         ParamsCFB cipher iv -> getCipher cipher key >>= (\c -> unpadded c (cfbDecrypt c iv bs))         ParamsCTR cipher iv -> getCipher cipher key >>= (\c -> unpadded c (ctrCombine c iv bs))+        ParamsDeriveHKDF d a -> deriveHKDF d a key >>= \derived -> contentDecrypt derived a bs   where     unpadded c decrypted =         case unpad (PKCS7 $ blockSize c) decrypted of@@ -753,6 +961,9 @@       -- ^ Counter with CBC-MAC     | forall c . BlockCipher c => GCM (ContentEncryptionCipher c)       -- ^ Galois Counter Mode+    | forall hashAlg . Hash.HashAlgorithm hashAlg+        => AuthDeriveHKDF (DigestProxy hashAlg)+      -- ^ Derivation of content-encryption key with HKDF  instance Show AuthContentEncryptionAlg where     show AUTH_ENC_128 = "AUTH_ENC_128"@@ -760,6 +971,7 @@     show CHACHA20_POLY1305 = "CHACHA20_POLY1305"     show (CCM c)      = shows c "_CCM"     show (GCM c)      = shows c "_GCM"+    show (AuthDeriveHKDF d) = "CMS_CEK_HKDF_" ++ show d  instance Enumerable AuthContentEncryptionAlg where     values = [ AUTH_ENC_128@@ -773,6 +985,8 @@              , GCM AES128              , GCM AES192              , GCM AES256++             , AuthDeriveHKDF SHA256              ]  instance OIDable AuthContentEncryptionAlg where@@ -788,6 +1002,8 @@     getObjectID (GCM AES192)       = [2,16,840,1,101,3,4,1,26]     getObjectID (GCM AES256)       = [2,16,840,1,101,3,4,1,46] +    getObjectID (AuthDeriveHKDF SHA256) = [1,2,840,113549,1,9,16,3,31]+     getObjectID ty = error ("Unsupported AuthContentEncryptionAlg: " ++ show ty)  instance OIDNameable AuthContentEncryptionAlg where@@ -832,9 +1048,8 @@ -- | Authenticated-content encryption algorithm with associated parameters -- (i.e. the nonce). ----- A value can be generated with functions 'generateAuthEnc128Params',--- 'generateAuthEnc256Params', 'generateChaChaPoly1305Params',--- 'generateCCMParams' and 'generateGCMParams'.+-- A value can be built with functions 'authEnc128Params', 'authEnc256Params',+-- 'generateChaChaPoly1305Params', 'generateCCMParams' and 'generateGCMParams'. data AuthContentEncryptionParams     = Params_AUTH_ENC_128 AuthEncParams       -- ^ authEnc with 128-bit keying material@@ -846,6 +1061,9 @@       -- ^ Counter with CBC-MAC     | forall c . BlockCipher c => ParamsGCM (ContentEncryptionCipher c) B.Bytes Int       -- ^ Galois Counter Mode+    | forall hashAlg . Hash.HashAlgorithm hashAlg+        => ParamsAuthDeriveHKDF (DigestProxy hashAlg) (ASN1ObjectExact AuthContentEncryptionParams)+      -- ^ Derivation of content-encryption key with HKDF  instance Show AuthContentEncryptionParams where     show = show . getAuthContentEncryptionAlg@@ -860,6 +1078,10 @@         cecI c1 == cecI c2 && iv1 == iv2 && (m1, l1) == (m2, l2)     ParamsGCM c1 iv1 len1  == ParamsGCM c2 iv2 len2  =         cecI c1 == cecI c2 && iv1 == iv2 && len1 == len2++    ParamsAuthDeriveHKDF d1 a1 == ParamsAuthDeriveHKDF d2 a2 =+        DigestAlgorithm d1 == DigestAlgorithm d2 && a1 == a2+     _               == _               = False  instance HasKeySize AuthContentEncryptionParams where@@ -868,38 +1090,39 @@     getKeySizeSpecifier (Params_CHACHA20_POLY1305 _) = KeySizeFixed 32     getKeySizeSpecifier (ParamsCCM c _ _ _)     = getCipherKeySizeSpecifier c     getKeySizeSpecifier (ParamsGCM c _ _)       = getCipherKeySizeSpecifier c+    getKeySizeSpecifier (ParamsAuthDeriveHKDF _ a) = getKeySizeSpecifier (exactObject a) -instance ASN1Elem e => ProduceASN1Object e AuthContentEncryptionParams where+instance ProduceASN1Object ASN1P AuthContentEncryptionParams where     asn1s param =         asn1Container Sequence (oid . params)       where         oid    = gOID (getObjectID $ getAuthContentEncryptionAlg param)         params = aceParameterASN1S param -instance Monoid e => ParseASN1Object e AuthContentEncryptionParams where+instance ParseASN1Object [ASN1Event] AuthContentEncryptionParams where     parse = onNextContainer Sequence $ do         OID oid <- getNext         withObjectID "authenticated-content encryption algorithm" oid             parseACEParameter -aceParameterASN1S :: ASN1Elem e => AuthContentEncryptionParams -> ASN1Stream e+aceParameterASN1S :: AuthContentEncryptionParams -> ASN1PS aceParameterASN1S (Params_AUTH_ENC_128 p) = asn1s p aceParameterASN1S (Params_AUTH_ENC_256 p) = asn1s p aceParameterASN1S (Params_CHACHA20_POLY1305 iv) = gOctetString (B.convert iv) aceParameterASN1S (ParamsCCM _ iv m _) =-    asn1Container Sequence (nonce . icvlen)+    asn1Container Sequence (if m == CCM_M12 then nonce else nonce . icvlen)   where     nonce  = gOctetString (B.convert iv)     icvlen = gIntVal (fromIntegral $ getM m) aceParameterASN1S (ParamsGCM _ iv len) =-    asn1Container Sequence (nonce . icvlen)+    asn1Container Sequence (if len == 12 then nonce else nonce . icvlen)   where     nonce  = gOctetString (B.convert iv)     icvlen = gIntVal (fromIntegral len)+aceParameterASN1S (ParamsAuthDeriveHKDF _ a) = asn1s a -parseACEParameter :: Monoid e-                  => AuthContentEncryptionAlg-                  -> ParseASN1 e AuthContentEncryptionParams+parseACEParameter :: AuthContentEncryptionAlg+                  -> ParseASN1 [ASN1Event] AuthContentEncryptionParams parseACEParameter AUTH_ENC_128 = Params_AUTH_ENC_128 <$> parse parseACEParameter AUTH_ENC_256 = Params_AUTH_ENC_256 <$> parse parseACEParameter CHACHA20_POLY1305 = do@@ -913,7 +1136,7 @@     let ivlen = B.length iv     when (ivlen < 7 || ivlen > 13) $         throwParseError $ "Parsed invalid CCM nonce length: " ++ show ivlen-    let Just l = fromL (15 - ivlen)+    let l = fromJust $ fromL (15 - ivlen)     m <- parseM     return (ParamsCCM c (B.convert iv) m l) parseACEParameter (GCM c)      = onNextContainer Sequence $ do@@ -924,6 +1147,7 @@     when (icvlen < 12 || icvlen > 16) $         throwParseError $ "Parsed invalid GCM ICV length: " ++ show icvlen     return (ParamsGCM c (B.convert iv) $ fromIntegral icvlen)+parseACEParameter (AuthDeriveHKDF d) = ParamsAuthDeriveHKDF d <$> parse  -- | Get the authenticated-content encryption algorithm. getAuthContentEncryptionAlg :: AuthContentEncryptionParams@@ -933,26 +1157,23 @@ getAuthContentEncryptionAlg (Params_CHACHA20_POLY1305 _) = CHACHA20_POLY1305 getAuthContentEncryptionAlg (ParamsCCM c _ _ _)     = CCM c getAuthContentEncryptionAlg (ParamsGCM c _ _)       = GCM c+getAuthContentEncryptionAlg (ParamsAuthDeriveHKDF d _) = AuthDeriveHKDF d --- | Generate random 'AUTH_ENC_128' parameters with the specified algorithms.-generateAuthEnc128Params :: MonadRandom m-                         => PBKDF2_PRF -> ContentEncryptionAlg -> MACAlgorithm-                         -> m AuthContentEncryptionParams-generateAuthEnc128Params prfAlg cea macAlg = do-    params <- generateEncryptionParams cea-    return $ Params_AUTH_ENC_128 $+-- | Get 'AUTH_ENC_128' parameters with the specified algorithms.+authEnc128Params :: PBKDF2_PRF -> ContentEncryptionParams -> MACAlgorithm+                 -> AuthContentEncryptionParams+authEnc128Params prfAlg params macAlg =+    Params_AUTH_ENC_128 $         AuthEncParams { prfAlgorithm = prfAlg                       , encAlgorithm = params                       , macAlgorithm = macAlg                       } --- | Generate random 'AUTH_ENC_256' parameters with the specified algorithms.-generateAuthEnc256Params :: MonadRandom m-                         => PBKDF2_PRF -> ContentEncryptionAlg -> MACAlgorithm-                         -> m AuthContentEncryptionParams-generateAuthEnc256Params prfAlg cea macAlg = do-    params <- generateEncryptionParams cea-    return $ Params_AUTH_ENC_256 $+-- | Get 'AUTH_ENC_256' parameters with the specified algorithms.+authEnc256Params :: PBKDF2_PRF -> ContentEncryptionParams -> MACAlgorithm+                 -> AuthContentEncryptionParams+authEnc256Params prfAlg params macAlg = do+    Params_AUTH_ENC_256 $         AuthEncParams { prfAlgorithm = prfAlg                       , encAlgorithm = params                       , macAlgorithm = macAlg@@ -981,19 +1202,37 @@     iv <- nonceGenerate 12     return (ParamsGCM c iv l) +-- | Use derivation of the content-encryption key with HKDF-SHA256+authDeriveEncryptionKey :: AuthContentEncryptionParams -> AuthContentEncryptionParams+authDeriveEncryptionKey = ParamsAuthDeriveHKDF SHA256 . derObjectExact++deriveHKDF :: (Hash.HashAlgorithm a, ProduceASN1Object ASN1P params, ByteArrayAccess ikm)+           => DigestProxy a -> params -> ikm -> Either StoreError B.ScrubbedBytes+deriveHKDF hashAlg params ikm =+    kdfApply (HKDF (DigestAlgorithm hashAlg)) (salt :: B.Bytes) len info ikm+  where+    info = encodeASN1S (asn1s params)+    len  = B.length ikm++    -- "The Cryptographic Message Syntax"+    salt = B.pack [84,104,101,32,67,114,121,112,116,111,103,114,97,112,104,105+                  ,99,32,77,101,115,115,97,103,101,32,83,121,110,116,97,120]+ -- | Encrypt a bytearray with the specified authenticated-content encryption -- key and algorithm. authContentEncrypt :: forall cek aad ba . (ByteArray cek, ByteArrayAccess aad, ByteArray ba)                    => cek-                   -> AuthContentEncryptionParams -> ba+                   -> ASN1ObjectExact AuthContentEncryptionParams                    -> aad -> ba -> Either StoreError (AuthTag, ba)-authContentEncrypt key params paramsRaw aad bs =-    case params of+authContentEncrypt key params aad bs =+    case exactObject params of         Params_AUTH_ENC_128 p   -> checkAuthKey 16 key >> authEncrypt p         Params_AUTH_ENC_256 p   -> checkAuthKey 32 key >> authEncrypt p         Params_CHACHA20_POLY1305 iv -> ccpInit key iv aad >>= ccpEncrypt         ParamsCCM cipher iv m l -> getAEAD cipher key (AEAD_CCM msglen m l) iv >>= encrypt (getM m)         ParamsGCM cipher iv len -> getAEAD cipher key AEAD_GCM iv >>= encrypt len+        ParamsAuthDeriveHKDF d a -> deriveHKDF d a key >>= \derived ->+            authContentEncrypt derived a aad bs   where     msglen  = B.length bs     force x = x `seq` Right x@@ -1011,7 +1250,8 @@     authEncrypt p@AuthEncParams{..} = do         let (encKey, macKey) = authKeys key p         encrypted <- contentEncrypt encKey encAlgorithm bs-        let macMsg = paramsRaw `B.append` encrypted `B.append` B.convert aad+        let paramsRaw = exactObjectRaw params+            macMsg = B.convert paramsRaw `B.append` encrypted `B.append` B.convert aad             found  = mac macAlgorithm macKey macMsg         return (found, encrypted) @@ -1019,21 +1259,25 @@ -- and algorithm. authContentDecrypt :: forall cek aad ba . (ByteArray cek, ByteArrayAccess aad, ByteArray ba)                    => cek-                   -> AuthContentEncryptionParams -> ba+                   -> ASN1ObjectExact AuthContentEncryptionParams                    -> aad -> ba -> AuthTag -> Either StoreError ba-authContentDecrypt key params paramsRaw aad bs expected =-    case params of+authContentDecrypt key params aad bs expected =+    case exactObject params of         Params_AUTH_ENC_128 p   -> checkAuthKey 16 key >> authDecrypt p         Params_AUTH_ENC_256 p   -> checkAuthKey 32 key >> authDecrypt p         Params_CHACHA20_POLY1305 iv -> ccpInit key iv aad >>= ccpDecrypt-        ParamsCCM cipher iv m l -> getAEAD cipher key (AEAD_CCM msglen m l) iv >>= decrypt-        ParamsGCM cipher iv _   -> getAEAD cipher key AEAD_GCM iv >>= decrypt+        ParamsCCM cipher iv m l -> getAEAD cipher key (AEAD_CCM msglen m l) iv >>= decrypt (getM m)+        ParamsGCM cipher iv len -> getAEAD cipher key AEAD_GCM iv >>= decrypt len+        ParamsAuthDeriveHKDF d a -> deriveHKDF d a key >>= \derived ->+            authContentDecrypt derived a aad bs expected   where     msglen  = B.length bs     badMac  = Left BadContentMAC -    decrypt :: AEAD a -> Either StoreError ba-    decrypt aead = maybe badMac Right (aeadSimpleDecrypt aead aad bs expected)+    decrypt :: Int -> AEAD a -> Either StoreError ba+    decrypt len aead+        | B.length (unAuthTag expected) /= len = badMac+        | otherwise = maybe badMac Right (aeadSimpleDecrypt aead aad bs expected)      ccpDecrypt :: ChaChaPoly1305.State -> Either StoreError ba     ccpDecrypt state@@ -1050,7 +1294,8 @@         | otherwise         = badMac       where         (encKey, macKey) = authKeys key p-        macMsg = paramsRaw `B.append` bs `B.append` B.convert aad+        paramsRaw = exactObjectRaw params+        macMsg = B.convert paramsRaw `B.append` bs `B.append` B.convert aad         found  = mac macAlgorithm macKey macMsg         acceptable = securityAcceptable macAlgorithm @@ -1122,7 +1367,7 @@     type AlgorithmType PBKDF2_PRF = PBKDF2_PRF     algorithmName _  = "PBKDF2 PRF"     algorithmType    = id-    parameterASN1S _ = id+    parameterASN1S _ = gNull     parseParameter p = getNextMaybe nullOrNothing >> return p  -- | Invoke the pseudorandom function.@@ -1154,6 +1399,10 @@     fromObjectID oid = unOIDNW <$> fromObjectID oid  -- | Key derivation algorithm and associated parameters.+--+-- Implementations are specialized to password inputs.  For other+-- implementations specialized to inputs having good entropy, like the shared+-- secrets produced by key-agreement schemes, see 'KeyDerivationFn'. data KeyDerivationFunc =       -- | Key derivation with PBKDF2       PBKDF2 { pbkdf2Salt           :: Salt       -- ^ Salt value@@ -1224,6 +1473,12 @@ kdfKeyLength PBKDF2{..} = pbkdf2KeyLength kdfKeyLength Scrypt{..} = scryptKeyLength +-- | Modify the optional key length stored in the KDF parameters.+kdfKeyLengthModify :: (Maybe Int -> Maybe Int)+                   -> KeyDerivationFunc -> KeyDerivationFunc+kdfKeyLengthModify f kdf@PBKDF2{..} = kdf{pbkdf2KeyLength = f pbkdf2KeyLength}+kdfKeyLengthModify f kdf@Scrypt{..} = kdf{scryptKeyLength = f scryptKeyLength}+ -- | Run a key derivation function to produce a result of the specified length -- using the supplied password. kdfDerive :: (ByteArrayAccess password, ByteArray out)@@ -1242,7 +1497,82 @@ generateSalt :: MonadRandom m => Int -> m Salt generateSalt = getRandomBytes +data TypeKeyDerivationFn+    = TypeHKDF DigestAlgorithm+    | TypeKDF3+    deriving (Show,Eq) +instance Enumerable TypeKeyDerivationFn where+    values = [ TypeHKDF (DigestAlgorithm SHA256)+             , TypeHKDF (DigestAlgorithm SHA384)+             , TypeHKDF (DigestAlgorithm SHA512)+             , TypeHKDF (DigestAlgorithm SHA3_224)+             , TypeHKDF (DigestAlgorithm SHA3_256)+             , TypeHKDF (DigestAlgorithm SHA3_384)+             , TypeHKDF (DigestAlgorithm SHA3_512)+             , TypeKDF3+             ]++instance OIDable TypeKeyDerivationFn where+    getObjectID (TypeHKDF (DigestAlgorithm SHA256))   = [1,2,840,113549,1,9,16,3,28]+    getObjectID (TypeHKDF (DigestAlgorithm SHA384))   = [1,2,840,113549,1,9,16,3,29]+    getObjectID (TypeHKDF (DigestAlgorithm SHA512))   = [1,2,840,113549,1,9,16,3,30]+    getObjectID (TypeHKDF (DigestAlgorithm SHA3_224)) = [1,2,840,113549,1,9,16,3,32]+    getObjectID (TypeHKDF (DigestAlgorithm SHA3_256)) = [1,2,840,113549,1,9,16,3,33]+    getObjectID (TypeHKDF (DigestAlgorithm SHA3_384)) = [1,2,840,113549,1,9,16,3,34]+    getObjectID (TypeHKDF (DigestAlgorithm SHA3_512)) = [1,2,840,113549,1,9,16,3,35]+    getObjectID (TypeHKDF hashAlg) = error ("Unsupported HKDF hash: " ++ show hashAlg)+    getObjectID TypeKDF3                              = [1,3,133,16,840,9,44,1,2]++instance OIDNameable TypeKeyDerivationFn where+    fromObjectID oid = unOIDNW <$> fromObjectID oid++-- | Key derivation algorithm and associated parameters.+--+-- Implementations are specialized to inputs having good entropy, like the+-- shared secrets produced by key-agreement schemes.  For other implementations+-- specialized to password inputs, see 'KeyDerivationFunc'.+data KeyDerivationFn+      -- | Key derivation with HKDF+    = HKDF DigestAlgorithm+      -- | Key derivation with KDF3+    | KDF3 DigestAlgorithm+    deriving (Show,Eq)++instance AlgorithmId KeyDerivationFn where+    type AlgorithmType KeyDerivationFn = TypeKeyDerivationFn++    algorithmName _ = "key derivation algorithm"+    algorithmType (HKDF alg) = TypeHKDF alg+    algorithmType (KDF3 _) = TypeKDF3++    parameterASN1S (HKDF _) = id+    parameterASN1S (KDF3 alg) = algorithmASN1S Sequence alg++    parseParameter (TypeHKDF alg) = return (HKDF alg)+    parseParameter TypeKDF3 = KDF3 <$> parseAlgorithm Sequence++kdfApply :: (ByteArrayAccess salt, ByteArrayAccess ikm, ByteArray out)+         => KeyDerivationFn -> salt -> Int -> ByteString -> ikm -> Either StoreError out+kdfApply (HKDF (DigestAlgorithm p)) salt len info ikm+    | len > maxLen = Left (InvalidParameter "HKDF OKM is too long")+    | otherwise = Right $ HKDF.expand (extract p ikm) info len+  where+    maxLen = 255 * digestSizeFromProxy p++    extract :: (Hash.HashAlgorithm a, ByteArrayAccess ikm)+            => DigestProxy a -> ikm -> HKDF.PRK a+    extract _ = HKDF.extract salt++kdfApply (KDF3 dig@(DigestAlgorithm p)) salt len info ikm+    | not (securityAcceptable dig) =+        Left (InvalidParameter "KDF3 digest too weak")+    | B.null salt =+        let RsaKem.KDF kdf = RsaKem.kdf3 (hashFromProxy p) len+         in Right $ kdf info ikm+    | otherwise = Left (InvalidInput "KDF3 salt must be empty")++ -- Key encryption  data KeyEncryptionType = TypePWRIKEK@@ -1347,6 +1677,7 @@         ParamsCBCRC2 len iv -> let cc = getRC2Cipher len key in either (return . Left) (\c -> wrapEncrypt cbcEncrypt c iv bs) cc         ParamsCFB cipher iv -> let cc = getCipher cipher key in either (return . Left) (\c -> wrapEncrypt cfbEncrypt c iv bs) cc         ParamsCTR _ _       -> return $ Left (InvalidParameter "Unable to wrap key in CTR mode")+        ParamsDeriveHKDF _ _ -> return $ Left (InvalidParameter "HKDF derivation is incompatible with key wrapping") keyEncrypt key AES128_WRAP      bs = return (getCipher AES128 key >>= (`AES_KW.wrap` bs)) keyEncrypt key AES192_WRAP      bs = return (getCipher AES192 key >>= (`AES_KW.wrap` bs)) keyEncrypt key AES256_WRAP      bs = return (getCipher AES256 key >>= (`AES_KW.wrap` bs))@@ -1368,6 +1699,7 @@         ParamsCBCRC2 len iv -> getRC2Cipher len key >>= (\c -> wrapDecrypt cbcDecrypt c iv bs)         ParamsCFB cipher iv -> getCipher cipher key >>= (\c -> wrapDecrypt cfbDecrypt c iv bs)         ParamsCTR _ _       -> Left (InvalidParameter "Unable to unwrap key in CTR mode")+        ParamsDeriveHKDF _ _ -> Left (InvalidParameter "HKDF derivation is incompatible with key unwrapping") keyDecrypt key AES128_WRAP      bs = getCipher AES128   key >>= (`AES_KW.unwrap` bs) keyDecrypt key AES192_WRAP      bs = getCipher AES192   key >>= (`AES_KW.unwrap` bs) keyDecrypt key AES256_WRAP      bs = getCipher AES256   key >>= (`AES_KW.unwrap` bs)@@ -1415,19 +1747,21 @@     fn formatted =         let firstPass = encFn cipher iv formatted             lastBlock = B.dropView firstPass (B.length firstPass - sz)-            Just iv'  = makeIV lastBlock+            iv'       = fromJust $ makeIV lastBlock  -- wrapped length >= sz          in encFn cipher iv' firstPass  wrapDecrypt :: (BlockCipher cipher, ByteArray ba)             => (cipher -> IV cipher -> ba -> ba)             -> cipher -> IV cipher -> ba -> Either StoreError ba-wrapDecrypt decFn cipher iv input = keyUnwrap (decFn cipher iv firstPass)+wrapDecrypt decFn cipher iv input+    | B.length beg < sz = Left (InvalidInput "wrapDecrypt: input too short")+    | otherwise = keyUnwrap (decFn cipher iv firstPass)   where     sz = blockSize cipher     (beg, lb) = B.splitAt (B.length input - sz) input     lastBlock = decFn cipher iv' lb-    Just iv'  = makeIV (B.dropView beg (B.length beg - sz))-    Just iv'' = makeIV lastBlock+    iv'       = fromJust $ makeIV (B.dropView beg (B.length beg - sz))+    iv''      = fromJust $ makeIV lastBlock     firstPass = decFn cipher iv'' beg `B.append` lastBlock  @@ -1539,12 +1873,12 @@ -- private key. transportDecrypt :: MonadRandom m                  => KeyTransportParams-                 -> X509.PrivKey+                 -> KeyPair                  -> ByteString                  -> m (Either StoreError ByteString)-transportDecrypt RSAES         (X509.PrivKeyRSA priv) bs =+transportDecrypt RSAES         (KeyPairRSA priv _) bs =     mapLeft RSAError <$> RSA.decryptSafer priv bs-transportDecrypt (RSAESOAEP p) (X509.PrivKeyRSA priv) bs+transportDecrypt (RSAESOAEP p) (KeyPairRSA priv _) bs     | securityAcceptable p =         withOAEPParams p $ \params ->             mapLeft RSAError <$> RSAOAEP.decryptSafer params priv bs@@ -1554,46 +1888,70 @@  -- Key agreement -data KeyAgreementType = TypeStdDH DigestAlgorithm-                      | TypeCofactorDH DigestAlgorithm+-- | Key derivation function used for key agreement.+data KeyAgreementKDF+    = forall hashAlg . Hash.HashAlgorithm hashAlg+      => KA_X963_KDF (DigestProxy hashAlg)+      -- ^ ANSI-X9.63-KDF key derivation function+    | forall hashAlg . Hash.HashAlgorithm hashAlg+       => KA_HKDF (DigestProxy hashAlg)+      -- ^ Extract-and-Expand HMAC-based key derivation function++deriving instance Show KeyAgreementKDF++instance Eq KeyAgreementKDF where+    KA_X963_KDF a == KA_X963_KDF b = DigestAlgorithm a == DigestAlgorithm b+    KA_HKDF a     == KA_HKDF b     = DigestAlgorithm a == DigestAlgorithm b+    _             == _             = False++data KeyAgreementType = TypeStdDH KeyAgreementKDF+                      | TypeCofactorDH KeyAgreementKDF                       deriving (Show,Eq)  instance Enumerable KeyAgreementType where-    values = [ TypeStdDH (DigestAlgorithm SHA1)-             , TypeStdDH (DigestAlgorithm SHA224)-             , TypeStdDH (DigestAlgorithm SHA256)-             , TypeStdDH (DigestAlgorithm SHA384)-             , TypeStdDH (DigestAlgorithm SHA512)+    values = [ TypeStdDH (KA_X963_KDF SHA1)+             , TypeStdDH (KA_X963_KDF SHA224)+             , TypeStdDH (KA_X963_KDF SHA256)+             , TypeStdDH (KA_X963_KDF SHA384)+             , TypeStdDH (KA_X963_KDF SHA512) -             , TypeCofactorDH (DigestAlgorithm SHA1)-             , TypeCofactorDH (DigestAlgorithm SHA224)-             , TypeCofactorDH (DigestAlgorithm SHA256)-             , TypeCofactorDH (DigestAlgorithm SHA384)-             , TypeCofactorDH (DigestAlgorithm SHA512)+             , TypeCofactorDH (KA_X963_KDF SHA1)+             , TypeCofactorDH (KA_X963_KDF SHA224)+             , TypeCofactorDH (KA_X963_KDF SHA256)+             , TypeCofactorDH (KA_X963_KDF SHA384)+             , TypeCofactorDH (KA_X963_KDF SHA512)++             , TypeStdDH (KA_HKDF SHA256)+             , TypeStdDH (KA_HKDF SHA384)+             , TypeStdDH (KA_HKDF SHA512)              ]  instance OIDable KeyAgreementType where-    getObjectID (TypeStdDH (DigestAlgorithm SHA1))        = [1,3,133,16,840,63,0,2]-    getObjectID (TypeStdDH (DigestAlgorithm SHA224))      = [1,3,132,1,11,0]-    getObjectID (TypeStdDH (DigestAlgorithm SHA256))      = [1,3,132,1,11,1]-    getObjectID (TypeStdDH (DigestAlgorithm SHA384))      = [1,3,132,1,11,2]-    getObjectID (TypeStdDH (DigestAlgorithm SHA512))      = [1,3,132,1,11,3]+    getObjectID (TypeStdDH (KA_X963_KDF SHA1))        = [1,3,133,16,840,63,0,2]+    getObjectID (TypeStdDH (KA_X963_KDF SHA224))      = [1,3,132,1,11,0]+    getObjectID (TypeStdDH (KA_X963_KDF SHA256))      = [1,3,132,1,11,1]+    getObjectID (TypeStdDH (KA_X963_KDF SHA384))      = [1,3,132,1,11,2]+    getObjectID (TypeStdDH (KA_X963_KDF SHA512))      = [1,3,132,1,11,3] -    getObjectID (TypeCofactorDH (DigestAlgorithm SHA1))   = [1,3,133,16,840,63,0,3]-    getObjectID (TypeCofactorDH (DigestAlgorithm SHA224)) = [1,3,132,1,14,0]-    getObjectID (TypeCofactorDH (DigestAlgorithm SHA256)) = [1,3,132,1,14,1]-    getObjectID (TypeCofactorDH (DigestAlgorithm SHA384)) = [1,3,132,1,14,2]-    getObjectID (TypeCofactorDH (DigestAlgorithm SHA512)) = [1,3,132,1,14,3]+    getObjectID (TypeCofactorDH (KA_X963_KDF SHA1))   = [1,3,133,16,840,63,0,3]+    getObjectID (TypeCofactorDH (KA_X963_KDF SHA224)) = [1,3,132,1,14,0]+    getObjectID (TypeCofactorDH (KA_X963_KDF SHA256)) = [1,3,132,1,14,1]+    getObjectID (TypeCofactorDH (KA_X963_KDF SHA384)) = [1,3,132,1,14,2]+    getObjectID (TypeCofactorDH (KA_X963_KDF SHA512)) = [1,3,132,1,14,3] +    getObjectID (TypeStdDH (KA_HKDF SHA256))  = [1,2,840,113549,1,9,16,3,19]+    getObjectID (TypeStdDH (KA_HKDF SHA384))  = [1,2,840,113549,1,9,16,3,20]+    getObjectID (TypeStdDH (KA_HKDF SHA512))  = [1,2,840,113549,1,9,16,3,21]+     getObjectID ty = error ("Unsupported KeyAgreementType: " ++ show ty)  instance OIDNameable KeyAgreementType where     fromObjectID oid = unOIDNW <$> fromObjectID oid  -- | Key agreement algorithm with associated parameters.-data KeyAgreementParams = StdDH DigestAlgorithm KeyEncryptionParams+data KeyAgreementParams = StdDH KeyAgreementKDF KeyEncryptionParams                           -- ^ 1-Pass D-H with Stardard ECDH-                        | CofactorDH DigestAlgorithm KeyEncryptionParams+                        | CofactorDH KeyAgreementKDF KeyEncryptionParams                           -- ^ 1-Pass D-H with Cofactor ECDH                         deriving (Show,Eq) @@ -1611,21 +1969,31 @@     parseParameter (TypeCofactorDH d) = CofactorDH d <$> parseAlgorithm Sequence  ecdhKeyMaterial :: (ByteArrayAccess bin, ByteArray bout)-                => DigestAlgorithm -> KeyEncryptionParams -> Maybe ByteString -> bin -> bout-ecdhKeyMaterial (DigestAlgorithm hashAlg) kep ukm zz-    | r == 0    = B.concat (map chunk [1..d])-    | otherwise = B.concat (map chunk [1..d]) `B.append` B.take r (chunk $ succ d)-  where-    (d, r)   = outLen `divMod` Hash.hashDigestSize prx+                => KeyAgreementKDF -> KeyEncryptionParams -> Maybe ByteString -> bin -> bout+ecdhKeyMaterial kdf kep ukm zz =+    case kdf of+        KA_HKDF hashAlg ->+            HKDF.expand (extract hashAlg zz) otherInfo outLen+        KA_X963_KDF hashAlg+            | r == 0    -> B.concat (map chunk [1..d])+            | otherwise -> B.concat (map chunk [1..d]) `B.append` B.take r (chunk $ succ d)+          where+            (d, r)   = outLen `divMod` Hash.hashDigestSize prx+            prx      = hashFromProxy hashAlg -    prx      = hashFromProxy hashAlg+            chunk     = B.convert . Hash.hashFinalize . hashCtx+            hashCtx'  = Hash.hashUpdate (Hash.hashInitWith prx) zz+            hashCtx i = Hash.hashUpdate (Hash.hashUpdate hashCtx' $ toWord32 i) otherInfo++  where     outLen   = getMaximumKeySize kep     outBits  = 8 * outLen     toWord32 = i2ospOf_ 4 . fromIntegral -    chunk     = B.convert . Hash.hashFinalize . hashCtx-    hashCtx'  = Hash.hashInitWith prx-    hashCtx i = Hash.hashUpdate (Hash.hashUpdate (Hash.hashUpdate hashCtx' zz) (toWord32 i)) otherInfo+    extract :: (Hash.HashAlgorithm a, ByteArrayAccess ikm)+            => DigestProxy a -> ikm -> HKDF.PRK a+    extract _ = HKDF.extract (fromMaybe B.empty ukm)+     otherInfo =         let ki  = algorithmASN1S Sequence kep             eui = case ukm of@@ -1701,8 +2069,8 @@ -- | Decrypt the specified content with an ECDH key pair and key-agreement -- algorithm. ecdhDecrypt :: ByteArray ba-            => KeyAgreementParams -> Maybe ByteString -> X509.PrivKey -> ByteString -> ba -> Either StoreError ba-ecdhDecrypt (StdDH dig kep) ukm (X509.PrivKeyEC priv) pt bs =+            => KeyAgreementParams -> Maybe ByteString -> KeyPair -> ByteString -> ba -> Either StoreError ba+ecdhDecrypt (StdDH dig kep) ukm (KeyPairEC priv _) pt bs =     case ecPrivKeyCurve priv of         Nothing    -> Left UnsupportedEllipticCurve         Just curve ->@@ -1713,16 +2081,16 @@                         s = ECDH.getShared curve d pub                         k = ecdhKeyMaterial dig kep ukm s :: B.ScrubbedBytes                     keyDecrypt k kep bs-ecdhDecrypt (StdDH dig kep) ukm (X509.PrivKeyX25519 priv) pt bs = do+ecdhDecrypt (StdDH dig kep) ukm (KeyPairX25519 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+ecdhDecrypt (StdDH dig kep) ukm (KeyPairX448 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 =+ecdhDecrypt (CofactorDH dig kep) ukm (KeyPairEC priv _) pt bs =     case ecPrivKeyCurve priv of         Nothing    -> Left UnsupportedEllipticCurve         Just curve ->@@ -1755,8 +2123,8 @@ ivGenerate :: (BlockCipher cipher, MonadRandom m) => cipher -> m (IV cipher) ivGenerate cipher = do     bs <- getRandomBytes (blockSize cipher)-    let Just iv = makeIV (bs :: ByteString)-    return iv+    let iv = makeIV (bs :: ByteString)+    return $! fromJust iv  nonceGenerate :: MonadRandom m => Int -> m B.Bytes nonceGenerate = getRandomBytes@@ -1790,7 +2158,7 @@  parseM :: Monoid e => ParseASN1 e CCM_M parseM = do-    IntVal l <- getNext+    l <- fromMaybe 12 <$> getNextMaybe intOrNothing     case l of         4  -> return CCM_M4         6  -> return CCM_M6@@ -1802,6 +2170,85 @@         i -> throwParseError ("Parsed invalid CCM parameter M: " ++ show i)  +-- Key encapsulation++data KeyEncapsulationType+    = TypeKeyEncapsulationRSA+    deriving (Show,Eq)++instance Enumerable KeyEncapsulationType where+    values = [TypeKeyEncapsulationRSA]++instance OIDable KeyEncapsulationType where+    getObjectID TypeKeyEncapsulationRSA = [1,0,18033,2,2,4]++instance OIDNameable KeyEncapsulationType where+    fromObjectID oid = unOIDNW <$> fromObjectID oid++-- | Key encapsulation mechanism (KEM) with associated parameters.+data KeyEncapsulationMechanism+    = KeyEncapsulationRSA KeyDerivationFn Int+      -- ^ Key encapsulation with RSA-KEM+    deriving (Show,Eq)++instance AlgorithmId KeyEncapsulationMechanism where+    type AlgorithmType KeyEncapsulationMechanism = KeyEncapsulationType+    algorithmName _ = "key encapsulation mechanism"++    algorithmType (KeyEncapsulationRSA _ _) = TypeKeyEncapsulationRSA++    parameterASN1S (KeyEncapsulationRSA kdf len)+        | (kdf, len) == defaultRsaKemParams = id+        | otherwise = asn1Container Sequence $+            algorithmASN1S Sequence kdf . gIntVal (toInteger len)++    parseParameter TypeKeyEncapsulationRSA =+        fmap manageDef $ onNextContainerMaybe Sequence $ do+            kdf <- parseAlgorithm Sequence+            IntVal len <- getNext+            when (len < 1) $+                throwParseError "Illegal keyLength in RsaKemParameters"+            return $ KeyEncapsulationRSA kdf (fromInteger len)+      where+        manageDef = fromMaybe (KeyEncapsulationRSA kdfDef lenDef)+        (kdfDef, lenDef) = defaultRsaKemParams++defaultRsaKemParams :: (KeyDerivationFn, Int)+defaultRsaKemParams = (KDF3 $ DigestAlgorithm SHA256, 16)++newtype RsaKemLen = RsaKemLen Int++instance HasStrength RsaKemLen where+    getSecurityBits (RsaKemLen len) = 8 * len++kdfRsaKem :: (ByteArrayAccess bIn, ByteArray bOut)+          => KeyDerivationFn -> Int -> RsaKem.KDF bIn (Either StoreError bOut)+kdfRsaKem kdf len+    | securityAcceptable (RsaKemLen len) = RsaKem.KDF $ kdfApply kdf noSalt len+    | otherwise = RsaKem.KDF $ \_ _ ->+        Left $ InvalidParameter "RSA-KEM shared-secret length too short"++kemEncap :: (MonadRandom m, ByteArray ct, ByteArray ss)+         => KeyEncapsulationMechanism -> X509.PubKey -> m (Either StoreError (ct, ss))+kemEncap (KeyEncapsulationRSA kdf len) (X509.PubKeyRSA pub) = do+    (ss, ct) <- RsaKem.encapsulate kdFn pub+    return $ (ct, ) <$> ss+  where kdFn = kdfRsaKem kdf len+kemEncap _ _ = return (Left UnexpectedPublicKeyType)++kemDecap :: (ByteArrayAccess ct, ByteArray ss)+         => KeyEncapsulationMechanism -> KeyPair -> ct -> Either StoreError ss+kemDecap (KeyEncapsulationRSA kdf len) (KeyPairRSA priv _) ct =+    case RsaKem.decapsulate kdFn priv (B.convert ct :: B.Bytes) of+        Just ss -> ss+        Nothing -> Left $ InvalidParameter "Invalid RSA-KEM ciphertext"+  where kdFn = kdfRsaKem kdf len+kemDecap _ _ _ = Left UnexpectedPrivateKeyType++noSalt :: ByteString+noSalt = mempty++ -- Mask generation functions  data MaskGenerationType = TypeMGF1@@ -1933,6 +2380,10 @@              , TypeECDSA (DigestAlgorithm SHA256)              , TypeECDSA (DigestAlgorithm SHA384)              , TypeECDSA (DigestAlgorithm SHA512)+             , TypeECDSA (DigestAlgorithm SHA3_224)+             , TypeECDSA (DigestAlgorithm SHA3_256)+             , TypeECDSA (DigestAlgorithm SHA3_384)+             , TypeECDSA (DigestAlgorithm SHA3_512)               , TypeEd25519              , TypeEd448@@ -1960,6 +2411,10 @@     getObjectID (TypeECDSA (DigestAlgorithm SHA256)) = [1,2,840,10045,4,3,2]     getObjectID (TypeECDSA (DigestAlgorithm SHA384)) = [1,2,840,10045,4,3,3]     getObjectID (TypeECDSA (DigestAlgorithm SHA512)) = [1,2,840,10045,4,3,4]+    getObjectID (TypeECDSA (DigestAlgorithm SHA3_224)) = [2,16,840,1,101,3,4,3,9]+    getObjectID (TypeECDSA (DigestAlgorithm SHA3_256)) = [2,16,840,1,101,3,4,3,10]+    getObjectID (TypeECDSA (DigestAlgorithm SHA3_384)) = [2,16,840,1,101,3,4,3,11]+    getObjectID (TypeECDSA (DigestAlgorithm SHA3_512)) = [2,16,840,1,101,3,4,3,12]      getObjectID TypeEd25519                          = [1,3,101,112]     getObjectID TypeEd448                            = [1,3,101,113]@@ -2007,23 +2462,22 @@     parseParameter TypeEd25519      = return Ed25519     parseParameter TypeEd448        = return Ed448 --- | 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 _ _ _ =+-- | Sign a message using the specified algorithm and private key.+signatureGenerate :: MonadRandom m => SignatureAlg -> KeyPair -> ByteString -> m (Either StoreError SignatureValue)+signatureGenerate RSAAnyHash _ _ =     error "signatureGenerate: should call signatureResolveHash first"-signatureGenerate (RSA alg)   (X509.PrivKeyRSA priv) (X509.PubKeyRSA _) msg =+signatureGenerate (RSA alg)   (KeyPairRSA priv _) 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) (X509.PubKeyRSA _) msg =+signatureGenerate (RSAPSS p)  (KeyPairRSA priv _) msg =     withPSSParams p $ \params ->         mapLeft RSAError <$> RSAPSS.signSafer params priv msg-signatureGenerate (DSA alg)   (X509.PrivKeyDSA priv) (X509.PubKeyDSA _) msg =+signatureGenerate (DSA alg)   (KeyPairDSA pair) msg =     case alg of         DigestAlgorithm t ->-            Right . dsaFromSignature <$> DSA.sign priv (hashFromProxy t) msg-signatureGenerate (ECDSA alg) (X509.PrivKeyEC priv)  (X509.PubKeyEC _)  msg =+            Right . dsaFromSignature <$> DSA.sign (DSA.toPrivateKey pair) (hashFromProxy t) msg+signatureGenerate (ECDSA alg) (KeyPairEC priv _)  msg =     case alg of         DigestAlgorithm t ->             case ecdsaToPrivateKey priv of@@ -2031,11 +2485,11 @@                 Just p  ->                     let h = hashFromProxy t                      in Right . ecdsaFromSignature <$> ECDSA.sign p h msg-signatureGenerate Ed25519 (X509.PrivKeyEd25519 priv) (X509.PubKeyEd25519 pub) msg =+signatureGenerate Ed25519 (KeyPairEd25519 priv pub) msg =     return . Right . B.convert $ Ed25519.sign priv pub msg-signatureGenerate Ed448 (X509.PrivKeyEd448 priv) (X509.PubKeyEd448 pub) msg =+signatureGenerate Ed448 (KeyPairEd448 priv pub) msg =     return . Right . B.convert $ Ed448.sign priv pub msg-signatureGenerate _ _ _ _ = return (Left UnexpectedPrivateKeyType)+signatureGenerate _ _ _ = return (Left UnexpectedPrivateKeyType)  -- | Verify a message signature using the specified algorithm and public key. signatureVerify :: SignatureAlg -> X509.PubKey -> ByteString -> SignatureValue -> Bool
src/Crypto/Store/CMS/Attribute.hs view
@@ -25,12 +25,20 @@     , setContentTypeAttr     , getMessageDigestAttr     , setMessageDigestAttr+    , getSigningTimeAttr+    , setSigningTimeAttr+    , setSigningTimeAttrCurrent     ) where +import Control.Monad.IO.Class+ import Data.ASN1.Types import Data.ByteString (ByteString)+import Data.Hourglass import Data.Maybe (fromMaybe) +import Time.System (dateCurrent)+ import Crypto.Store.ASN1.Generate import Crypto.Store.ASN1.Parse import Crypto.Store.CMS.Type@@ -124,3 +132,36 @@ -- | Add or replace the @messageDigest@ attribute in a list of attributes. setMessageDigestAttr :: ByteString -> [Attribute] -> [Attribute] setMessageDigestAttr d = setAttributeASN1S messageDigest (gOctetString d)+++-- Signing time++signingTime :: OID+signingTime = [1,2,840,113549,1,9,5]++-- | Return the value of the @signingTime@ attribute.+getSigningTimeAttr :: [Attribute] -> Maybe DateTime+getSigningTimeAttr attrs = runParseAttribute signingTime attrs $ do+    ASN1Time _ t offset <- getNext+    let validOffset = maybe True (== TimezoneOffset 0) offset+    if validOffset+        then return t+        else fail "getSigningTimeAttr: invalid timezone"++-- | Add or replace the @signingTime@ attribute in a list of attributes.+setSigningTimeAttr :: DateTime -> [Attribute] -> [Attribute]+setSigningTimeAttr t =+    let normalize val = val { dtTime = (dtTime val) { todNSec = 0 } }+        offset = Just (TimezoneOffset 0)+        ty | t >= timeConvert (Date 2050 January 1) = TimeGeneralized+           | t <  timeConvert (Date 1950 January 1) = TimeGeneralized+           | otherwise                              = TimeUTC+     in setAttributeASN1S signingTime (gASN1Time ty (normalize t) offset)++-- | Add or replace the @signingTime@ attribute in a list of attributes with the+-- current time.  This is equivalent to calling 'setSigningTimeAttr' with the+-- result of 'dateCurrent'.+setSigningTimeAttrCurrent :: MonadIO m => [Attribute] -> m [Attribute]+setSigningTimeAttrCurrent attrs = do+    t <- liftIO dateCurrent+    return (setSigningTimeAttr t attrs)
src/Crypto/Store/CMS/Authenticated.hs view
@@ -10,7 +10,8 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-} module Crypto.Store.CMS.Authenticated-    ( AuthenticatedData(..)+    ( EncapsulatedContent+    , AuthenticatedData(..)     ) where  import Control.Applicative@@ -77,7 +78,7 @@     parse =         onNextContainer Sequence $ do             IntVal v <- getNext-            when (v `notElem` [0, 1, 3]) $+            unless (v `elem` [0, 1, 3]) $                 throwParseError ("AuthenticatedData: parsed invalid version: " ++ show v)             oi <- parseOriginatorInfo (Container Context 0) <|> return mempty             ris <- onNextContainer Set parse
src/Crypto/Store/CMS/Encrypted.hs view
@@ -101,5 +101,3 @@     parseEncryptedContent = parseWrapped <|> parsePrimitive     parseWrapped  = onNextContainer (Container Context 0) parseOctetStrings     parsePrimitive = do Other Context 0 bs <- getNext; return bs-    parseOctetString = do OctetString bs <- getNext; return bs-    parseOctetStrings = B.concat <$> getMany parseOctetString
src/Crypto/Store/CMS/Enveloped.hs view
@@ -42,6 +42,10 @@     , PasswordRecipientInfo(..)     , forPasswordRecipient     , withRecipientPassword+    -- * Key Encapsulation recipients+    , KEMRecipientInfo(..)+    , forKeyEncapRecipient+    , withRecipientKeyEncap     ) where  import Control.Applicative@@ -67,6 +71,7 @@ import Crypto.Store.CMS.Type import Crypto.Store.CMS.Util import Crypto.Store.Error+import Crypto.Store.Keys  -- | Encrypted key. type EncryptedKey = ByteString@@ -81,6 +86,10 @@ -- -- Some key-derivation functions add restrictions to what characters -- are supported.+--+-- Beware: 'Data.String.fromString' truncates multi-byte characters.+-- If the string may contain non-ASCII characters, prefer instead+-- @'Crypto.Store.PKCS5.fromProtectionPassword' . 'Data.String.fromString'@. type Password = ByteString  -- | Union type related to identification of the recipient.@@ -91,15 +100,13 @@  instance ASN1Elem e => ProduceASN1Object e RecipientIdentifier where     asn1s (RecipientIASN iasn) = asn1s iasn-    asn1s (RecipientSKI  ski)  = asn1Container (Container Context 0)-                                    (gOctetString ski)+    asn1s (RecipientSKI  ski)  = gMany [Other Context 0 ski]  instance Monoid e => ParseASN1Object e RecipientIdentifier where     parse = parseIASN <|> parseSKI       where parseIASN = RecipientIASN <$> parse             parseSKI  = RecipientSKI  <$>-                onNextContainer (Container Context 0) parseBS-            parseBS = do { OctetString bs <- getNext; return bs }+                do { Other Context 0 bs <- getNext; return bs }  getKTVersion :: RecipientIdentifier -> Integer getKTVersion (RecipientIASN _) = 0@@ -164,8 +171,7 @@  instance ASN1Elem e => ProduceASN1Object e OriginatorIdentifierOrKey where     asn1s (OriginatorIASN iasn)   = asn1s iasn-    asn1s (OriginatorSKI  ski)    = asn1Container (Container Context 0)-                                       (gOctetString ski)+    asn1s (OriginatorSKI  ski)    = gMany [Other Context 0 ski]     asn1s (OriginatorPublic pub)  =         originatorPublicKeyASN1S (Container Context 1) pub @@ -173,8 +179,7 @@     parse = parseIASN <|> parseSKI <|> parsePublic       where parseIASN = OriginatorIASN <$> parse             parseSKI  = OriginatorSKI  <$>-                onNextContainer (Container Context 0) parseBS-            parseBS = do { OctetString bs <- getNext; return bs }+                do { Other Context 0 bs <- getNext; return bs }             parsePublic  = OriginatorPublic <$>                 parseOriginatorPublicKey (Container Context 1) @@ -186,14 +191,13 @@  instance ASN1Elem e => ProduceASN1Object e KeyAgreeRecipientIdentifier where     asn1s (KeyAgreeRecipientIASN iasn) = asn1s iasn-    asn1s (KeyAgreeRecipientKI   ki)   = asn1Container (Container Context 0)-                                            (asn1s ki)+    asn1s (KeyAgreeRecipientKI   ki)   = keyIdentifierASN1S (Container Context 0) ki  instance Monoid e => ParseASN1Object e KeyAgreeRecipientIdentifier where     parse = parseIASN <|> parseKI       where parseIASN = KeyAgreeRecipientIASN <$> parse             parseKI   = KeyAgreeRecipientKI   <$>-                onNextContainer (Container Context 0) parse+                parseKeyIdentifier (Container Context 0)  -- | Encrypted key for a recipient in a key-agreement RI. data RecipientEncryptedKey = RecipientEncryptedKey@@ -218,6 +222,16 @@                           -> Maybe EncryptedKey findRecipientEncryptedKey cert list = rekEncryptedKey <$> find fn list   where+    fn rek = matchRecipient cert $ case rekRid rek of+        KeyAgreeRecipientIASN iasn -> RecipientIASN iasn+        KeyAgreeRecipientKI   ki   -> RecipientSKI (keyIdentifier ki)++matchRecipient :: SignedCertificate -> RecipientIdentifier -> Bool+matchRecipient cert rid =+    case rid of+        RecipientIASN iasn -> matchIASN iasn+        RecipientSKI  ski  -> matchSKI ski+  where     c = signedObject (getSigned cert)     matchIASN iasn =         (iasnIssuer iasn, iasnSerial iasn) == (certIssuerDN c, certSerial c)@@ -225,11 +239,8 @@         case extensionGet (certExtensions c) of             Just (ExtSubjectKeyId idBs) -> idBs == ski             Nothing                     -> False-    fn rek = case rekRid rek of-                 KeyAgreeRecipientIASN iasn -> matchIASN iasn-                 KeyAgreeRecipientKI   ki   -> matchSKI (keyIdentifier ki) --- | Additional information in a 'KeyIdentifier'.+-- | Additional information in a t'KeyIdentifier'. data OtherKeyAttribute = OtherKeyAttribute     { keyAttrId :: OID    -- ^ attribute identifier     , keyAttr   :: [ASN1] -- ^ attribute value@@ -255,23 +266,26 @@     }     deriving (Show,Eq) -instance ASN1Elem e => ProduceASN1Object e KeyIdentifier where-    asn1s KeyIdentifier{..} = asn1Container Sequence (keyId . date . other)-      where-        keyId = gOctetString keyIdentifier-        date  = optASN1S keyDate $ \v -> gASN1Time TimeGeneralized v Nothing-        other = optASN1S keyOther asn1s+keyIdentifierASN1S :: ASN1Elem e+                   => ASN1ConstructionType -> KeyIdentifier -> ASN1Stream e+keyIdentifierASN1S ty KeyIdentifier{..} =+    asn1Container ty (keyId . date . other)+  where+    keyId = gOctetString keyIdentifier+    date  = optASN1S keyDate $ \v -> gASN1Time TimeGeneralized v Nothing+    other = optASN1S keyOther asn1s -instance Monoid e => ParseASN1Object e KeyIdentifier where-    parse = onNextContainer Sequence $ do-        OctetString keyId <- getNext-        date <- getNextMaybe dateTimeOrNothing-        b <- hasNext-        other <- if b then Just <$> parse else return Nothing-        return KeyIdentifier { keyIdentifier = keyId-                             , keyDate = date-                             , keyOther = other-                             }+parseKeyIdentifier :: Monoid e+                   => ASN1ConstructionType -> ParseASN1 e KeyIdentifier+parseKeyIdentifier ty = onNextContainer ty $ do+    OctetString keyId <- getNext+    date <- getNextMaybe dateTimeOrNothing+    b <- hasNext+    other <- if b then Just <$> parse else return Nothing+    return KeyIdentifier { keyIdentifier = keyId+                         , keyDate = date+                         , keyOther = other+                         }  -- | Recipient using key transport. data KTRecipientInfo = KTRecipientInfo@@ -306,7 +320,73 @@     }     deriving (Show,Eq) --- | Information for a recipient of an 'EnvelopedData'.  An element contains+-- | Recipient using key encapsulation.+data KEMRecipientInfo = KEMRecipientInfo+    { kemRid :: RecipientIdentifier                       -- ^ identifier of recipient+    , kemEncapsulationParams :: KeyEncapsulationMechanism -- ^ key encapsulation mechanism+    , kemCipherText :: ByteString                         -- ^ ciphertext for this recipient+    , kemDerivationFn :: KeyDerivationFn                  -- ^ key derivation used+    , kemKekLength :: Int                                 -- ^ size of key encryption key+    , kemUkm :: Maybe UserKeyingMaterial                  -- ^ user keying material+    , kemEncryptionParams :: KeyEncryptionParams          -- ^ key encryption algorithm+    , kemEncryptedKey :: EncryptedKey                     -- ^ encrypted content-encryption key+    }+    deriving (Show,Eq)++instance ASN1Elem e => ProduceASN1Object e KEMRecipientInfo where+    asn1s KEMRecipientInfo{..} =+        asn1Container Sequence (ver . rid . kem . ct . kdf . len . ukm . kep . ek)+      where+        ver = gIntVal 0+        rid = asn1s kemRid+        kem = algorithmASN1S Sequence kemEncapsulationParams+        ct  = gOctetString kemCipherText+        kdf = algorithmASN1S Sequence kemDerivationFn+        len = gIntVal (toInteger kemKekLength)+        ukm = optASN1S kemUkm $ asn1Container (Container Context 0) . gOctetString+        kep = algorithmASN1S Sequence kemEncryptionParams+        ek  = gOctetString kemEncryptedKey++instance Monoid e => ParseASN1Object e KEMRecipientInfo where+    parse = onNextContainer Sequence $ do+        IntVal 0 <- getNext+        rid <- parse+        kem <- parseAlgorithm Sequence+        OctetString ct <- getNext+        kdf <- parseAlgorithm Sequence+        IntVal len <- getNext+        when (len < 1 || len > 65535) $+            throwParseError ("KEMRecipientInfo: parsed invalid kekLength: " ++ show len)+        ukm <- onNextContainerMaybe (Container Context 0) $+                   do { OctetString bs <- getNext; return bs }+        kep <- parseAlgorithm Sequence+        OctetString ek <- getNext+        return KEMRecipientInfo { kemRid = rid+                                , kemEncapsulationParams = kem+                                , kemCipherText = ct+                                , kemDerivationFn = kdf+                                , kemKekLength = fromInteger len+                                , kemUkm = ukm+                                , kemEncryptionParams = kep+                                , kemEncryptedKey = ek+                                }++data CMSORIforKEMOtherInfo = CMSORIforKEMOtherInfo+    { kemoiWrap :: KeyEncryptionParams+    , kemoiKekLength :: Int+    , kemoiUkm :: Maybe UserKeyingMaterial+    }+    deriving (Show,Eq)++instance ASN1Elem e => ProduceASN1Object e CMSORIforKEMOtherInfo where+    asn1s CMSORIforKEMOtherInfo{..} =+        asn1Container Sequence (wrap . len . ukm)+      where+        wrap = algorithmASN1S Sequence kemoiWrap+        len = gIntVal (toInteger kemoiKekLength)+        ukm = optASN1S kemoiUkm $ asn1Container (Container Context 0) . gOctetString++-- | Information for a recipient of an t'EnvelopedData'.  An element contains -- the content-encryption key in encrypted form. data RecipientInfo = KTRI KTRecipientInfo                      -- ^ Recipient using key transport@@ -316,6 +396,8 @@                      -- ^ Recipient using key encryption                    | PasswordRI PasswordRecipientInfo                      -- ^ Recipient using password-based protection+                   | KEMRI KEMRecipientInfo+                     -- ^ Recipient using key encapsulation     deriving (Show,Eq)  instance ASN1Elem e => ProduceASN1Object e RecipientInfo where@@ -343,7 +425,7 @@         asn1Container (Container Context 2) (ver . kid . kep . ek)       where         ver = gIntVal 4-        kid = asn1s kekId+        kid = keyIdentifierASN1S Sequence kekId         kep = algorithmASN1S Sequence kekKeyEncryptionParams         ek  = gOctetString kekEncryptedKey @@ -355,23 +437,30 @@         kep = algorithmASN1S Sequence priKeyEncryptionParams         ek  = gOctetString priEncryptedKey +    asn1s (KEMRI ri) =+        asn1Container (Container Context 4) (typ . val)+      where+        typ = gOID [1,2,840,113549,1,9,16,13,3]+        val = asn1s ri+ instance Monoid e => ParseASN1Object e RecipientInfo where     parse = do         c <- onNextContainerMaybe Sequence parseKT              `orElse` onNextContainerMaybe (Container Context 1) parseKA              `orElse` onNextContainerMaybe (Container Context 2) parseKEK              `orElse` onNextContainerMaybe (Container Context 3) parsePassword+             `orElse` onNextContainerMaybe (Container Context 4) parseOther         case c of             Just val -> return val             Nothing  -> throwParseError "RecipientInfo: unable to parse"       where         parseKT = KTRI <$> do             IntVal v <- getNext-            when (v `notElem` [0, 2]) $+            unless (v `elem` [0, 2]) $                 throwParseError ("RecipientInfo: parsed invalid KT version: " ++ show v)             rid <- parse             ktp <- parseAlgorithm Sequence-            (OctetString ek) <- getNext+            OctetString ek <- getNext             return KTRecipientInfo { ktRid = rid                                    , ktKeyTransportParams = ktp                                    , ktEncryptedKey = ek@@ -392,9 +481,9 @@          parseKEK = KEKRI <$> do             IntVal 4 <- getNext-            kid <- parse+            kid <- parseKeyIdentifier Sequence             kep <- parseAlgorithm Sequence-            (OctetString ek) <- getNext+            OctetString ek <- getNext             return KEKRecipientInfo { kekId = kid                                     , kekKeyEncryptionParams = kep                                     , kekEncryptedKey = ek@@ -404,23 +493,29 @@             IntVal 0 <- getNext             kdf <- parseAlgorithm (Container Context 0)             kep <- parseAlgorithm Sequence-            (OctetString ek) <- getNext+            OctetString ek <- getNext             return PasswordRecipientInfo { priKeyDerivationFunc = kdf                                          , priKeyEncryptionParams = kep                                          , priEncryptedKey = ek                                          } +        parseOther = KEMRI <$> do+            OID [1,2,840,113549,1,9,16,13,3] <- getNext+            parse+ isVersion0 :: RecipientInfo -> Bool isVersion0 (KTRI x)       = getKTVersion (ktRid x) == 0 isVersion0 (KARI _)       = False      -- because version is always 3 isVersion0 (KEKRI _)      = False      -- because version is always 4 isVersion0 (PasswordRI _) = True       -- because version is always 0+isVersion0 (KEMRI _)      = True       -- because version is always 0  isPwriOri :: RecipientInfo -> Bool isPwriOri (KTRI _)       = False isPwriOri (KARI _)       = False isPwriOri (KEKRI _)      = False isPwriOri (PasswordRI _) = True+isPwriOri (KEMRI _)      = True  -- | Enveloped content information. data EnvelopedData content = EnvelopedData@@ -506,9 +601,9 @@ -- -- This function can be used as parameter to -- 'Crypto.Store.CMS.openEnvelopedData'.-withRecipientKeyTrans :: MonadRandom m => PrivKey -> ConsumerOfRI m-withRecipientKeyTrans privKey (KTRI KTRecipientInfo{..}) =-    transportDecrypt ktKeyTransportParams privKey ktEncryptedKey+withRecipientKeyTrans :: MonadRandom m => KeyPair -> ConsumerOfRI m+withRecipientKeyTrans keyPair (KTRI KTRecipientInfo{..}) =+    transportDecrypt ktKeyTransportParams keyPair ktEncryptedKey withRecipientKeyTrans _ _ = pure (Left RecipientTypeMismatch)  -- | Generate a Key Agreement recipient from a certificate and@@ -551,16 +646,18 @@ -- -- This function can be used as parameter to -- 'Crypto.Store.CMS.openEnvelopedData'.-withRecipientKeyAgree :: MonadRandom m => PrivKey -> SignedCertificate -> ConsumerOfRI m-withRecipientKeyAgree priv cert (KARI KARecipientInfo{..}) =-    case kaOriginator of-        OriginatorPublic (OriginatorPublicKeyEC _ ba) ->-            case findRecipientEncryptedKey cert kaRecipientEncryptedKeys of-                Nothing -> pure (Left RecipientKeyNotFound)-                Just ek ->-                    let pub = bitArrayGetData ba-                     in pure (ecdhDecrypt kaKeyAgreementParams kaUkm priv pub ek)-        _ -> pure (Left UnsupportedOriginatorFormat)+withRecipientKeyAgree :: MonadRandom m => KeyPair -> SignedCertificate -> ConsumerOfRI m+withRecipientKeyAgree pair cert (KARI KARecipientInfo{..})+    | keyPairMatchesCert pair cert =+        case kaOriginator of+            OriginatorPublic (OriginatorPublicKeyEC _ ba) ->+                case findRecipientEncryptedKey cert kaRecipientEncryptedKeys of+                    Nothing -> pure (Left RecipientKeyNotFound)+                    Just ek ->+                        let pub = bitArrayGetData ba+                         in pure (ecdhDecrypt kaKeyAgreementParams kaUkm pair pub ek)+            _ -> pure (Left UnsupportedOriginatorFormat)+    | otherwise = pure $ Left PublicPrivateKeyMismatch withRecipientKeyAgree _ _ _        = pure (Left RecipientTypeMismatch)  -- | Generate a Key Encryption Key recipient from a key encryption key and@@ -627,3 +724,84 @@     len = fromMaybe (getMaximumKeySize priKeyEncryptionParams)                     (kdfKeyLength priKeyDerivationFunc) withRecipientPassword _ _ = pure (Left RecipientTypeMismatch)++-- | Generate a Key Encapsulation recipient from a certificate and+-- desired algorithms.  The recipient info will contain the KEM ciphertext.+--+-- This function can be used as parameter to 'Crypto.Store.CMS.envelopData'.+--+-- To avoid decreasing the security strength, selected algorithms should all be+-- equal or stronger than the content encryption key.+forKeyEncapRecipient :: MonadRandom m+                     => SignedCertificate+                     -> KeyDerivationFn+                     -> KeyEncryptionParams+                     -> KeyEncapsulationMechanism+                     -> ProducerOfRI m+forKeyEncapRecipient cert kdf kep params inkey = do+    ephemeral <- kemEncap params (certPubKey obj)+    case ephemeral of+        Right (ct, ss) ->+            case kdfApply kdf noSalt len prm (ss :: EncryptedKey) of+                Left err -> pure $ Left err+                Right kek -> do+                    ek <- keyEncrypt (kek :: EncryptedKey) kep inkey+                    pure (KEMRI . build ct <$> ek)+        Left err -> pure $ Left err+  where+    obj = signedObject (getSigned cert)+    isn = IssuerAndSerialNumber (certIssuerDN obj) (certSerial obj)+    prm = encodeASN1S (asn1s info)++    len  = getMaximumKeySize kep+    info = CMSORIforKEMOtherInfo+        { kemoiWrap = kep+        , kemoiKekLength = len+        , kemoiUkm = Nothing+        }++    build ct ek =+        KEMRecipientInfo+            { kemRid = RecipientIASN isn+            , kemEncapsulationParams = params+            , kemCipherText = ct+            , kemDerivationFn = kdf+            , kemKekLength = len+            , kemUkm = Nothing+            , kemEncryptionParams = kep+            , kemEncryptedKey = ek+            }++-- | Use a Key Encapsulation recipient, knowing the recipient private key.+-- The recipient certificate is also used to determine if a recipient info+-- is applicable.+--+-- This function can be used as parameter to+-- 'Crypto.Store.CMS.openEnvelopedData'.+withRecipientKeyEncap :: MonadRandom m => KeyPair -> SignedCertificate -> ConsumerOfRI m+withRecipientKeyEncap pair cert (KEMRI KEMRecipientInfo{..})+    | not (keyPairMatchesCert pair cert) =+        pure $ Left PublicPrivateKeyMismatch+    | not (matchRecipient cert kemRid) =+        pure $ Left NoRecipientInfoMatched+    | otherwise =+        case kemDecap kemEncapsulationParams pair kemCipherText of+            Left err -> pure $ Left err+            Right ss | len == kemKekLength -> pure $+                case kdfApply kemDerivationFn noSalt kemKekLength prm (ss :: EncryptedKey) of+                    Left err -> Left err+                    Right kek -> keyDecrypt (kek :: EncryptedKey)+                        kemEncryptionParams kemEncryptedKey+            Right _ -> pure $ Left (InvalidInput "Wrong kekLength in KEMRecipientInfo")+  where+    len  = getMaximumKeySize kemEncryptionParams+    prm  = encodeASN1S (asn1s info)+    info = CMSORIforKEMOtherInfo+        { kemoiWrap = kemEncryptionParams+        , kemoiKekLength = kemKekLength+        , kemoiUkm = kemUkm+        }+withRecipientKeyEncap _ _ _        = pure (Left RecipientTypeMismatch)++noSalt :: ByteString+noSalt = mempty
src/Crypto/Store/CMS/Info.hs view
@@ -6,6 +6,7 @@ -- Portability : unknown -- -- CMS content information.+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -21,7 +22,9 @@     , toDetachedCI     ) where +#if !(MIN_VERSION_base(4,13,0)) import Control.Monad.Fail (MonadFail)+#endif  import Data.ASN1.Types import Data.ByteString (ByteString)@@ -105,12 +108,7 @@ dataASN1S = gOctetString  parseData :: Monoid e => ParseASN1 e ByteString-parseData = do-    next <- getNext-    case next of-        OctetString bs -> return bs-        _              -> throwParseError "Data: parsed unexpected content"-+parseData = parseOctetString  -- Encapsulation 
src/Crypto/Store/CMS/OriginatorInfo.hs view
@@ -6,6 +6,7 @@ -- Portability : unknown -- --+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-}@@ -24,7 +25,9 @@  import Data.ASN1.Types import Data.Maybe (fromMaybe)+#if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup+#endif import Data.X509  import Crypto.Store.ASN1.Generate
src/Crypto/Store/CMS/PEM.hs view
@@ -11,6 +11,7 @@     , readCMSFileFromMemory     , berToContentInfo     , pemToContentInfo+    , pemToContentInfoAccum     , writeCMSFile     , writeCMSFileToMemory     , contentInfoToDER@@ -18,7 +19,7 @@     ) where  import qualified Data.ByteString as B-import           Data.Maybe (catMaybes)+import           Data.Either (rights)  import Crypto.Store.CMS.Info import Crypto.Store.CMS.Util@@ -37,24 +38,24 @@ readCMSFileFromMemory = either (const []) accumulate . pemParseBS  accumulate :: [PEM] -> [ContentInfo]-accumulate = catMaybes . foldr (flip pemToContentInfo) []+accumulate = rights . map pemToContentInfo  -- | Read a content info from a bytearray in BER format. berToContentInfo :: B.ByteString -> Either StoreError ContentInfo berToContentInfo = decodeASN1Object --- | Read a content info from a 'PEM' element and add it to the accumulator+-- | Read a content info from a t'PEM' element and add it to the accumulator -- list.-pemToContentInfo :: [Maybe ContentInfo] -> PEM -> [Maybe ContentInfo]-pemToContentInfo acc pem-    | pemName pem `elem` names = decode (pemContent pem)-    | otherwise                = Nothing : acc-  where-    names = [ "CMS", "PKCS7" ]-    decode bs =-        case berToContentInfo bs of-            Left _ -> Nothing : acc-            Right info -> Just info : acc+pemToContentInfoAccum :: [Maybe ContentInfo] -> PEM -> [Maybe ContentInfo]+pemToContentInfoAccum acc pem =+    either (const Nothing) Just (pemToContentInfo pem) : acc++-- | Read a content info from a t'PEM' element.+pemToContentInfo :: PEM -> Either StoreError ContentInfo+pemToContentInfo pem+    | pemName pem `elem` names = berToContentInfo (pemContent pem)+    | otherwise                = Left UnexpectedNameForPEM+  where names = [ "CMS", "PKCS7" ]   -- Writing to PEM format
src/Crypto/Store/CMS/Signed.hs view
@@ -31,6 +31,7 @@ import           Data.ASN1.Types import           Data.ByteString (ByteString) import qualified Data.ByteString as B+import           Data.Hourglass import           Data.List import           Data.Maybe import           Data.X509@@ -47,6 +48,7 @@ import Crypto.Store.CMS.Type import Crypto.Store.CMS.Util import Crypto.Store.Error+import Crypto.Store.Keys  -- | Encapsulated content. type EncapsulatedContent = ByteString@@ -90,7 +92,7 @@         dig <- parseAlgorithm Sequence         sAttrs <- parseAttributes (Container Context 0)         alg <- parseAlgorithm Sequence-        (OctetString sig) <- getNext+        OctetString sig <- getNext         uAttrs <- parseAttributes (Container Context 1)         return SignerInfo { siSignerId = sid                           , siDigestAlgorithm = dig@@ -116,15 +118,13 @@  instance ASN1Elem e => ProduceASN1Object e SignerIdentifier where     asn1s (SignerIASN iasn) = asn1s iasn-    asn1s (SignerSKI  ski)  = asn1Container (Container Context 0)-                                  (gOctetString ski)+    asn1s (SignerSKI  ski)  = gMany [Other Context 0 ski]  instance Monoid e => ParseASN1Object e SignerIdentifier where     parse = parseIASN <|> parseSKI       where parseIASN = SignerIASN <$> parse             parseSKI  = SignerSKI  <$>-                onNextContainer (Container Context 0) parseBS-            parseBS = do { OctetString bs <- getNext; return bs }+                do { Other Context 0 bs <- getNext; return bs }  -- | Try to find a certificate with the specified identifier. findSigner :: SignerIdentifier@@ -149,10 +149,10 @@         (x : _, r) -> Just (x, r)         ([]   , _)    -> Nothing --- | Function able to produce a 'SignerInfo'.+-- | Function able to produce a t'SignerInfo'. type ProducerOfSI m = ContentType -> ByteString -> m (Either StoreError (SignerInfo, [CertificateChoice], [RevocationInfoChoice])) --- | Function able to consume a 'SignerInfo'.+-- | Function able to consume a t'SignerInfo'. type ConsumerOfSI m = ContentType -> ByteString -> SignerInfo -> [CertificateChoice] -> [RevocationInfoChoice] -> m Bool  -- | Create a signer info with the specified signature algorithm and@@ -167,17 +167,19 @@ -- the full message. certSigner :: MonadRandom m            => SignatureAlg-           -> PrivKey+           -> KeyPair            -> CertificateChain            -> Maybe [Attribute]            -> [Attribute]            -> ProducerOfSI m-certSigner alg priv (CertificateChain chain) sAttrsM uAttrs ct msg =-    fmap build <$> generate+certSigner _ _ (CertificateChain []) _ _ _ _ =+    pure $ Left (InvalidInput "Empty certificate chain")+certSigner alg pair (CertificateChain chain@(cert:_)) sAttrsM uAttrs ct msg+    | keyPairMatchesKey pair pub = fmap build <$> generate+    | otherwise = pure $ Left PublicPrivateKeyMismatch   where     md   = digest dig msg     def  = DigestAlgorithm Crypto.Store.CMS.Algorithms.SHA256-    cert = head chain     obj  = signedObject (getSigned cert)     isn  = IssuerAndSerialNumber (certIssuerDN obj) (certSerial obj)     pub  = certPubKey obj@@ -192,7 +194,7 @@                 let l = setContentTypeAttr ct $ setMessageDigestAttr md attrs                  in (l, encodeAuthAttrs l) -    generate  = signatureGenerate alg' priv pub input+    generate  = signatureGenerate alg' pair input     build sig =         let si = SignerInfo { siSignerId = SignerIASN isn                             , siDigestAlgorithm = dig@@ -226,16 +228,17 @@ -- valid.  All transmitted certificates are implicitely trusted and all CRLs are -- ignored. withSignerKey :: Applicative f => ConsumerOfSI f-withSignerKey = withSignerCertificate (\_ -> pure True)+withSignerKey = withSignerCertificate (\_ _ -> pure True)  -- | Verify that the signature is valid with one of the X.509 certificates -- contained in the signed data, and verify that the signer certificate is valid -- using the validation function supplied.  All CRLs are ignored. withSignerCertificate :: Applicative f-                      => (CertificateChain -> f Bool) -> ConsumerOfSI f+                      => (Maybe DateTime -> CertificateChain -> f Bool)+                      -> ConsumerOfSI f withSignerCertificate validate ct msg SignerInfo{..} certs crls =     case getCertificateChain of-        Just chain -> validate chain+        Just chain -> validate mSigningTime chain         Nothing    -> pure False   where     getCertificateChain = do@@ -245,6 +248,8 @@         guard validSignature         return $ CertificateChain (cert : others) +    mSigningTime = getSigningTimeAttr siSignedAttrs+     x509Certificates = mapMaybe asX509 certs      asX509 (CertificateCertificate c) = Just c@@ -322,16 +327,10 @@     onNextContainer Sequence $ do         OID oid <- getNext         withObjectID "content type" oid $ \ct ->-            wrap ct <$> onNextContainerMaybe (Container Context 0) parseInner+            wrap ct <$> onNextContainerMaybe (Container Context 0) parseOctetString   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
src/Crypto/Store/CMS/Util.hs view
@@ -24,9 +24,14 @@     , Enumerable(..)     , OIDNameableWrapper(..)     , withObjectID+    -- * Parsing octet strings+    , parseOctetString+    , parseOctetStringPrim+    , parseOctetStrings     -- * Parsing and encoding ASN.1 objects     , ASN1Event     , ASN1ObjectExact(..)+    , derObjectExact     , ProduceASN1Object(..)     , encodeASN1Object     , ParseASN1Object(..)@@ -41,12 +46,15 @@     , orElse     ) where +import Control.Applicative+ import           Data.ASN1.BinaryEncoding import           Data.ASN1.BinaryEncoding.Raw import           Data.ASN1.Encoding import           Data.ASN1.OID import           Data.ASN1.Types import           Data.ByteString (ByteString)+import qualified Data.ByteString as B import           Data.List (find) import           Data.X509 @@ -171,6 +179,10 @@     , exactObjectRaw :: ByteString  -- ^ The raw representation of this object     } deriving Show +-- | Create an ASN.1 object with a raw data in DER format.+derObjectExact :: ProduceASN1Object ASN1P a => a -> ASN1ObjectExact a+derObjectExact obj = ASN1ObjectExact obj (encodeASN1Object obj)+ instance Eq a => Eq (ASN1ObjectExact a)     where a == b = exactObject a == exactObject b @@ -231,3 +243,22 @@     case va of         Nothing -> pb         _       -> return va++-- | Parse an octet string, in primitive or constructed encodings.+parseOctetString :: Monoid e => ParseASN1 e ByteString+parseOctetString = parseOctetStringPrim <|> parseConstructed+  where+    parseConstructed = onNextContainer (Container Universal 4) parseOctetStrings++-- | Parse an octet string in primitive encoding.+parseOctetStringPrim :: Monoid e => ParseASN1 e ByteString+parseOctetStringPrim = do+    next <- getNext+    case next of+        OctetString bs -> return bs+        _ -> throwParseError "parseOctetStringPrim: parsed unexpected content"++-- | Parse some octet strings, in primitive or constructed encodings, and+-- concatenate the result.+parseOctetStrings :: Monoid e => ParseASN1 e ByteString+parseOctetStrings = B.concat <$> getMany parseOctetString
src/Crypto/Store/Error.hs view
@@ -26,6 +26,8 @@       -- ^ Error while decoding ASN.1 content     | ParseFailure String       -- ^ Error while parsing an ASN.1 object+    | UnexpectedNameForPEM+      -- ^ The name of the given PEM object is not supported     | DecryptionFailed       -- ^ Unable to decrypt, incorrect key or password?     | BadContentMAC@@ -42,6 +44,8 @@       -- ^ Some condition is not met about input password     | InvalidParameter String       -- ^ Some condition is not met about algorithm parameters+    | PublicPrivateKeyMismatch+      -- ^ The public key or certificate does not match the private key     | UnexpectedPublicKeyType       -- ^ The algorithm expects another public key type     | UnexpectedPrivateKeyType
src/Crypto/Store/KeyWrap/AES.hs view
@@ -10,6 +10,7 @@ -- -- Should be used with a cipher from module "Crypto.Cipher.AES". {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-} module Crypto.Store.KeyWrap.AES     ( wrap     , unwrap@@ -20,7 +21,9 @@ import           Data.Bits import           Data.ByteArray (ByteArray, ByteArrayAccess, Bytes) import qualified Data.ByteArray as B-import           Data.List+#if !(MIN_VERSION_base(4,20,0))+import           Data.List (foldl')+#endif import           Data.Word  import Crypto.Cipher.Types
src/Crypto/Store/KeyWrap/RC2.hs view
@@ -16,6 +16,7 @@  import           Data.ByteArray (ByteArray) import qualified Data.ByteArray as B+import           Data.Maybe (fromJust)  import Crypto.Cipher.Types import Crypto.Hash@@ -62,7 +63,7 @@             temp1      = cbcEncrypt cipher iv lcekpadicv             temp2      = B.append (B.convert iv) temp1             temp3      = reverseBytes temp2-            Just iv'   = makeIV iv4adda22c79e82105+            iv'        = fromJust $ makeIV iv4adda22c79e82105          in cbcEncrypt cipher iv' temp3  -- | Unwrap an encrypted RC2 key with the specified RC2 cipher.@@ -75,14 +76,14 @@     | otherwise          = invalid   where     inLen            = B.length wrapped-    Just iv'         = makeIV iv4adda22c79e82105+    iv'              = fromJust $ makeIV iv4adda22c79e82105     temp3            = cbcDecrypt cipher iv' wrapped     temp2            = reverseBytes temp3     (ivBs, temp1)    = B.splitAt 8 temp2-    Just iv          = makeIV ivBs+    iv               = fromJust $ makeIV ivBs     lcekpadicv       = cbcDecrypt cipher iv temp1     (lcekpad, icv)   = B.splitAt (inLen - 16) lcekpadicv-    Just (l, cekpad) = B.uncons lcekpad+    (l, cekpad)      = fromJust $ B.uncons lcekpad     len              = fromIntegral l     padlen           = inLen - 16 - len - 1     cek              = B.take len cekpad
src/Crypto/Store/KeyWrap/TripleDES.hs view
@@ -15,6 +15,7 @@  import           Data.ByteArray (ByteArray) import qualified Data.ByteArray as B+import           Data.Maybe (fromJust)  import Crypto.Cipher.Types import Crypto.Hash@@ -40,7 +41,7 @@         (InvalidInput "KeyWrap.TripleDES: invalid length for content encryption key")   where     inLen    = B.length cek-    Just iv' = makeIV iv4adda22c79e82105+    iv'      = fromJust $ makeIV iv4adda22c79e82105     cekicv   = B.append cek (checksum cek)     temp1    = cbcEncrypt cipher iv cekicv     temp2    = B.append (B.convert iv) temp1@@ -56,11 +57,11 @@     | otherwise                    = invalid   where     inLen         = B.length wrapped-    Just iv'      = makeIV iv4adda22c79e82105+    iv'           = fromJust $ makeIV iv4adda22c79e82105     temp3         = cbcDecrypt cipher iv' wrapped     temp2         = reverseBytes temp3     (ivBs, temp1) = B.splitAt 8 temp2-    Just iv       = makeIV ivBs+    iv            = fromJust $ makeIV ivBs     cekicv        = cbcDecrypt cipher iv temp1     (cek, icv)    = B.splitAt 24 cekicv     invalid       = Left BadChecksum
+ src/Crypto/Store/Keys.hs view
@@ -0,0 +1,122 @@+-- |+-- Module      : Crypto.Store.Keys+-- License     : BSD-style+-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>+-- Stability   : experimental+-- Portability : unknown+--+--+{-# LANGUAGE RecordWildCards #-}+module Crypto.Store.Keys+    ( KeyPair(..), keyPairFromPrivKey, keyPairToPrivKey, keyPairToPubKey+    , keyPairMatchesKey, keyPairMatchesCert+    ) where++import Data.Function (on)+import Data.Maybe (fromMaybe)++import qualified Data.X509 as X509+import Data.X509.EC++import qualified Crypto.PubKey.RSA.Types as RSA+import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448 as X448+import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Crypto.PubKey.Ed448 as Ed448++import Crypto.Store.PKCS8.EC++-- | Holds a private and public key together, with a guarantee that they both+-- match.  Therefore no constructor is exposed.  Content may be accessed+-- through functions 'keyPairToPrivKey' and 'keyPairToPubKey'.+--+-- Call function 'keyPairFromPrivKey' to build a @KeyPair@.+data KeyPair =+      KeyPairRSA RSA.PrivateKey RSA.PublicKey             -- ^ RSA key pair+    | KeyPairDSA DSA.KeyPair                              -- ^ DSA key pair+    | KeyPairEC X509.PrivKeyEC X509.PubKeyEC              -- ^ EC key pair+    | KeyPairX25519 X25519.SecretKey X25519.PublicKey     -- ^ X25519 key pair+    | KeyPairX448 X448.SecretKey X448.PublicKey           -- ^ X448 key pair+    | KeyPairEd25519 Ed25519.SecretKey Ed25519.PublicKey  -- ^ Ed25519 key pair+    | KeyPairEd448 Ed448.SecretKey Ed448.PublicKey        -- ^ Ed448 key pair++instance Show KeyPair where+    showsPrec d keyPair = showParen (d > 10) $+        showString "keyPairFromPrivKey " . showsPrec 11 (keyPairToPrivKey keyPair)++instance Eq KeyPair where+    (==) = (==) `on` keyPairToPrivKey++-- | Builds a key pair from an X.509 private key.+keyPairFromPrivKey :: X509.PrivKey -> KeyPair+keyPairFromPrivKey (X509.PrivKeyRSA priv) = KeyPairRSA priv (RSA.toPublicKey (RSA.KeyPair priv))+keyPairFromPrivKey (X509.PrivKeyDSA priv) = KeyPairDSA (dsaPairFromPriv priv)+keyPairFromPrivKey (X509.PrivKeyEC priv) = KeyPairEC priv (ecPubFromPriv priv)+keyPairFromPrivKey (X509.PrivKeyX25519 priv) = KeyPairX25519 priv (X25519.toPublic priv)+keyPairFromPrivKey (X509.PrivKeyX448 priv) = KeyPairX448 priv (X448.toPublic priv)+keyPairFromPrivKey (X509.PrivKeyEd25519 priv) = KeyPairEd25519 priv (Ed25519.toPublic priv)+keyPairFromPrivKey (X509.PrivKeyEd448 priv) = KeyPairEd448 priv (Ed448.toPublic priv)++dsaPairFromPriv :: DSA.PrivateKey -> DSA.KeyPair+dsaPairFromPriv k = DSA.KeyPair params y x+  where y       = DSA.calculatePublic params x+        params  = DSA.private_params k+        x       = DSA.private_x k++ecPubFromPriv :: X509.PrivKeyEC -> X509.PubKeyEC+ecPubFromPriv priv = case priv of+    X509.PrivKeyEC_Prime{..} -> X509.PubKeyEC_Prime+        { pubkeyEC_pub = getSerializedPoint curve privkeyEC_priv+        , pubkeyEC_a = privkeyEC_a+        , pubkeyEC_b = privkeyEC_b+        , pubkeyEC_prime = privkeyEC_prime+        , pubkeyEC_generator = privkeyEC_generator+        , pubkeyEC_order = privkeyEC_order+        , pubkeyEC_cofactor = privkeyEC_cofactor+        , pubkeyEC_seed = privkeyEC_seed+        }+    X509.PrivKeyEC_Named{..} -> X509.PubKeyEC_Named+        { X509.pubkeyEC_name = privkeyEC_name+        , X509.pubkeyEC_pub = getSerializedPoint curve privkeyEC_priv+        }+  where curve = fromMaybe (error "ecPubFromPriv: invalid EC parameters") (ecPrivKeyCurve priv)++-- | Gets the X.509 private key in a key pair.+keyPairToPrivKey :: KeyPair -> X509.PrivKey+keyPairToPrivKey (KeyPairRSA priv _) = X509.PrivKeyRSA priv+keyPairToPrivKey (KeyPairDSA pair) = X509.PrivKeyDSA (DSA.toPrivateKey pair)+keyPairToPrivKey (KeyPairEC priv _) = X509.PrivKeyEC priv+keyPairToPrivKey (KeyPairX25519 priv _) = X509.PrivKeyX25519 priv+keyPairToPrivKey (KeyPairX448 priv _) = X509.PrivKeyX448 priv+keyPairToPrivKey (KeyPairEd25519 priv _) = X509.PrivKeyEd25519 priv+keyPairToPrivKey (KeyPairEd448 priv _) = X509.PrivKeyEd448 priv++-- | Gets the X.509 public key in a key pair.+keyPairToPubKey :: KeyPair -> X509.PubKey+keyPairToPubKey (KeyPairRSA _ pub) = X509.PubKeyRSA pub+keyPairToPubKey (KeyPairDSA pair) = X509.PubKeyDSA (DSA.toPublicKey pair)+keyPairToPubKey (KeyPairEC _ pub) = X509.PubKeyEC pub+keyPairToPubKey (KeyPairX25519 _ pub) = X509.PubKeyX25519 pub+keyPairToPubKey (KeyPairX448 _ pub) = X509.PubKeyX448 pub+keyPairToPubKey (KeyPairEd25519 _ pub) = X509.PubKeyEd25519 pub+keyPairToPubKey (KeyPairEd448 _ pub) = X509.PubKeyEd448 pub++-- | Returns 'True' when the given X.509 public key is consistent with a key+-- pair, which means that the public key can be derived from the private key in+-- the key pair.+keyPairMatchesKey :: KeyPair -> X509.PubKey -> Bool+keyPairMatchesKey (KeyPairRSA _ pub) (X509.PubKeyRSA other) = pub == other+keyPairMatchesKey (KeyPairDSA pair) (X509.PubKeyDSA other) = DSA.toPublicKey pair == other+keyPairMatchesKey (KeyPairEC _ pub) (X509.PubKeyEC other) = pub == other+keyPairMatchesKey (KeyPairX25519 _ pub) (X509.PubKeyX25519 other) = pub == other+keyPairMatchesKey (KeyPairX448 _ pub) (X509.PubKeyX448 other) = pub == other+keyPairMatchesKey (KeyPairEd25519 _ pub) (X509.PubKeyEd25519 other) = pub == other+keyPairMatchesKey (KeyPairEd448 _ pub) (X509.PubKeyEd448 other) = pub == other+keyPairMatchesKey _ _ = False++keyPairMatchesCert :: KeyPair -> X509.SignedCertificate -> Bool+keyPairMatchesCert keyPair cert =+    let obj = X509.signedObject (X509.getSigned cert)+        pub = X509.certPubKey obj+     in keyPairMatchesKey keyPair pub
src/Crypto/Store/PEM.hs view
@@ -11,6 +11,7 @@     , writePEMs     , pemsWriteBS     , pemsWriteLBS+    , mkPEM     , module Data.PEM     ) where @@ -19,6 +20,9 @@ import qualified Data.ByteString.Lazy as L  -- | Read a PEM file from disk.+--+-- The function uses lazy IO so will retain an open file descriptor until the+-- list spine is fully evaluated. readPEMs :: FilePath -> IO [PEM] readPEMs filepath = either error id . pemParseLBS <$> L.readFile filepath @@ -33,3 +37,7 @@ -- | Write a PEM file to disk. writePEMs :: FilePath -> [PEM] -> IO () writePEMs filepath = L.writeFile filepath . pemsWriteLBS++-- | Make a PEM without headers.+mkPEM :: String -> B.ByteString -> PEM+mkPEM name bs = PEM { pemName = name, pemHeader = [], pemContent = bs}
src/Crypto/Store/PKCS12.hs view
@@ -8,15 +8,17 @@ -- Personal Information Exchange Syntax, aka PKCS #12. -- -- Only password integrity mode and password privacy modes are supported.+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Crypto.Store.PKCS12-    ( IntegrityParams+    ( IntegrityParams(..)     , readP12File     , readP12FileFromMemory     , writeP12File@@ -58,19 +60,29 @@     , toNamedCredential     -- * Password-based protection     , Password+    , OptAuthenticated(..)+    , recoverAuthenticated+    , ProtectionPassword+    , emptyNotTerminated+    , fromProtectionPassword+    , toProtectionPassword     , OptProtected(..)     , recover     , recoverA     ) where +import Control.Applicative import Control.Monad  import           Data.ASN1.Types import qualified Data.ByteArray as B import qualified Data.ByteString as BS import           Data.List (partition)-import           Data.Maybe (fromMaybe, mapMaybe)+import           Data.Maybe (isJust, fromMaybe, mapMaybe)+#if !(MIN_VERSION_base(4,11,0)) import           Data.Semigroup+#endif+import           Data.String (fromString) import qualified Data.X509 as X509 import qualified Data.X509.Validation as X509 @@ -82,47 +94,101 @@ import Crypto.Store.CMS.Algorithms import Crypto.Store.CMS.Attribute import Crypto.Store.CMS.Encrypted+import Crypto.Store.CMS.Enveloped import Crypto.Store.CMS.Util import Crypto.Store.Error+import Crypto.Store.Keys import Crypto.Store.PKCS5 import Crypto.Store.PKCS5.PBES1 import Crypto.Store.PKCS8  +-- Password-based integrity++-- | Data type for objects that are possibly authenticated with a password.+--+-- Content is verified and retrieved by providing a 'Password' value.  When+-- verification is successful, a value of type 'ProtectionPassword' is also+-- returned and this value can be fed to an inner decryption layer that needs+-- the same password (usual case for PKCS #12).+data OptAuthenticated a = Unauthenticated a+                          -- ^ Value is not authenticated+                        | Authenticated (Password -> Either StoreError (ProtectionPassword, a))+                          -- ^ Value is authenticated with a password++instance Functor OptAuthenticated where+    fmap f (Unauthenticated x) = Unauthenticated (f x)+    fmap f (Authenticated g)   = Authenticated (fmap (fmap f) . g)++-- | Try to recover an 'OptAuthenticated' content using the specified password.+--+-- When successful, the content is returned, as well as the password converted+-- to type 'ProtectionPassword'.  This password value can then be fed to the+-- inner decryption layer when both passwords are known to be same (usual case+-- for PKCS #12).+recoverAuthenticated :: Password -> OptAuthenticated a -> Either StoreError (ProtectionPassword, a)+recoverAuthenticated pwd (Unauthenticated x) = Right (toProtectionPassword pwd, x)+recoverAuthenticated pwd (Authenticated f)   = f pwd++ -- Decoding and parsing  -- | Read a PKCS #12 file from disk.-readP12File :: FilePath -> IO (Either StoreError (OptProtected PKCS12))+readP12File :: FilePath -> IO (Either StoreError (OptAuthenticated PKCS12)) readP12File path = readP12FileFromMemory <$> BS.readFile path  -- | Read a PKCS #12 file from a bytearray in BER format.-readP12FileFromMemory :: BS.ByteString -> Either StoreError (OptProtected PKCS12)+readP12FileFromMemory :: BS.ByteString -> Either StoreError (OptAuthenticated PKCS12) readP12FileFromMemory ber = decode ber >>= integrity   where     integrity PFX{..} =         case macData of-            Nothing -> Unprotected <$> decode authSafeData-            Just md -> return $ Protected (verify md authSafeData)+            Nothing -> Unauthenticated <$> decode authSafeData+            Just md -> return $ Authenticated (verify md authSafeData)      verify MacData{..} content pwdUTF8 =-        case digAlg of-            DigestAlgorithm d ->-                let fn key macAlg bs-                        | not (securityAcceptable macAlg) =-                            Left (InvalidParameter "Integrity MAC too weak")-                        | macValue == mac macAlg key bs = decode bs-                        | otherwise = Left BadContentMAC-                 in pkcs12mac Left fn d macParams content pwdUTF8+        case macAlg of+            MacTraditional (DigestAlgorithm d) -> loop (toProtectionPasswords pwdUTF8)+              where+                -- iterate over all possible representations of a password+                -- until a successful match is found+                loop []           = Left BadContentMAC+                loop (pwd:others) =+                    let fn key digAlg bs+                            | not (securityAcceptable digAlg) =+                                Left (InvalidParameter "Integrity MAC too weak")+                            | macValue == mac digAlg key bs = (pwd,) <$> decode bs+                            | otherwise = loop others+                     in pkcs12mac Left fn d macParams content pwd +            -- for RFC 9579 the primary representation is always used, this is+            -- fine because we assume the encryption layer also uses that+            -- representation+            MacAuthScheme authScheme+                | not (hasExplicitKeyLength authScheme) ->+                    Left (InvalidParameter "KDF key length must be explicit")+                | not (securityAcceptable authScheme) ->+                    Left (InvalidParameter "Integrity MAC too weak")+                | macValue == pbMac authScheme content pwd -> (pwd,) <$> decode content+                | otherwise -> Left BadContentMAC+              where pwd = toProtectionPassword pwdUTF8 +    hasExplicitKeyLength (PBMAC1 p) = isJust $ kdfKeyLength (pbmac1KDF p)++ -- Generating and encoding  -- | Parameters used for password integrity mode.-type IntegrityParams = (DigestAlgorithm, PBEParameter)+data IntegrityParams+    = TraditionalIntegrity DigestAlgorithm PBEParameter+      -- ^ Traditional PKCS #12 integrity, with a digest algoritm+    | AuthSchemeIntegrity AuthenticationScheme+      -- ^ Integrity with an authentication scheme such as PBMAC1+    deriving (Show,Eq)  -- | Write a PKCS #12 file to disk. writeP12File :: FilePath-             -> IntegrityParams -> Password+             -> IntegrityParams -> ProtectionPassword              -> PKCS12              -> IO (Either StoreError ()) writeP12File path intp pw aSafe =@@ -131,21 +197,36 @@         Right bs -> Right <$> BS.writeFile path bs  -- | Write a PKCS #12 file to a bytearray in DER format.-writeP12FileToMemory :: IntegrityParams -> Password+writeP12FileToMemory :: IntegrityParams -> ProtectionPassword                      -> PKCS12                      -> Either StoreError BS.ByteString-writeP12FileToMemory (alg@(DigestAlgorithm hashAlg), pbeParam) pwdUTF8 aSafe =+writeP12FileToMemory intp pwdUTF8 aSafe =     encode <$> protect   where     content   = encodeASN1Object aSafe     encode md = encodeASN1Object PFX { authSafeData = content, macData = Just md } -    protect = pkcs12mac Left fn hashAlg pbeParam content pwdUTF8-    fn key macAlg bs = Right MacData { digAlg    = alg-                                     , macValue  = mac macAlg key bs-                                     , macParams = pbeParam-                                     }+    protect = case intp of+        TraditionalIntegrity alg@(DigestAlgorithm hashAlg) pbeParam ->+            let fn key macAlg bs = Right MacData { macAlg    = MacTraditional alg+                                                 , macValue  = mac macAlg key bs+                                                 , macParams = pbeParam+                                                 }+             in pkcs12mac Left fn hashAlg pbeParam content pwdUTF8+        AuthSchemeIntegrity authScheme+            | pwdUTF8 == emptyNotTerminated ->+                Left (InvalidPassword "Authentication scheme requires terminated password")+            | otherwise ->+                Right MacData { macAlg    = MacAuthScheme (transform authScheme)+                              , macValue  = pbMac authScheme content pwdUTF8+                              , macParams = unused+                              }+    unused = PBEParameter (fromString "NOT USED") 1 +    transform (PBMAC1 p) = PBMAC1 (ensureExplicitKeyLength p)+    ensureExplicitKeyLength p = p { pbmac1KDF = kdfKeyLengthModify fn (pbmac1KDF p) }+      where fn = Just . fromMaybe (getMaximumKeySize $ pbmac1AScheme p)+ -- | Write a PKCS #12 file without integrity protection to disk. writeUnprotectedP12File :: FilePath -> PKCS12 -> IO () writeUnprotectedP12File path = BS.writeFile path . writeUnprotectedP12FileToMemory@@ -189,8 +270,12 @@         m <- if b then Just <$> parse else pure Nothing         return PFX { authSafeData = d, macData = m } +data MacAlg = MacTraditional DigestAlgorithm+            | MacAuthScheme AuthenticationScheme+            deriving (Show,Eq)+ data MacData = MacData-    { digAlg :: DigestAlgorithm+    { macAlg :: MacAlg     , macValue :: MessageAuthenticationCode     , macParams :: PBEParameter     }@@ -201,29 +286,34 @@         asn1Container Sequence (m . s . i)       where         m = asn1Container Sequence (a . v)-        a = algorithmASN1S Sequence digAlg+        a = gMacAlg macAlg         v = gOctetString (B.convert macValue)         s = gOctetString (pbeSalt macParams)         i = gIntVal (fromIntegral $ pbeIterationCount macParams) +        gMacAlg (MacTraditional alg) = algorithmASN1S Sequence alg+        gMacAlg (MacAuthScheme authScheme) = algorithmASN1S Sequence authScheme+ instance Monoid e => ParseASN1Object e MacData where     parse = onNextContainer Sequence $ do         (a, v) <- onNextContainer Sequence $ do-            a <- parseAlgorithm Sequence+            a <- parseTraditional <|> parseAuthScheme             OctetString v <- getNext             return (a, v)         OctetString s <- getNext         b <- hasNext         IntVal i <- if b then getNext else pure (IntVal 1)-        return MacData { digAlg = a+        return MacData { macAlg = a                        , macValue = AuthTag (B.convert v)                        , macParams = PBEParameter s (fromIntegral i)                        }-+      where+        parseTraditional = MacTraditional <$> parseAlgorithm Sequence+        parseAuthScheme = MacAuthScheme <$> parseAlgorithm Sequence  -- AuthenticatedSafe --- | PKCS #12 privacy wrapper, adding optional encryption to 'SafeContents'.+-- | PKCS #12 privacy wrapper, adding optional encryption to t'SafeContents'. -- ASN.1 equivalent is @AuthenticatedSafe@. -- -- The semigroup interface allows to combine multiple pieces encrypted@@ -263,7 +353,7 @@ unencrypted = PKCS12 . (:[]) . Unencrypted  -- | Build a PKCS #12 encrypted with the specified scheme and password.-encrypted :: EncryptionScheme -> Password -> SafeContents -> Either StoreError PKCS12+encrypted :: EncryptionScheme -> ProtectionPassword -> SafeContents -> Either StoreError PKCS12 encrypted alg pwd sc = PKCS12 . (:[]) . Encrypted <$> encrypt alg pwd bs   where bs = encodeASN1Object sc @@ -412,7 +502,7 @@     fromObjectID oid = unOIDNW <$> fromObjectID oid  -- | Main bag payload in PKCS #12 contents.-data SafeInfo = KeyBag (FormattedKey X509.PrivKey) -- ^ unencrypted private key+data SafeInfo = KeyBag (FormattedKey KeyPair)      -- ^ unencrypted private key               | PKCS8ShroudedKeyBag PKCS5          -- ^ encrypted private key               | CertBag (Bag CertInfo)             -- ^ certificate               | CRLBag (Bag CRLInfo)               -- ^ CRL@@ -472,7 +562,7 @@ filterByLocalKeyId :: BS.ByteString -> SafeContents -> SafeContents filterByLocalKeyId d = filterBags ((== Just d) . getLocalKeyId) -getSafeKeysId :: SafeContents -> [OptProtected (Id X509.PrivKey)]+getSafeKeysId :: SafeContents -> [OptProtected (Id KeyPair)] getSafeKeysId (SafeContents scs) = loop scs   where     loop []           = []@@ -489,10 +579,10 @@         return (mkId k bag)  -- | Return all private keys contained in the safe contents.-getSafeKeys :: SafeContents -> [OptProtected X509.PrivKey]+getSafeKeys :: SafeContents -> [OptProtected KeyPair] getSafeKeys = map (fmap unId) . getSafeKeysId -getAllSafeKeysId :: [SafeContents] -> OptProtected [Id X509.PrivKey]+getAllSafeKeysId :: [SafeContents] -> OptProtected [Id KeyPair] getAllSafeKeysId = applySamePassword . concatMap getSafeKeysId  -- | Return all private keys contained in the safe content list.  All shrouded@@ -502,7 +592,7 @@ -- least is encrypted.  This does not mean all keys were actually protected in -- the input.  If detailed view is required then function 'getSafeKeys' is -- available.-getAllSafeKeys :: [SafeContents] -> OptProtected [X509.PrivKey]+getAllSafeKeys :: [SafeContents] -> OptProtected [KeyPair] getAllSafeKeys = applySamePassword . concatMap getSafeKeys  getSafeX509CertsId :: SafeContents -> [Id X509.SignedCertificate]@@ -557,21 +647,25 @@                 -- 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)+                guard (keyPairMatchesCert k leaf)+                pure (buildCertificateChain leaf certs, keyPairToPrivKey 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)+                        guard (keyPairMatchesCert k leaf)+                        pure (buildCertificateChain leaf certs, keyPairToPrivKey k)                     Nothing   -> do-                        -- no identifier available, so we simply return all-                        -- certificates with input order-                        guard (not $ null certs)-                        pure (X509.CertificateChain certs, k)+                        -- no identifier available, so we have to search all+                        -- certificates for the one consistent with the private+                        -- key+                        leaf <- single (filterWithPrivKey k certs)+                        pure (buildCertificateChain leaf certs, keyPairToPrivKey k)+    filterWithPrivKey = filter . keyPairMatchesCert --- | Extract the private key and certificate chain from a 'PKCS12' value.  A+-- | Extract the private key and certificate chain from a t'PKCS12' value.  A -- credential is returned when the structure contains exactly one private key -- and at least one X.509 certificate. toCredential :: PKCS12 -> OptProtected (Maybe (X509.CertificateChain, X509.PrivKey))@@ -586,16 +680,17 @@     fn keys  = do         k <- single keys         leaf <- single (getAllSafeX509Certs filtered)-        pure (buildCertificateChain leaf certs, k)+        guard (keyPairMatchesCert k leaf)+        pure (buildCertificateChain leaf certs, keyPairToPrivKey k)  -- | Extract a private key and certificate chain with the specified friendly--- name from a 'PKCS12' value.  A credential is returned when the structure+-- name from a t'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.+-- | Build a t'PKCS12' value containing a private key and certificate chain. -- Distinct encryption is applied for both.  Encrypting the certificate chain is -- optional. --@@ -603,12 +698,12 @@ -- values so that the salt is not reused twice in the encryption process. fromCredential :: Maybe EncryptionScheme -- for certificates                -> EncryptionScheme       -- for private key-               -> Password+               -> ProtectionPassword                -> (X509.CertificateChain, X509.PrivKey)                -> Either StoreError PKCS12 fromCredential = fromCredential' id --- | Build a 'PKCS12' value containing a private key and certificate chain+-- | Build a t'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.@@ -618,7 +713,7 @@ fromNamedCredential :: String                     -> Maybe EncryptionScheme -- for certificates                     -> EncryptionScheme       -- for private key-                    -> Password+                    -> ProtectionPassword                     -> (X509.CertificateChain, X509.PrivKey)                     -> Either StoreError PKCS12 fromNamedCredential name = fromCredential' (setFriendlyName name)@@ -626,12 +721,13 @@ fromCredential' :: ([Attribute] -> [Attribute])                 -> Maybe EncryptionScheme -- for certificates                 -> EncryptionScheme       -- for private key-                -> Password+                -> ProtectionPassword                 -> (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+fromCredential' _ _ _ _ (X509.CertificateChain [], _) =+    Left (InvalidInput "Empty certificate chain")+fromCredential' trans algChain algKey pwd (X509.CertificateChain certs@(leaf:_), key) =+    (<>) <$> pkcs12Chain <*> pkcs12Key   where     pkcs12Key   = unencrypted <$> scKeyOrError     pkcs12Chain =@@ -643,12 +739,16 @@     certAttrs     = attrs : repeat []     toCertBag a c = Bag (CertBag (Bag (CertX509 c) [])) a -    scKeyOrError = wrap <$> encrypt algKey pwd encodedKey+    scKeyOrError+        | keyPairMatchesCert pair leaf =+            wrap <$> encrypt algKey pwd encodedKey+        | otherwise = Left PublicPrivateKeyMismatch      wrap shrouded = SafeContents [Bag (PKCS8ShroudedKeyBag shrouded) attrs]-    encodedKey    = encodeASN1Object (FormattedKey PKCS8Format key)+    encodedKey    = encodeASN1Object (FormattedKey PKCS8Format pair)+    pair          = keyPairFromPrivKey key -    X509.Fingerprint keyId = X509.getFingerprint (head certs) X509.HashSHA1+    X509.Fingerprint keyId = X509.getFingerprint leaf X509.HashSHA1     attrs = trans (setLocalKeyId keyId [])  -- Standard attributes@@ -743,7 +843,7 @@ parseOctetStringObject :: (Monoid e, ParseASN1Object [ASN1Event] obj)                        => String -> ParseASN1 e obj parseOctetStringObject name = do-    OctetString bs <- getNext+    bs <- parseOctetString     case decode bs of         Left e  -> throwParseError (name ++ ": " ++ show e)         Right c -> return c
src/Crypto/Store/PKCS5.hs view
@@ -11,7 +11,10 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} module Crypto.Store.PKCS5-    ( Password+    ( ProtectionPassword+    , emptyNotTerminated+    , fromProtectionPassword+    , toProtectionPassword     , EncryptedContent     -- * High-level API     , PKCS5(..)@@ -21,6 +24,9 @@     , EncryptionScheme(..)     , PBEParameter(..)     , PBES2Parameter(..)+    -- * Message authentication schemes+    , AuthenticationScheme(..)+    , PBMAC1Parameter(..)     -- * Key derivation     , KeyDerivationFunc(..)     , PBKDF2_PRF(..)@@ -30,23 +36,27 @@     , ContentEncryptionParams     , ContentEncryptionAlg(..)     , ContentEncryptionCipher(..)-    , generateEncryptionParams+    , ecbParams+    , generateCBCParams+    , generateRC2EncryptionParams+    , generateCFBParams+    , generateCTRParams     , getContentEncryptionAlg     -- * Low-level API     , pbEncrypt     , pbDecrypt+    , pbMac     ) where  import           Data.ASN1.Types-import           Data.ByteArray (ByteArrayAccess) import           Data.ByteString (ByteString) import           Data.Maybe (fromMaybe)  import Crypto.Store.ASN1.Parse import Crypto.Store.ASN1.Generate import Crypto.Store.CMS.Algorithms+import Crypto.Store.CMS.Authenticated import Crypto.Store.CMS.Encrypted-import Crypto.Store.CMS.Enveloped import Crypto.Store.CMS.Util import Crypto.Store.Error import Crypto.Store.PKCS5.PBES1@@ -163,7 +173,66 @@ instance Monoid e => ParseASN1Object e EncryptionScheme where     parse = parseAlgorithm Sequence +data AuthenticationSchemeType = Type_PBMAC1 +instance Enumerable AuthenticationSchemeType where+    values = [ Type_PBMAC1+             ]++instance OIDable AuthenticationSchemeType where+    getObjectID Type_PBMAC1 = [1,2,840,113549,1,5,14]++instance OIDNameable AuthenticationSchemeType where+    fromObjectID oid = unOIDNW <$> fromObjectID oid++-- | Password-Based Message Authentication Scheme (PBMAC).+newtype AuthenticationScheme = PBMAC1 PBMAC1Parameter -- ^ PBMAC1+                             deriving (Show,Eq)++instance HasStrength AuthenticationScheme where+    getSecurityBits (PBMAC1 p) = getSecurityBits (pbmac1AScheme p)++-- | PBMAC1 parameters.+data PBMAC1Parameter = PBMAC1Parameter+    { pbmac1KDF     :: KeyDerivationFunc  -- ^ Key derivation function+    , pbmac1AScheme :: MACAlgorithm       -- ^ Underlying message authentication scheme+    }+    deriving (Show,Eq)++instance ASN1Elem e => ProduceASN1Object e PBMAC1Parameter where+    asn1s PBMAC1Parameter{..} =+        let kdFunc  = algorithmASN1S Sequence pbmac1KDF+            aScheme = algorithmASN1S Sequence pbmac1AScheme+         in asn1Container Sequence (kdFunc . aScheme)++instance Monoid e => ParseASN1Object e PBMAC1Parameter where+    parse = onNextContainer Sequence $ do+        kdFunc  <- parseAlgorithm Sequence+        aScheme <- parseAlgorithm Sequence+        case kdfKeyLength kdFunc of+            Nothing -> return ()+            Just sz+                | validateKeySize aScheme sz -> return ()+                | otherwise -> throwParseError "PBMAC1Parameter: parsed key length incompatible with message authentication scheme"+        return PBMAC1Parameter { pbmac1KDF = kdFunc, pbmac1AScheme = aScheme }++instance AlgorithmId AuthenticationScheme where+    type AlgorithmType AuthenticationScheme = AuthenticationSchemeType+    algorithmName _  = "message authentication scheme"++    algorithmType (PBMAC1 _)                 = Type_PBMAC1++    parameterASN1S (PBMAC1 p)                 = asn1s p++    parseParameter Type_PBMAC1                 = PBMAC1 <$> parse++instance ASN1Elem e => ProduceASN1Object e AuthenticationScheme where+    asn1s = algorithmASN1S Sequence++instance Monoid e => ParseASN1Object e AuthenticationScheme where+    parse = parseAlgorithm Sequence++ -- High-level API  -- | Content encrypted with a Password-Based Encryption Scheme (PBES).@@ -192,20 +261,20 @@     fromASN1 = runParseASN1State parse  -- | Encrypt a bytestring with the specified encryption scheme and password.-encrypt :: EncryptionScheme -> Password -> ByteString -> Either StoreError PKCS5+encrypt :: EncryptionScheme -> ProtectionPassword -> ByteString -> Either StoreError PKCS5 encrypt alg pwd bs = build <$> pbEncrypt alg bs pwd   where     build ed = ed `seq` PKCS5 { encryptionAlgorithm = alg, encryptedData = ed }  -- | Decrypt the PKCS #5 content with the specified password.-decrypt :: PKCS5 -> Password -> Either StoreError ByteString+decrypt :: PKCS5 -> ProtectionPassword -> Either StoreError ByteString decrypt obj = pbDecrypt (encryptionAlgorithm obj) (encryptedData obj)   -- Encryption Schemes  -- | Encrypt a bytestring with the specified encryption scheme and password.-pbEncrypt :: EncryptionScheme -> ByteString -> Password+pbEncrypt :: EncryptionScheme -> ByteString -> ProtectionPassword           -> Either StoreError EncryptedContent pbEncrypt (PBES2 p)                 = pbes2  contentEncrypt p pbEncrypt (PBE_MD5_DES_CBC p)       = pkcs5  Left contentEncrypt MD5  DES p@@ -219,7 +288,7 @@  -- | Decrypt an encrypted bytestring with the specified encryption scheme and -- password.-pbDecrypt :: EncryptionScheme -> EncryptedContent -> Password -> Either StoreError ByteString+pbDecrypt :: EncryptionScheme -> EncryptedContent -> ProtectionPassword -> Either StoreError ByteString pbDecrypt (PBES2 p)                 = pbes2  contentDecrypt p pbDecrypt (PBE_MD5_DES_CBC p)       = pkcs5  Left contentDecrypt MD5  DES p pbDecrypt (PBE_SHA1_DES_CBC p)      = pkcs5  Left contentDecrypt SHA1 DES p@@ -230,9 +299,21 @@ pbDecrypt (PBE_SHA1_RC2_128 p)      = pkcs12rc2 Left contentDecrypt SHA1 128 p pbDecrypt (PBE_SHA1_RC2_40 p)       = pkcs12rc2 Left contentDecrypt SHA1 40 p -pbes2 :: ByteArrayAccess password-      => (Key -> ContentEncryptionParams -> ByteString -> result)-      -> PBES2Parameter -> ByteString -> password -> result+pbes2 :: (Key -> ContentEncryptionParams -> ByteString -> result)+      -> PBES2Parameter -> ByteString -> ProtectionPassword -> result pbes2 encdec PBES2Parameter{..} bs pwd = encdec key pbes2EScheme bs-  where key = kdfDerive pbes2KDF len pwd :: Key+  where key = kdfDerive pbes2KDF len (fromProtectionPassword pwd) :: Key         len = fromMaybe (getMaximumKeySize pbes2EScheme) (kdfKeyLength pbes2KDF)+++-- Message Authentication Schemes++-- | Authenticate a message with the specified authentication scheme and+-- password.+pbMac :: AuthenticationScheme -> EncapsulatedContent -> ProtectionPassword -> MessageAuthenticationCode+pbMac (PBMAC1 p) = pbmac1 p++pbmac1 :: PBMAC1Parameter -> ByteString -> ProtectionPassword -> MessageAuthenticationCode+pbmac1 PBMAC1Parameter{..} bs pwd = mac pbmac1AScheme key bs+  where key = kdfDerive pbmac1KDF len (fromProtectionPassword pwd) :: Key+        len = fromMaybe (getMaximumKeySize pbmac1AScheme) (kdfKeyLength pbmac1KDF)
src/Crypto/Store/PKCS5/PBES1.hs view
@@ -14,6 +14,11 @@ module Crypto.Store.PKCS5.PBES1     ( PBEParameter(..)     , Key+    , ProtectionPassword+    , emptyNotTerminated+    , fromProtectionPassword+    , toProtectionPassword+    , toProtectionPasswords     , pkcs5     , pkcs12     , pkcs12rc2@@ -38,6 +43,7 @@ import           Data.ByteString (ByteString) import           Data.Maybe (fromMaybe) import           Data.Memory.PtrMethods+import           Data.String (IsString(..)) import           Data.Word  import           Foreign.Ptr (plusPtr)@@ -49,6 +55,64 @@ import Crypto.Store.CMS.Util import Crypto.Store.Error +-- | A password stored as a sequence of UTF-8 bytes.+--+-- Some key-derivation functions add restrictions to what characters+-- are supported.+--+-- The data type provides a special value 'emptyNotTerminated' that is used+-- as alternate representation of empty passwords on some systems and that+-- produces encryption results different than an empty bytearray.+--+-- Conversion to/from a regular sequence of bytes is possible with functions+-- 'toProtectionPassword' and 'fromProtectionPassword'.+--+-- Beware: the 'fromString' implementation correctly handles multi-byte+-- characters, so here is not equivalent to the 'ByteString' counterpart.+data ProtectionPassword = NullPassword | PasswordUTF8 ByteString+    deriving Eq++instance Show ProtectionPassword where+    showsPrec _ NullPassword     = showString "emptyNotTerminated"+    showsPrec d (PasswordUTF8 b) = showParen (d > 10) $+        showString "toProtectionPassword " . showsPrec 11 b++instance IsString ProtectionPassword where+    fromString = PasswordUTF8 . B.convert . S.toBytes S.UTF8 . fromString++instance ByteArrayAccess ProtectionPassword where+    length = applyPP 0 B.length+    withByteArray = B.withByteArray . fromProtectionPassword++applyPP :: a -> (ByteString -> a) -> ProtectionPassword -> a+applyPP d _ NullPassword     = d+applyPP _ f (PasswordUTF8 b) = f b++-- | A value denoting an empty password, but having a special encoding when+-- deriving a symmetric key on some systems, like the certificate export+-- wizard on Windows.+--+-- This value is different from @'toProtectionPassword' ""@ and can be tried+-- when decrypting content with a password known to be empty.+emptyNotTerminated :: ProtectionPassword+emptyNotTerminated = NullPassword++-- | Extract the UTF-8 bytes in a password value.+fromProtectionPassword :: ProtectionPassword -> ByteString+fromProtectionPassword = applyPP B.empty id++-- | Build a password value from a sequence of UTF-8 bytes.+--+-- When the password is empty, the special value 'emptyNotTerminated' may+-- be tried as well.+toProtectionPassword :: ByteString -> ProtectionPassword+toProtectionPassword = PasswordUTF8++toProtectionPasswords :: ByteString -> [ProtectionPassword]+toProtectionPasswords bs+    | B.null bs = [PasswordUTF8 B.empty, NullPassword]+    | otherwise = [PasswordUTF8 bs]+ -- | Secret key. type Key = B.ScrubbedBytes @@ -88,8 +152,9 @@ rc4Combine key = Right . snd . RC4.combine (RC4.initialize key)  -- | Conversion to UCS2 from UTF-8, ignoring non-BMP bits.-toUCS2 :: (ByteArrayAccess butf8, ByteArray bucs2) => butf8 -> Maybe bucs2-toUCS2 pwdUTF8+toUCS2 :: ByteArray bucs2 => ProtectionPassword -> Maybe bucs2+toUCS2 NullPassword = Just B.empty+toUCS2 (PasswordUTF8 pwdUTF8)     | B.null r  = Just pwdUCS2     | otherwise = Nothing   where@@ -105,19 +170,19 @@  -- | Apply PBKDF1 on the specified password and run an encryption or decryption -- function on some input using derived key and IV.-pkcs5 :: (Hash.HashAlgorithm hash, BlockCipher cipher, ByteArrayAccess password)+pkcs5 :: (Hash.HashAlgorithm hash, BlockCipher cipher)       => (StoreError -> result)       -> (Key -> ContentEncryptionParams -> ByteString -> result)       -> DigestProxy hash       -> ContentEncryptionCipher cipher       -> PBEParameter       -> ByteString-      -> password+      -> ProtectionPassword       -> result pkcs5 failure encdec hashAlg cec pbeParam bs pwd     | proxyBlockSize cec /= 8 = failure (InvalidParameter "Invalid cipher block size")     | otherwise =-        case pbkdf1 hashAlg pwd pbeParam 16 of+        case pbkdf1 hashAlg (fromProtectionPassword pwd) pbeParam 16 of             Left err -> failure err             Right dk ->                 let (key, iv) = B.splitAt 8 (dk :: Key)@@ -145,14 +210,14 @@  -- | Apply PKCS #12 derivation on the specified password and run an encryption -- or decryption function on some input using derived key and IV.-pkcs12 :: (Hash.HashAlgorithm hash, BlockCipher cipher, ByteArrayAccess password)+pkcs12 :: (Hash.HashAlgorithm hash, BlockCipher cipher)        => (StoreError -> result)        -> (Key -> ContentEncryptionParams -> ByteString -> result)        -> DigestProxy hash        -> ContentEncryptionCipher cipher        -> PBEParameter        -> ByteString-       -> password+       -> ProtectionPassword        -> result pkcs12 failure encdec hashAlg cec pbeParam bs pwdUTF8 =     case toUCS2 pwdUTF8 of@@ -168,14 +233,14 @@ -- | Apply PKCS #12 derivation on the specified password and run an encryption -- or decryption function on some input using derived key and IV.  This variant -- uses an RC2 cipher with the EKL specified (effective key length).-pkcs12rc2 :: (Hash.HashAlgorithm hash, ByteArrayAccess password)+pkcs12rc2 :: Hash.HashAlgorithm hash           => (StoreError -> result)           -> (Key -> ContentEncryptionParams -> ByteString -> result)           -> DigestProxy hash           -> Int           -> PBEParameter           -> ByteString-          -> password+          -> ProtectionPassword           -> result pkcs12rc2 failure encdec hashAlg len pbeParam bs pwdUTF8 =     case toUCS2 pwdUTF8 of@@ -191,14 +256,14 @@ -- | Apply PKCS #12 derivation on the specified password and run an encryption -- or decryption function on some input using derived key.  This variant does -- not derive any IV and is required for RC4.-pkcs12stream :: (Hash.HashAlgorithm hash, ByteArrayAccess password)+pkcs12stream :: Hash.HashAlgorithm hash              => (StoreError -> result)              -> (Key -> ByteString -> result)              -> DigestProxy hash              -> Int              -> PBEParameter              -> ByteString-             -> password+             -> ProtectionPassword              -> result pkcs12stream failure encdec hashAlg keyLen pbeParam bs pwdUTF8 =     case toUCS2 pwdUTF8 of@@ -209,13 +274,13 @@  -- | Apply PKCS #12 derivation on the specified password and run a MAC function -- on some input using derived key.-pkcs12mac :: (Hash.HashAlgorithm hash, ByteArrayAccess password)+pkcs12mac :: Hash.HashAlgorithm hash           => (StoreError -> result)           -> (Key -> MACAlgorithm -> ByteString -> result)           -> DigestProxy hash           -> PBEParameter           -> ByteString-          -> password+          -> ProtectionPassword           -> result pkcs12mac failure macFn hashAlg pbeParam bs pwdUTF8 =     case toUCS2 pwdUTF8 of
src/Crypto/Store/PKCS8.hs view
@@ -9,7 +9,11 @@ -- -- Presents an API similar to "Data.X509.Memory" and "Data.X509.File" but -- allows to write private keys and provides support for password-based--- encryption.+-- encryption.  Private keys are now stored along with the corresponding+-- public key in a type 'KeyPair'.  Components of type 'X509.PrivKey' and+--  'X509.PubKey' can be obtained through functions 'keyPairToPrivKey' and+-- 'keyPairToPubKey'.  Function 'keyPairFromPrivKey' can be called to build a+-- 'KeyPair'. -- -- Functions to read a private key return an object wrapped in the -- 'OptProtected' data type.@@ -25,17 +29,26 @@     ( readKeyFile     , readKeyFileFromMemory     , pemToKey+    , pemToKeyAccum     , writeKeyFile     , writeKeyFileToMemory     , keyToPEM     , writeEncryptedKeyFile     , writeEncryptedKeyFileToMemory     , encryptKeyToPEM+    -- * Key pairs+    , KeyPair+    , keyPairFromPrivKey+    , keyPairToPrivKey+    , keyPairToPubKey     -- * Serialization formats     , PrivateKeyFormat(..)     , FormattedKey(..)     -- * Password-based protection-    , Password+    , ProtectionPassword+    , emptyNotTerminated+    , fromProtectionPassword+    , toProtectionPassword     , OptProtected(..)     , recover     , recoverA@@ -51,7 +64,10 @@ import Data.ASN1.BinaryEncoding import Data.ASN1.BitArray import Data.ASN1.Encoding+import Data.ASN1.Prim+import Data.Bifunctor (first) import Data.ByteArray (ByteArrayAccess, convert)+import Data.Either (rights) import Data.Maybe import qualified Data.X509 as X509 import qualified Data.ByteString as B@@ -63,13 +79,14 @@ 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 qualified Crypto.PubKey.RSA.Types as RSA  import Crypto.Store.ASN1.Generate import Crypto.Store.ASN1.Parse import Crypto.Store.CMS.Attribute import Crypto.Store.CMS.Util import Crypto.Store.Error+import Crypto.Store.Keys import Crypto.Store.PEM import Crypto.Store.PKCS5 import Crypto.Store.PKCS8.EC@@ -78,7 +95,7 @@ -- | Data type for objects that are possibly protected with a password. data OptProtected a = Unprotected a                       -- ^ Value is unprotected-                    | Protected (Password -> Either StoreError a)+                    | Protected (ProtectionPassword -> Either StoreError a)                       -- ^ Value is protected with a password  instance Functor OptProtected where@@ -86,7 +103,7 @@     fmap f (Protected g)   = Protected (fmap f . g)  -- | Try to recover an 'OptProtected' content using the specified password.-recover :: Password -> OptProtected a -> Either StoreError a+recover :: ProtectionPassword -> OptProtected a -> Either StoreError a recover _   (Unprotected x) = Right x recover pwd (Protected f)   = f pwd @@ -98,12 +115,14 @@ -- > -- > [encryptedKey] <- readKeyFile "privkey.pem" -- > let askForPassword = putStr "Please enter password: " >> B.getLine--- > result <- recoverA askForPassword encryptedKey+-- > result <- recoverA (toProtectionPassword <$> askForPassword) encryptedKey -- > case result of -- >     Left err  -> putStrLn $ "Unable to recover key: " ++ show err -- >     Right key -> print key recoverA :: Applicative f-         => f Password -> OptProtected a -> f (Either StoreError a)+         => f ProtectionPassword+         -> OptProtected a+         -> f (Either StoreError a) recoverA _   (Unprotected x) = pure (Right x) recoverA get (Protected f)   = fmap f get @@ -111,63 +130,72 @@ -- Reading from PEM format  -- | Read private keys from a PEM file.-readKeyFile :: FilePath -> IO [OptProtected X509.PrivKey]+readKeyFile :: FilePath -> IO [OptProtected KeyPair] readKeyFile path = accumulate <$> readPEMs path  -- | Read private keys from a bytearray in PEM format.-readKeyFileFromMemory :: B.ByteString -> [OptProtected X509.PrivKey]+readKeyFileFromMemory :: B.ByteString -> [OptProtected KeyPair] readKeyFileFromMemory = either (const []) accumulate . pemParseBS -accumulate :: [PEM] -> [OptProtected X509.PrivKey]-accumulate = catMaybes . foldr (flip pemToKey) []+accumulate :: [PEM] -> [OptProtected KeyPair]+accumulate = rights . map pemToKey --- | Read a private key from a 'PEM' element and add it to the accumulator list.-pemToKey :: [Maybe (OptProtected X509.PrivKey)] -> PEM -> [Maybe (OptProtected X509.PrivKey)]-pemToKey acc pem =-    case decodeASN1' BER (pemContent pem) of-        Left _     -> acc-        Right asn1 -> run (getParser $ pemName pem) asn1 : acc+-- | Read a private key from a t'PEM' element and add it to the accumulator+-- list.+--+-- This API is modelled after the original @pemToKey@ in "Data.X509.Memory".+pemToKeyAccum :: [Maybe (OptProtected KeyPair)] -> PEM -> [Maybe (OptProtected KeyPair)]+pemToKeyAccum acc pem =+    case pemToKey pem of+        Left (DecodingError _) -> acc+        Left _                 -> Nothing : acc+        Right key              -> Just key : acc -  where-    run p = either (const Nothing) Just . runParseASN1 p+-- | Read a private key from a t'PEM' element.+pemToKey :: PEM -> Either StoreError (OptProtected KeyPair)+pemToKey pem = do+    asn1 <- mapLeft DecodingError $ decodeASN1' BER (pemContent pem)+    parser <- getParser (pemName pem)+    mapLeft ParseFailure $ runParseASN1 parser asn1 +  where     allTypes  = unFormat <$> parse-    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+    rsa       = parseFormattedKeyPair (keyPairFromPrivKey . X509.PrivKeyRSA)+    dsa       = parseFormattedKeyPair KeyPairDSA+    ecdsa     = parseFormattedKeyPair (keyPairFromPrivKey . X509.PrivKeyEC)+    x25519    = parseModernKeyPair (keyPairFromPrivKey . X509.PrivKeyX25519)+    x448      = parseModernKeyPair (keyPairFromPrivKey . X509.PrivKeyX448)+    ed25519   = parseModernKeyPair (keyPairFromPrivKey . X509.PrivKeyEd25519)+    ed448     = parseModernKeyPair (keyPairFromPrivKey . X509.PrivKeyEd448)     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+    getParser "PRIVATE KEY"           = return (Unprotected <$> allTypes)+    getParser "RSA PRIVATE KEY"       = return (Unprotected <$> rsa)+    getParser "DSA PRIVATE KEY"       = return (Unprotected <$> dsa)+    getParser "EC PRIVATE KEY"        = return (Unprotected <$> ecdsa)+    getParser "X25519 PRIVATE KEY"    = return (Unprotected <$> x25519)+    getParser "X448 PRIVATE KEY"      = return (Unprotected <$> x448)+    getParser "ED25519 PRIVATE KEY"   = return (Unprotected <$> ed25519)+    getParser "ED448 PRIVATE KEY"     = return (Unprotected <$> ed448)+    getParser "ENCRYPTED PRIVATE KEY" = return (Protected   <$> encrypted)+    getParser _                       = Left UnexpectedNameForPEM      inner decfn pwd = do         decrypted <- decfn pwd         asn1 <- mapLeft DecodingError $ decodeASN1' BER decrypted-        case run allTypes asn1 of-            Nothing -> Left (ParseFailure "No key parsed after decryption")-            Just k  -> return k+        case runParseASN1 allTypes asn1 of+            Left _   -> Left (ParseFailure "No key parsed after decryption")+            Right k  -> return k   -- Writing to PEM format  -- | Write unencrypted private keys to a PEM file.-writeKeyFile :: PrivateKeyFormat -> FilePath -> [X509.PrivKey] -> IO ()+writeKeyFile :: PrivateKeyFormat -> FilePath -> [KeyPair] -> IO () writeKeyFile fmt path = writePEMs path . map (keyToPEM fmt)  -- | Write unencrypted private keys to a bytearray in PEM format.-writeKeyFileToMemory :: PrivateKeyFormat -> [X509.PrivKey] -> B.ByteString+writeKeyFileToMemory :: PrivateKeyFormat -> [KeyPair] -> B.ByteString writeKeyFileToMemory fmt = pemsWriteBS . map (keyToPEM fmt)  -- | Write a PKCS #8 encrypted private key to a PEM file.@@ -178,10 +206,10 @@ -- Fresh 'EncryptionScheme' parameters should be generated for each key to -- encrypt. writeEncryptedKeyFile :: FilePath-                      -> EncryptionScheme -> Password-> X509.PrivKey+                      -> EncryptionScheme -> ProtectionPassword -> KeyPair                       -> IO (Either StoreError ())-writeEncryptedKeyFile path alg pwd privKey =-    let pem = encryptKeyToPEM alg pwd privKey+writeEncryptedKeyFile path alg pwd keyPair =+    let pem = encryptKeyToPEM alg pwd keyPair      in either (return . Left) (fmap Right . writePEMs path . (:[])) pem  -- | Write a PKCS #8 encrypted private key to a bytearray in PEM format.@@ -191,66 +219,67 @@ -- -- Fresh 'EncryptionScheme' parameters should be generated for each key to -- encrypt.-writeEncryptedKeyFileToMemory :: EncryptionScheme -> Password -> X509.PrivKey-                              -> Either StoreError B.ByteString-writeEncryptedKeyFileToMemory alg pwd privKey =-    pemWriteBS <$> encryptKeyToPEM alg pwd privKey+writeEncryptedKeyFileToMemory :: EncryptionScheme -> ProtectionPassword+                              -> KeyPair -> Either StoreError B.ByteString+writeEncryptedKeyFileToMemory alg pwd keyPair =+    pemWriteBS <$> encryptKeyToPEM alg pwd keyPair  -- | Generate an unencrypted PEM for a private key.-keyToPEM :: PrivateKeyFormat -> X509.PrivKey -> PEM+keyToPEM :: PrivateKeyFormat -> KeyPair -> PEM keyToPEM TraditionalFormat = keyToTraditionalPEM keyToPEM PKCS8Format       = keyToModernPEM -keyToTraditionalPEM :: X509.PrivKey -> PEM-keyToTraditionalPEM privKey =+keyToTraditionalPEM :: KeyPair -> PEM+keyToTraditionalPEM keyPair =     mkPEM (typeTag ++ " PRIVATE KEY") (encodeASN1S asn1)-  where (typeTag, asn1) = traditionalPrivKeyASN1S privKey+  where (typeTag, asn1) = traditionalPrivKeyASN1S keyPair -traditionalPrivKeyASN1S :: ASN1Elem e => X509.PrivKey -> (String, ASN1Stream e)-traditionalPrivKeyASN1S privKey =-    case privKey of-        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)+traditionalPrivKeyASN1S :: ASN1Elem e => KeyPair -> (String, ASN1Stream e)+traditionalPrivKeyASN1S keyPair =+    case keyPair of+        KeyPairRSA k _ -> ("RSA", traditional k)+        KeyPairDSA p   -> ("DSA", traditional p)+        KeyPairEC  k _ -> ("EC",  traditional k)+        KeyPairX25519  k _ -> ("X25519",  tradModern k)+        KeyPairX448    k _ -> ("X448",    tradModern k)+        KeyPairEd25519 k _ -> ("ED25519", tradModern k)+        KeyPairEd448   k _ -> ("ED448",   tradModern k)   where+    traditional :: ProduceASN1Object e (Traditional a) => a -> ASN1Stream e     traditional a = asn1s (Traditional a)-    tradModern a  = asn1s (Modern [] a) -keyToModernPEM :: X509.PrivKey -> PEM-keyToModernPEM privKey = mkPEM "PRIVATE KEY" (encodeASN1S asn1)-  where asn1 = modernPrivKeyASN1S [] privKey+    tradModern :: ProduceASN1Object e (Modern a) => a -> ASN1Stream e+    tradModern = modernASN1S (const $ keyPairToPubKey keyPair) -modernPrivKeyASN1S :: ASN1Elem e => [Attribute] -> X509.PrivKey -> ASN1Stream e-modernPrivKeyASN1S attrs privKey =-    case privKey of-        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+keyToModernPEM :: KeyPair -> PEM+keyToModernPEM keyPair = mkPEM "PRIVATE KEY" (encodeASN1S asn1)+  where asn1 = modernASN1S keyPairToPubKey keyPair++modernPrivKeyASN1S :: ASN1Elem e+                   => [Attribute] -> Maybe GenericPubKey -> KeyPair -> ASN1Stream e+modernPrivKeyASN1S attrs mPub keyPair =+    case keyPair of+        KeyPairRSA k _ -> modern k+        KeyPairDSA p   -> modern p+        KeyPairEC  k _ -> modern k+        KeyPairX25519  k _ -> modern k+        KeyPairX448    k _ -> modern k+        KeyPairEd25519 k _ -> modern k+        KeyPairEd448   k _ -> modern k   where-    modern a = asn1s (Modern attrs a)+    modern a = asn1s (Modern attrs mPub a)  -- | Generate a PKCS #8 encrypted PEM for a private key. -- -- Fresh 'EncryptionScheme' parameters should be generated for each key to -- encrypt.-encryptKeyToPEM :: EncryptionScheme -> Password -> X509.PrivKey+encryptKeyToPEM :: EncryptionScheme -> ProtectionPassword -> KeyPair                 -> Either StoreError PEM-encryptKeyToPEM alg pwd privKey = toPEM <$> encrypt alg pwd bs-  where bs = pemContent (keyToModernPEM privKey)+encryptKeyToPEM alg pwd keyPair = toPEM <$> encrypt alg pwd bs+  where bs = pemContent (keyToModernPEM keyPair)         toPEM pkcs8 = mkPEM "ENCRYPTED PRIVATE KEY" (encodeASN1Object pkcs8) -mkPEM :: String -> B.ByteString -> PEM-mkPEM name bs = PEM { pemName = name, pemHeader = [], pemContent = bs} - -- Private key formats: traditional (SSLeay compatible) and modern (PKCS #8)  -- | Private-key serialization format.@@ -265,15 +294,65 @@ parseTraditional :: ParseASN1Object e (Traditional a) => ParseASN1 e a parseTraditional = unTraditional <$> parse -data Modern a = Modern [Attribute] a+data Modern a = Modern [Attribute] (Maybe GenericPubKey) a  instance Functor Modern where-    fmap f (Modern attrs a) = Modern attrs (f a)+    fmap f (Modern attrs mPub a) = Modern attrs mPub (f a) -parseModern :: ParseASN1Object e (Modern a) => ParseASN1 e a-parseModern = unModern <$> parse-  where unModern (Modern _ a) = a+modernASN1S :: ProduceASN1Object e (Modern a)+            => (a -> X509.PubKey) -> a -> ASN1Stream e+modernASN1S toPubKey a = asn1s (Modern [] mPub a)+  where+    noPubKey = True  -- we never generate with publicKey but the code exists+    mPub | noPubKey  = Nothing+         | otherwise = case getGenericPubKey toPubKey a of+             Right (gpk, []) -> Just gpk+             _ -> Nothing +parseModernKeyPair :: ParseASN1Object e (Modern a)+                   => (a -> KeyPair) -> ParseASN1 e KeyPair+parseModernKeyPair = parseModern keyPairToPubKey++parseModern :: ParseASN1Object e (Modern b)+            => (a -> X509.PubKey) -> (b -> a) -> ParseASN1 e a+parseModern toPubKey mapFn = do+    Modern _ mPub b <- parse+    verifyPubKey toPubKey (mapFn b) mPub++verifyPubKey :: (a -> X509.PubKey) -> a -> Maybe GenericPubKey -> ParseASN1 e a+verifyPubKey _ a Nothing = return a+verifyPubKey toPubKey a (Just gpk)+    | Right (gpk, []) == derived = return a+    | otherwise = throwParseError "PKCS8 public key does not match private key"+  where derived = getGenericPubKey toPubKey a++getGenericPubKey :: (a -> X509.PubKey) -> a -> Either String (GenericPubKey, [ASN1])+getGenericPubKey toPubKey a = runParseASN1State parsePubKeyGen asn1+  where asn1 = toASN1 (toPubKey a) []++parsePubKeyGen :: Monoid e => ParseASN1 e GenericPubKey+parsePubKeyGen = onNextContainer Sequence $ do+    void $ getNextContainer Sequence  -- ignore algorithm,+    BitString bits <- getNext         -- we want the bit string only+    return (GenericPubKey $ bitArrayGetData bits)++newtype GenericPubKey = GenericPubKey B.ByteString+    deriving (Show,Eq)++instance ASN1Elem e => ProduceASN1Object e (Maybe GenericPubKey) where+    asn1s Nothing = id+    asn1s (Just (GenericPubKey bits)) = gMany [Other Context 1 bs]+      where bs = putBitString (toBitArray bits 0)++instance Monoid e => ParseASN1Object e (Maybe GenericPubKey) where+    parse = Just . GenericPubKey <$> parseTaggedBitString <|> return Nothing+      where+        parseTaggedBitString = do+            Other _ 1 bs <- getNext+            case getBitString bs of+                Right (BitString bits) -> return (bitArrayGetData bits)+                r -> throwParseError ("GenericPubKey: invalid bit string: " ++ show r)+ -- | A key associated with format.  Allows to implement 'ASN1Object' instances. data FormattedKey a = FormattedKey PrivateKeyFormat a     deriving (Show,Eq)@@ -281,16 +360,35 @@ instance Functor FormattedKey where     fmap f (FormattedKey fmt a) = FormattedKey fmt (f a) -instance (ProduceASN1Object e (Traditional a), ProduceASN1Object e (Modern a)) => ProduceASN1Object e (FormattedKey a) where-    asn1s (FormattedKey TraditionalFormat k) = asn1s (Traditional k)-    asn1s (FormattedKey PKCS8Format k)       = asn1s (Modern [] k)+instance ASN1Elem e => ProduceASN1Object e (FormattedKey KeyPair) where+    asn1s = formattedASN1S keyPairToPubKey -instance (Monoid e, ParseASN1Object e (Traditional a), ParseASN1Object e (Modern a)) => ParseASN1Object e (FormattedKey a) where-    parse = (modern <$> parseModern) <|> (traditional <$> parseTraditional)-      where-        traditional = FormattedKey TraditionalFormat-        modern      = FormattedKey PKCS8Format+instance Monoid e => ParseASN1Object e (FormattedKey KeyPair) where+    parse = parseFormatted keyPairToPubKey +formattedASN1S :: (ProduceASN1Object e (Traditional a), ProduceASN1Object e (Modern a))+               => (a -> X509.PubKey) -> FormattedKey a -> ASN1Stream e+formattedASN1S _ (FormattedKey TraditionalFormat k) = asn1s (Traditional k)+formattedASN1S toPubKey (FormattedKey PKCS8Format k) = modernASN1S toPubKey k++parseFormattedKeyPair :: ParseASN1Object e (Modern a)+                      => (a -> KeyPair) -> ParseASN1 e KeyPair+parseFormattedKeyPair mapFn =+    unFormat <$> parseFormattedInternal keyPairToPubKey mapFn++parseFormatted :: (ParseASN1Object e (Traditional a), ParseASN1Object e (Modern a))+               => (a -> X509.PubKey) -> ParseASN1 e (FormattedKey a)+parseFormatted toPubKey = parseFormattedInternal toPubKey id++parseFormattedInternal :: (ParseASN1Object e (Traditional a), ParseASN1Object e (Modern b))+                       => (a -> X509.PubKey) -> (b -> a) -> ParseASN1 e (FormattedKey a)+parseFormattedInternal toPubKey mapFn =+    (modern <$> parseModern toPubKey mapFn) <|>+    (traditional <$> parseTraditional)+  where+    traditional = FormattedKey TraditionalFormat+    modern      = FormattedKey PKCS8Format+ unFormat :: FormattedKey a -> a unFormat (FormattedKey _ a) = a @@ -298,56 +396,46 @@ -- Private Keys  instance ASN1Object (FormattedKey X509.PrivKey) where+    toASN1 = toASN1 . fmap keyPairFromPrivKey+    fromASN1 = fmap (first (fmap keyPairToPrivKey)) <$> fromASN1++instance ASN1Object (FormattedKey KeyPair) where     toASN1   = asn1s     fromASN1 = runParseASN1State parse -instance ASN1Elem e => ProduceASN1Object e (Traditional X509.PrivKey) where-    asn1s (Traditional privKey) = snd $ traditionalPrivKeyASN1S privKey+instance ASN1Elem e => ProduceASN1Object e (Traditional KeyPair) where+    asn1s (Traditional keyPair) = snd $ traditionalPrivKeyASN1S keyPair -instance Monoid e => ParseASN1Object e (Traditional X509.PrivKey) where+instance Monoid e => ParseASN1Object e (Traditional KeyPair) where     parse = rsa <|> dsa <|> ecdsa       where-        rsa   = Traditional . X509.PrivKeyRSA . unTraditional <$> parse-        dsa   = Traditional . X509.PrivKeyDSA . DSA.toPrivateKey . unTraditional <$> parse-        ecdsa = Traditional . X509.PrivKeyEC . unTraditional <$> parse+        rsa   = Traditional . keyPairFromPrivKey . X509.PrivKeyRSA . unTraditional <$> parse+        dsa   = Traditional . keyPairFromPrivKey . X509.PrivKeyDSA . DSA.toPrivateKey . unTraditional <$> parse+        ecdsa = Traditional . keyPairFromPrivKey . X509.PrivKeyEC . unTraditional <$> parse -instance ASN1Elem e => ProduceASN1Object e (Modern X509.PrivKey) where-    asn1s (Modern attrs privKey) = modernPrivKeyASN1S attrs privKey+instance ASN1Elem e => ProduceASN1Object e (Modern KeyPair) where+    asn1s (Modern attrs mPub keyPair) = modernPrivKeyASN1S attrs mPub keyPair -instance Monoid e => ParseASN1Object e (Modern X509.PrivKey) where+instance Monoid e => ParseASN1Object e (Modern KeyPair) where     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-    IntVal v <- getNext-    when (v /= 0 && v /= 1) $-        throwParseError ("PKCS8: parsed invalid version: " ++ show v)--skipPublicKey :: Monoid e => ParseASN1 e ()-skipPublicKey = void (fmap Just parseTaggedPrimitive <|> return Nothing)-  where parseTaggedPrimitive = do { Other _ 1 bs <- getNext; return bs }--parseAttrKeys :: Monoid e => ParseASN1 e ([Attribute], B.ByteString)-parseAttrKeys = do-    OctetString bs <- getNext-    attrs <- parseAttributes (Container Context 0)-    skipPublicKey-    return (attrs, bs)+        rsa   = fmap (keyPairFromPrivKey . X509.PrivKeyRSA) <$> parse+        dsa   = fmap (keyPairFromPrivKey . X509.PrivKeyDSA . DSA.toPrivateKey) <$> parse+        ecdsa = fmap (keyPairFromPrivKey . X509.PrivKeyEC) <$> parse+        x25519  = fmap (keyPairFromPrivKey . X509.PrivKeyX25519) <$> parse+        x448    = fmap (keyPairFromPrivKey . X509.PrivKeyX448) <$> parse+        ed25519 = fmap (keyPairFromPrivKey . X509.PrivKeyEd25519) <$> parse+        ed448   = fmap (keyPairFromPrivKey . X509.PrivKeyEd448) <$> parse   -- RSA +toPubKeyRSA :: RSA.PrivateKey -> X509.PubKey+toPubKeyRSA = X509.PubKeyRSA . RSA.toPublicKey . RSA.KeyPair+ instance ASN1Object (FormattedKey RSA.PrivateKey) where-    toASN1   = asn1s-    fromASN1 = runParseASN1State parse+    toASN1   = formattedASN1S toPubKeyRSA+    fromASN1 = runParseASN1State (parseFormatted toPubKeyRSA)  instance ASN1Elem e => ProduceASN1Object e (Traditional RSA.PrivateKey) where     asn1s (Traditional privKey) =@@ -391,34 +479,37 @@         return (Traditional privKey)  instance ASN1Elem e => ProduceASN1Object e (Modern RSA.PrivateKey) where-    asn1s (Modern attrs privKey) =+    asn1s (Modern attrs mPub privKey) =         asn1Container Sequence (v . alg . bs . att)       where-        v     = gIntVal 0+        v     = versionASN1S mPub         alg   = asn1Container Sequence (oid . gNull)         oid   = gOID [1,2,840,113549,1,1,1]         bs    = gOctetString (encodeASN1Object $ Traditional privKey)-        att   = attributesASN1S (Container Context 0) attrs+        att   = attrKeysASN1S attrs mPub  instance Monoid e => ParseASN1Object e (Modern RSA.PrivateKey) where     parse = onNextContainer Sequence $ do-        skipVersion+        v2 <- parseVersion         Null <- onNextContainer Sequence $ do                     OID [1,2,840,113549,1,1,1] <- getNext                     getNext-        (attrs, bs) <- parseAttrKeys+        (attrs, bs, mPub) <- parseAttrKeys v2         let inner = decodeASN1' BER bs             strError = Left .  ("PKCS8: error decoding inner RSA: " ++) . show         case either strError (runParseASN1 parseTraditional) inner of              Left err -> throwParseError ("PKCS8: error parsing inner RSA: " ++ err)-             Right privKey -> return (Modern attrs privKey)+             Right privKey -> return (Modern attrs mPub privKey)   -- DSA +toPubKeyDSA :: DSA.KeyPair -> X509.PubKey+toPubKeyDSA = X509.PubKeyDSA . DSA.toPublicKey+ instance ASN1Object (FormattedKey DSA.KeyPair) where-    toASN1   = asn1s-    fromASN1 = runParseASN1State parse+    toASN1   = formattedASN1S toPubKeyDSA+    fromASN1 = runParseASN1State (parseFormatted toPubKeyDSA)  instance ASN1Elem e => ProduceASN1Object e (Traditional DSA.KeyPair) where     asn1s (Traditional (DSA.KeyPair params pub priv)) =@@ -437,27 +528,27 @@         return (Traditional $ DSA.KeyPair params pub priv)  instance ASN1Elem e => ProduceASN1Object e (Modern DSA.KeyPair) where-    asn1s (Modern attrs (DSA.KeyPair params _ priv)) =+    asn1s (Modern attrs mPub (DSA.KeyPair params _ priv)) =         asn1Container Sequence (v . alg . bs . att)       where-        v     = gIntVal 0+        v     = versionASN1S mPub         alg   = asn1Container Sequence (oid . pr)         oid   = gOID [1,2,840,10040,4,1]         pr    = asn1Container Sequence (pqgASN1S params)         bs    = gOctetString (encodeASN1S $ gIntVal priv)-        att   = attributesASN1S (Container Context 0) attrs+        att   = attrKeysASN1S attrs mPub  instance Monoid e => ParseASN1Object e (Modern DSA.KeyPair) where     parse = onNextContainer Sequence $ do-        skipVersion+        v2 <- parseVersion         params <- onNextContainer Sequence $ do                       OID [1,2,840,10040,4,1] <- getNext                       onNextContainer Sequence parsePQG-        (attrs, bs) <- parseAttrKeys+        (attrs, bs, mPub) <- parseAttrKeys v2         case decodeASN1' BER bs of             Right [IntVal priv] ->                 let pub = DSA.calculatePublic params priv-                 in return (Modern attrs $ DSA.KeyPair params pub priv)+                 in return (Modern attrs mPub $ DSA.KeyPair params pub priv)             Right _ -> throwParseError "PKCS8: invalid format when parsing inner DSA"             Left  e -> throwParseError ("PKCS8: error parsing inner DSA: " ++ show e) @@ -477,18 +568,15 @@                       , DSA.params_g = g                       } -dsaPrivToPair :: DSA.PrivateKey -> DSA.KeyPair-dsaPrivToPair k = DSA.KeyPair params pub x-  where pub     = DSA.calculatePublic params x-        params  = DSA.private_params k-        x       = DSA.private_x k - -- ECDSA +toPubKeyEC :: X509.PrivKeyEC -> X509.PubKey+toPubKeyEC = keyPairToPubKey . keyPairFromPrivKey . X509.PrivKeyEC+ instance ASN1Object (FormattedKey X509.PrivKeyEC) where-    toASN1   = asn1s-    fromASN1 = runParseASN1State parse+    toASN1   = formattedASN1S toPubKeyEC+    fromASN1 = runParseASN1State (parseFormatted toPubKeyEC)  instance ASN1Elem e => ProduceASN1Object e (Traditional X509.PrivKeyEC) where     asn1s = innerEcdsaASN1S True . unTraditional@@ -497,27 +585,27 @@     parse = Traditional <$> parseInnerEcdsa Nothing  instance ASN1Elem e => ProduceASN1Object e (Modern X509.PrivKeyEC) where-    asn1s (Modern attrs privKey) = asn1Container Sequence (v . f . bs . att)+    asn1s (Modern attrs mPub privKey) = asn1Container Sequence (v . f . bs . att)       where-        v     = gIntVal 0+        v     = versionASN1S mPub         f     = asn1Container Sequence (oid . curveFnASN1S privKey)         oid   = gOID [1,2,840,10045,2,1]         bs    = gOctetString (encodeASN1S inner)         inner = innerEcdsaASN1S False privKey-        att   = attributesASN1S (Container Context 0) attrs+        att   = attrKeysASN1S attrs mPub  instance Monoid e => ParseASN1Object e (Modern X509.PrivKeyEC) where     parse = onNextContainer Sequence $ do-        skipVersion+        v2 <- parseVersion         f <- onNextContainer Sequence $ do             OID [1,2,840,10045,2,1] <- getNext             parseCurveFn-        (attrs, bs) <- parseAttrKeys+        (attrs, bs, mPub) <- parseAttrKeys v2         let inner = decodeASN1' BER bs             strError = Left .  ("PKCS8: error decoding inner EC: " ++) . show         case either strError (runParseASN1 $ parseInnerEcdsa $ Just f) inner of             Left err -> throwParseError ("PKCS8: error parsing inner EC: " ++ err)-            Right privKey -> return (Modern attrs privKey)+            Right privKey -> return (Modern attrs mPub privKey)  innerEcdsaASN1S :: ASN1Elem e => Bool -> X509.PrivKeyEC -> ASN1Stream e innerEcdsaASN1S addC k@@ -626,69 +714,55 @@ -- 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+    asn1s = produceModernEddsa [1,3,101,110]  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+    parse = parseModernEddsa "X25519" [1,3,101,110] X25519.secretKey  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+    asn1s = produceModernEddsa [1,3,101,111]  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+    parse = parseModernEddsa "X448" [1,3,101,111] X448.secretKey  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+    asn1s = produceModernEddsa [1,3,101,112]  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+    parse = parseModernEddsa "Ed25519" [1,3,101,112] Ed25519.secretKey  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+    asn1s = produceModernEddsa [1,3,101,113]  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+    parse = parseModernEddsa "Ed448" [1,3,101,113] Ed448.secretKey +-- * Producer helpers++produceModernEddsa :: (ASN1Elem e, ByteArrayAccess key) => OID -> Modern key -> ASN1Stream e+produceModernEddsa oid (Modern attrs mPub privKey) = asn1Container Sequence (v . alg . bs . att)+  where+    v     = versionASN1S mPub+    alg   = asn1Container Sequence (gOID oid)+    bs    = innerEddsaASN1S privKey+    att   = attrKeysASN1S attrs mPub+ innerEddsaASN1S :: (ASN1Elem e, ByteArrayAccess key) => key -> ASN1Stream e innerEddsaASN1S key = gOctetString (encodeASN1S inner)   where inner = gOctetString (convert key) +-- * Parser helpers++parseModernEddsa :: Monoid e => String -> OID -> (B.ByteString -> CryptoFailable a) -> ParseASN1 e (Modern a)+parseModernEddsa name expectedOid buildKey = onNextContainer Sequence $ do+  v2 <- parseVersion+  onNextContainer Sequence $ do+    OID oid <- getNext+    when (oid /= expectedOid) $+      throwParseError ("PKCS8: while parsing " ++ name ++ " expected OID " ++ show expectedOid ++ " while got " ++ show oid)+  (attrs, bs, mPub) <- parseAttrKeys v2+  Modern attrs mPub <$> parseInnerEddsa name buildKey bs+ parseInnerEddsa :: Monoid e                 => String                 -> (B.ByteString -> CryptoFailable key)@@ -707,3 +781,28 @@             CryptoPassed privKey -> return privKey             CryptoFailed _       ->                 throwParseError ("PKCS8: parsed invalid " ++ name ++ " secret key")++versionASN1S :: ASN1Elem e => Maybe GenericPubKey -> ASN1Stream e+versionASN1S mPub = gIntVal (if isJust mPub then 1 else 0)++parseVersion :: Monoid e => ParseASN1 e Bool+parseVersion = do+    IntVal v <- getNext+    when (v /= 0 && v /= 1) $+        throwParseError ("PKCS8: parsed invalid version: " ++ show v)+    return (v /= 0)++attrKeysASN1S :: ASN1Elem e => [Attribute] -> Maybe GenericPubKey -> ASN1Stream e+attrKeysASN1S attrs mPub = att . asn1s mPub+  where att = attributesASN1S (Container Context 0) attrs++parseAttrKeys :: Monoid e+              => Bool+              -> ParseASN1 e ([Attribute], B.ByteString, Maybe GenericPubKey)+parseAttrKeys v2 = do+    OctetString bs <- getNext+    attrs <- parseAttributes (Container Context 0)+    mPub <- parse+    when (isJust mPub && not v2) $+        throwParseError "PKCS8: public key allowed only for version 2"+    return (attrs, bs, mPub)
+ src/Crypto/Store/PubKey/RSA/KEM.hs view
@@ -0,0 +1,93 @@+-- |+-- Module      : Crypto.Store.PubKey.RSA.KEM+-- License     : BSD-style+-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>+-- Stability   : experimental+-- Portability : unknown+--+-- RSA as a Key-Encapsulation Mechanism (KEM).+module Crypto.Store.PubKey.RSA.KEM+    ( KDF(..), kdf3+    -- * Operations+    , encapsulate, encapsulateWith, decapsulate+    ) where++import           Data.ByteArray (ByteArray, ByteArrayAccess, Bytes)+import qualified Data.ByteArray as B+import           Data.ByteString (ByteString, empty)++import           Crypto.Hash+import           Crypto.Number.Generate+import           Crypto.Number.Serialize (os2ip, i2ospOf_)+import qualified Crypto.PubKey.RSA.Prim as RSA+import qualified Crypto.PubKey.RSA.Types as RSA+import           Crypto.Random++-- | Key derivation used by RSA-KEM.+newtype KDF bIn bOut = KDF (ByteString -> bIn -> bOut)++-- | KDF3 from ANSI X9.44-2007 (R2017)+kdf3 :: (HashAlgorithm a, ByteArrayAccess bIn, ByteArray bOut)+     => a -> Int -> KDF bIn bOut+kdf3 hashAlg outLen = KDF (doKDF3 hashAlg outLen)++doKDF3 :: (HashAlgorithm a, ByteArrayAccess bIn, ByteArray bOut)+       => a -> Int -> ByteString -> bIn -> bOut+doKDF3 hashAlg outLen otherInfo input+    | r == 0    = B.concat $ map doChunk [ 1 .. k ]+    | otherwise = B.take outLen $ B.concat $ map doChunk [ 1 .. k + 1 ]+  where+    (k, r) = outLen `divMod` blk+    blk    = hashDigestSize hashAlg+    doChunk i =+        let ctx0 = hashInitWith hashAlg+            ctx1 = hashUpdate ctx0 (i2ospOf_ 4 (toInteger i) :: Bytes)+            ctx2 = hashUpdate ctx1 input+            ctx3 = hashUpdate ctx2 otherInfo+         in hashFinalize ctx3++-- | Generate a shared secret key and an associated ciphertext using randomness.+encapsulate :: (MonadRandom m, ByteArray ciphertext)+            => KDF ciphertext sharedSecret+            -> RSA.PublicKey+            -> m (sharedSecret, ciphertext)+encapsulate kdf pub = encap kdf pub <$> generateMax (RSA.public_n pub)++-- | Generate a shared secret key and an associated ciphertext using a+-- specified random input.  This input must be an integer in range [0, n) and+-- not repeated with other encapsulations.  For testing purposes.+encapsulateWith :: ByteArray ciphertext+                => KDF ciphertext sharedSecret+                -> RSA.PublicKey+                -> Integer+                -> Maybe (sharedSecret, ciphertext)+encapsulateWith kdf pub z+    | z < 0 || z >= RSA.public_n pub = Nothing+    | otherwise = Just $ encap kdf pub z++encap :: ByteArray ciphertext+      => KDF ciphertext sharedSecret+      -> RSA.PublicKey+      -> Integer+      -> (sharedSecret, ciphertext)+encap (KDF kdf) pub z = (ss, ct)+  where+    zz  = i2ospOf_ (RSA.public_size pub) z+    ct  = RSA.ep pub zz+    ss  = kdf empty zz++-- | Return the shared secret for a given ciphertext.+decapsulate :: ByteArray ciphertext+            => KDF ciphertext sharedSecret+            -> RSA.PrivateKey+            -> ciphertext+            -> Maybe sharedSecret+decapsulate (KDF kdf) priv ct+    | B.length ct < RSA.public_size pub = Nothing+    | c >= RSA.public_n pub = Nothing+    | otherwise = Just ss+  where+    pub = RSA.private_pub priv+    c   = os2ip ct+    zz  = RSA.dp Nothing priv ct+    ss  = kdf empty zz
src/Crypto/Store/Util.hs view
@@ -20,7 +20,9 @@ import           Data.Bits import           Data.ByteArray (ByteArray, ByteArrayAccess) import qualified Data.ByteArray as B-import           Data.List+#if !(MIN_VERSION_base(4,20,0))+import           Data.List (foldl')+#endif import           Data.Memory.Endian import           Data.Word 
src/Crypto/Store/X509.hs view
@@ -20,6 +20,7 @@     , readPubKeyFile     , readPubKeyFileFromMemory     , pemToPubKey+    , pemToPubKeyAccum     , writePubKeyFile     , writePubKeyFileToMemory     , pubKeyToPEM@@ -36,7 +37,7 @@ import Data.ASN1.Types import Data.ASN1.BinaryEncoding import Data.ASN1.Encoding-import Data.Maybe+import Data.Either (rights) import Data.Proxy import qualified Data.X509 as X509 import qualified Data.ByteString as B@@ -46,7 +47,9 @@ import Crypto.Store.ASN1.Generate import Crypto.Store.ASN1.Parse import Crypto.Store.CMS.Util+import Crypto.Store.Error import Crypto.Store.PEM+import Crypto.Store.Util   -- | Class of signed objects convertible to PEM.@@ -78,24 +81,32 @@ readPubKeyFileFromMemory = either (const []) accumulate . pemParseBS  accumulate :: [PEM] -> [X509.PubKey]-accumulate = catMaybes . foldr (flip pemToPubKey) []+accumulate = rights . map pemToPubKey --- | Read a public key from a 'PEM' element and add it to the accumulator list.-pemToPubKey :: [Maybe X509.PubKey] -> PEM -> [Maybe X509.PubKey]-pemToPubKey acc pem =-    case decodeASN1' BER (pemContent pem) of-        Left _     -> acc-        Right asn1 -> run (getParser $ pemName pem) asn1 : acc+-- | Read a public key from a t'PEM' element and add it to the accumulator list.+--+-- This API is modelled after function @pemToKey@ in "Data.X509.Memory".+pemToPubKeyAccum :: [Maybe X509.PubKey] -> PEM -> [Maybe X509.PubKey]+pemToPubKeyAccum acc pem =+    case pemToPubKey pem of+        Left (DecodingError _) -> acc+        Left _                 -> Nothing : acc+        Right pubKey           -> Just pubKey : acc -  where-    run p asn1 =-        case p asn1 of-            Right (pubKey, []) -> Just pubKey-            _                  -> Nothing+-- | Read a public key from a t'PEM' element.+pemToPubKey :: PEM -> Either StoreError X509.PubKey+pemToPubKey pem = do+    asn1 <- mapLeft DecodingError $ decodeASN1' BER (pemContent pem)+    parser <- getParser (pemName pem)+    (pubKey, unparsed) <- mapLeft ParseFailure $ parser asn1+    case unparsed of+        [] -> return pubKey+        er -> Left $ ParseFailure ("pemToPubKey: remaining state " ++ show er) -    getParser "PUBLIC KEY"           = fromASN1-    getParser "RSA PUBLIC KEY"       = runParseASN1State rsapkParser-    getParser _                      = const (Left undefined)+  where+    getParser "PUBLIC KEY"           = return fromASN1+    getParser "RSA PUBLIC KEY"       = return (runParseASN1State rsapkParser)+    getParser _                      = Left UnexpectedNameForPEM      rsapkParser = (\(RSAPublicKey pub) -> X509.PubKeyRSA pub) <$> parse @@ -149,9 +160,6 @@ signedToPEM :: forall a. SignedObject a => X509.SignedExact a -> PEM signedToPEM obj = mkPEM (signedObjectName prx) (X509.encodeSignedObject obj)   where prx = Proxy :: Proxy a--mkPEM :: String -> B.ByteString -> PEM-mkPEM name bs = PEM { pemName = name, pemHeader = [], pemContent = bs}   -- RSA public keys
tests/CMS/Instances.hs view
@@ -1,9 +1,9 @@+{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- | Orphan instances. module CMS.Instances     ( arbitraryPassword     , arbitraryAttributes-    , arbitraryIntegrityDigest     , arbitrarySigVer     , arbitraryEnvDev     ) where@@ -11,6 +11,7 @@ import           Data.ASN1.Types import qualified Data.ByteArray as B import           Data.ByteString (ByteString)+import           Data.Maybe (fromJust) import           Data.X509  import GHC.TypeLits@@ -21,6 +22,7 @@  import Crypto.Store.CMS import Crypto.Store.Error+import Crypto.Store.PKCS8  import X509.Instances @@ -125,7 +127,7 @@  arbitraryNat :: Gen SomeNat arbitraryNat = unwrap <$> arbitrary-  where unwrap (Positive i) = let Just n = someNatVal (127 + i) in n+  where unwrap (Positive i) = let n = someNatVal (127 + i) in fromJust n  instance Arbitrary DigestAlgorithm where     arbitrary = oneof@@ -137,24 +139,37 @@         , pure $ DigestAlgorithm SHA256         , pure $ DigestAlgorithm SHA384         , pure $ DigestAlgorithm SHA512+        , pure $ DigestAlgorithm SHA3_224+        , pure $ DigestAlgorithm SHA3_256+        , pure $ DigestAlgorithm SHA3_384+        , pure $ DigestAlgorithm SHA3_512         , pure $ DigestAlgorithm SHAKE128_256         , pure $ DigestAlgorithm SHAKE256_512         , (\(SomeNat p) -> DigestAlgorithm (SHAKE128 p)) <$> arbitraryNat         , (\(SomeNat p) -> DigestAlgorithm (SHAKE256 p)) <$> arbitraryNat         ] -arbitraryIntegrityDigest :: Gen DigestAlgorithm-arbitraryIntegrityDigest = elements+arbitraryDigest :: Gen DigestAlgorithm+arbitraryDigest = elements     [ DigestAlgorithm MD5     , DigestAlgorithm SHA1     , DigestAlgorithm SHA224     , DigestAlgorithm SHA256     , DigestAlgorithm SHA384     , DigestAlgorithm SHA512+    , DigestAlgorithm SHA3_224+    , DigestAlgorithm SHA3_256+    , DigestAlgorithm SHA3_384+    , DigestAlgorithm SHA3_512     ]  instance Arbitrary MACAlgorithm where-    arbitrary = (\(DigestAlgorithm alg) -> HMAC alg) <$> arbitraryIntegrityDigest+    arbitrary = oneof+        [ (\(DigestAlgorithm alg) -> HMAC alg) <$> arbitraryDigest+        , (\(SomeNat p) -> KMAC_SHAKE128 p) <$> arbitraryNat <*> arbitraryCtx+        , (\(SomeNat p) -> KMAC_SHAKE256 p) <$> arbitraryNat <*> arbitraryCtx+        ]+      where arbitraryCtx = elements [ B.empty , "ctx" , "custom string" ]  instance Arbitrary OAEPParams where     arbitrary = do@@ -197,33 +212,30 @@         , pure $ ECDSA (DigestAlgorithm SHA256)         , pure $ ECDSA (DigestAlgorithm SHA384)         , pure $ ECDSA (DigestAlgorithm SHA512)+        , pure $ ECDSA (DigestAlgorithm SHA3_224)+        , pure $ ECDSA (DigestAlgorithm SHA3_256)+        , pure $ ECDSA (DigestAlgorithm SHA3_384)+        , pure $ ECDSA (DigestAlgorithm SHA3_512)          , pure Ed25519         , pure Ed448         ] -arbitraryKeyPair :: SignatureAlg -> Gen (PubKey, PrivKey)-arbitraryKeyPair RSAAnyHash = do-    (pub, priv) <- arbitraryRSA-    return (PubKeyRSA pub, PrivKeyRSA priv)-arbitraryKeyPair (RSA _) = do-    (pub, priv) <- arbitraryRSA-    return (PubKeyRSA pub, PrivKeyRSA priv)-arbitraryKeyPair (RSAPSS _) = do-    (pub, priv) <- arbitraryRSA-    return (PubKeyRSA pub, PrivKeyRSA priv)-arbitraryKeyPair (DSA _) = do-    (pub, priv) <- arbitraryDSA-    return (PubKeyDSA pub, PrivKeyDSA priv)-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)+arbitraryKeyPair :: SignatureAlg -> Gen KeyPair+arbitraryKeyPair RSAAnyHash =+    keyPairFromPrivKey . PrivKeyRSA . snd <$> arbitraryRSA+arbitraryKeyPair (RSA _) =+    keyPairFromPrivKey . PrivKeyRSA . snd <$> arbitraryRSA+arbitraryKeyPair (RSAPSS _) =+    keyPairFromPrivKey . PrivKeyRSA . snd <$> arbitraryRSA+arbitraryKeyPair (DSA _) =+    keyPairFromPrivKey . PrivKeyDSA <$> arbitraryPrivDSA+arbitraryKeyPair (ECDSA _) =+    keyPairFromPrivKey . PrivKeyEC . snd <$> arbitraryNamedEC+arbitraryKeyPair Ed25519 =+    keyPairFromPrivKey . PrivKeyEd25519 . snd <$> arbitraryEd25519+arbitraryKeyPair Ed448 =+    keyPairFromPrivKey . PrivKeyEd448 . snd <$> arbitraryEd448  arbitrarySigVer :: SignatureAlg -> Gen ([ProducerOfSI Gen], ConsumerOfSI Gen) arbitrarySigVer alg = sized $ \n -> do@@ -233,11 +245,12 @@     return (sigFns, verFn)   where     onePair = do-        (pub, priv) <- arbitraryKeyPair alg+        pair <- arbitraryKeyPair alg+        let pub = keyPairToPubKey pair         chain <- arbitraryCertificateChain pub         sAttrs <- oneof [ pure Nothing, Just <$> arbitraryAttributes ]         uAttrs <- arbitraryAttributes-        return (certSigner alg priv chain sAttrs uAttrs, withPublicKey pub)+        return (certSigner alg pair chain sAttrs uAttrs, withPublicKey pub)  instance Arbitrary PBKDF2_PRF where     arbitrary = elements@@ -270,13 +283,19 @@         , CFB Camellia128          , CTR Camellia128++        , DeriveHKDF SHA256         ]  instance Arbitrary ContentEncryptionParams where     arbitrary = arbitrary >>= gen       where+        gen (ECB c) = return (ecbParams c)+        gen (CBC c) = generateCBCParams c         gen CBC_RC2 = choose (24, 512) >>= generateRC2EncryptionParams-        gen alg     = generateEncryptionParams alg+        gen (CFB c) = generateCFBParams c+        gen (CTR c) = generateCTRParams c+        gen (DeriveHKDF _) = deriveEncryptionKey <$> arbitrary  instance Arbitrary AuthContentEncryptionAlg where     arbitrary = elements@@ -291,22 +310,23 @@         , GCM AES128         , GCM AES192         , GCM AES256++        , AuthDeriveHKDF SHA256         ]  instance Arbitrary AuthContentEncryptionParams where     arbitrary = do         alg <- arbitrary         case alg of-            AUTH_ENC_128 -> arb3 generateAuthEnc128Params-            AUTH_ENC_256 -> arb3 generateAuthEnc256Params+            AUTH_ENC_128 -> arb3 authEnc128Params+            AUTH_ENC_256 -> arb3 authEnc256Params             CHACHA20_POLY1305 -> generateChaChaPoly1305Params             CCM c -> do m <- arbitraryM                         l <- arbitraryL                         generateCCMParams c m l             GCM c -> choose (12,16) >>= generateGCMParams c-      where arb3 fn = do-                a <- arbitrary; b <- arbitrary; c <- arbitrary-                fn a b c+            AuthDeriveHKDF _ -> authDeriveEncryptionKey <$> arbitrary+      where arb3 fn = fn <$> arbitrary <*> arbitrary <*> arbitrary  arbitraryM :: Gen CCM_M arbitraryM = elements@@ -344,6 +364,18 @@                           , scryptKeyLength = Nothing                           } +instance Arbitrary KeyDerivationFn where+    arbitrary = elements (map HKDF digests ++ map KDF3 digests)+      where+        digests = [ DigestAlgorithm SHA256+                  , DigestAlgorithm SHA384+                  , DigestAlgorithm SHA512+                  , DigestAlgorithm SHA3_224+                  , DigestAlgorithm SHA3_256+                  , DigestAlgorithm SHA3_384+                  , DigestAlgorithm SHA3_512+                  ]+ instance Arbitrary KeyTransportParams where     arbitrary = oneof         [ pure RSAES@@ -377,20 +409,25 @@ arbitraryAgreeParams :: Bool -> KeyEncryptionParams -> Gen KeyAgreementParams arbitraryAgreeParams allowCofactorDH alg     | allowCofactorDH = oneof-        [ flip StdDH alg <$> arbitraryDigest-        , flip CofactorDH alg <$> arbitraryDigest+        [ flip StdDH alg <$> elements stdKDFs+        , flip CofactorDH alg <$> elements cofactorKDFs         ]-    | otherwise = flip StdDH alg <$> arbitraryDigest+    | otherwise = flip StdDH alg <$> elements stdKDFs   where-    arbitraryDigest =-        elements-            [ DigestAlgorithm SHA1-            , DigestAlgorithm SHA224-            , DigestAlgorithm SHA256-            , DigestAlgorithm SHA384-            , DigestAlgorithm SHA512-            ]+    cofactorKDFs =+        [ KA_X963_KDF SHA1+        , KA_X963_KDF SHA224+        , KA_X963_KDF SHA256+        , KA_X963_KDF SHA384+        , KA_X963_KDF SHA512+        ] +    stdKDFs = cofactorKDFs +++        [ KA_HKDF SHA256+        , KA_HKDF SHA384+        , KA_HKDF SHA512+        ]+ arbitraryEnvDev :: ContentEncryptionKey                 -> Gen ([ProducerOfRI Gen], ConsumerOfRI Gen) arbitraryEnvDev cek = sized $ \n -> do@@ -400,25 +437,26 @@     return (envFns, devFn)   where     len     = B.length cek-    onePair = oneof [ arbitraryKT, arbitraryKA, arbitraryKEK, arbitraryPW ]+    onePair = oneof [ arbitraryKT, arbitraryKA, arbitraryKEK, arbitraryPW, arbitraryKEM ]      arbitraryKT = do         (pub, priv) <- arbitraryLargeRSA+        let pair = keyPairFromPrivKey (PrivKeyRSA priv)         cert <- arbitrarySignedCertificate (PubKeyRSA pub)         ktp  <- arbitrary         let envFn = forKeyTransRecipient cert ktp-            devFn = withRecipientKeyTrans (PrivKeyRSA priv)+            devFn = withRecipientKeyTrans pair         return (envFn, devFn)      arbitraryKA = do-        (cert, priv) <- arbitraryDHParams+        (cert, pair) <- arbitraryDHParams         let allowCofactorDH =-                case priv of+                case keyPairToPrivKey pair of                     PrivKeyEC _ -> True                     _           -> False         kap <- arbitraryAlg >>= arbitraryAgreeParams allowCofactorDH         let envFn = forKeyAgreeRecipient cert kap-            devFn = withRecipientKeyAgree priv cert+            devFn = withRecipientKeyAgree pair cert         return (envFn, devFn)      arbitraryKEK = do@@ -430,10 +468,18 @@     arbitraryPW  = do         pwd <- arbitraryPassword         kdf <- arbitrary-        cea <- arbitrary `suchThat` notModeCTR+        cea <- arbitrary `suchThat` compatiblePW         let es = PWRIKEK cea         return (forPasswordRecipient pwd kdf es, withRecipientPassword pwd) +    arbitraryKEM  = do+        (cert, pair, params) <- arbitraryKEMParams+        kdf <- arbitrary+        kep <- arbitraryAlg+        let envFn = forKeyEncapRecipient cert kdf kep params+            devFn = withRecipientKeyEncap pair cert+        return (envFn, devFn)+     arbitraryAlg         | len == 24      = oneof [ return AES128_WRAP                                  , return AES192_WRAP@@ -463,27 +509,44 @@                               , arbitraryCredX448                               ] +    arbitraryKEMParams = arbitraryCredRSA >>= rsaKemToKEM++    rsaKemToKEM (cert, pair) = do+        kdf <- arbitrary+        keyLen <- choose (8,32)+        return (cert, pair, KeyEncapsulationRSA kdf keyLen)+     arbitraryCredNamedEC = do         (pub, priv) <- arbitraryNamedEC+        let pair = keyPairFromPrivKey (PrivKeyEC priv)         cert <- arbitrarySignedCertificate (PubKeyEC pub)-        return (cert, PrivKeyEC priv)+        return (cert, pair)      arbitraryCredX25519 = do         (pub, priv) <- arbitraryX25519+        let pair = keyPairFromPrivKey (PrivKeyX25519 priv)         cert <- arbitrarySignedCertificate (PubKeyX25519 pub)-        return (cert, PrivKeyX25519 priv)+        return (cert, pair)      arbitraryCredX448 = do         (pub, priv) <- arbitraryX448+        let pair = keyPairFromPrivKey (PrivKeyX448 priv)         cert <- arbitrarySignedCertificate (PubKeyX448 pub)-        return (cert, PrivKeyX448 priv)+        return (cert, pair) -    -- key wrapping in PWRIKEK is incompatible with CTR mode so we must never-    -- generate this combination-    notModeCTR params =+    arbitraryCredRSA = do+        (pub, priv) <- arbitraryRSA+        let pair = keyPairFromPrivKey (PrivKeyRSA priv)+        cert <- arbitrarySignedCertificate (PubKeyRSA pub)+        return (cert, pair)++    -- key wrapping in PWRIKEK is incompatible with CTR mode or HKDF key+    -- derivation, so we must never generate these combinations+    compatiblePW params =         case getContentEncryptionAlg params of-            CTR _ -> False-            _     -> True+            CTR _        -> False+            DeriveHKDF _ -> False+            _            -> True  instance Arbitrary OriginatorInfo where     arbitrary = OriginatorInfo <$> arbitrary <*> arbitrary
tests/CMS/Tests.hs view
@@ -1,4 +1,5 @@ -- | CMS tests.+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} module CMS.Tests (cmsTests) where  import Control.Monad@@ -180,12 +181,16 @@                 ci' = toAttachedCI dd'             ci @?= ci'   where path  = testFile "cms-digested-data.pem"-        algs  = [ ("MD5",    DigestAlgorithm MD5)-                , ("SHA1",   DigestAlgorithm SHA1)-                , ("SHA224", DigestAlgorithm SHA224)-                , ("SHA256", DigestAlgorithm SHA256)-                , ("SHA384", DigestAlgorithm SHA384)-                , ("SHA512", DigestAlgorithm SHA512)+        algs  = [ ("MD5",      DigestAlgorithm MD5)+                , ("SHA1",     DigestAlgorithm SHA1)+                , ("SHA224",   DigestAlgorithm SHA224)+                , ("SHA256",   DigestAlgorithm SHA256)+                , ("SHA384",   DigestAlgorithm SHA384)+                , ("SHA512",   DigestAlgorithm SHA512)+                , ("SHA3_224", DigestAlgorithm SHA3_224)+                , ("SHA3_256", DigestAlgorithm SHA3_256)+                , ("SHA3_384", DigestAlgorithm SHA3_384)+                , ("SHA3_512", DigestAlgorithm SHA3_512)                 ]  encryptedDataTests :: TestTree@@ -230,29 +235,90 @@  authEnvelopedDataTests :: TestTree authEnvelopedDataTests =-    testCaseSteps "AuthEnvelopedData" $ \step -> do-        cms <- readCMSFile path-        assertEqual "unexpected parse count" count (length cms)+    testGroup "AuthEnvelopedData"+        [ testKT "KTRI" path3+        , testKA "KARI" path4+        , test "KEKRI" path1 withRecipientKey+        ]+  where test caseName path f = testCaseSteps caseName $ \step -> do+            cms <- readCMSFile path+            assertEqual "unexpected parse count" (length keys) (length cms) -        forM_ (zip [0..] cms) $ \(index, ci) -> do-            step ("testing vector " ++ show (index :: Int))+            forM_ (zip [0..] cms) $ \(index, ci) -> do+                let (name, key) = keys !! index++                step ("testing " ++ name)+                ae <- getAuthEnveloppedAttached ci+                result <- openAuthEnvelopedData (f key) ae+                assertRight result (verifyInnerMessage message)+        testKT caseName path = testCaseSteps caseName $ \step -> do+            let rsaPath = testFile "rsa-unencrypted-pkcs8.pem"+            [Unprotected priv] <- readKeyFile rsaPath++            cms <- readCMSFile path+            assertEqual "unexpected parse count" (length modes * length keys) (length cms)++            let pairs = [ (c, m) | c <- map fst keys, m <- modes ]+            forM_ (zip pairs cms) $ \((c, m), ci) -> do+                step ("testing " ++ c ++ " with " ++ m)+                ae <- getAuthEnveloppedAttached ci+                result <- openAuthEnvelopedData (withRecipientKeyTrans priv) ae+                assertRight result (verifyInnerMessage message)+        testKA caseName path = testCaseSteps caseName $ \step -> do+            let ecdsaKeyPath  = testFile "ecdsa-p256-unencrypted-pkcs8.pem"+                ecdsaCertPath = testFile "ecdsa-p256-self-signed-cert.pem"+            [Unprotected priv] <- readKeyFile ecdsaKeyPath+            [cert] <- readSignedObject ecdsaCertPath++            cms <- readCMSFile path+            assertEqual "unexpected parse count" (length mds * length keys) (length cms)++            let pairs = [ (c, h) | c <- map fst keys, h <- mds ]+            forM_ (zip pairs cms) $ \((c, h), ci) -> do+                step ("testing " ++ c ++ " with " ++ h)+                ae <- getAuthEnveloppedAttached ci+                result <- openAuthEnvelopedData (withRecipientKeyAgree priv cert) ae+                assertRight result (verifyInnerMessage message)+        getAuthEnveloppedAttached ci = do             assertBool "unexpected type" (hasType AuthEnvelopedDataType ci)             let AuthEnvelopedDataCI aeEncap = ci-            ae <- getAttached aeEncap-            result <- openAuthEnvelopedData (withRecipientPassword pwd) ae-            assertRight result (verifyInnerMessage msg)+            getAttached aeEncap+        path1 = testFile "cms-auth-enveloped-kekri-data.pem"+        path3 = testFile "cms-auth-enveloped-ktri-data.pem"+        path4 = testFile "cms-auth-enveloped-kari-data.pem"+        keys  = [ ("AES128_GCM",           testKey 16)+                , ("AES192_GCM",           testKey 24)+                , ("AES256_GCM",           testKey 32)+                ]+        modes = [ "RSAES-PKCS1"+                , "RSAES-OAEP"+                ]+        mds   = [ "SHA1"+                , "SHA224"+                , "SHA256"+                , "SHA384"+                , "SHA512"+                ] -            step ("testing encoded vector " ++ show index)-            let [Just ci'] = pemToContentInfo [] (contentInfoToPEM 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"-        pwd   = fromString "password"-        msg   = fromString "Some test data\NUL"-        count = 2+rfc9690Tests :: TestTree+rfc9690Tests = testCase "rfc9690" $ do+    keys <- readKeyFile path+    length keys @?= 1+    certs <- readSignedObject path+    length certs @?= 1+    cms <- readCMSFile path+    length cms @?= 1 +    let Right pair = recover (fromString "not-used") key+        [EnvelopedDataCI envelopedEncapData] = cms+        [cert] = certs+        [key] = keys++    envelopedData <- fromAttached envelopedEncapData+    result <- openEnvelopedData (withRecipientKeyEncap pair cert) envelopedData+    result @?= Right (DataCI $ fromString "Hello, world!")+  where path = testFile "rfc9690.pem"+ propertyTests :: TestTree propertyTests = localOption (QuickCheckMaxSize 5) $ testGroup "properties"     [ testProperty "marshalling" $ \l ->@@ -324,5 +390,6 @@         , digestedDataTests         , encryptedDataTests         , authEnvelopedDataTests+        , rfc9690Tests         , propertyTests         ]
tests/KeyWrap/RC2.hs view
@@ -6,6 +6,7 @@  import           Data.ByteString (ByteString) import qualified Data.ByteString as B+import           Data.Maybe (fromJust)  import Crypto.Cipher.Types import Crypto.Error@@ -68,12 +69,11 @@     initCipher ekl k = throwCryptoError (rc2WithEffectiveKeyLength ekl k)      wrapUnwrapProperty :: TestKey RC2 -> TestIV RC2 -> Message -> Gen Property-    wrapUnwrapProperty (Key key) (IV ivBs) (Message msg) = do+    wrapUnwrapProperty (Key key) iv (Message msg) = do         ekl <- choose (1, 1024)         let ctx = initCipher ekl key-        wrapped <- wrap ctx iv msg+        wrapped <- wrap ctx (unIV iv) msg         return $ (wrapped >>= unwrap ctx) === Right msg-      where Just iv = makeIV ivBs      makeTest :: Integer -> Vector -> TestTree     makeTest i Vector{..} =@@ -82,7 +82,7 @@             , testCase "Unwrap" (unwrap ctx vecCiphertext @?= Right vecPlaintext)             ]       where ctx = initCipher vecEKL vecKey-            Just iv = makeIV vecIV+            iv = fromJust $ makeIV vecIV             doWrap = wrap' Left withRandomPad             withRandomPad f len                 | B.length vecPad /= len = Left (InvalidInput "unexpected length")
tests/KeyWrap/TripleDES.hs view
@@ -5,6 +5,7 @@ module KeyWrap.TripleDES (tripledeskwTests) where  import Data.ByteString (ByteString, pack)+import Data.Maybe (fromJust)  import Crypto.Cipher.TripleDES import Crypto.Cipher.Types@@ -62,10 +63,9 @@     initCipher k = throwCryptoError (cipherInit k)      wrapUnwrapProperty :: TestKey cipher -> TestIV cipher -> Message -> Property-    wrapUnwrapProperty (Key key) (IV ivBs) (Message msg) =-        (wrap ctx iv msg >>= unwrap ctx) === Right msg+    wrapUnwrapProperty (Key key) iv (Message msg) =+        (wrap ctx (unIV iv) msg >>= unwrap ctx) === Right msg       where ctx = initCipher key-            Just iv = makeIV ivBs      makeTest :: Integer -> Vector -> TestTree     makeTest i Vector{..} =@@ -74,7 +74,7 @@             , testCase "Unwrap" (unwrap ctx vecCiphertext @?= Right vecPlaintext)             ]       where ctx = initCipher vecKey-            Just iv = makeIV vecIV+            iv = fromJust $ makeIV vecIV  tripledeskwTests :: TestTree tripledeskwTests = testGroup "KeyWrap.TripleDES"
tests/PKCS12/Instances.hs view
@@ -1,18 +1,23 @@+{-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- | Orphan instances. module PKCS12.Instances     ( arbitraryPassword     , arbitraryAlias-    , arbitraryIntegrityParams+    , arbitraryTraditionalIntegrity+    , arbitraryAuthSchemeIntegrity     , arbitraryPKCS12     ) where  import qualified Data.ByteArray as B import           Data.ByteString (ByteString)+#if !(MIN_VERSION_base(4,11,0)) import           Data.Semigroup+#endif  import Test.Tasty.QuickCheck +import Crypto.Store.CMS import Crypto.Store.PKCS12 import Crypto.Store.PKCS5 @@ -26,10 +31,32 @@ arbitraryAlias = resize 16 asciiChar   where asciiChar = listOf $ choose ('\x20','\x7f') -arbitraryIntegrityParams :: Gen IntegrityParams-arbitraryIntegrityParams = (,) <$> arbitraryIntegrityDigest <*> arbitrary+instance Arbitrary PBMAC1Parameter where+    arbitrary = PBMAC1Parameter <$> arbitrary <*> arbitrary -arbitraryPKCS12 :: Password -> Gen PKCS12+arbitraryAuthScheme :: Gen AuthenticationScheme+arbitraryAuthScheme = PBMAC1 <$> arbitrary++arbitraryIntegrityDigest :: Gen DigestAlgorithm+arbitraryIntegrityDigest = elements+    [ DigestAlgorithm MD2+    , DigestAlgorithm MD4+    , DigestAlgorithm MD5+    , DigestAlgorithm SHA1+    , DigestAlgorithm SHA224+    , DigestAlgorithm SHA256+    , DigestAlgorithm SHA384+    , DigestAlgorithm SHA512+    ]++arbitraryTraditionalIntegrity :: Gen IntegrityParams+arbitraryTraditionalIntegrity =+    TraditionalIntegrity <$> arbitraryIntegrityDigest <*> arbitrary++arbitraryAuthSchemeIntegrity :: Gen IntegrityParams+arbitraryAuthSchemeIntegrity = AuthSchemeIntegrity <$> arbitraryAuthScheme++arbitraryPKCS12 :: ProtectionPassword -> Gen PKCS12 arbitraryPKCS12 pwd = do     p <- one     ps <- listOf one
tests/PKCS12/Tests.hs view
@@ -6,6 +6,7 @@ import Data.PEM (pemContent) import Data.String (fromString) +import Crypto.Store.Error import Crypto.Store.PKCS12 import Crypto.Store.PKCS8 import Crypto.Store.X509 (readSignedObject)@@ -38,13 +39,12 @@         let r = readP12FileFromMemory (pemContent pem)          assertRight r $ \integrity ->-            assertRight (recover pwd integrity) $ \privacy ->-                assertRight (recover pwd $ unPKCS12 privacy) $ \scs -> do+            assertRight (recoverAuthenticated pwd integrity) $ \(ppwd, privacy) ->+                assertRight (recover ppwd $ unPKCS12 privacy) $ \scs -> do                     step ("Testing " ++ name)-                    recover pwd (getAllSafeKeys scs) @?= Right [key]+                    recover ppwd (getAllSafeKeys scs) @?= Right [key]                     getAllSafeX509Certs scs @?= certs   where-    pwd :: Password     pwd = fromString "dontchangeme"      nameIntegrity n = "integrity with " ++ n@@ -60,6 +60,8 @@     integrityModes = [ "SHA-1"                      , "SHA-256"                      , "SHA-384"+                     , "PBMAC1 with SHA-256"+                     , "PBMAC1 with SHA-512"                      ]      privacyModes   = [ "aes-128-cbc"@@ -67,33 +69,105 @@                      , "PBE-SHA1-RC2-40"                      ] +testEmptyPassword :: TestTree+testEmptyPassword = testCaseSteps "empty password" $ \step -> do+    step "Reading PKCS #12 files"+    pems <- readPEMs path+    length pems @?= length infos++    forM_ (zip infos pems) $ \((name, numKeys, numCerts), pem) -> do+        let r = readP12FileFromMemory (pemContent pem)++        assertRight r $ \integrity ->+            assertRight (recoverAuthenticated pwd integrity) $ \(ppwd, privacy) ->+                assertRight (recover ppwd $ unPKCS12 privacy) $ \scs -> do+                    step ("Testing " ++ name)+                    assertRight (recover ppwd $ getAllSafeKeys scs) $ \keys ->+                        length keys @?= numKeys+                    length (getAllSafeX509Certs scs) @?= numCerts+  where+    pwd = fromString ""++    path  = testFile "pkcs12-empty-password.pem"+    infos = [ ("Windows Certificate Export Wizard", 1, 2)+            , ("OpenSSL", 1, 1)+            , ("GnuTLS with --empty-password", 1, 1)+            , ("GnuTLS with --null-password", 1, 1)+            ]++testRfc9579 :: TestTree+testRfc9579 = testCaseSteps "rfc9579" $ \step -> do+    step "Reading PKCS #12 files"+    pems <- readPEMs path+    length pems @?= length infos++    forM_ (zip infos pems) $ \((name, integrityError), pem) -> do+        let r = readP12FileFromMemory (pemContent pem)++        assertRight r $ \integrity -> case integrityError of+            Nothing ->+                assertRight (recoverAuthenticated pwd integrity) $ \(ppwd, privacy) ->+                    assertRight (recover ppwd $ unPKCS12 privacy) $ \scs -> do+                        step ("Testing " ++ name)+                        assertRight (recover ppwd $ getAllSafeKeys scs) $ \keys ->+                            length keys @?= 1+                        length (getAllSafeX509Certs scs) @?= 1+            Just expectedErr ->+                assertLeft (recoverAuthenticated pwd integrity) $ \err -> do+                    err @?= expectedErr+                    step ("Testing " ++ name)+  where+    pwd = fromString "1234"++    path  = testFile "pkcs12-rfc9579.pem"+    infos = [ ("SHA-256 HMAC and PRF", Nothing)+            , ("SHA-256 HMAC and SHA-512 PRF", Nothing)+            , ("SHA-512 HMAC and PRF", Nothing)+            , ("Incorrect Iteration Count", Just BadContentMAC)+            , ("Incorrect Salt", Just BadContentMAC)+            , ("Missing Key Length",+                Just $ InvalidParameter "KDF key length must be explicit")+            ]+ propertyTests :: TestTree propertyTests = localOption (QuickCheckMaxSize 5) $ testGroup "properties"     [ testProperty "marshalling" $ do-        pE <- arbitraryPassword+        pE <- arbitrary         c <- arbitraryPKCS12 pE         let r = readP12FileFromMemory $ writeUnprotectedP12FileToMemory c-        return $ Right (Right c) === (recover (fromString "not-used") <$> r)-    , testProperty "marshalling with authentication" $ do-        params <- arbitraryIntegrityParams-        c <- arbitraryPassword >>= arbitraryPKCS12-        pI <- arbitraryPassword+            unused = fromString "not-used"+        return $ Right (Right c) === (fmap snd . recoverAuthenticated unused <$> r)+    , testProperty "marshalling with traditional authentication" $ do+        params <- arbitraryTraditionalIntegrity+        c <- arbitrary >>= arbitraryPKCS12+        pI <- arbitrary         let r = readP12FileFromMemory <$> writeP12FileToMemory params pI c-        return $ Right (Right (Right c)) === (fmap (recover pI) <$> r)+            p = fromProtectionPassword pI+        return $ Right (Right (Right (pI, c))) === (fmap (recoverAuthenticated p) <$> r)+    , testProperty "marshalling with authentication scheme" $ do+        params <- arbitraryAuthSchemeIntegrity+        c <- arbitrary >>= arbitraryPKCS12+        pI <- arbitrary `suchThat` (/= emptyNotTerminated)+        let r = readP12FileFromMemory <$> writeP12FileToMemory params pI c+            p = fromProtectionPassword pI+        return $ Right (Right (Right (pI, c))) === (fmap (recoverAuthenticated p) <$> r)     , localOption (QuickCheckTests 20) $ testProperty "converting credentials" $         \pChain pKey privKey ->+            supportedAsEncodedPubKey privKey ==>             testCredConv privKey toCredential (fromCredential pChain pKey)     , localOption (QuickCheckTests 20) $ testProperty "converting named credentials" $-        \pChain pKey privKey -> do-            name <- arbitraryAlias-            testCredConv privKey-                (toNamedCredential name)-                (fromNamedCredential name pChain pKey)+        \pChain pKey privKey ->+            supportedAsEncodedPubKey privKey ==> do+                name <- arbitraryAlias+                testCredConv privKey+                    (toNamedCredential name)+                    (fromNamedCredential name pChain pKey)     ]   where     testCredConv privKey to from = do-        pwd <- arbitraryPassword-        chain <- arbitrary >>= arbitraryCertificateChain+        pwd <- arbitrary+        let pub = keyPairToPubKey (keyPairFromPrivKey privKey)+        chain <- arbitraryCertificateChain pub         chain' <- shuffleCertificateChain chain         let cred = (chain, privKey)             r = from pwd (chain', privKey) >>= recover pwd . to@@ -104,5 +178,7 @@     testGroup "PKCS12"         [ testType "RSA"                        "rsa"         , testType "Ed25519"                    "ed25519"+        , testEmptyPassword+        , testRfc9579         , propertyTests         ]
tests/PKCS8/Instances.hs view
@@ -15,6 +15,11 @@  import CMS.Instances +instance Arbitrary ProtectionPassword where+    arbitrary = oneof [ return emptyNotTerminated+                      , toProtectionPassword <$> arbitraryPassword+                      ]+ instance Arbitrary PBEParameter where     arbitrary = do         salt <- generateSalt 8@@ -35,14 +40,18 @@                       , PBE_SHA1_RC2_40 <$> arbitrary                       ] +instance Arbitrary KeyPair where+    arbitrary = keyPairFromPrivKey <$> arbitrary+ instance Arbitrary PrivateKeyFormat where     arbitrary = elements [ TraditionalFormat, PKCS8Format ] -instance Arbitrary (FormattedKey PrivKey) where+instance Arbitrary (FormattedKey KeyPair) where     arbitrary = do-        key <- arbitrary+        pair <- arbitrary+        let key = keyPairToPrivKey pair         fmt <- if pkcs8only key then return PKCS8Format else arbitrary-        return (FormattedKey fmt key)+        return (FormattedKey fmt pair)  pkcs8only :: PrivKey -> Bool pkcs8only (PrivKeyX25519  _)   = True
tests/PKCS8/Tests.hs view
@@ -1,4 +1,5 @@ -- | PKCS #8 tests.+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} module PKCS8.Tests (pkcs8Tests) where  import qualified Data.ByteString as B@@ -11,10 +12,12 @@ import Test.Tasty.QuickCheck  import Util-import PKCS8.Instances+import PKCS8.Instances () -keyTests :: String -> TestTree-keyTests prefix =+data KeyTestType = InnerOuter | OnlyOuter++keyTests :: KeyTestType -> String -> TestTree+keyTests InnerOuter prefix =     testGroup "PrivateKey"         [ testCase "read outer" $ do               kOuter <- readKeyFile fOuter@@ -43,6 +46,19 @@   where     fInner = testFile (prefix ++ "-unencrypted-trad.pem")     fOuter = testFile (prefix ++ "-unencrypted-pkcs8.pem")+keyTests OnlyOuter prefix =+    testGroup "PrivateKey"+        [ testCase "read" $ do+              kOuter <- readKeyFile fOuter+              length kOuter @?= 1+        , testCase "write" $ do+              bs <- B.readFile fOuter+              let kOuter = readKeyFileFromMemory bs+                  [Unprotected kO] = kOuter+              writeKeyFileToMemory PKCS8Format [kO] @?= bs+        ]+  where+    fOuter = testFile (prefix ++ "-unencrypted-pkcs8.pem")  encryptedKeyTests :: String -> TestTree encryptedKeyTests prefix =@@ -72,20 +88,26 @@                            in all (\(Protected getKey) -> getKey pwd == Right key) kE                 ] -testType :: TestName -> String -> TestTree-testType name prefix =+testType :: TestName -> KeyTestType -> String -> TestTree+testType name ty prefix =     testGroup name-        [ keyTests prefix+        [ keyTests ty prefix         , encryptedKeyTests prefix         ] +rfc8410Tests :: TestTree+rfc8410Tests = testCase "rfc8410" $ do+    keys <- readKeyFile path+    length keys @?= 2+  where path = testFile "rfc8410.pem"+ propertyTests :: TestTree propertyTests = localOption (QuickCheckMaxSize 5) $ testGroup "properties"     [ testProperty "marshalling" $ \fmt l ->         let r = readKeyFileFromMemory $ writeKeyFileToMemory fmt l         in map Right l === map (recover $ fromString "not-used") r     , testProperty "marshalling with encryption" $ \es k -> do-        p <- arbitraryPassword+        p <- arbitrary         let r = readKeyFileFromMemory <$> writeEncryptedKeyFileToMemory es p k         return $ Right [Right k] === (map (recover p) <$> r)     ]@@ -93,13 +115,14 @@ pkcs8Tests :: TestTree pkcs8Tests =     testGroup "PKCS8"-        [ testType "RSA"                        "rsa"-        , 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"+        [ testType "RSA"                        InnerOuter  "rsa"+        , testType "DSA"                        InnerOuter  "dsa"+        , testType "EC (named curve)"           InnerOuter  "ecdsa-p256"+        , testType "EC (explicit prime curve)"  InnerOuter  "ecdsa-epc"+        , testType "X25519"                     OnlyOuter   "x25519"+        , testType "X448"                       OnlyOuter   "x448"+        , testType "Ed25519"                    OnlyOuter   "ed25519"+        , testType "Ed448"                      OnlyOuter   "ed448"+        , rfc8410Tests         , propertyTests         ]
tests/Util.hs view
@@ -2,18 +2,19 @@ -- | Test utilities. module Util     ( assertJust+    , assertLeft     , assertRight     , getAttached     , getDetached     , testFile     , TestKey(..)-    , TestIV(..)+    , TestIV, unIV     ) where  import Control.Monad (when)  import Data.ByteString (ByteString, pack)-import Data.Maybe (isNothing)+import Data.Maybe (fromJust, isNothing)  import Crypto.Cipher.Types import Crypto.Store.CMS@@ -25,6 +26,11 @@ assertJust (Just a) f = f a assertJust Nothing  _ = assertFailure "expecting Just but got Nothing" +assertLeft :: Show b => Either a b -> (a -> Assertion) -> Assertion+assertLeft (Left a)  f = f a+assertLeft (Right val) _ =+    assertFailure ("expecting Left but got: Right " ++ show val)+ assertRight :: Show a => Either a b -> (b -> Assertion) -> Assertion assertRight (Right b)  f = f b assertRight (Left val) _ =@@ -35,14 +41,14 @@     let m = fromAttached e     when (isNothing m) $         assertFailure "expecting attached but got detached content"-    let Just r = m in return r+    return $! fromJust m  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+    return $! fromJust m  testFile :: String -> FilePath testFile name = "tests/files/" ++ name@@ -58,6 +64,9 @@       where cipher = undefined :: cipher  newtype TestIV cipher = IV ByteString deriving (Show, Eq)++unIV :: BlockCipher cipher => TestIV cipher -> IV cipher+unIV (IV ivBS) = fromJust $ makeIV ivBS  instance BlockCipher cipher => Arbitrary (TestIV cipher) where     arbitrary = IV . pack <$> vector (blockSize cipher)
tests/X509/Instances.hs view
@@ -4,7 +4,7 @@     ( arbitraryOID     , arbitraryRSA     , arbitraryLargeRSA-    , arbitraryDSA+    , arbitraryPrivDSA     , arbitraryNamedEC     , arbitraryX25519     , arbitraryX448@@ -13,6 +13,7 @@     , arbitrarySignedCertificate     , arbitraryCertificateChain     , shuffleCertificateChain+    , supportedAsEncodedPubKey     ) where  import           Control.Monad (zipWithM)@@ -58,7 +59,7 @@  instance Arbitrary PubKey where     arbitrary = oneof [ PubKeyRSA . fst <$> arbitraryRSA-                      , PubKeyDSA . fst <$> arbitraryDSA+                      , PubKeyDSA <$> arbitraryPubDSA                       , PubKeyEC . fst  <$> arbitraryNamedEC                       --, PubKeyEC . fst  <$> arbitraryExplicitPrimeCurve                       , PubKeyX25519 . fst <$> arbitraryX25519@@ -69,7 +70,7 @@  instance Arbitrary PrivKey where     arbitrary = oneof [ PrivKeyRSA . snd <$> arbitraryRSA-                      , PrivKeyDSA . snd <$> arbitraryDSA+                      , PrivKeyDSA <$> arbitraryPrivDSA                       , PrivKeyEC . snd  <$> arbitraryNamedEC                       , PrivKeyEC . snd  <$> arbitraryExplicitPrimeCurve                       , PrivKeyX25519 . snd <$> arbitraryX25519@@ -78,6 +79,12 @@                       , PrivKeyEd448 . snd <$> arbitraryEd448                       ] +-- package 'x509' has not implemented the serialization of PrivKeyEC_Prime+-- so we cannot use it in a certificate+supportedAsEncodedPubKey :: PrivKey -> Bool+supportedAsEncodedPubKey (PrivKeyEC PrivKeyEC_Prime{}) = False+supportedAsEncodedPubKey _ = True+ arbitraryRSA :: Gen (RSA.PublicKey, RSA.PrivateKey) arbitraryRSA = do     n <- elements [ 768, 1024 ]     -- enough bits to sign with SHA-512@@ -90,13 +97,11 @@     e <- elements [ 3, 0x10001 ]     RSA.generate (n `div` 8) e -arbitraryDSA :: Gen (DSA.PublicKey, DSA.PrivateKey)+arbitraryDSA :: Gen DSA.KeyPair arbitraryDSA = do     x <- DSA.generatePrivate params     let y = DSA.calculatePublic params x-        priv = DSA.PrivateKey { DSA.private_params = params, DSA.private_x = x }-        pub = DSA.PublicKey { DSA.public_params = params, DSA.public_y = y }-    return (pub, priv)+    return (DSA.KeyPair params y x)   where     -- DSA parameters were generated using 'openssl dsaparam -C 2048'     params = DSA.Params@@ -104,6 +109,12 @@         , DSA.params_g = 0x10E51AEA37880C5E52DD477ED599D55050C47012D038B9E4B3199C9DE9A5B873B1ABC8B954F26AFEA6C028BCE1783CFE19A88C64E4ED6BFD638802A78457A5C25ABEA98BE9C6EF18A95504C324315EABE7C1EA50E754591E3EFD3D33D4AE47F82F8978ABC871C135133767ACC60683F065430C749C43893D73596B12D5835A78778D0140B2F63B32A5658308DD5BA6BBC49CF6692929FA6A966419404F9A2C216860E3F339EDDB49AD32C294BDB4C9C6BB0D1CC7B691C65968C3A0A5106291CD3810147C8A16B4BFE22968AD9D3890733F4AA9ACD8687A5B981653A4B1824004639956E8C1EDAF31A8224191E8ABD645D2901F5B164B4B93F98039A6EAEC6088         , DSA.params_q = 0xE1FDFADD32F46B5035EEB3DB81F9974FBCA69BE2223E62FCA8C77989B2AACDF7         }++arbitraryPrivDSA :: Gen DSA.PrivateKey+arbitraryPrivDSA = DSA.toPrivateKey <$> arbitraryDSA++arbitraryPubDSA :: Gen DSA.PublicKey+arbitraryPubDSA = DSA.toPublicKey <$> arbitraryDSA  arbitraryNamedEC :: Gen (PubKeyEC, PrivKeyEC) arbitraryNamedEC = do
tests/X509/Tests.hs view
@@ -1,4 +1,5 @@ -- | X.509 tests.+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} module X509.Tests (x509Tests) where  import qualified Data.ByteString as B@@ -35,7 +36,7 @@               writeSignedObjectToMemory objs @?= bs         , testCase "write public key" $ do               bs <- B.readFile fKey-              let key = head (readPubKeyFileFromMemory bs)+              let (key : _) = readPubKeyFileFromMemory bs               assertBool "first key differs" $                   writePubKeyFileToMemory [key] `B.isPrefixOf` bs         ]@@ -43,6 +44,12 @@     fCert = testFile (prefix ++ "-self-signed-cert.pem")     fKey  = testFile (prefix ++ "-public.pem") +rfc8410Tests :: TestTree+rfc8410Tests = testCase "rfc8410" $ do+    keys <- readPubKeyFile path+    length keys @?= 1+  where path = testFile "rfc8410.pem"+ propertyTests :: TestTree propertyTests = localOption (QuickCheckMaxSize 5) $ testGroup "properties"     [ testProperty "marshalling public keys" $ \keys ->@@ -70,5 +77,6 @@         , keyTests "X448"                       "x448"       1         , keyTests "Ed25519"                    "ed25519"    1         , keyTests "Ed448"                      "ed448"      1+        , rfc8410Tests         , propertyTests         ]
− tests/files/cms-auth-enveloped-data-rfc6476.pem
@@ -1,15 +0,0 @@------BEGIN PKCS7------MIHjBgsqhkiG9w0BCRABF6CB0zCB0AIBADFho18CAQCgGwYJKoZIhvcNAQUMMA4E-CLfrI6dr0gUWAgITiDAjBgsqhkiG9w0BCRADCTAUBggqhkiG9w0DBwQIZpECRWtz-u5kEGDCjerXY8odQ7EEEromZJvAurk/j81IrozBSBgkqhkiG9w0BBwEwMwYLKoZI-hvcNAQkQAw8wJDAUBggqhkiG9w0DBwQI0tCBcU09nxEwDAYIKwYBBQUIAQIFAIAQ-OsYGYUFdAH0RNc1p4VbKEAQUM2Xo8PMHBoYdqEcsbTodlCFAZH4=------END PKCS7-----------BEGIN PKCS7------MIH9BgsqhkiG9w0BCRABF6CB7TCB6gIBADFyo3ACAQCgGwYJKoZIhvcNAQUMMA4E-COe3h9+CHRLMAgITiDAsBgsqhkiG9w0BCRADCTAdBglghkgBZQMEAQIEEBHZXFIK-Or8isjBw7/R9bvYEIBg5IifDwiwqpp8qsHckdarYWJzNu0yu0w3Cyx2DlGw3MFsG-CSqGSIb3DQEHATA8BgsqhkiG9w0BCRADDzAtMB0GCWCGSAFlAwQBAgQQtyUCdoQ8-WBulMOJAJ+7DBjAMBggrBgEFBQgBAgUAgBCYNg8MeWI2tS0tnhxihR4QBBSIpMGy-ungbyvkUsOX80Y34AuKyng==------END PKCS7-----
+ tests/files/cms-auth-enveloped-kari-data.pem view
@@ -0,0 +1,135 @@+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBwaGBvgIBA6BRoU8wCQYHKoZIzj0CAQNC+AARLFBy9/eI4x8mR5wpZ774i/LeS8/FzuGegHdAo19bTdzUlv9kd69/0GzRF5ntH+27ueue5YBRmFOKSRnLiU2mq3MBgGCSuBBRCGSD8AAjALBglghkgBZQMEAQUwTDBK+MC4wITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4A+BBjXV1HlxJT+Z8o6n5HC4RNRe7j4sP48HpIwgAYJKoZIhvcNAQcBMB4GCWCGSAFl+AwQBBjARBAypQxH70WFEpg8UWToCARCggAQO6V6dHzqpE6FXmRa7arIAAAAABBCl+bbL/+bic+x2r79kZpLsSAAAAAAAA+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBvqGBuwIBA6BRoU8wCQYHKoZIzj0CAQNC+AASJYlJVbYCr1BOjZpCH5hjLV/2X23Xc0Z5eTI8FBqUXlYHH7d1S/jEGJDfCcvIx+TV9swOSRFK92I9kSVd9XCN1eMBUGBiuBBAELADALBglghkgBZQMEAQUwTDBKMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABBgE+Xp5V6/AbK9bFcwjRJ3fm20YWgQ2nBowwgAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQB+BjARBAyFVoCkD/eJA9YqjI8CARCggAQOpQwAlrBtnityfLYqU2sAAAAABBDLM+53+Uk0Pn3Y7MZ+Mbn5eAAAAAAAA+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBvqGBuwIBA6BRoU8wCQYHKoZIzj0CAQNC+AASufl1PpqFUZDuTCXzKMaGerIO5843VUFSwBudjyvovjAHTcGwPJjvS+3EH4NEn+7o/W7P09hwTSZ/rsBbBJPu+QMBUGBiuBBAELATALBglghkgBZQMEAQUwTDBKMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABBie+ykbOXeqVMmbDWw3Ehn/QTjMlNs1uCL4wgAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQB+BjARBAxEc1PrzckaDXBkWjgCARCggAQOuyUnmFAV/xahqSadwG8AAAAABBBi++VJ+NnU5UVDQ6q1zMRqfAAAAAAAA+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBvqGBuwIBA6BRoU8wCQYHKoZIzj0CAQNC+AATX8MYoU25w7qYU/CFl+JZdFQ1ePZP2yL+FUJeV76Etegb+eeTl0dG70MIpVP3c+uABRryMgV3jAniwgDnLDcLCrMBUGBiuBBAELAjALBglghkgBZQMEAQUwTDBKMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABBhe+7wfppjJXy8UnxeR3KYGpjqhss/Vdb/4wgAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQB+BjARBAyS8HZXcf5oDpq7XSYCARCggAQON5pXJ2YeE3u0Tu/JggsAAAAABBCDUZMb+f9iObxa0nURyr2uNAAAAAAAA+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBvqGBuwIBA6BRoU8wCQYHKoZIzj0CAQNC+AAQ50l98gMx9YTsLyif3Aj6ypa+8auXV415+hA7bgI5aXrY+20tvGbUheWAdCUM/+DVLWcYhsWEwTEmL6Z5dMrQDCMBUGBiuBBAELAzALBglghkgBZQMEAQUwTDBKMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABBjI+vXtw3gmmAn/yORO1a/NTTGqgmpOQq+swgAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQB+BjARBAw/RTlPW3aoaAWpb4ACARCggAQO7kbgLubm2M+hoanfMdkAAAAABBAeQyvO+tOT7PWNiqKaFTu9HAAAAAAAA+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByaGBxgIBA6BRoU8wCQYHKoZIzj0CAQNC+AAS/VBtw4zaTtkAb45PJ8cuAYshMXy/F1lKa3ZA4E6u3np+dfQ+kBT5VrxpegvFj+Q/NdOxV4CTatDnNo8iH5h2d4MBgGCSuBBRCGSD8AAjALBglghkgBZQMEARkwVDBS+MC4wITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4A+BCB5X3DkEOElcCL2KEOiVJKcQpTqQWynlMyWeLe805y4YDCABgkqhkiG9w0BBwEw+HgYJYIZIAWUDBAEaMBEEDPX7veh9yNKL511dCwIBEKCABA5/r2CzelEUDNFaJtk8+jQAAAAAEEOWvi2b2dguSk94AjnrFBSAAAAAAAAA=+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBxqGBwwIBA6BRoU8wCQYHKoZIzj0CAQNC+AAQA3+RNupbspd7hUIQg7vXkrzB01ofr/7s7WuASl0OdzgJ3mBt3a5xtW7IqTE+B+3Vw9J4mkAa9PLi3Iq9SEkZ5aMBUGBiuBBAELADALBglghkgBZQMEARkwVDBSMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCDl+4XWpLmEPvdcqjhnf7xp8sDnDIVZ/eKXxzQ8YU7+avTCABgkqhkiG9w0BBwEwHgYJ+YIZIAWUDBAEaMBEEDIKxljwkGp/uATZNuQIBEKCABA4JT8FfOkbtIUd9TPivOgAA+AAAEEOTGxEy5JLrS0+nAg5HtSDEAAAAAAAA=+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBxqGBwwIBA6BRoU8wCQYHKoZIzj0CAQNC+AARJpGAQMIpwZgHJpg2o1wmxGYoNcgtHEhedsOFLpQdou3G69y0eDgX7nB0tvRt7+oYZEbgLPk7f4AECFVWSxnby9MBUGBiuBBAELATALBglghkgBZQMEARkwVDBSMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCD0+HNfAnxgaNrlr1hC6M6O34Vyq1ZZ7MAInchQ6QjW8/TCABgkqhkiG9w0BBwEwHgYJ+YIZIAWUDBAEaMBEEDDj5sfWnIjCmTf1y9wIBEKCABA5k60PO+CFkHN/evabZmwAA+AAAEEJCwak43a0Ga4riBRKY9CNsAAAAAAAA=+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBxqGBwwIBA6BRoU8wCQYHKoZIzj0CAQNC+AAQj7fNzGeluKhvEL1t7QM9hnKiNItc4UEkHFzz3WJqepjx14l057XMAQoiyut9G++W+Ddqx+ozVQOxu1RO5BHEY/MBUGBiuBBAELAjALBglghkgBZQMEARkwVDBSMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCBO+XDmcoiudX6iCODyKyf+kj4ahTrVfH/kWYbocdvgrCzCABgkqhkiG9w0BBwEwHgYJ+YIZIAWUDBAEaMBEEDL9ZV62lILi166Kr/wIBEKCABA7ayB6TLDk4NOQYpj6kvAAA+AAAEEOiMHVSsVG7a7WcQp7DQmcgAAAAAAAA=+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBxqGBwwIBA6BRoU8wCQYHKoZIzj0CAQNC+AAQnuS9ANZe6b7GWHi5qQQozrTubhuGToeFZAdQtiz95z5OrgWkmiX6ERkwaN1xB+06M7crT9vuG0pX4GoboI4p4nMBUGBiuBBAELAzALBglghkgBZQMEARkwVDBSMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCA0+PNagjdeo/KS1G+Y/lqmh3Rvz0T4rIVYChhNdvZWxbzCABgkqhkiG9w0BBwEwHgYJ+YIZIAWUDBAEaMBEEDInQ2rexl6MGVX7tsgIBEKCABA4yHXaDnnVGt09Ctn67lgAA+AAAEEGUG2rPiuRtGhTarSuS64uQAAAAAAAA=+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGB0aGBzgIBA6BRoU8wCQYHKoZIzj0CAQNC+AARnR8BsuxfZyGI8EufwUGDG+XuPF/1XzhhogH1DpHQy9XON4DUyLvlkWP+voIwE+uMApKF5ESL1IL8T6UnzoN55AMBgGCSuBBRCGSD8AAjALBglghkgBZQMEAS0wXDBa+MC4wITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4A+BCgetPskb6M89OJ/Q6C9WwJYZrbT9Upvvktf8EHiRPWPri4BWOc8hcn8MIAGCSqG+SIb3DQEHATAeBglghkgBZQMEAS4wEQQMxjZlfwz1CXn6KQBeAgEQoIAEDq6U8zcr+EcjV5QDm7KA0AAAAAAQQruuKaBMv3S7igAIPPQp5jwAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBzqGBywIBA6BRoU8wCQYHKoZIzj0CAQNC+AAQEtpMiE4i41ln4lBfxorG/Mz7ENYFhI3FFjzvphl+/n58x3AhZHsKdJQfiBgwQ+NbRjVoSg6gqA1NgwlJfMdC5yMBUGBiuBBAELADALBglghkgBZQMEAS0wXDBaMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCie+fGtGheoPu9MlqzfxIkshaxv58mwcvnrO7Zjztbr/Y8iu+mQc6UVHMIAGCSqGSIb3+DQEHATAeBglghkgBZQMEAS4wEQQMRnxKZZFpluuCvzMUAgEQoIAEDgbHkpLneCpj+J3kFkjUHAAAAAAQQiRN5pWovrd3DmXkxUfScsgAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBzqGBywIBA6BRoU8wCQYHKoZIzj0CAQNC+AARK9880f2VoFvmoN8xEHSn68EplcakaakOQgEqIJqYhQtk+jzWcONqFH13Tydf0+twC6i18Ip33oM4qNO5baDw+/MBUGBiuBBAELATALBglghkgBZQMEAS0wXDBaMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCgU+SlctsM4RmvAM189y5SF74X37M5v7amwj7Q2NxcwdqYq/T2Spvjh0MIAGCSqGSIb3+DQEHATAeBglghkgBZQMEAS4wEQQMMaNatdENR6VvbGKnAgEQoIAEDmuLMPNqEAZV+4G5072SbAAAAAAQQZil2/HiSIzjZEhj2xQjSCgAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBzqGBywIBA6BRoU8wCQYHKoZIzj0CAQNC+AASbBeETneRZ79Q5cGBu17gZFZM++ZWzsMvcmx6Z1gYztIKYWtndW+INxVvrln4D+Of8maq6/IfumffkvZpfqBnKPMBUGBiuBBAELAjALBglghkgBZQMEAS0wXDBaMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABCho+nDmmA4NRBHpBGIGI0SwDPTw6CO1Adwjz6uhtcKd99duC7o50kQZgMIAGCSqGSIb3+DQEHATAeBglghkgBZQMEAS4wEQQMJjonwKRKw6ThEDRlAgEQoIAEDs/bEIQQtKP9+EZOmpMsFAAAAAAQQxCZqg6Ec63VcVAJgsVofaAAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGBzqGBywIBA6BRoU8wCQYHKoZIzj0CAQNC+AAT0yC7d8EmBaHZecCKf8SOdDqm/o2CJteAk7ICcqbMjBhJc93ckpyE4o1Z/gB1Y+1zwgerWFeaZXfObcRxh2CxZGMBUGBiuBBAELAzALBglghkgBZQMEAS0wXDBaMC4w+ITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJAJf6ZvHS4D4ABChH+RQJ5OmptF0ap3vq7lknTscDMjwld7FhuDlxurwXb61f/Ubv8RsGAMIAGCSqGSIb3+DQEHATAeBglghkgBZQMEAS4wEQQMEy1/SaOMEMJCYYB/AgEQoIAEDiDXAvTAGTTP+4RN37jTOAAAAAAQQEJHRPVxSAJBdZ//YdeM1LQAAAAAAAA==+-----END CMS-----
+ tests/files/cms-auth-enveloped-kekri-data.pem view
@@ -0,0 +1,18 @@+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADExoi8CAQQwAwQBMDALBglghkgBZQMEAQUE+GKdm3FAAb+cJzRlmBNrIxZJR/lO7Hl9I9TCABgkqhkiG9w0BBwEwHgYJYIZIAWUD+BAEGMBEEDBy96EqGGAX5GsRkvgIBEKCABA4/POyH35OvITLf81WHwQAAAAAEEKuM+jJcreoQAUFYD+Y3HRCkAAAAAAAA=+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADE5ojcCAQQwAwQBMDALBglghkgBZQMEARkE+IKEh8evpAq55aqYZa5EcwTXkGGFsx9Xljf5YWzLOVzPJMIAGCSqGSIb3DQEHATAe+BglghkgBZQMEARowEQQM2qmxxSb4nQshdsBZAgEQoIAEDj/eccFTdRRtFS59ge54+AAAAAAQQlv+TpgF6XXZsjdVzg5uV7wAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADFBoj8CAQQwAwQBMDALBglghkgBZQMEAS0E+KCp5RYkgCzU42HF7XXOaFQOEsk9bUFFxgV9NDlyVAo+2RrIPgVXXkMEwgAYJKoZI+hvcNAQcBMB4GCWCGSAFlAwQBLjARBAx/18iNa9gajkGa3qYCARCggAQOZ0Hn+848+qApi912KpsQAAAAABBBpz1K5ojGHJznVxTUAWfD9AAAAAAAA+-----END CMS-----
+ tests/files/cms-auth-enveloped-ktri-data.pem view
@@ -0,0 +1,54 @@+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByDCBxQIBADAuMCExHzAdBgkqhkiG9w0B+CQEWEHRlc3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzANBgkqhkiG9w0BAQEFAASB+gA/maH0B5EK0DLU5rfbTGet4uBklSW9TkEfAskjNJ6C1KfePfy20oNfnsKMS5Mp1+q/3Bz77LgH8+TWBZ5LKbgOT6AiMpWlUuUqzuchxmC9nx5P/ew0WJPQME5igMaV0z+1rstr7dMZtIMa8aTQMzJ+LgguRNOYBuBLvB1w1vaxrc+MIAGCSqGSIb3DQEHATAe+BglghkgBZQMEAQYwEQQMlYZ0LFrSPPCSCobkAgEQoIAEDpNaDCywbuuvs15vS6S7+AAAAAAQQGLodMaocucmqaveg0fT5xQAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByDCBxQIBADAuMCExHzAdBgkqhkiG9w0B+CQEWEHRlc3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzANBgkqhkiG9w0BAQcwAASB+gFrnvmHQe5ja0uvf6ni5ea0Jkss11cJ62lpyq41AYD52AwaoCC+FFdykgQBxgpO++1Byj0cUzzpxFoV354aZg2xUKu7RrN2ogUlei8TdEctfe5imOHPaD+W94xSipT46++VEXs+k7LsrJMtqBHmNvSZGIz/r6PpJMB0+Q5m1dqJQ8gMIAGCSqGSIb3DQEHATAe+BglghkgBZQMEAQYwEQQM44g5NAyvNtg1f7IhAgEQoIAEDvxH9L2wJAxh02F+yTgv+AAAAAAQQa+rmrc/5NDP5/kB9FDvnkwAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByDCBxQIBADAuMCExHzAdBgkqhkiG9w0B+CQEWEHRlc3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzANBgkqhkiG9w0BAQEFAASB+gBiKzwAxQcgNfC5I/jsSWn559jU2f/ndZE4he3ojQua1HHrVVJUFVbwUC7bYs3Vf+nHjo5AHi0i408asv4l2BgH3JWVkhnaUtHEajnipWkBEF2Bipb5u2i5NpeCMATdnX+vUuUL6UAarWQttcWu+/J+73ZtV0ecrd8ybTIlcOx0QrtMIAGCSqGSIb3DQEHATAe+BglghkgBZQMEARowEQQM+TWtLNHBl3EU+JUdAgEQoIAEDtXaoIj5rEJLWljHXk0e+AAAAAAQQjXRzifuEu8tEWisIQkPk6QAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByDCBxQIBADAuMCExHzAdBgkqhkiG9w0B+CQEWEHRlc3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzANBgkqhkiG9w0BAQcwAASB+gDBMbmVbY3ghH4Pc9N/kDCoYVb6Mp93oUOjLhVpx71Bqxv8xoWXzT4NV2Z3ssE/F+k3RG1Y7LMYfv2I0QVZetuWJIehRBqouY/uW4RM7WWemuLT46M7Qszoi3vMnZ7Tuj+8tbxQUeie4dBO0+MMluNYj4hc/gzaCxsy0nqISG6ERGFMIAGCSqGSIb3DQEHATAe+BglghkgBZQMEARowEQQM8zQfTdCgHG5KfkebAgEQoIAEDoJVmSUFhPA5dLsML5F9+AAAAAAQQE3SXLSthb+rnxYrwohzIvQAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByDCBxQIBADAuMCExHzAdBgkqhkiG9w0B+CQEWEHRlc3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzANBgkqhkiG9w0BAQEFAASB+gIIv6EsKwGSUU0M4EHjI8mgYF1mm32nPIHf4xwYuKcgE9Klu8/Dob2k+Q5705aAc+0mQvGiA8iaguiveGzvXegyuv/LVjl7bz1aTYkXwo22+5J3YstFJPrum+S1FbBdvI+cKPzkUjaJDus1R0j36Zej2wYnTUJNSo4SE1lolcyAUj/MIAGCSqGSIb3DQEHATAe+BglghkgBZQMEAS4wEQQMtwUSL7+j+tyKBlp3AgEQoIAEDp3QpaI+/etoalZIMW+G+AAAAAAQQ99WOo5xvNPX1CYu/2b9LoQAAAAAAAA==+-----END CMS-----+-----BEGIN CMS-----+MIAGCyqGSIb3DQEJEAEXoIAwgAIBADGByDCBxQIBADAuMCExHzAdBgkqhkiG9w0B+CQEWEHRlc3RAZXhhbXBsZS5jb20CCQD3DetZLCk3mzANBgkqhkiG9w0BAQcwAASB+gC5UFYfqMeb1SoHKS3G2dr+ofYwU4+JzVrqCJnE6WKSQI3a50MN63TTpjuiOxnWU+TCzo7QUIFCUms+NIFNR1FGmV58SycQqs/9gQcR0Xp9mnL0LkGte6pIIEnro+dHES+aY+6eRViwFOZ6GO0qOODUgLHfYdotcqnuKEs8oV3IJb1MIAGCSqGSIb3DQEHATAe+BglghkgBZQMEAS4wEQQMnJh28SAHjbT8F2U3AgEQoIAEDkcXXJq6tkKrY3tI5ecW+AAAAAAQQK2+jlHbQwEjmeP0vQxBRZwAAAAAAAA==+-----END CMS-----
tests/files/cms-digested-data.pem view
@@ -25,3 +25,22 @@ EAQOaGVsbG8sIHdvcmxkDQoEQNmRYGENk6X3fR9Sk9k++QSKUPYBvF+nGKHL4FXN QVoNz4kKv9BI4MPnsV77ikuw7kWGR3OFLWO8waCyFK+EEu0= -----END CMS-----+-----BEGIN CMS-----+MFwGCSqGSIb3DQEHBaBPME0CAQAwCwYJYIZIAWUDBAIHMB0GCSqGSIb3DQEHAaAQ+BA5oZWxsbywgd29ybGQNCgQcrHRNG/sKxDCiVbjFsYvAvrXdZNaPFFkUnKb7eg==+-----END CMS-----+-----BEGIN CMS-----+MGAGCSqGSIb3DQEHBaBTMFECAQAwCwYJYIZIAWUDBAIIMB0GCSqGSIb3DQEHAaAQ+BA5oZWxsbywgd29ybGQNCgQgead3JnYtNJOYL50QfPgWkqUtpmuiU9T7itIfOmfa+3l4=+-----END CMS-----+-----BEGIN CMS-----+MHAGCSqGSIb3DQEHBaBjMGECAQAwCwYJYIZIAWUDBAIJMB0GCSqGSIb3DQEHAaAQ+BA5oZWxsbywgd29ybGQNCgQwPvv+XafJtPR2UXRqu2t6l3BuJeoOghvrAfPbbRQ2+cr5IHYmzioNIZ1HlrP6RG0R3+-----END CMS-----+-----BEGIN CMS-----+MIGABgkqhkiG9w0BBwWgczBxAgEAMAsGCWCGSAFlAwQCCjAdBgkqhkiG9w0BBwGg+EAQOaGVsbG8sIHdvcmxkDQoEQHjsLFT6QOVe+FI6fBZhWEIHpXzDX9L4/ARGWEXp+rKVTw6DfjRFICt66Mxxqn4WbRCa/FJXAnVY1PduwTN1Fiqw=+-----END CMS-----
tests/files/ed25519-pkcs12.pem view
@@ -86,6 +86,64 @@ AA== -----END PKCS12----- -----BEGIN PKCS12-----+MIIE6QIBAzCCBGMGCSqGSIb3DQEHAaCCBFQEggRQMIIETDCCAsoGCSqGSIb3DQEH+BqCCArswggK3AgEAMIICsAYJKoZIhvcNAQcBMF8GCSqGSIb3DQEFDTBSMDEGCSqG+SIb3DQEFDDAkBBADZyEJTf+SYDt1cuTOQqVaAgIIADAMBggqhkiG9w0CCQUAMB0G+CWCGSAFlAwQBKgQQxjg/dHyDo8ty5b1MLNsCs4CCAkB15cjQKudM1ywzel7q2pYU+tOU6nBPPd5oblD8fiH+TeOuNb9WXLlPB1Ra5ywqYrbqbPNpFNriigV20orksM1Dk+/BCTXfxgaC4RO1Y3zNANiMCw8rafSpm5fX/tU12tc8kei0D1TcmaR3+i135Nl3xZ+zFXmm0lQ/rPHHYwW8uzAQOT9QVUmkie7P54aCRj99PCpVcv0W5sxqKYmmyfJerqZ+DtY6Q9ZMbe7jiu4bUxKZ06ggfl/UYwmIkT2xjN/EpWm0EO+hVIgIMq09kEkFQyiH+LP37rxOrOQ1dzopztALm3+8o2UiWTm53CwDJx8+0Qvb6eldLNpgUDZcrvjyRUlzF+gaQGq6OmkRxWme1A0xC/vc+fppW75YnFbKdMzDfmbxDwzjYprAiFlk+4bPmSZHuP+/mKx+jYFXubOV/4G4CrO9toHtztJVRm2O48WY8JqFKSCvlW2/PqMry0v4pxHRrKc+N1hWyhIxAYUf8+A4an5XEvuE3rwmFzHU3SlxvpWECVO+qzteY+/pMXEwdJiqSiH3+ICsGaGcnWE+UOSsVkp3hje6MIkGYMec7dakG785Zwwp+zQgZ9vXKa4rmvFg79tnL+VJbehXobf5tBbg8c525Pw4siYNaD6bk2VJbpew2F3OrwslmlhlxRPZEt/oGY+dgK+haRPhAAfVxnFav+fApm2IkR9nbEqomthrCoD5VoIWEXS3IVrvu9RAlqBOpKPp+kZ+cU472Uy+4MKJ5kME2f1b6AyuHzT/KfmItKTB4qdSCHgwggF6BgkqhkiG9w0BBwGg+ggFrBIIBZzCCAWMwggFfBgsqhkiG9w0BDAoBAqCBpjCBozBfBgkqhkiG9w0BBQ0w+UjAxBgkqhkiG9w0BBQwwJAQQA137X5gsqDKTJ08FgEK6mAICCAAwDAYIKoZIhvcN+AgkFADAdBglghkgBZQMEASoEEPkEeCkd5FOqDNhqcFGwngwEQIJ4pEDx6DBFChYf+p+/XCMom7KTB9JwjxJPaYeroUVBEjz1MpOntyJetOjJzruEXGMB/cR4ARJNnS25++PnNJN5oxgaYwIwYJKoZIhvcNAQkVMRYEFBUJzG3CAW0LFRvdr7FoWoQ3wIq6MH8G+CSqGSIb3DQEJFDFyHnAAUABLAEMAUwAxADIAIAAoAGUAZAAyADUANQAxADkAKQAg+AC0AbQBhAGMAYQBsAGcAIABzAGgAYQAyADUANgAgAC0AcABiAG0AYQBjADEAXwBw+AGIAawBkAGYAMgBfAG0AZAAgAHMAaABhADIANQA2MH0wbTBJBgkqhkiG9w0BBQ4w+PDAsBgkqhkiG9w0BBQwwHwQIH6958Xey3xgCAggAAgEgMAwGCCqGSIb3DQIJBQAw+DAYIKoZIhvcNAgkFAAQgGHJF7GEkuv9ZXtkHlH9yDHwFRjTYsH0peoVdnVVTScME+CB+vefF3st8YAgIIAA==+-----END PKCS12-----+-----BEGIN PKCS12-----+MIIFCwIBAzCCBGMGCSqGSIb3DQEHAaCCBFQEggRQMIIETDCCAsoGCSqGSIb3DQEH+BqCCArswggK3AgEAMIICsAYJKoZIhvcNAQcBMF8GCSqGSIb3DQEFDTBSMDEGCSqG+SIb3DQEFDDAkBBBBRdQTNqGIKpGPW0+w4J0FAgIIADAMBggqhkiG9w0CCQUAMB0G+CWCGSAFlAwQBKgQQ0KQc53YHlFcirdn9bulXVoCCAkCGp/FB1JeyqO5yQqM3wRrd+CBLA+0Cg8u64CoagWGmjfYsGWiobeaUOnFc62GORJNH/vozKfU70aYRbGw0iO7La+VKzl+VPt9m5e2jC9QUVhtE/kH0jpx0fuYK68DWMklDHrLpSFHfNbZAwFzOcqojaG+oknhWAVYDrIkjFGcWWBDbHHeSQA+kEisjrnUIL7cUavu4OQVxveXNQ0DOFlnfOc6+HOZ2+9w4MvjbJUKo9cuqVPzISyLvp4jp2LGOdTOM2OCRWlZh8SelmKwfSJTKhGhx+xPMOeN+Pg7MeVJvUWOdGEmQdGGti2cBX5ysORBcjgsh1cS+tVYCPwxP1RZVIAfac+KitW8q4Q3E/RncomQs8R0lLi26kadPGj0OUoAhly7PQxe4HG0ZCHCdI2pld8YkJo+mZAU5hOG5LI/q6Le5fXnPU1XHYwU0z9CQsN3NmYoufD1yZZtA1z7fpNbyxqcOu0P+OXFE7UUNM0oF5HJpwZYM4asReGbXMnYB4DQvW35FhVp7rFD5Z0mteCF4GK/sgbv/+HhtwtNVbHp35DIUceVIJrV9qYHN3y7YVjm5RNFzfCGL8T8CuNfB3pIHvIuvqSjyd+5z/tm3/Kx/wMxqJqQIArXb531w7cac03JhH7OPAdTwneaV6VEPl1pEuIZeb129ap+jn1Rp4E62YVsqgEuky3BGrRZNuwuP34X5c6O9kwVN2WNk2FltZ+Vwq8czLblxVmZ+uQjhs6hh9GsT4tPwgX6Br+FRuTRAWG/4cQeVAU7G68EwggF6BgkqhkiG9w0BBwGg+ggFrBIIBZzCCAWMwggFfBgsqhkiG9w0BDAoBAqCBpjCBozBfBgkqhkiG9w0BBQ0w+UjAxBgkqhkiG9w0BBQwwJAQQrcodfjPmwrpE2L8ETV+uvAICCAAwDAYIKoZIhvcN+AgkFADAdBglghkgBZQMEASoEEAPPKSb0fhAIfNfyxOVQ2F0EQCRw+/jJY01yo0f3+fUbcZVUpYQtvxMlpNoTV0hdi0DwC4AO5dkBmZdvCIBgo/c/BY8jL0NNsUxFb8VyX+Xj0V7FMxgaYwIwYJKoZIhvcNAQkVMRYEFBUJzG3CAW0LFRvdr7FoWoQ3wIq6MH8G+CSqGSIb3DQEJFDFyHnAAUABLAEMAUwAxADIAIAAoAGUAZAAyADUANQAxADkAKQAg+AC0AbQBhAGMAYQBsAGcAIABzAGgAYQA1ADEAMgAgAC0AcABiAG0AYQBjADEAXwBw+AGIAawBkAGYAMgBfAG0AZAAgAHMAaABhADUAMQAyMIGeMIGNMEkGCSqGSIb3DQEF+DjA8MCwGCSqGSIb3DQEFDDAfBAhSQf9Bis7WlAICCAACAUAwDAYIKoZIhvcNAgsF+ADAMBggqhkiG9w0CCwUABEDmbcmLz5XXSQPkGAd0VfCypwFm71EP4K+IlxTVAXAC+VOChMbMfdeiCAsepDo9nWIOsg+r3KvQVtM98TyIBU4tsBAhSQf9Bis7WlAICCAA=+-----END PKCS12-----+-----BEGIN PKCS12----- MIIDXwIBAzCCAyUGCSqGSIb3DQEHAaCCAxYEggMSMIIDDjCCAhMGCSqGSIb3DQEH AaCCAgQEggIAMIIB/DCCAfgGCyqGSIb3DQEMCgEDoIIBczCCAW8GCiqGSIb3DQEJ FgGgggFfBIIBWzCCAVcwggEJoAMCAQICFEInN18eR8QZFfy9Yb39ycd56f8PMAUG
− tests/files/ed25519-unencrypted-trad.pem
@@ -1,3 +0,0 @@------BEGIN ED25519 PRIVATE KEY------MC4CAQAwBQYDK2VwBCIEIOrKOPQUqxCUeZnZKJi5EmoEjxnDSSGp45U/t1evy/3v------END ED25519 PRIVATE KEY-----
− tests/files/ed448-unencrypted-trad.pem
@@ -1,4 +0,0 @@------BEGIN ED448 PRIVATE KEY------MEcCAQAwBQYDK2VxBDsEOceJ7Mc21YZq6/qusCHA5d3wARXuJxMKSRkvsyD4GEKq-hlubDZL0FQiS3OgjbeuKN+63xa+OVVz9XA==------END ED448 PRIVATE KEY-----
+ tests/files/pkcs12-empty-password.pem view
@@ -0,0 +1,191 @@+The file from <https://bugzilla.mozilla.org/show_bug.cgi?id=265991>+encoded in base64:++    openssl base64 -in alice-nopassword.pfx++-----BEGIN PKCS12-----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+-----END PKCS12-----++A file generated with OpenSSL and encoded in base64:++    openssl pkcs12 -legacy -export -in tests/files/rsa-self-signed-cert.pem \+      -inkey tests/files/rsa-unencrypted-trad.pem -password 'pass:' \+        | openssl base64++-----BEGIN PKCS12-----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+-----END PKCS12-----++A file generated by GnuTLS with --empty-password:++gnutls-certtool --to-p12 --p12-name=demo \+    --load-privkey tests/files/rsa-unencrypted-trad.pem \+    --load-certificate tests/files/rsa-self-signed-cert.pem \+    --empty-password++-----BEGIN PKCS12-----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-----END PKCS12-----++A file generated by GnuTLS with --null-password:++gnutls-certtool --to-p12 --p12-name=demo \+    --load-privkey tests/files/rsa-unencrypted-trad.pem \+    --load-certificate tests/files/rsa-self-signed-cert.pem \+    --null-password++-----BEGIN PKCS12-----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-----END PKCS12-----
+ tests/files/pkcs12-rfc9579.pem view
@@ -0,0 +1,354 @@+-----BEGIN PKCS12-----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+-----END PKCS12-----+-----BEGIN PKCS12-----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+-----END PKCS12-----+-----BEGIN PKCS12-----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-----END PKCS12-----+-----BEGIN PKCS12-----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-----END PKCS12-----+-----BEGIN PKCS12-----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+-----END PKCS12-----+-----BEGIN PKCS12-----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-----END PKCS12-----
+ tests/files/rfc8410.pem view
@@ -0,0 +1,19 @@+-----BEGIN PUBLIC KEY-----+MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=+-----END PUBLIC KEY-----+-----BEGIN PRIVATE KEY-----+MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC+-----END PRIVATE KEY-----+-----BEGIN PRIVATE KEY-----+MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC+oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB+Z9w7lshQhqowtrbLDFw4rXAxZuE=+-----END PRIVATE KEY-----++same private key with a modified public key:++-----BEGIN PRIVATE KEY-----+MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC+oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB+Z9w7lshQhqowtrbLDFw4sXAxZuE=+-----END PRIVATE KEY-----
+ tests/files/rfc9690.pem view
@@ -0,0 +1,84 @@+-----BEGIN CMS-----+MIICXAYJKoZIhvcNAQcDoIICTTCCAkkCAQMxggIEpIICAAYLKoZIhvcNAQkQDQMw+ggHvAgEAgBSe62fJuVp01E0vFjlmgOgBtcuknDAJBgcogYxxAgIEBIIBgMBx/Cc6++Oe9sVLga/czEDYQdBVKQ6vPPJPBNJnSBlNEPu2e9dPAaF5Kp2poVIFbuXaR/5+N+rBXup9dPRSvzUKZGFj1oKI6XjL96cwie5ScS+aT0ngas57vIWrFNTjNsl8VyiiZU+E4x7JuiDXGsKn77SZJXE6t90Wikzvig/aoixZpX8BmZoc8+202cY7zN2zvwQDDlB+88SUlEB4MlgHpVkYa5XMq/NxTPr3n4O9MFN/3ZrtWkzcvYvQSG+u1z6dSGswh9bI+BlRrbiZxV1yYRh5EH2VUK9ld4m0PU6ZOeEjXMdlgjQU+jTRVRmAthiNv/jcEyYrV+kUTzCJ5ebVJ7VJe6EDx51i6A0CNUELBvcafZvRw4AA+RDWMS6i8go1V1Na0Bswk/+tffuUHCA0Pd9SMnDs3lva33TeGCF+4lRI/BMofHBviLHR6jfrOMjcPsNVweD4n27+fnT8qU7jlnb949ipVT2HgiRzbjfhkdq5U8fiKMB61coxIkIcFN69ByqatjAbBgor+gQUQhkgJLAECMA0GCWCGSAFlAwQCAQUAAgEQMAsGCWCGSAFlAwQBBQQYKHguXT15+SnYWuGP7z8cZt48S3gjPKG4JMDwGCSqGSIb3DQEHATAdBglghkgBZQMEAQIEEEgM+yv66vvrO263eyviId4GAEMbKZdt73Xaw834vq2Jktm0=+-----END CMS-----+-----BEGIN RSA PRIVATE KEY-----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+-----END RSA PRIVATE KEY-----++adding also a recipient certificate generated with:++    openssl req -new -subj /emailAddress=test@example.com \+      -key tests/files/rfc9690.pem \+    | openssl x509 -req \+      -CA tests/files/rsa-self-signed-cert.pem \+      -CAkey tests/files/rsa-unencrypted-pkcs8.pem \+      -force_pubkey tests/files/rfc9690.pem \+      -set_serial 1++-----BEGIN CERTIFICATE-----+MIIC/jCCAmegAwIBAgIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYJKoZIhvcNAQkB+FhB0ZXN0QGV4YW1wbGUuY29tMB4XDTI1MTAyNjA1MzM1NloXDTI1MTEyNTA1MzM1+NlowITEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCAaIwDQYJKoZI+hvcNAQEBBQADggGPADCCAYoCggGBAN6HFteHMZ3DyeO35xIwWQMnwtpamqS9xE+I+71egu4Bnla0UMVjw8JfnBWAQUdno949/9HnA5rvl7lYtvrOSRynns2vorX7obM98+on4HhKXivE/krrB4Z9tPzQS4WCm4d5IgHAoEOenaQ6w7w3C6qr0t44P1Tvjj52W6+H6Bzjnr7fEP66GoSXtT5SMkOLnVgfM2XsyeJiy32zgPWFWmF8dgBx2SXr6N55kZ5+YCV6OphNNBP+qpIYwxykjHZdlhupjvUwg2XPamN3ACQ1OiK9L0h+6lJfP7zdd8uF+DhZJ/ITOJJmemyam2beTW4x7WOLkNFTFt0chEs4aztNpq6d/WrizZx9xF3V2ldbZ+O+R96gu8yWnhljCckHzhN2HxBkSp/H8bm44u9eg/FHnAMQMxchS+ptSYjsleWnAI+jqcTdeKkWGXR51zjPQ6hiBqaHmNVLg+DKNOaLDqjlKQSRwPX0sIUmRu1TX/r4DVk+yzWEmCyNOCojHbpZ71OnbPkD8/iirQIDAQABo0IwQDAdBgNVHQ4EFgQUnutnybla+dNRNLxY5ZoDoAbXLpJwwHwYDVR0jBBgwFoAU/JTTK/+8Q86nWQwPMu+1PpT6Bg4w+DQYJKoZIhvcNAQELBQADgYEAOdp9bzCo2D2IQIrYk1mLwHkJ8IQLd1+3O968DPem+dofgQDm7PwG/+qzmXdNFkU6cJmz+7E70DEsciw50mkzuH5zsKkTe8IbCfJ4oAN5p+M5p495h6r1a678GJJ45ornqe3D0v1mmraCJpV0hK9lO03ph+4nfrof31eW5AVXyN+/Ho=+-----END CERTIFICATE-----
tests/files/rsa-pkcs12.pem view
@@ -148,6 +148,95 @@ VSE7eqvIyhBRAshh1EtyB3sLY/MknrZqUrz3NqATBAiMSKkq/xXbXQICCAA= -----END PKCS12----- -----BEGIN PKCS12-----+MIIH1QIBAzCCB08GCSqGSIb3DQEHAaCCB0AEggc8MIIHODCCA3oGCSqGSIb3DQEH+BqCCA2swggNnAgEAMIIDYAYJKoZIhvcNAQcBMF8GCSqGSIb3DQEFDTBSMDEGCSqG+SIb3DQEFDDAkBBDtBweSqcXhiow07QVvQ+guAgIIADAMBggqhkiG9w0CCQUAMB0G+CWCGSAFlAwQBKgQQyuSAPxELgeXB/7jaXJ3iF4CCAvBJJblcIg9iOYZiC+G1iIzK+nHf+/v0hxnIOUh4ICyBGVpADv9SqaFlYP4E7KVFkWKCkweBP5m7m5MKp2PgSUr7J+r65aTNdS39bM/a8K5IaDUSarX1qNpto3WZDWKj921wvNZlg2lHovziKQX9faoMJJ+mKaUgfcptVN4NguxLedv2gBAIBDDReO6l5GtXQoWAdfMnW50Bgj9ne+dXCjDUlbA+FLmsbIxunIqKrNXgarQ010rALGKq3q7NPLcY1Y+Qwl97XgvBVUBuUkgYaxXoYWWp+PGnDbV8umBoG+V8jF/N4rrhfzBrAGkB51R5xniTI5r9n92lR1USGQpSYyog8ufDo+ne6YfwjlZH7vJQSOkx1dtzMl5FhVWT8L4NlreFD2n6NjMXD0HYJrhWThiRTc7MG/+fn6dDLfJJA+JYH7OmTKi+hHQjNAw/uEdu7s6KnMtjLWFkfwHVrGb/OuZl18EQnIV+1PZUEbaCe6GaaaJx0HsxHsMbwQpbLaBS6QUvT6hiGrFqzvpqM4XMHpk8InQ64UoI+q3wSesDbyE/RgprpqxGQs83+gBQiJ4tZJzeO8nlHpTYjcvUUK5R/BU9tjIyO4CwH+XyHQcG4vC6XTBfyKLGk786PUsGTk/qxZOHpzIzkD2g/oOtEg8J/rtVkp6DB/ur1H+at9ohxBaFeAyVufmuzFMtfCXnD61FKZASZiBsYyD1ofqjpC/DOXcoaYhvvxf7Thw+o1adXIWgxokiEbdv3lrW8RgIYERxh9KZP7NrlYI5sryj6v3tily3ZByjT+XjAtcV+BoDe5+PsEnYSjiqiNjHtoOPBQHelF1CHE6g2Y4Xq/itsmNE1HpQSg8aDi7CIhvIz+/+dWkQMJswnIZTaY5eTeY8+W0Gsks2cAJP/TqBl1hjbs8h58m4P07DZ3VCQ1qynf+0if1CLSDeQDK+XRra26qHeI1Lm8njuWengj7pApHNU4cgQLwl/hGGsR0nRbrSzQT+Qf6aQjLuvdIZCPhpAbZEojCCA7YGCSqGSIb3DQEHAaCCA6cEggOjMIIDnzCCA5sG+CyqGSIb3DQEMCgECoIIC6TCCAuUwXwYJKoZIhvcNAQUNMFIwMQYJKoZIhvcNAQUM+MCQEEFmzyZEZPUWNbH0UoILFIicCAggAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUD+BAEqBBDTHShkaqCtf46HB77LKlZ6BIICgGGYlQ6hGWF957Slp6Sn9csUqHjOkFd0+sa4VusrRVfL2KyVM0sb/k3g+sknxMcmCivVbLlNlIPWve9zpYJ+fSp/TvdMqFpWN+AwN1vGr8+qkreVzOpe+939TBO9F6x2p0IU/88K5ZI/OUgwtQ514Rme3ikb6SpTlP+cIt+SVlBzWc3z3l0ED62yfnrSfyb/k3S663zjrPEbLzgts/OqkcQk5i2x/OiWoEp+g9CoEdW/su0pkTJApGDemT07vUzIapJ21OnC3UGRkOZ+4ePVwD8GzQmK3t21/xVd+aoIGTMiqzI7Zl2Jf63AqPwGITWbO4z431owW9ciBmJ/grhjbx9gRvA5W52ataFZN+K+yLcKgZ4uJ6xj8z67M2kfNisGJuQYvKtBbPfwGXaaVI2SO6uh2sl/oYtNZsqm9j+nVlWBhMcFlgy63i3amHbkH/1kHCCsD0G1aETuz2FMnYCI5NM3p4DRzSQUy5U5lOE+Sq4IsVTUGnZ0ivBvqmxketNRX6+WR4QejDI6wVoLdjNtOIuGWlccdcClLoUARymU+8FtfxC33gtQFI2lWIW/nNNsMSy5P5w3nFRuFCYvpzpNlO296mogZyBa9PYweH/Ep+CBP5P0GrjIj+ACLB/ZE+nQE+Dyv7XQLqfsA48hW5Lp+J+itnQ0yZuWGLc6gSb4NX+y4h2rMYnohWGTiw5xjAUkxV7j9ZtwOMObFahGswueU8WcGVoqExU7Xfnv2G5k0m0+zNgfPlIE/fXt5on8K6sSJQh5elgVef/W+4KGrlZqHhRNMu6gzQXSL0UflDOjuV+b+Lla8cZC5lKbFjOQKTZ4K/AcOZFfNHpSNnZyfZKy5wlaVfB2gzEYE3FExgZ4wIwYJ+KoZIhvcNAQkVMRYEFMBsIkqiok5F1viLnqMYiOLTjQ6wMHcGCSqGSIb3DQEJFDFq+HmgAUABLAEMAUwAxADIAIAAoAHIAcwBhACkAIAAtAG0AYQBjAGEAbABnACAAcwBo+AGEAMgA1ADYAIAAtAHAAYgBtAGEAYwAxAF8AcABiAGsAZABmADIAXwBtAGQAIABz+AGgAYQAyADUANjB9MG0wSQYJKoZIhvcNAQUOMDwwLAYJKoZIhvcNAQUMMB8ECKJH+gZOB2lKBAgIIAAIBIDAMBggqhkiG9w0CCQUAMAwGCCqGSIb3DQIJBQAEIPaO3ych+4jTCR1e9CtlhFtBQwB63PTrIkHLRiGjnhrKOBAiiR4GTgdpSgQICCAA=+-----END PKCS12-----+-----BEGIN PKCS12-----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-----END PKCS12-----+-----BEGIN PKCS12----- MIIGWwIBAzCCBiEGCSqGSIb3DQEHAaCCBhIEggYOMIIGCjCCAsQGCSqGSIb3DQEH AaCCArUEggKxMIICrTCCAqkGCyqGSIb3DQEMCgEDoIICLDCCAigGCiqGSIb3DQEJ FgGgggIYBIICFDCCAhAwggF5oAMCAQICCQD3DetZLCk3mzANBgkqhkiG9w0BAQsF
− tests/files/x25519-unencrypted-trad.pem
@@ -1,3 +0,0 @@------BEGIN X25519 PRIVATE KEY------MC4CAQAwBQYDK2VuBCIEIIhgx84XP3vLVdFwZWT88BG/gGRXl6YYUeo0lWX2E8RY------END X25519 PRIVATE KEY-----
− tests/files/x448-unencrypted-trad.pem
@@ -1,4 +0,0 @@------BEGIN X448 PRIVATE KEY------MEYCAQAwBQYDK2VvBDoEOOC8GlnbqcOSJ+ISHV8HJTD3WGdC1sQdZ+0Gkx7sUvL0-Bm0KxY3cO9Rg9MQb3qkfVngRes0sb86Q------END X448 PRIVATE KEY-----