Пример #1
0
def test_ecdh_es_direct():
    bob_key = Key.generate(KeyAlg.P256)
    bob_jwk = bob_key.get_jwk_public()
    ephem_key = Key.generate(KeyAlg.P256)
    ephem_jwk = ephem_key.get_jwk_public()
    message = b"Hello there"
    alg = "ECDH-ES"
    enc = "A256GCM"
    apu = "Alice"
    apv = "Bob"
    protected_b64 = b64_url(f'{{"alg":"{alg}",'
                            f'"enc":"{enc}",'
                            f'"apu":"{b64_url(apu)}",'
                            f'"apv":"{b64_url(apv)}",'
                            f'"epk":{ephem_jwk}}}').encode("ascii")
    encrypted_msg = EcdhEs(enc, apu, apv).encrypt_direct(KeyAlg.A256GCM,
                                                         ephem_key,
                                                         bob_jwk,
                                                         message,
                                                         aad=protected_b64)
    ciphertext, tag, nonce = encrypted_msg.parts

    # switch to receiver

    message_recv = EcdhEs(enc, apu, apv).decrypt_direct(
        KeyAlg.A256GCM,
        ephem_jwk,
        bob_key,
        ciphertext,
        nonce=nonce,
        tag=tag,
        aad=protected_b64,
    )
    assert message_recv == message
Пример #2
0
    async def test_es_encrypt_x(self, session: Session):
        alg = KeyAlg.X25519
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())

        with pytest.raises(
            test_module.DidcommEnvelopeError, match="No message recipients"
        ):
            _ = test_module.ecdh_es_encrypt({}, MESSAGE)

        with async_mock.patch(
            "aries_askar.Key.generate",
            async_mock.MagicMock(side_effect=AskarError(99, "")),
        ):
            with pytest.raises(
                test_module.DidcommEnvelopeError,
                match="Error creating content encryption key",
            ):
                _ = test_module.ecdh_es_encrypt({BOB_KID: bob_pk}, MESSAGE)

        with async_mock.patch(
            "aries_askar.Key.aead_encrypt",
            async_mock.MagicMock(side_effect=AskarError(99, "")),
        ):
            with pytest.raises(
                test_module.DidcommEnvelopeError,
                match="Error encrypting",
            ):
                _ = test_module.ecdh_es_encrypt({BOB_KID: bob_pk}, MESSAGE)
Пример #3
0
def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key:
    """Instantiate a new keypair with an optional seed value."""
    if key_type == KeyType.ED25519:
        alg = KeyAlg.ED25519
        method = None
    # elif key_type == KeyType.BLS12381G1:
    #     alg = KeyAlg.BLS12_381_G1
    elif key_type == KeyType.BLS12381G2:
        alg = KeyAlg.BLS12_381_G2
        method = SeedMethod.BlsKeyGen
    # elif key_type == KeyType.BLS12381G1G2:
    #     alg = KeyAlg.BLS12_381_G1G2
    else:
        raise WalletError(f"Unsupported key algorithm: {key_type}")
    if seed:
        try:
            if key_type == KeyType.ED25519:
                # not a seed - it is the secret key
                seed = validate_seed(seed)
                return Key.from_secret_bytes(alg, seed)
            else:
                return Key.from_seed(alg, seed, method=method)
        except AskarError as err:
            if err.code == AskarErrorCode.INPUT:
                raise WalletError("Invalid seed for key generation") from None
    else:
        return Key.generate(alg)
Пример #4
0
def test_ed25519():
    key = Key.generate(KeyAlg.ED25519)
    assert key.algorithm == KeyAlg.ED25519
    message = b"test message"
    sig = key.sign_message(message)
    assert key.verify_signature(message, sig)
    x25519_key = key.convert_key(KeyAlg.X25519)

    x25519_key_2 = Key.generate(KeyAlg.X25519)
    _kex = x25519_key.key_exchange(KeyAlg.XC20P, x25519_key_2)

    key.get_jwk_public()
