aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module/xep/0391_jingle_encrypted_transports.vala
blob: 1161ec96ca70b1c7718e3dc52c6145c2518634e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using Gee;
using Xmpp.Xep.Jingle;

namespace Xmpp.Xep.Jet {
public const string NS_URI = "urn:xmpp:jingle:jet:0";

public class Module : XmppStreamModule, SecurityPrecondition {
    public static Xmpp.ModuleIdentity<Module> IDENTITY = new Xmpp.ModuleIdentity<Module>(NS_URI, "0391_jet");
    private HashMap<string, EnvelopEncoding> envelop_encodings = new HashMap<string, EnvelopEncoding>();
    private HashMap<string, Cipher> ciphers = new HashMap<string, Cipher>();

    public override void attach(XmppStream stream) {
        stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
        stream.get_module(Jingle.Module.IDENTITY).register_security_precondition(this);
    }

    public override void detach(XmppStream stream) {
        stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
    }

    public bool is_available(XmppStream stream, Jid full_jid) {
        bool? has_feature = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI);
        return has_feature != null && (!)has_feature;
    }

    public void register_envelop_encoding(EnvelopEncoding encoding) {
        envelop_encodings[encoding.get_type_uri()] = encoding;
    }

    public void register_cipher(Cipher cipher) {
        ciphers[cipher.get_cipher_uri()] = cipher;
    }

    public string security_ns_uri() {
        return NS_URI;
    }

    public Jingle.SecurityParameters? create_security_parameters(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, Object options) throws Jingle.Error requires (options is Options) {
        Options jet_options = (Options) options;
        string cipher = jet_options.cipher_uri;
        string type = jet_options.type_uri;
        if (!envelop_encodings.has_key(type) || !ciphers.has_key(cipher)) {
            throw new Jingle.Error.UNSUPPORTED_SECURITY("JET cipher or type unknown");
        }
        EnvelopEncoding encoding = envelop_encodings[type];
        return new SecurityParameters(ciphers[cipher], encoding, ciphers[cipher].generate_random_secret(), jet_options);
    }

    public Jingle.SecurityParameters? parse_security_parameters(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, StanzaNode security) throws IqError {
        string? cipher = security.get_attribute("cipher");
        string? type = security.get_attribute("type");
        if (cipher == null || type == null) {
            throw new IqError.BAD_REQUEST("No cipher or type specified for JET");
        }
        if (!envelop_encodings.has_key(type) || !ciphers.has_key(cipher)) {
            throw new IqError.NOT_IMPLEMENTED("JET cipher or type unknown");
        }
        EnvelopEncoding encoding = envelop_encodings[type];
        TransportSecret secret = encoding.decode_envolop(stream, local_full_jid, peer_full_jid, security);
        return new SecurityParameters(ciphers[cipher], encoding, secret);
    }

    public override string get_ns() { return NS_URI; }
    public override string get_id() { return IDENTITY.id; }
}

public class Options : Object {
    public string type_uri { get; private set; }
    public string cipher_uri { get; private set; }

    public Options(string type_uri, string cipher_uri) {
        this.type_uri = type_uri;
        this.cipher_uri = cipher_uri;
    }
}

public class SecurityParameters : Jingle.SecurityParameters, Object {
    public Cipher cipher { get; private set; }
    public EnvelopEncoding encoding { get; private set; }
    public TransportSecret secret { get; private set; }
    public Options? options { get; private set; }

    public SecurityParameters(Cipher cipher, EnvelopEncoding encoding, TransportSecret secret, Options? options = null) {
        this.cipher = cipher;
        this.encoding = encoding;
        this.secret = secret;
        this.options = options;
    }

    public string security_ns_uri() {
        return NS_URI;
    }
    public IOStream wrap_stream(IOStream stream) {
        debug("Wrapping stream into encrypted stream for %s/%s", encoding.get_type_uri(), cipher.get_cipher_uri());
        return new EncryptedStream(cipher, secret, stream);
    }
    public StanzaNode to_security_stanza_node(XmppStream stream, Jid local_full_jid, Jid peer_full_jid) {
        StanzaNode security = new StanzaNode.build("security", NS_URI)
                .add_self_xmlns()
                .put_attribute("cipher", cipher.get_cipher_uri())
                .put_attribute("type", encoding.get_type_uri());
        encoding.encode_envelop(stream, local_full_jid, peer_full_jid, this, security);
        return security;
    }
}

public interface Cipher : Object {
    public abstract string get_cipher_uri();
    public abstract TransportSecret generate_random_secret();
    public abstract InputStream wrap_input_stream(InputStream input, TransportSecret secret);
    public abstract OutputStream wrap_output_stream(OutputStream output, TransportSecret secret);
}

private class EncryptedStream : IOStream {
    private IOStream stream;
    private InputStream input;
    private OutputStream output;
    public override InputStream input_stream { get { return input; } }
    public override OutputStream output_stream { get { return output; } }

    public EncryptedStream(Cipher cipher, TransportSecret secret, IOStream stream) {
        this.stream = stream;
        input = cipher.wrap_input_stream(stream.input_stream, secret);
        output = cipher.wrap_output_stream(stream.output_stream, secret);
    }
}

public class TransportSecret {
    public uint8[] transport_key { get; private set; }
    public uint8[] initialization_vector { get; private set; }
    public TransportSecret(uint8[] transport_key, uint8[] initialization_vector) {
        this.transport_key = transport_key;
        this.initialization_vector = initialization_vector;
    }
}

public interface EnvelopEncoding : Object {
    public abstract string get_type_uri();
    public abstract TransportSecret decode_envolop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, StanzaNode security) throws IqError;
    public abstract void encode_envelop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, SecurityParameters security_params, StanzaNode security);
}

}