Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/0-release-notes/new-ciphersuites
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for 3 more MLS ciphersuites. To enable MLS, all supported signature schemes (ed25519 and the three ecdsa variants) now need to have private keys specified in galley's configuration file.
5 changes: 3 additions & 2 deletions charts/galley/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ data:
federationDomain: {{ .settings.federationDomain }}
{{- if $.Values.secrets.mlsPrivateKeys }}
mlsPrivateKeyPaths:
{{- if $.Values.secrets.mlsPrivateKeys.removal.ed25519 }}
removal:
ed25519: "/etc/wire/galley/secrets/removal_ed25519.pem"
{{- end }}
ecdsa_secp256r1_sha256: "/etc/wire/galley/secrets/removal_ecdsa_secp256r1_sha256.pem"
ecdsa_secp384r1_sha384: "/etc/wire/galley/secrets/removal_ecdsa_secp384r1_sha384.pem"
ecdsa_secp521r1_sha512: "/etc/wire/galley/secrets/removal_ecdsa_secp521r1_sha512.pem"
{{- end }}
disabledAPIVersions: {{ toJson .settings.disabledAPIVersions }}
{{- if .settings.featureFlags }}
Expand Down
9 changes: 9 additions & 0 deletions charts/galley/templates/secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ data:
{{- if .Values.secrets.mlsPrivateKeys.removal.ed25519 }}
removal_ed25519.pem: {{ .Values.secrets.mlsPrivateKeys.removal.ed25519 | b64enc | quote }}
{{- end -}}
{{- if .Values.secrets.mlsPrivateKeys.removal.ecdsa_secp256r1_sha256 }}
removal_ecdsa_secp256r1_sha256.pem: {{ .Values.secrets.mlsPrivateKeys.removal.ecdsa_secp256r1_sha256 | b64enc | quote }}
{{- end -}}
{{- if .Values.secrets.mlsPrivateKeys.removal.ecdsa_secp384r1_sha384 }}
removal_ecdsa_secp384r1_sha384.pem: {{ .Values.secrets.mlsPrivateKeys.removal.ecdsa_secp384r1_sha384 | b64enc | quote }}
{{- end -}}
{{- if .Values.secrets.mlsPrivateKeys.removal.ecdsa_secp521r1_sha512 }}
removal_ecdsa_secp521r1_sha512.pem: {{ .Values.secrets.mlsPrivateKeys.removal.ecdsa_secp521r1_sha512 | b64enc | quote }}
{{- end -}}
{{- end -}}