Пример #5
0
    async def test_1pu_decrypt_x(self):
        alg = KeyAlg.X25519
        alice_sk = Key.generate(alg)
        alice_pk = Key.from_jwk(alice_sk.get_jwk_public())
        bob_sk = Key.generate(alg)

        message_unknown_alg = JweEnvelope(
            protected={"alg": "NOT-SUPPORTED"},
        )
        message_unknown_alg.add_recipient(
            JweRecipient(encrypted_key=b"0000", header={"kid": BOB_KID})
        )
        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Unsupported ECDH-1PU algorithm",
        ):
            _ = test_module.ecdh_1pu_decrypt(
                message_unknown_alg,
                BOB_KID,
                bob_sk,
                alice_pk,
            )

        message_unknown_enc = JweEnvelope(
            protected={"alg": "ECDH-1PU+A128KW", "enc": "UNKNOWN"},
        )
        message_unknown_enc.add_recipient(
            JweRecipient(encrypted_key=b"0000", header={"kid": BOB_KID})
        )
        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Unsupported ECDH-1PU content encryption",
        ):
            _ = test_module.ecdh_1pu_decrypt(
                message_unknown_enc, BOB_KID, bob_sk, alice_pk
            )

        message_invalid_epk = JweEnvelope(
            protected={"alg": "ECDH-1PU+A128KW", "enc": "A256CBC-HS512", "epk": {}},
        )
        message_invalid_epk.add_recipient(
            JweRecipient(encrypted_key=b"0000", header={"kid": BOB_KID})
        )
        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Error loading ephemeral key",
        ):
            _ = test_module.ecdh_1pu_decrypt(
                message_invalid_epk,
                BOB_KID,
                bob_sk,
                alice_pk,
            )
def test_crypto_box_seal():
    x25519_key = Key.generate(KeyAlg.X25519)

    msg = b"test message"
    sealed = crypto_box_seal(x25519_key, msg)
    opened = crypto_box_seal_open(x25519_key, sealed)
    assert msg == opened
Пример #7
0
def _pack_message(to_verkeys: Sequence[str], from_key: Optional[Key],
                  message: bytes) -> bytes:
    """Encode a message using the DIDComm v1 'pack' algorithm."""
    wrapper = JweEnvelope()
    cek = Key.generate(KeyAlg.C20P)
    # avoid converting to bytes object: this way the only copy is zeroed afterward
    cek_b = key_get_secret_bytes(cek._handle)
    sender_vk = (bytes_to_b58(from_key.get_public_bytes()).encode("utf-8")
                 if from_key else None)
    sender_xk = from_key.convert_key(KeyAlg.X25519) if from_key else None

    for target_vk in to_verkeys:
        target_xk = Key.from_public_bytes(KeyAlg.ED25519,
                                          b58_to_bytes(target_vk)).convert_key(
                                              KeyAlg.X25519)
        if sender_vk:
            enc_sender = crypto_box.crypto_box_seal(target_xk, sender_vk)
            nonce = crypto_box.random_nonce()
            enc_cek = crypto_box.crypto_box(target_xk, sender_xk, cek_b, nonce)
            wrapper.add_recipient(
                JweRecipient(
                    encrypted_key=enc_cek,
                    header=OrderedDict([
                        ("kid", target_vk),
                        ("sender", b64url(enc_sender)),
                        ("iv", b64url(nonce)),
                    ]),
                ))
        else:
            enc_sender = None
            nonce = None
            enc_cek = crypto_box.crypto_box_seal(target_xk, cek_b)
            wrapper.add_recipient(
                JweRecipient(encrypted_key=enc_cek, header={"kid": target_vk}))
    wrapper.set_protected(
        OrderedDict([
            ("enc", "xchacha20poly1305_ietf"),
            ("typ", "JWM/1.0"),
            ("alg", "Authcrypt" if from_key else "Anoncrypt"),
        ]),
        auto_flatten=False,
    )
    enc = cek.aead_encrypt(message, aad=wrapper.protected_bytes)
    ciphertext, tag, nonce = enc.parts
    wrapper.set_payload(ciphertext, nonce, tag)
    return wrapper.to_json().encode("utf-8")
