diff --git a/changelog.d/2-features/mls-x509-improvements b/changelog.d/2-features/mls-x509-improvements new file mode 100644 index 0000000000..36e3d457df --- /dev/null +++ b/changelog.d/2-features/mls-x509-improvements @@ -0,0 +1 @@ +The public key in an x509 credential is now checked against that of the client diff --git a/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs b/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs index 568943c026..c3bc1855b6 100644 --- a/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs +++ b/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs @@ -23,7 +23,7 @@ module Wire.API.MLS.KeyPackage KeyPackageData (..), DeleteKeyPackages (..), KeyPackage (..), - credentialIdentity, + credentialIdentityAndKey, keyPackageIdentity, kpRef, kpRef', @@ -231,9 +231,9 @@ instance HasField "extensions" KeyPackage [Extension] where instance HasField "leafNode" KeyPackage LeafNode where getField = (.tbs.value.leafNode) -credentialIdentity :: Credential -> Either Text ClientIdentity -credentialIdentity (BasicCredential i) = decodeMLS' i -credentialIdentity (X509Credential certs) = do +credentialIdentityAndKey :: Credential -> Either Text (ClientIdentity, Maybe X509.PubKey) +credentialIdentityAndKey (BasicCredential i) = (,) <$> decodeMLS' i <*> pure Nothing +credentialIdentityAndKey (X509Credential certs) = do bs <- case certs of [] -> Left "Invalid x509 certificate chain" (c : _) -> pure c @@ -242,20 +242,20 @@ credentialIdentity (X509Credential certs) = do X509.decodeSignedCertificate bs -- FUTUREWORK: verify signature let cert = X509.getCertificate signed - certificateIdentity cert + certificateIdentityAndKey cert keyPackageIdentity :: KeyPackage -> Either Text ClientIdentity -keyPackageIdentity kp = credentialIdentity kp.leafNode.credential +keyPackageIdentity kp = fst <$> credentialIdentityAndKey kp.leafNode.credential -certificateIdentity :: X509.Certificate -> Either Text ClientIdentity -certificateIdentity cert = +certificateIdentityAndKey :: X509.Certificate -> Either Text (ClientIdentity, Maybe X509.PubKey) +certificateIdentityAndKey cert = let getNames (X509.ExtSubjectAltName names) = names getURI (X509.AltNameURI u) = Just u getURI _ = Nothing altNames = maybe [] getNames (X509.extensionGet (X509.certExtensions cert)) ids = map sanIdentity (mapMaybe getURI altNames) in case partitionEithers ids of - (_, (cid : _)) -> pure cid + (_, (cid : _)) -> pure (cid, Just (X509.certPubKey cert)) ((e : _), []) -> Left e _ -> Left "No SAN URIs found" diff --git a/libs/wire-api/src/Wire/API/MLS/Validation.hs b/libs/wire-api/src/Wire/API/MLS/Validation.hs index f97e7fc021..2f98d96942 100644 --- a/libs/wire-api/src/Wire/API/MLS/Validation.hs +++ b/libs/wire-api/src/Wire/API/MLS/Validation.hs @@ -23,9 +23,12 @@ module Wire.API.MLS.Validation where import Control.Applicative +import Control.Error.Util +import Data.ByteArray qualified as BA import Data.Text.Lazy qualified as LT import Data.Text.Lazy.Builder qualified as LT import Data.Text.Lazy.Builder.Int qualified as LT +import Data.X509 qualified as X509 import Imports hiding (cs) import Wire.API.MLS.Capabilities import Wire.API.MLS.CipherSuite @@ -97,16 +100,17 @@ validateLeafNode cs mIdentity extra leafNode = do ) $ Left "Invalid LeafNode signature" - validateCredential mIdentity leafNode.credential + validateCredential cs leafNode.signatureKey mIdentity leafNode.credential validateSource extra.tag leafNode.source validateCapabilities (credentialTag leafNode.credential) leafNode.capabilities -validateCredential :: Maybe ClientIdentity -> Credential -> Either Text () -validateCredential mIdentity cred = do +validateCredential :: CipherSuiteTag -> ByteString -> Maybe ClientIdentity -> Credential -> Either Text () +validateCredential cs pkey mIdentity cred = do -- FUTUREWORK: check signature in the case of an x509 credential - identity <- + (identity, mkey) <- either credentialError pure $ - credentialIdentity cred + credentialIdentityAndKey cred + traverse_ (validateCredentialKey (csSignatureScheme cs) pkey) mkey unless (maybe True (identity ==) mIdentity) $ Left "client identity does not match credential identity" where @@ -114,6 +118,11 @@ validateCredential mIdentity cred = do Left $ "Failed to parse identity: " <> e +validateCredentialKey :: SignatureSchemeTag -> ByteString -> X509.PubKey -> Either Text () +validateCredentialKey Ed25519 pk1 (X509.PubKeyEd25519 pk2) = + note "Certificate public key does not match client's" $ guard (pk1 == BA.convert pk2) +validateCredentialKey _ _ _ = Left "Certificate signature scheme does not match client's public key" + validateSource :: LeafNodeSourceTag -> LeafNodeSource -> Either Text () validateSource t s = do let t' = leafNodeSourceTag s diff --git a/nix/pkgs/mls-test-cli/default.nix b/nix/pkgs/mls-test-cli/default.nix index bbaab19f30..7cd0852fd4 100644 --- a/nix/pkgs/mls-test-cli/default.nix +++ b/nix/pkgs/mls-test-cli/default.nix @@ -13,8 +13,8 @@ let src = fetchFromGitHub { owner = "wireapp"; repo = "mls-test-cli"; - rev = "d16b4e9d4e93b731e81cd04a00620f2c6a36e696"; - sha256 = "sha256-2p5m6R80dnyJShAvjmO+ZbX8wxMtuFmvPnp9uX4eezc="; + rev = "e6e6ce0c29f0e48e84b4ccef058130aca0625492"; + sha256 = "sha256-J9M8w3GJnULH3spKEuPGCL/t43zb2Wd+YfZ0LY3YITo="; }; cargoLockFile = builtins.toFile "cargo.lock" (builtins.readFile "${src}/Cargo.lock"); in rustPlatform.buildRustPackage rec {