{{- if $.Values.config.enableFederation }}
Expand Down
13 changes: 12 additions & 1 deletion docs/src/developer/reference/config-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,26 @@ For example:
mlsPrivateKeyPaths:
removal:
ed25519: /etc/secrets/ed25519.pem
ecdsa_secp256r1_sha256: /etc/secrets/ecdsa_secp256r1_sha256
ecdsa_secp384r1_sha384: /etc/secrets/ecdsa_secp384r1_sha384
ecdsa_secp521r1_sha512: /etc/secrets/ecdsa_secp521r1_sha512
```

A simple way to generate an ed25519 private key, discarding the corresponding
certificate, is to run the following command:

```
openssl req -nodes -newkey ed25519 -keyout ed25519.pem -out /dev/null -subj /
openssl genpkey -algorithm ed25519
```

ECDSA private keys can be generated with:

```
openssl genpkey -algorithm ec -genparam dsa -pkeyopt ec_paramgen_curve:P-256
```

and similar (replace `P-256` with `P-384` or `P-521`).

## Feature flags

> Also see [Wire docs](https://docs.wire.com/how-to/install/team-feature-settings.html) where some of the feature flags are documented from an operations point of view.
Expand Down
22 changes: 22 additions & 0 deletions hack/helm_vars/wire-server/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,28 @@ galley:
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIAocCDXsKIAjb65gOUn5vEF0RIKnVJkKR4ebQzuZ709c
-----END PRIVATE KEY-----
ecdsa_secp256r1_sha256: |
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg3qjgQ9U+/rTBObn9
tXSVi2UtHksRDXmQ1VOszFZfjryhRANCAATNkLmZZLyORf5D3PUOxt+rkJTE5vuD
aCqZ7sE5NSN8InRRwuQ1kv0oblDVeQA89ZlHqyxx75JPK+/air7Z1n5I
-----END PRIVATE KEY-----
ecdsa_secp384r1_sha384: |
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBLwv3i5LDz9b++O0iw
QAit/Uq7L5PWPgKN99wCm8xkZnuyqWujXW4wvlVUVlZWgh2hZANiAAT0+RXKE31c
VxdYazaVopY50/nV9c18uRdqoENBvtxuD6oDtJtU6oCS/Htkd8JEArTQ9ZHqq144
yRjuc3d2CqvJmEA/lzIBk9wnz+lghFhvB4TkSHvvLyEBc9DZvhb4EEQ=
-----END PRIVATE KEY-----
ecdsa_secp521r1_sha512: |
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBiaEARm5BMaRct1xj
MlemUHijWGAoHtNMhSttSr4jo0WxMwfMnvnDQJSlO2Zs4Tzum2j5eO34EHu6MUrv
qquZYwyhgYkDgYYABAHuvCV/+gJitvAbDwgrBHZJ41oy8Lc+wPIM7Yp6s/vTzTsG
Klo7aMdkx6DUjv/56tVD9bZNulFAjwS8xoIyWg8NSAE1ofo8CBvN1XGZOWuMYjEh
zLrZADduEnOvayw5sEvm135WC0vWjPJaYwKZPdDIXUz9ILJPgNe3gEUvHsDEXvdX
lw==
-----END PRIVATE KEY-----
rabbitmq:
username: {{ .Values.rabbitmqUsername }}
password: {{ .Values.rabbitmqPassword }}
Expand Down
5 changes: 5 additions & 0 deletions integration/test/API/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ listConversations user cnvs = do
req
& addJSONObject ["qualified_ids" .= cnvs]

getMLSPublicKeys :: (HasCallStack, MakesValue user) => user -> App Response
getMLSPublicKeys user = do
req <- baseRequest user Galley Versioned "/mls/public-keys"
submit "GET" req

postMLSMessage :: HasCallStack => ClientIdentity -> ByteString -> App Response
postMLSMessage cid msg = do
req <- baseRequest cid Galley Versioned "/mls/messages"
Expand Down
96 changes: 56 additions & 40 deletions integration/test/MLS/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ mlscli :: (HasCallStack) => ClientIdentity -> [String] -> Maybe ByteString -> Ap
mlscli cid args mbstdin = do
groupOut <- randomFileName
let substOut = argSubst "<group-out>" groupOut
cs <- (.ciphersuite) <$> getMLSState
let scheme = csSignatureScheme cs

gs <- getClientGroupState cid

Expand All @@ -88,23 +90,24 @@ mlscli cid args mbstdin = do
Just groupData -> do
fn <- toRandomFile groupData
pure (argSubst "<group-in>" fn)
store <- maybe randomFileName toRandomFile gs.keystore
store <- case Map.lookup scheme gs.keystore of
Nothing -> do
bd <- getBaseDir
liftIO $ createDirectory (bd </> cid2Str cid)

-- initialise new keystore
path <- randomFileName
ctype <- make gs.credType & asString
void $ runCli path ["init", "--ciphersuite", cs.code, "-t", ctype, cid2Str cid] Nothing
pure path
Just s -> toRandomFile s

let args' = map (substIn . substOut) args
for_ args' $ \arg ->
when (arg `elem` ["<group-in>", "<group-out>"]) $
assertFailure ("Unbound arg: " <> arg)

out <-
spawn
( proc
"mls-test-cli"
( ["--store", store]
<> args'
)
)
mbstdin

out <- runCli store args' mbstdin
setGroup <- do
groupOutWritten <- liftIO $ doesFileExist groupOut
if groupOutWritten
Expand All @@ -114,12 +117,23 @@ mlscli cid args mbstdin = do
else pure id
setStore <- do
storeData <- liftIO (BS.readFile store)
pure $ \x -> x {keystore = Just storeData}
pure $ \x -> x {keystore = Map.insert scheme storeData x.keystore}

setClientGroupState cid ((setGroup . setStore) gs)
setClientGroupState cid (setGroup (setStore gs))

pure out

runCli :: HasCallStack => FilePath -> [String] -> Maybe ByteString -> App ByteString
runCli store args mStdin =
spawn
( proc
"mls-test-cli"
( ["--store", store]
<> args
)
)
mStdin

argSubst :: String -> String -> String -> String
argSubst from to_ s =
if s == from then to_ else s
Expand All @@ -130,45 +144,33 @@ createWireClient u = do
c <- addClient u def {lastPrekey = Just lpk} >>= getJSON 201
mkClientIdentity u c

data CredentialType = BasicCredentialType | X509CredentialType

instance MakesValue CredentialType where
make BasicCredentialType = make "basic"
make X509CredentialType = make "x509"

instance (HasTests x) => HasTests (CredentialType -> x) where
mkTests m n s f x =
mkTests m (n <> "[ctype=basic]") s f (x BasicCredentialType)
<> mkTests m (n <> "[ctype=x509]") s f (x X509CredentialType)
-- data CredentialType = BasicCredentialType | X509CredentialType
--
-- instance MakesValue CredentialType where
-- make BasicCredentialType = make "basic"
-- make X509CredentialType = make "x509"

data InitMLSClient = InitMLSClient
{credType :: CredentialType}

instance Default InitMLSClient where
def = InitMLSClient {credType = BasicCredentialType}

initMLSClient :: (HasCallStack) => InitMLSClient -> ClientIdentity -> App ()
initMLSClient opts cid = do
bd <- getBaseDir
mls <- getMLSState
liftIO $ createDirectory (bd </> cid2Str cid)
ctype <- make opts.credType & asString
void $ mlscli cid ["init", "--ciphersuite", mls.ciphersuite.code, "-t", ctype, cid2Str cid] Nothing

-- | Create new mls client and register with backend.
createMLSClient :: (MakesValue u, HasCallStack) => InitMLSClient -> u -> App ClientIdentity
createMLSClient opts u = do
cid <- createWireClient u
initMLSClient opts cid
setClientGroupState cid def {credType = opts.credType}

-- set public key
pkey <- mlscli cid ["public-key"] Nothing
ciphersuite <- (.ciphersuite) <$> getMLSState
bindResponse
( updateClient
cid
def
{ mlsPublicKeys =
Just (object ["ed25519" .= T.decodeUtf8 (Base64.encode pkey)])
Just (object [csSignatureScheme ciphersuite .= T.decodeUtf8 (Base64.encode pkey)])
}
)
$ \resp -> resp.status `shouldMatchInt` 200
Expand All @@ -177,17 +179,17 @@ createMLSClient opts u = do
-- | create and upload to backend
uploadNewKeyPackage :: (HasCallStack) => ClientIdentity -> App String
uploadNewKeyPackage cid = do
mls <- getMLSState
(kp, ref) <- generateKeyPackage cid mls.ciphersuite
(kp, ref) <- generateKeyPackage cid

-- upload key package
bindResponse (uploadKeyPackages cid [kp]) $ \resp ->
resp.status `shouldMatchInt` 201

pure ref

generateKeyPackage :: (HasCallStack) => ClientIdentity -> Ciphersuite -> App (ByteString, String)
generateKeyPackage cid suite = do
generateKeyPackage :: HasCallStack => ClientIdentity -> App (ByteString, String)
generateKeyPackage cid = do
suite <- (.ciphersuite) <$> getMLSState
kp <- mlscli cid ["key-package", "create", "--ciphersuite", suite.code] Nothing
ref <- B8.unpack . Base64.encode <$> mlscli cid ["key-package", "ref", "-"] (Just kp)
fp <- keyPackageFile cid ref
Expand Down Expand Up @@ -244,8 +246,11 @@ resetGroup cid conv = do

resetClientGroup :: ClientIdentity -> String -> App ()
resetClientGroup cid gid = do
removalKeyPath <- asks (.removalKeyPath)
mls <- getMLSState
removalKeyPaths <- asks (.removalKeyPaths)
removalKeyPath <-
assertOne $
Map.lookup (csSignatureScheme mls.ciphersuite) removalKeyPaths
void $
mlscli
cid
Expand Down Expand Up @@ -706,7 +711,7 @@ spawn cp minput = do
getClientGroupState :: (HasCallStack) => ClientIdentity -> App ClientGroupState
getClientGroupState cid = do
mls <- getMLSState
pure $ Map.findWithDefault emptyClientGroupState cid mls.clientGroupState
pure $ Map.findWithDefault def cid mls.clientGroupState

setClientGroupState :: (HasCallStack) => ClientIdentity -> ClientGroupState -> App ()
setClientGroupState cid g =
Expand Down Expand Up @@ -761,6 +766,17 @@ createApplicationMessage cid messageContent = do
setMLSCiphersuite :: Ciphersuite -> App ()
setMLSCiphersuite suite = modifyMLSState $ \mls -> mls {ciphersuite = suite}

withCiphersuite :: HasCallStack => Ciphersuite -> App a -> App a
withCiphersuite suite action = do
suite0 <- (.ciphersuite) <$> getMLSState
setMLSCiphersuiteIO <- appToIOKleisli setMLSCiphersuite
actionIO <- appToIO action
liftIO $
bracket
(setMLSCiphersuiteIO suite)
(const (setMLSCiphersuiteIO suite0))
(const actionIO)

leaveCurrentConv ::
(HasCallStack) =>
ClientIdentity ->
Expand All @@ -778,7 +794,7 @@ leaveCurrentConv cid = do
{ members = Set.difference mls.members (Set.singleton cid)
}

getCurrentConv :: (HasCallStack) => ClientIdentity -> App Value
getCurrentConv :: HasCallStack => ClientIdentity -> App Value
getCurrentConv cid = do
mls <- getMLSState
(conv, mSubId) <- objSubConv mls.convId
Expand Down
Loading