Пример #8
0
def ecdh_es_encrypt(to_verkeys: Mapping[str, Key], message: bytes) -> bytes:
    """Encode a message using DIDComm v2 anonymous encryption."""
    wrapper = JweEnvelope(with_flatten_recipients=False)

    alg_id = "ECDH-ES+A256KW"
    enc_id = "XC20P"
    enc_alg = KeyAlg.XC20P
    wrap_alg = KeyAlg.A256KW

    if not to_verkeys:
        raise DidcommEnvelopeError("No message recipients")

    try:
        cek = Key.generate(enc_alg)
    except AskarError:
        raise DidcommEnvelopeError("Error creating content encryption key")

    for (kid, recip_key) in to_verkeys.items():
        try:
            epk = Key.generate(recip_key.algorithm, ephemeral=True)
        except AskarError:
            raise DidcommEnvelopeError("Error creating ephemeral key")
        enc_key = ecdh.EcdhEs(alg_id, None,
                              None).sender_wrap_key(wrap_alg, epk, recip_key,
                                                    cek)
        wrapper.add_recipient(
            JweRecipient(
                encrypted_key=enc_key.ciphertext,
                header={
                    "kid": kid,
                    "epk": epk.get_jwk_public()
                },
            ))

    wrapper.set_protected(OrderedDict([
        ("alg", alg_id),
        ("enc", enc_id),
    ]))
    try:
        payload = cek.aead_encrypt(message, aad=wrapper.protected_bytes)
    except AskarError:
        raise DidcommEnvelopeError("Error encrypting message payload")
    wrapper.set_payload(payload.ciphertext, payload.nonce, payload.tag)

    return wrapper.to_json().encode("utf-8")
Пример #9
0
def ecdh_1pu_decrypt(
    wrapper: JweEnvelope,
    recip_kid: str,
    recip_key: Key,
    sender_key: Key,
) -> Tuple[str, str, str]:
    """Decode a message with DIDComm v2 authenticated encryption."""

    alg_id = wrapper.protected.get("alg")
    if alg_id in ("ECDH-1PU+A128KW", "ECDH-1PU+A256KW"):
        wrap_alg = alg_id[9:]
    else:
        raise DidcommEnvelopeError(f"Unsupported ECDH-1PU algorithm: {alg_id}")

    enc_alg = wrapper.protected.get("enc")
    if enc_alg not in ("A128CBC-HS256", "A256CBC-HS512"):
        raise DidcommEnvelopeError(
            f"Unsupported ECDH-1PU content encryption: {enc_alg}")

    recip = wrapper.get_recipient(recip_kid)
    if not recip:
        raise DidcommEnvelopeError(f"Recipient header not found: {recip_kid}")

    try:
        epk = Key.from_jwk(wrapper.protected.get("epk"))
    except AskarError:
        raise DidcommEnvelopeError("Error loading ephemeral key")

    apu = wrapper.protected.get("apu")
    apv = wrapper.protected.get("apv")

    try:
        cek = ecdh.Ecdh1PU(alg_id, apu, apv).receiver_unwrap_key(
            wrap_alg,
            enc_alg,
            epk,
            sender_key,
            recip_key,
            recip.encrypted_key,
            cc_tag=wrapper.tag,
        )
    except AskarError:
        raise DidcommEnvelopeError("Error decrypting content encryption key")

    try:
        plaintext = cek.aead_decrypt(
            wrapper.ciphertext,
            nonce=wrapper.iv,
            tag=wrapper.tag,
            aad=wrapper.combined_aad,
        )
    except AskarError:
        raise DidcommEnvelopeError("Error decrypting message payload")

    return plaintext
Пример #10
0
def _extract_payload_key(sender_cek: dict,
                         recip_secret: Key) -> Tuple[bytes, str]:
    """
    Extract the payload key from pack recipient details.

    Returns: A tuple of the CEK and sender verkey
    """
    recip_x = recip_secret.convert_key(KeyAlg.X25519)

    if sender_cek["nonce"] and sender_cek["sender"]:
        sender_vk = crypto_box.crypto_box_seal_open(
            recip_x, sender_cek["sender"]).decode("utf-8")
        sender_x = Key.from_public_bytes(
            KeyAlg.ED25519, b58_to_bytes(sender_vk)).convert_key(KeyAlg.X25519)
        cek = crypto_box.crypto_box_open(recip_x, sender_x, sender_cek["key"],
                                         sender_cek["nonce"])
    else:
        sender_vk = None
        cek = crypto_box.crypto_box_seal_open(recip_x, sender_cek["key"])
    return cek, sender_vk
Пример #11
0
    async def test_es_round_trip(self, session: Session):
        alg = KeyAlg.X25519
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())
        carol_sk = Key.generate(KeyAlg.P256)  # testing mixed recipient key types
        carol_pk = Key.from_jwk(carol_sk.get_jwk_public())

        enc_message = test_module.ecdh_es_encrypt(
            {BOB_KID: bob_pk, CAROL_KID: carol_pk}, MESSAGE
        )

        # receiver must have the private keypair accessible
        await session.insert_key("my_sk", bob_sk, tags={"kid": BOB_KID})

        plaintext, recip_kid, sender_kid = await test_module.unpack_message(
            session, enc_message
        )
        assert recip_kid == BOB_KID
        assert sender_kid is None
        assert plaintext == MESSAGE
Пример #12
0
def test_aes_cbc_hmac():
    key = Key.generate(KeyAlg.A128CBC_HS256)
    assert key.algorithm == KeyAlg.A128CBC_HS256

    data = b"test message"
    nonce = key.aead_random_nonce()
    params = key.aead_params()
    assert params.nonce_length == 16
    assert params.tag_length == 16
    enc = key.aead_encrypt(data, nonce=nonce, aad=b"aad")
    dec = key.aead_decrypt(enc, nonce=nonce, aad=b"aad")
    assert data == bytes(dec)
Пример #13
0
    async def test_1pu_round_trip(self, session: Session):
        alg = KeyAlg.X25519
        alice_sk = Key.generate(alg)
        alice_pk = Key.from_jwk(alice_sk.get_jwk_public())
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())

        enc_message = test_module.ecdh_1pu_encrypt(
            {BOB_KID: bob_pk}, ALICE_KID, alice_sk, MESSAGE
        )

        # receiver must have the private keypair accessible
        await session.insert_key("my_sk", bob_sk, tags={"kid": BOB_KID})
        # for now at least, insert the sender public key so it can be resolved
        await session.insert_key("alice_pk", alice_pk, tags={"kid": ALICE_KID})

        plaintext, recip_kid, sender_kid = await test_module.unpack_message(
            session, enc_message
        )
        assert recip_kid == BOB_KID
        assert sender_kid == ALICE_KID
        assert plaintext == MESSAGE
Пример #14
0
    async def verify_message(
        self,
        message: Union[List[bytes], bytes],
        signature: bytes,
        from_verkey: str,
        key_type: KeyType,
    ) -> bool:
        """
        Verify a signature against the public key of the signer.

        Args:
            message: The message to verify
            signature: The signature to verify
            from_verkey: Verkey to use in verification
            key_type: The key type to derive the signature verification algorithm from

        Returns:
            True if verified, else False

        Raises:
            WalletError: If the verkey is not provided
            WalletError: If the signature is not provided
            WalletError: If the message is not provided
            WalletError: If another backend error occurs

        """
        if not from_verkey:
            raise WalletError("Verkey not provided")
        if not signature:
            raise WalletError("Signature not provided")
        if not message:
            raise WalletError("Message not provided")

        verkey = b58_to_bytes(from_verkey)

        if key_type == KeyType.ED25519:
            try:
                pk = Key.from_public_bytes(KeyAlg.ED25519, verkey)
                return pk.verify_signature(message, signature)
            except AskarError as err:
                raise WalletError(
                    "Exception when verifying message signature") from err

        # other key types are currently verified outside of Askar
        return verify_signed_message(
            message=message,
            signature=signature,
            verkey=verkey,
            key_type=key_type,
        )
Пример #15
0
def test_ecdh_es_wrapped():
    bob_key = Key.generate(KeyAlg.X25519)
    bob_jwk = bob_key.get_jwk_public()
    ephem_key = Key.generate(KeyAlg.X25519)
    ephem_jwk = ephem_key.get_jwk_public()
    message = b"Hello there"
    alg = "ECDH-ES+A128KW"
    enc = "A256GCM"
    apu = "Alice"
    apv = "Bob"
    protected_b64 = b64_url(f'{{"alg":"{alg}",'
                            f'"enc":"{enc}",'
                            f'"apu":"{b64_url(apu)}",'
                            f'"apv":"{b64_url(apv)}",'
                            f'"epk":{ephem_jwk}}}').encode("ascii")
    cek = Key.generate(KeyAlg.A256GCM)
    encrypted_msg = cek.aead_encrypt(message, aad=protected_b64)
    ciphertext, tag, nonce = encrypted_msg.parts
    encrypted_key = EcdhEs(alg, apu,
                           apv).sender_wrap_key(KeyAlg.A128KW, ephem_key,
                                                bob_jwk, cek)
    encrypted_key = encrypted_key.ciphertext

    # switch to receiver

    cek_recv = EcdhEs(alg, apu, apv).receiver_unwrap_key(
        KeyAlg.A128KW,
        KeyAlg.A256GCM,
        ephem_jwk,
        bob_key,
        encrypted_key,
    )
    message_recv = cek_recv.aead_decrypt(ciphertext,
                                         nonce=nonce,
                                         tag=tag,
                                         aad=protected_b64)
    assert message_recv == message
Пример #16
0
async def test_key_store(store: Store):
    # test key operations in a new session
    async with store as session:
        # Create a new keypair
        keypair = Key.generate(KeyAlg.ED25519)

        # Store keypair
        key_name = "testkey"
        await session.insert_key(key_name,
                                 keypair,
                                 metadata="metadata",
                                 tags={"a": "b"})

        # Fetch keypair
        fetch_key = await session.fetch_key(key_name)
        assert fetch_key and fetch_key.name == key_name and fetch_key.tags == {
            "a": "b"
        }

        # Update keypair
        await session.update_key(key_name,
                                 metadata="updated metadata",
                                 tags={"a": "c"})

        # Fetch keypair
        fetch_key = await session.fetch_key(key_name)
        assert fetch_key and fetch_key.name == key_name and fetch_key.tags == {
            "a": "c"
        }

        # Check key equality
        thumbprint = keypair.get_jwk_thumbprint()
        assert fetch_key.key.get_jwk_thumbprint() == thumbprint

        # Fetch with filters
        keys = await session.fetch_all_keys(alg=KeyAlg.ED25519,
                                            thumbprint=thumbprint,
                                            tag_filter={"a": "c"},
                                            limit=1)
        assert len(keys) == 1 and keys[0].name == key_name

        # Remove
        await session.remove_key(key_name)
        assert await session.fetch_key(key_name) is None
Пример #17
0
def test_bls_keygen():
    key = Key.from_seed(
        KeyAlg.BLS12_381_G1G2,
        b"testseed000000000000000000000001",
        method=SeedMethod.BlsKeyGen,
    )
    assert key.get_jwk_public(KeyAlg.BLS12_381_G1) == (
        '{"crv":"BLS12381_G1","kty":"EC","x":'
        '"h56eYI8Qkq5hitICb-ik8wRTzcn6Fd4iY8aDNVc9q1xoPS3lh4DB_B4wNtar1HrV"}')
    assert key.get_jwk_public(KeyAlg.BLS12_381_G2) == (
        '{"crv":"BLS12381_G2","kty":"EC",'
        '"x":"iZIOsO6BgLV72zCrBE2ym3DEhDYcghnUMO4O8IVVD8yS-C_zu6OA3L-ny-AO4'
        'rbkAo-WuApZEjn83LY98UtoKpTufn4PCUFVQZzJNH_gXWHR3oDspJaCbOajBfm5qj6d"}'
    )
    assert key.get_jwk_public() == (
        '{"crv":"BLS12381_G1G2","kty":"EC",'
        '"x":"h56eYI8Qkq5hitICb-ik8wRTzcn6Fd4iY8aDNVc9q1xoPS3lh4DB_B4wNtar1H'
        "rViZIOsO6BgLV72zCrBE2ym3DEhDYcghnUMO4O8IVVD8yS-C_zu6OA3L-ny-AO4rbk"
        'Ao-WuApZEjn83LY98UtoKpTufn4PCUFVQZzJNH_gXWHR3oDspJaCbOajBfm5qj6d"}')
Пример #18
0
async def _unpack_message(session: Session,
                          enc_message: bytes) -> Tuple[str, str, str]:
    """Decode a message using the DIDComm v1 'unpack' algorithm."""
    try:
        wrapper = JweEnvelope.from_json(enc_message)
    except ValidationError:
        raise WalletError("Invalid packed message")

    alg = wrapper.protected.get("alg")
    is_authcrypt = alg == "Authcrypt"
    if not is_authcrypt and alg != "Anoncrypt":
        raise WalletError("Unsupported pack algorithm: {}".format(alg))

    recips = extract_pack_recipients(wrapper.recipients())

    payload_key, sender_vk = None, None
    for recip_vk in recips:
        recip_key_entry = await session.fetch_key(recip_vk)
        if recip_key_entry:
            payload_key, sender_vk = _extract_payload_key(
                recips[recip_vk], recip_key_entry.key)
            break

    if not payload_key:
        raise WalletError("No corresponding recipient key found in {}".format(
            tuple(recips)))
    if not sender_vk and is_authcrypt:
        raise WalletError(
            "Sender public key not provided for Authcrypt message")

    cek = Key.from_secret_bytes(KeyAlg.C20P, payload_key)
    message = cek.aead_decrypt(
        wrapper.ciphertext,
        nonce=wrapper.iv,
        tag=wrapper.tag,
        aad=wrapper.protected_bytes,
    )
    return message, recip_vk, sender_vk
Пример #19
0
def ecdh_1pu_encrypt(to_verkeys: Mapping[str, Key], sender_kid: str,
                     sender_key: Key, message: bytes) -> bytes:
    """Encode a message using DIDComm v2 authenticated encryption."""
    wrapper = JweEnvelope(with_flatten_recipients=False)

    alg_id = "ECDH-1PU+A256KW"
    enc_id = "A256CBC-HS512"
    enc_alg = KeyAlg.A256CBC_HS512
    wrap_alg = KeyAlg.A256KW
    agree_alg = sender_key.algorithm

    if not to_verkeys:
        raise DidcommEnvelopeError("No message recipients")

    try:
        cek = Key.generate(enc_alg)
    except AskarError:
        raise DidcommEnvelopeError("Error creating content encryption key")

    try:
        epk = Key.generate(agree_alg, ephemeral=True)
    except AskarError:
        raise DidcommEnvelopeError("Error creating ephemeral key")

    apu = b64url(sender_kid)
    apv = []
    for (kid, recip_key) in to_verkeys.items():
        if agree_alg:
            if agree_alg != recip_key.algorithm:
                raise DidcommEnvelopeError(
                    "Recipient key types must be consistent")
        else:
            agree_alg = recip_key.algorithm
        apv.append(kid)
    apv.sort()
    apv = b64url(".".join(apv))

    wrapper.set_protected(
        OrderedDict([
            ("alg", alg_id),
            ("enc", enc_id),
            ("apu", apu),
            ("apv", apv),
            ("epk", json.loads(epk.get_jwk_public())),
            ("skid", sender_kid),
        ]))
    try:
        payload = cek.aead_encrypt(message, aad=wrapper.protected_bytes)
    except AskarError:
        raise DidcommEnvelopeError("Error encrypting message payload")
    wrapper.set_payload(payload.ciphertext, payload.nonce, payload.tag)

    for (kid, recip_key) in to_verkeys.items():
        enc_key = ecdh.Ecdh1PU(alg_id, apu,
                               apv).sender_wrap_key(wrap_alg,
                                                    epk,
                                                    sender_key,
                                                    recip_key,
                                                    cek,
                                                    cc_tag=payload.tag)
        wrapper.add_recipient(
            JweRecipient(encrypted_key=enc_key.ciphertext, header={"kid":
                                                                   kid}))

    return wrapper.to_json().encode("utf-8")
Пример #20
0
    async def test_unpack_message_1pu_x(self, session: Session):
        alg = KeyAlg.X25519
        alice_sk = Key.generate(alg)
        alice_pk = Key.from_jwk(alice_sk.get_jwk_public())
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())

        # receiver must have the private keypair accessible
        await session.insert_key("my_sk", bob_sk, tags={"kid": BOB_KID})
        # for now at least, insert the sender public key so it can be resolved
        await session.insert_key("alice_pk", alice_pk, tags={"kid": ALICE_KID})

        message_1pu_no_skid = json.dumps(
            {
                "protected": b64url(json.dumps({"alg": "ECDH-1PU+A128KW"})),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Sender key ID not provided",
        ):
            _ = await test_module.unpack_message(session, message_1pu_no_skid)

        message_1pu_unknown_skid = json.dumps(
            {
                "protected": b64url(
                    json.dumps({"alg": "ECDH-1PU+A128KW", "skid": "UNKNOWN"})
                ),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Sender public key not found",
        ):
            _ = await test_module.unpack_message(session, message_1pu_unknown_skid)

        message_1pu_apu_invalid = json.dumps(
            {
                "protected": b64url(
                    json.dumps({"alg": "ECDH-1PU+A128KW", "skid": "A", "apu": "A"})
                ),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Invalid apu value",
        ):
            _ = await test_module.unpack_message(session, message_1pu_apu_invalid)

        message_1pu_apu_mismatch = json.dumps(
            {
                "protected": b64url(
                    json.dumps(
                        {
                            "alg": "ECDH-1PU+A128KW",
                            "skid": ALICE_KID,
                            "apu": b64url("UNKNOWN"),
                        }
                    )
                ),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Mismatch between skid and apu",
        ):
            _ = await test_module.unpack_message(session, message_1pu_apu_mismatch)
Пример #21
0
def test_ecdh_1pu_wrapped_expected():
    ephem = Key.from_jwk("""
        {"kty": "OKP",
         "crv": "X25519",
         "x": "k9of_cpAajy0poW5gaixXGs9nHkwg1AFqUAFa39dyBc",
         "d": "x8EVZH4Fwk673_mUujnliJoSrLz0zYzzCWp5GUX2fc8"}
    """)
    alice = Key.from_jwk("""
        {"kty": "OKP",
         "crv": "X25519",
         "x": "Knbm_BcdQr7WIoz-uqit9M0wbcfEr6y-9UfIZ8QnBD4",
         "d": "i9KuFhSzEBsiv3PKVL5115OCdsqQai5nj_Flzfkw5jU"}
    """)
    bob = Key.from_jwk("""
        {"kty": "OKP",
         "crv": "X25519",
         "x": "BT7aR0ItXfeDAldeeOlXL_wXqp-j5FltT0vRSG16kRw",
         "d": "1gDirl_r_Y3-qUa3WXHgEXrrEHngWThU3c9zj9A2uBg"}
    """)

    alg = "ECDH-1PU+A128KW"
    enc = "A256CBC-HS512"
    apu = "Alice"
    apv = "Bob and Charlie"
    protected_b64 = b64_url(
        f'{{"alg":"{alg}",'
        f'"enc":"{enc}",'
        f'"apu":"{b64_url(apu)}",'
        f'"apv":"{b64_url(apv)}",'
        '"epk":'
        '{"kty":"OKP",'
        '"crv":"X25519",'
        '"x":"k9of_cpAajy0poW5gaixXGs9nHkwg1AFqUAFa39dyBc"}}').encode("ascii")
    protected = (f'{{"alg":"{alg}",'
                 f'"enc":"{enc}",'
                 f'"apu":"{b64_url(apu)}",'
                 f'"apv":"{b64_url(apv)}",'
                 '"epk":'
                 '{"kty":"OKP",'
                 '"crv":"X25519",'
                 '"x":"k9of_cpAajy0poW5gaixXGs9nHkwg1AFqUAFa39dyBc"}}')

    assert protected == (
        '{"alg":"ECDH-1PU+A128KW",'
        '"enc":"A256CBC-HS512",'
        '"apu":"QWxpY2U",'  # Alice
        '"apv":"Qm9iIGFuZCBDaGFybGll",'  # Bob and Charlie
        '"epk":'
        '{"kty":"OKP",'
        '"crv":"X25519",'
        '"x":"k9of_cpAajy0poW5gaixXGs9nHkwg1AFqUAFa39dyBc"}}')

    cek = Key.from_secret_bytes(
        KeyAlg.A256CBC_HS512,
        bytes.fromhex("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0"
                      "efeeedecebeae9e8e7e6e5e4e3e2e1e0"
                      "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0"
                      "cfcecdcccbcac9c8c7c6c5c4c3c2c1c0"),
    )
    iv = bytes.fromhex("000102030405060708090a0b0c0d0e0f")
    message = b"Three is a magic number."

    enc = cek.aead_encrypt(message, nonce=iv, aad=protected_b64)
    ciphertext, cc_tag = enc.ciphertext, enc.tag
    assert b64_url(ciphertext) == "Az2IWsISEMDJvyc5XRL-3-d-RgNBOGolCsxFFoUXFYw"
    assert b64_url(cc_tag) == "HLb4fTlm8spGmij3RyOs2gJ4DpHM4hhVRwdF_hGb3WQ"

    derived = Ecdh1PU(alg, apu, apv)._derive_key(
        KeyAlg.A128KW,
        ephem,
        sender_key=alice,
        receiver_key=bob,
        cc_tag=cc_tag,
        receive=False,
    )
    assert derived.get_secret_bytes() == bytes.fromhex(
        "df4c37a0668306a11e3d6b0074b5d8df")

    encrypted_key = derived.wrap_key(cek).ciphertext_tag
    assert b64_url(encrypted_key) == (
        "pOMVA9_PtoRe7xXW1139NzzN1UhiFoio8lGto9cf0t8PyU-"
        "sjNXH8-LIRLycq8CHJQbDwvQeU1cSl55cQ0hGezJu2N9IY0QN")

    # test sender_wrap_key
    encrypted_key_2 = Ecdh1PU(alg, apu, apv).sender_wrap_key(
        KeyAlg.A128KW,
        ephem,
        alice,
        bob,
        cek,
        cc_tag=cc_tag,
    )
    assert encrypted_key_2.ciphertext_tag == encrypted_key

    # Skipping key derivation for Charlie.
    # Assemble encrypted_key, iv, cc_tag, ciphertext, and headers into a JWE envelope here.
    # Receiver disassembles envelope and..

    derived_recv = Ecdh1PU(alg, apu, apv)._derive_key(
        KeyAlg.A128KW,
        ephem,
        sender_key=alice,
        receiver_key=bob,
        cc_tag=cc_tag,
        receive=True,
    )

    cek_recv = derived_recv.unwrap_key(KeyAlg.A256CBC_HS512, encrypted_key)
    assert cek_recv.get_jwk_secret() == cek.get_jwk_secret()

    message_recv = cek_recv.aead_decrypt(ciphertext,
                                         nonce=iv,
                                         aad=protected_b64,
                                         tag=cc_tag)
    assert message_recv == message

    # test receiver_wrap_key
    cek_recv_2 = Ecdh1PU(alg, apu, apv).receiver_unwrap_key(
        KeyAlg.A128KW,
        KeyAlg.A256CBC_HS512,
        ephem,
        sender_key=alice,
        receiver_key=bob,
        ciphertext=encrypted_key,
        cc_tag=cc_tag,
    )
    assert cek_recv_2.get_jwk_secret() == cek.get_jwk_secret()