Browse Source

Implemented userid validation and pluggable interface to implement different authentication backends.

Rewrote comments in the example configuration.
Moved all global test code into a plugin (plugin-test-authorize.js).
pull/28/head
Simon Eisenmann 11 years ago
parent
commit
a46f36fd48
  1. 109
      doc/plugin-test-authorize.js
  2. 116
      server.conf.in
  3. 7
      src/app/spreed-speakfreely-server/main.go
  4. 8
      src/app/spreed-speakfreely-server/session.go
  5. 43
      src/app/spreed-speakfreely-server/sessions.go
  6. 112
      src/app/spreed-speakfreely-server/users.go
  7. 3
      static/js/base.js
  8. 26
      static/js/libs/sjcl.js
  9. 4
      static/js/main.js
  10. 64
      static/js/services/mediastream.js

109
doc/plugin-test-authorize.js

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
/*
* Spreed Speak Freely.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed Speak Freely.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
define(['angular', 'sjcl'], function(angular, sjcl) {
return {
initialize: function(app) {
var lastNonce = null;
var lastUserid = null;
var disconnectTimeout = null;
app.run(["$window", "mediaStream", function($window, mediaStream) {
console.log("Injecting test plugin functions to window ...");
$window.testDisconnect = function() {
if (disconnectTimeout) {
$window.clearInterval(disconnectTimeout);
disconnectTimeout = null;
console.info("Stopped disconnector.");
return;
}
disconnectTimeout = $window.setInterval(function() {
console.info("Test disconnect!");
mediaStream.connector.conn.close();
}, 10000);
console.info("Started disconnector.");
};
$window.testCreateSuserid = function(key, userid) {
var k = sjcl.codec.utf8String.toBits(key);
var foo = new sjcl.misc.hmac(k, sjcl.hash.sha256)
var expiration = parseInt(((new Date).getTime()/1000)+3600, 10);
var useridCombo = ""+expiration+":"+userid;
var secret = foo.mac(useridCombo);
return [useridCombo, sjcl.codec.base64.fromBits(secret)]
};
$window.testAuthorize = function(useridCombo, secret) {
console.log("Testing authorize with userid", useridCombo, secret);
var url = mediaStream.url.api("sessions") + "/" + mediaStream.api.id + "/";
console.log("URL", url);
var data = {
id: mediaStream.api.id,
sid: mediaStream.api.sid,
useridcombo: useridCombo,
secret: secret
}
console.log("Data", data);
$.ajax({
type: "PATCH",
url: url,
contentType: "application/json",
dataType: "json",
data: JSON.stringify(data),
success: function(data) {
if (data.success) {
lastNonce = data.nonce;
lastUserid = data.userid;
console.log("Retrieved nonce", lastNonce, lastUserid);
}
},
error: function() {
console.log("error", arguments)
}
});
};
$window.testAuthenticate = function() {
if (!lastNonce || !lastUserid) {
console.log("Run testAuthorize first.");
return
}
mediaStream.api.requestAuthentication(lastUserid, lastNonce);
};
}]);
}
}
});

116
server.conf.in

@ -1,35 +1,99 @@ @@ -1,35 +1,99 @@
# Spreed Speak Freely server example configuration
; Spreed Speak Freely server example configuration
[http]
; HTTP listener in format ip:port.
listen = 127.0.0.1:8080
#root = /usr/share/spreed-speakfreely-server/www
#readtimeout = 10
#writetimeout = 10
#basePath = /some/sub/path/ # Set this when running behind a web server under a sub path.
#maxfd = 32768 # Try to set max open files limit on start (works only when run as root).
#stats = true # Provide stats API at /api/v1/stats (do not enable this in production or unprotected!).
#pprofListen = 127.0.0.1:6060 # See http://golang.org/pkg/net/http/pprof/ for details
; Full path to directory where to find the server web assets.
;root = /usr/share/spreed-speakfreely-server/www
; HTTP socket read timeout in seconds.
;readtimeout = 10
; HTTP socket write timeout in seconds.
;writetimeout = 10
; Use basePath if the server does not run on the root path (/) of your server.
;basePath = /some/sub/path/
; Set maximum number of open files (only works when run as root).
;maxfd = 32768
; Enable stats API /api/v1/stats for debugging (not for production use!).
;stats = false
; Enable HTTP listener for golang pprof module. See
; http://golang.org/pkg/net/http/pprof/ for details.
;pprofListen = 127.0.0.1:6060
[https]
#listen = 127.0.0.1:8443
#certificate = server.crt # Full path to certificate.
#key = server.key # Full path to key.
#minVersion = SSLv3 # Minimal supported encryption (SSLv3, TLSv1, TLSv1.1, TLSv1.2).
#readtimeout = 10
#writetimeout = 10
; Native HTTPS listener in format ip:port.
;listen = 127.0.0.1:8443
; Full path to PEM encoded certificate chain.
;certificate = server.crt
; Full path to PEM encoded private key.
;key = server.key
; Mimimal supported encryption standard (SSLv3, TLSv1, TLSv1.1 or TLSv1.2).
;minVersion = SSLv3
; HTTPS socket read timeout in seconds.
;readtimeout = 10
; HTTPS socket write timeout in seconds.
;writetimeout = 10
[app]
#title = Spreed Speak Freely
#ver = 1234 # version string to use for static resource
#stunURIs = stun.l.google.com:19302
#turnURIs = turn:turnserver:port?transport=udp turn:anotherturnserver:port?transport=tcp turns:turnserver:443?transport=tcp
#turnSecret = the-default-turn-shared-secret-do-not-keep
sessionSecret = the-default-secret-do-not-keep-me # Use 32 or 64 bytes random data
#tokenFile = tokens.txt # If set, everyone needs to give one of the tokens to launch the web client. One token per line in the file.
#globalRoom = global # Enables a global room. Users in that room are in all rooms.
#defaultRoomEnabled = true # Set to false to disable default room.
#extra = /usr/share/spreed-speakfreely-server/extra # Extra templates directory. Add .html files to define extra-* template slots here.
#plugin = plugins/example1 # Plugin support.
; HTML page title
;title = Spreed Speak Freely
; Version string to use for static resources. This defaults to the server
; version and should only be changed when you use your own way to invalidate
; long cached static resources.
;ver = 1234
; STUN server URIs in format host:port. You can provide multiple seperated by
; space. If you do not have one use a public one like stun.l.google.com:19302.
; If you have a TURN server you do not need to set an STUN server as the TURN
; server will normally do STUN too.
;stunURIs = stun.l.google.com:19302
; TURN server URIs in format host:port?transport=udp|tcp. You can provide
; multiple seperated by space. If you do not have at least one TURN server then
; some users will not be able to use the server as the peer to peer connection
; cannot be established without a TURN server due to firewall reasons. An open
; source TURN server which is fully supported can be found at
; https://code.google.com/p/rfc5766-turn-server/.
;turnURIs = turn:turnserver:port?transport=udp turns:turnserver:443?transport=tcp
; Shared secret authentication for TURN user generation if the TURN server is
; protected (which it should be).
; See http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 for details.
; A supported TURN server is https://code.google.com/p/rfc5766-turn-server/.
;turnSecret = the-default-turn-shared-secret-do-not-keep
; Session secret to use for session id generator. 32 or 64 bytes of random data
; are recommented.
sessionSecret = the-default-secret-do-not-keep-me
; Full path to a text file containig client tokens which a user needs to enter
; when accessing the web client. Each line in this file represents a valid token.
;tokenFile = tokens.txt
; The name of a global room. If enabled it should be kept secret. Users in that
; room are visible in all other rooms.
;globalRoom = global
; The default room is the room at the root URL of the servers base address and
; all users will join this room if enabled. If it is disabled then a room join
; form will be shown instead.
;defaultRoomEnabled = true
; Full path to an extra templates directory. Templates in this directory ending
; with .html will be parsed on startup and can be used to fill the supported
; extra-* template slots. If the extra folder has a sub folder "static", the
; resources in this static folder will be available as /extra/static/filename
; relative to your servers base URL.
;extra = /usr/share/spreed-speakfreely-server/extra
; URL relative to the servers base path for a plugin javascript file which is
; automatically loaded on web client start for all users. You can put your
; plugin in the extra/static folder (see above) or provide another folder using
; a front end webserver. Check the doc folder for more info about plugins and
; examples.
;plugin = extra/static/myplugin.js
[log]
#logfile = /var/log/spreed-speakfreely-server.log
;logfile = /var/log/spreed-speakfreely-server.log
[users]
; Set to true to enable user functionality.
;enabled = false
; Set authorization mode for users. Currently implemented is the "sharedsecret"
; mode which does validate the userid with a HMAC authentication secret.
; The format goes like this:
; BASE64(HMAC-SHA-256(secret, expirationTimestampInSeconds:userid))
;mode = sharedsecret
; The shared secred for HMAC validation in "sharedsecret" mode. Best use 32 or
; 64 bytes of random data.
;sharedsecret_secret = some-secret-do-not-keep

7
src/app/spreed-speakfreely-server/main.go

@ -270,6 +270,9 @@ func runner(runtime phoenix.Runtime) error { @@ -270,6 +270,9 @@ func runner(runtime phoenix.Runtime) error {
tokenProvider = TokenFileProvider(tokenFile)
}
// Create Users handler.
users := NewUsers(runtime)
// Create configuration data structure.
config = NewConfig(title, ver, runtimeVersion, basePath, stunURIs, turnURIs, tokenProvider != nil, globalRoomid, defaultRoomEnabled, plugin)
@ -343,7 +346,9 @@ func runner(runtime phoenix.Runtime) error { @@ -343,7 +346,9 @@ func runner(runtime phoenix.Runtime) error {
api.SetMux(r.PathPrefix("/api/v1/").Subrouter())
api.AddResource(&Rooms{}, "/rooms")
api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens")
api.AddResource(&Sessions{hub: hub}, "/sessions/{id}/")
if users.Enabled {
api.AddResource(&Sessions{hub: hub, users: users}, "/sessions/{id}/")
}
if statsEnabled {
api.AddResourceWithWrapper(&Stats{hub: hub}, httputils.MakeGzipHandler, "/stats")
log.Println("Stats are enabled!")

8
src/app/spreed-speakfreely-server/session.go

@ -205,10 +205,10 @@ type SessionUpdate struct { @@ -205,10 +205,10 @@ type SessionUpdate struct {
}
type SessionToken struct {
Id string
Sid string
Userid string
Nonce string `json:"Nonce,omitempty"`
Id string // Public session id.
Sid string // Secret session id.
Userid string // Public user id.
Nonce string `json:"Nonce,omitempty"` // User autentication nonce.
}
func init() {

43
src/app/spreed-speakfreely-server/sessions.go

@ -24,16 +24,26 @@ package main @@ -24,16 +24,26 @@ package main
import (
"encoding/json"
"github.com/gorilla/mux"
"log"
"net/http"
)
type SessionNonce struct {
Nonce string `json:"nonce"`
Userid string `json:"userid"`
Success bool `json:"success"`
}
type SessionNonceRequest struct {
Id string `json:"id"` // Public session id.
Sid string `json:"sid"` // Private session id.
UseridCombo string `json:"useridcombo"` // Public user id as used secret (Expiration:Userid)
Secret string `json:"secret"` // base64(hmac-sha265(SecretKey, UseridCombo))
}
type Sessions struct {
hub *Hub
users *Users
}
// Patch is used to add a userid to a given session (login).
@ -43,8 +53,8 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H @@ -43,8 +53,8 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H
error := false
decoder := json.NewDecoder(request.Body)
var st SessionToken
err := decoder.Decode(&st)
var snr SessionNonceRequest
err := decoder.Decode(&snr)
if err != nil {
error = true
}
@ -56,32 +66,42 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H @@ -56,32 +66,42 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H
}
// Make sure data matches request.
if id != st.Id {
if id != snr.Id {
error = true
log.Println("Session patch failed - request id mismatch.")
}
// Make sure that we have a Sid.
if st.Sid == "" {
if snr.Sid == "" {
error = true
log.Println("Session patch failed - sid empty.")
}
// Make sure that we have a user.
if st.Userid == "" {
// Validate with users handler.
userid, err := sessions.users.Handler.Validate(&snr)
if err != nil {
error = true
log.Println("Session patch failed - users validation failed.", err)
}
// TODO(longsleep): Validate userid.
// Make sure that we have a user.
if userid == "" {
error = true
log.Println("Session patch failed - userid empty.")
}
// Make sure Sid matches session.
if !sessions.hub.ValidateSession(st.Id, st.Sid) {
if !sessions.hub.ValidateSession(snr.Id, snr.Sid) {
log.Println("Session patch failed - validation failed.")
error = true
}
var nonce string
if !error {
// FIXME(longsleep): Not running this might releal error state with a timing attack.
nonce, err = sessions.hub.sessiontokenHandler(&st)
// FIXME(longsleep): Not running this might reveal error state with a timing attack.
nonce, err = sessions.hub.sessiontokenHandler(&SessionToken{Id: snr.Id, Sid: snr.Sid, Userid: userid})
if err != nil {
log.Println("Session patch failed - handle failed.", err)
error = true
}
}
@ -90,6 +110,7 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H @@ -90,6 +110,7 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H
return 403, NewApiError("session_patch_failed", "Failed to patch session"), http.Header{"Content-Type": {"application/json"}}
}
return 200, &SessionNonce{Nonce: nonce, Success: true}, http.Header{"Content-Type": {"application/json"}}
log.Printf("Session patch successfull %s -> %s\n", snr.Id, userid)
return 200, &SessionNonce{Nonce: nonce, Userid: userid, Success: true}, http.Header{"Content-Type": {"application/json"}}
}

112
src/app/spreed-speakfreely-server/users.go

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
/*
* Spreed Speak Freely.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed Speak Freely.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"github.com/strukturag/phoenix"
"log"
"strconv"
"strings"
"time"
)
type UsersHandler interface {
Validate(snr *SessionNonceRequest) (string, error)
}
type UsersSharedsecretHandler struct {
secret []byte
}
func (uh *UsersSharedsecretHandler) Validate(snr *SessionNonceRequest) (string, error) {
// Parse UseridCombo.
useridCombo := strings.SplitN(snr.UseridCombo, ":", 2)
expirationString, userid := useridCombo[0], useridCombo[1]
expiration, err := strconv.ParseInt(expirationString, 10, 64)
if err != nil {
return "", err
}
// Check expiration.
if time.Unix(expiration, 0).Before(time.Now()) {
return "", errors.New("expired secret")
}
// Check HMAC.
foo := hmac.New(sha256.New, uh.secret)
foo.Write([]byte(snr.UseridCombo))
fooSecret := base64.StdEncoding.EncodeToString(foo.Sum(nil))
if snr.Secret != fooSecret {
return "", errors.New("invalid secret")
}
return userid, nil
}
type Users struct {
Enabled bool
Handler UsersHandler
}
func NewUsers(runtime phoenix.Runtime) *Users {
enabled := false
enabledString, err := runtime.GetString("users", "enabled")
if err == nil {
enabled = enabledString == "true"
}
var handler UsersHandler
if enabled {
mode, _ := runtime.GetString("users", "mode")
switch mode {
case "sharedsecret":
secret, _ := runtime.GetString("users", "sharedsecret_secret")
if secret != "" {
handler = &UsersSharedsecretHandler{secret: []byte(secret)}
}
default:
mode = ""
}
if handler == nil {
enabled = false
} else {
log.Printf("Enabled users handler '%s'.\n", mode)
}
}
return &Users{
Enabled: enabled,
Handler: handler,
}
}

3
static/js/base.js

@ -31,5 +31,6 @@ define([ @@ -31,5 +31,6 @@ define([
'audiocontext',
'rAF',
'humanize',
'sha'
'sha',
'sjcl'
], function(){});

26
static/js/libs/sjcl.js

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// http://bitwiseshiftleft.github.io/sjcl/
// ./configure --without-all --with-sha256 --with-sha512 --with-sha1 --with-hmac --with-codecBase64 --with-codecString
// Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh, Stanford University.
// SJCL is dual-licensed under the GNU GPL version 2.0 or higher, and a 2-clause BSD license.
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.l(a.slice(b/32),32-(b&31)).slice(1);return void 0===c?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var e=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-e^a[b/32+1|0]>>>e:a[b/32|0]>>>e)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],e=sjcl.bitArray.getPartial(c);return 32===e?a.concat(b):sjcl.bitArray.l(b,e,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return!1;var c=0,e;for(e=0;e<a.length;e++)c|=a[e]^b[e];return 0===
c},l:function(a,b,c,e){var d;d=0;for(void 0===e&&(e=[]);32<=b;b-=32)e.push(c),c=0;if(0===b)return e.concat(a);for(d=0;d<a.length;d++)e.push(c|a[d]>>>b),c=a[d]<<32-b;d=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(d);e.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:e.pop(),1));return e},p:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),e,d;for(e=0;e<c/8;e++)0===(e&3)&&(d=a[e/4]),b+=String.fromCharCode(d>>>24),d<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,e=0;for(c=0;c<a.length;c++)e=e<<8|a.charCodeAt(c),3===(c&3)&&(b.push(e),e=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),e));return b}};
sjcl.codec.base64={j:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var e="",d=0,h=sjcl.codec.base64.j,k=0,f=sjcl.bitArray.bitLength(a);c&&(h=h.substr(0,62)+"-_");for(c=0;6*e.length<f;)e+=h.charAt((k^a[c]>>>d)>>>26),6>d?(k=a[c]<<6-d,d+=26,c++):(k<<=6,d-=6);for(;e.length&3&&!b;)e+="=";return e},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],e,d=0,h=sjcl.codec.base64.j,k=0,f;b&&(h=h.substr(0,62)+"-_");for(e=0;e<a.length;e++){f=h.indexOf(a.charAt(e));
if(0>f)throw new sjcl.exception.invalid("this isn't base64!");26<d?(d-=26,c.push(k^f>>>d),k=f<<32-d):(d+=6,k^=f<<32-d)}d&56&&c.push(sjcl.bitArray.partial(d&56,k,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.d[0]||this.h();a?(this.c=a.c.slice(0),this.b=a.b.slice(0),this.a=a.a):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.c=this.f.slice(0);this.b=[];this.a=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.b=sjcl.bitArray.concat(this.b,a);b=this.a;a=this.a=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.e(c.splice(0,16));return this},finalize:function(){var a,b=this.b,c=this.c,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.a/
4294967296));for(b.push(this.a|0);b.length;)this.e(b.splice(0,16));this.reset();return c},f:[],d:[],h:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,e;a:for(;64>b;c++){for(e=2;e*e<=c;e++)if(0===c%e)continue a;8>b&&(this.f[b]=a(Math.pow(c,0.5)));this.d[b]=a(Math.pow(c,1/3));b++}},e:function(a){var b,c,e=a.slice(0),d=this.c,h=this.d,k=d[0],f=d[1],g=d[2],u=d[3],m=d[4],v=d[5],w=d[6],x=d[7];for(a=0;64>a;a++)16>a?b=e[a]:(b=e[a+1&15],c=e[a+14&15],b=e[a&15]=(b>>>7^b>>>18^b>>>3^
b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+e[a&15]+e[a+9&15]|0),b=b+x+(m>>>6^m>>>11^m>>>25^m<<26^m<<21^m<<7)+(w^m&(v^w))+h[a],x=w,w=v,v=m,m=u+b|0,u=g,g=f,f=k,k=b+(f&g^u&(f^g))+(f>>>2^f>>>13^f>>>22^f<<30^f<<19^f<<10)|0;d[0]=d[0]+k|0;d[1]=d[1]+f|0;d[2]=d[2]+g|0;d[3]=d[3]+u|0;d[4]=d[4]+m|0;d[5]=d[5]+v|0;d[6]=d[6]+w|0;d[7]=d[7]+x|0}};sjcl.hash.sha512=function(a){this.d[0]||this.h();a?(this.c=a.c.slice(0),this.b=a.b.slice(0),this.a=a.a):this.reset()};sjcl.hash.sha512.hash=function(a){return(new sjcl.hash.sha512).update(a).finalize()};
sjcl.hash.sha512.prototype={blockSize:1024,reset:function(){this.c=this.f.slice(0);this.b=[];this.a=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.b=sjcl.bitArray.concat(this.b,a);b=this.a;a=this.a=b+sjcl.bitArray.bitLength(a);for(b=1024+b&-1024;b<=a;b+=1024)this.e(c.splice(0,32));return this},finalize:function(){var a,b=this.b,c=this.c,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+4;a&31;a++)b.push(0);b.push(0);b.push(0);
b.push(Math.floor(this.a/0x100000000));for(b.push(this.a|0);b.length;)this.e(b.splice(0,32));this.reset();return c},f:[],n:[12372232,13281083,9762859,1914609,15106769,4090911,4308331,8266105],d:[],o:[2666018,15689165,5061423,9034684,4764984,380953,1658779,7176472,197186,7368638,14987916,16757986,8096111,1480369,13046325,6891156,15813330,5187043,9229749,11312229,2818677,10937475,4324308,1135541,6741931,11809296,16458047,15666916,11046850,698149,229999,945776,13774844,2541862,12856045,9810911,11494366,
7844520,15576806,8533307,15795044,4337665,16291729,5553712,15684120,6662416,7413802,12308920,13816008,4303699,9366425,10176680,13195875,4295371,6546291,11712675,15708924,1519456,15772530,6568428,6495784,8568297,13007125,7492395,2515356,12632583,14740254,7262584,1535930,13146278,16321966,1853211,294276,13051027,13221564,1051980,4080310,6651434,14088940,4675607],h:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}function b(a){return 0x10000000000*(a-Math.floor(a))&255}var c=0,e=2,d;a:for(;80>
c;e++){for(d=2;d*d<=e;d++)if(0===e%d)continue a;8>c&&(this.f[2*c]=a(Math.pow(e,0.5)),this.f[2*c+1]=b(Math.pow(e,0.5))<<24|this.n[c]);this.d[2*c]=a(Math.pow(e,1/3));this.d[2*c+1]=b(Math.pow(e,1/3))<<24|this.o[c];c++}},e:function(a){var b,c,e=a.slice(0),d=this.c,h=this.d,k=d[0],f=d[1],g=d[2],u=d[3],m=d[4],v=d[5],w=d[6],x=d[7],R=d[8],H=d[9],S=d[10],I=d[11],T=d[12],J=d[13],U=d[14],K=d[15],q=k,n=f,A=g,y=u,B=m,z=v,N=w,C=x,r=R,p=H,L=S,D=I,M=T,E=J,O=U,F=K;for(a=0;80>a;a++){if(16>a)b=e[2*a],c=e[2*a+1];else{c=
e[2*(a-15)];var l=e[2*(a-15)+1];b=(l<<31|c>>>1)^(l<<24|c>>>8)^c>>>7;var s=(c<<31|l>>>1)^(c<<24|l>>>8)^(c<<25|l>>>7);c=e[2*(a-2)];var t=e[2*(a-2)+1],l=(t<<13|c>>>19)^(c<<3|t>>>29)^c>>>6,t=(c<<13|t>>>19)^(t<<3|c>>>29)^(c<<26|t>>>6),P=e[2*(a-7)],Q=e[2*(a-16)],G=e[2*(a-16)+1];c=s+e[2*(a-7)+1];b=b+P+(c>>>0<s>>>0?1:0);c+=t;b+=l+(c>>>0<t>>>0?1:0);c+=G;b+=Q+(c>>>0<G>>>0?1:0)}e[2*a]=b|=0;e[2*a+1]=c|=0;var P=r&L^~r&M,V=p&D^~p&E,t=q&A^q&B^A&B,X=n&y^n&z^y&z,Q=(n<<4|q>>>28)^(q<<30|n>>>2)^(q<<25|n>>>7),G=(q<<4|
n>>>28)^(n<<30|q>>>2)^(n<<25|q>>>7),Y=h[2*a],W=h[2*a+1],l=F+((r<<18|p>>>14)^(r<<14|p>>>18)^(p<<23|r>>>9)),s=O+((p<<18|r>>>14)^(p<<14|r>>>18)^(r<<23|p>>>9))+(l>>>0<F>>>0?1:0),l=l+V,s=s+(P+(l>>>0<V>>>0?1:0)),l=l+W,s=s+(Y+(l>>>0<W>>>0?1:0)),l=l+c,s=s+(b+(l>>>0<c>>>0?1:0));c=G+X;b=Q+t+(c>>>0<G>>>0?1:0);O=M;F=E;M=L;E=D;L=r;D=p;p=C+l|0;r=N+s+(p>>>0<C>>>0?1:0)|0;N=B;C=z;B=A;z=y;A=q;y=n;n=l+c|0;q=s+b+(n>>>0<l>>>0?1:0)|0}f=d[1]=f+n|0;d[0]=k+q+(f>>>0<n>>>0?1:0)|0;u=d[3]=u+y|0;d[2]=g+A+(u>>>0<y>>>0?1:0)|0;v=
d[5]=v+z|0;d[4]=m+B+(v>>>0<z>>>0?1:0)|0;x=d[7]=x+C|0;d[6]=w+N+(x>>>0<C>>>0?1:0)|0;H=d[9]=H+p|0;d[8]=R+r+(H>>>0<p>>>0?1:0)|0;I=d[11]=I+D|0;d[10]=S+L+(I>>>0<D>>>0?1:0)|0;J=d[13]=J+E|0;d[12]=T+M+(J>>>0<E>>>0?1:0)|0;K=d[15]=K+F|0;d[14]=U+O+(K>>>0<F>>>0?1:0)|0}};sjcl.hash.sha1=function(a){a?(this.c=a.c.slice(0),this.b=a.b.slice(0),this.a=a.a):this.reset()};sjcl.hash.sha1.hash=function(a){return(new sjcl.hash.sha1).update(a).finalize()};
sjcl.hash.sha1.prototype={blockSize:512,reset:function(){this.c=this.f.slice(0);this.b=[];this.a=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.b=sjcl.bitArray.concat(this.b,a);b=this.a;a=this.a=b+sjcl.bitArray.bitLength(a);for(b=this.blockSize+b&-this.blockSize;b<=a;b+=this.blockSize)this.e(c.splice(0,16));return this},finalize:function(){var a,b=this.b,c=this.c,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);
b.push(Math.floor(this.a/0x100000000));for(b.push(this.a|0);b.length;)this.e(b.splice(0,16));this.reset();return c},f:[1732584193,4023233417,2562383102,271733878,3285377520],d:[1518500249,1859775393,2400959708,3395469782],e:function(a){var b,c,e,d,h,k,f=a.slice(0),g=this.c;c=g[0];e=g[1];d=g[2];h=g[3];k=g[4];for(a=0;79>=a;a++)16<=a&&(f[a]=(f[a-3]^f[a-8]^f[a-14]^f[a-16])<<1|(f[a-3]^f[a-8]^f[a-14]^f[a-16])>>>31),b=19>=a?e&d|~e&h:39>=a?e^d^h:59>=a?e&d|e&h|d&h:79>=a?e^d^h:void 0,b=(c<<5|c>>>27)+b+k+f[a]+
this.d[Math.floor(a/20)]|0,k=h,h=d,d=e<<30|e>>>2,e=c,c=b;g[0]=g[0]+c|0;g[1]=g[1]+e|0;g[2]=g[2]+d|0;g[3]=g[3]+h|0;g[4]=g[4]+k|0}};sjcl.misc.hmac=function(a,b){this.k=b=b||sjcl.hash.sha256;var c=[[],[]],e,d=b.prototype.blockSize/32;this.g=[new b,new b];a.length>d&&(a=b.hash(a));for(e=0;e<d;e++)c[0][e]=a[e]^909522486,c[1][e]=a[e]^1549556828;this.g[0].update(c[0]);this.g[1].update(c[1]);this.i=new b(this.g[0])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){if(this.m)throw new sjcl.exception.invalid("encrypt on already updated hmac called!");this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.i=new this.k(this.g[0]);this.m=!1};sjcl.misc.hmac.prototype.update=function(a){this.m=!0;this.i.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.i.finalize(),a=(new this.k(this.g[1])).update(a).finalize();this.reset();return a};

4
static/js/main.js

@ -49,6 +49,7 @@ require.config({ @@ -49,6 +49,7 @@ require.config({
'humanize': 'libs/humanize',
'sha': 'libs/sha',
'dialogs': 'libs/angular/dialogs.min',
'sjcl': 'libs/sjcl',
'partials': '../partials',
'sounds': '../sounds',
@ -110,6 +111,9 @@ require.config({ @@ -110,6 +111,9 @@ require.config({
'dialogs': {
deps: ['angular', 'angular-sanitize'],
exports: 'angular'
},
'sjcl': {
exports: 'sjcl'
}
}
});

64
static/js/services/mediastream.js

@ -40,70 +40,6 @@ define([ @@ -40,70 +40,6 @@ define([
var api = new Api(connector);
var webrtc = new WebRTC(api);
var td = null;
$window.testDisconnect = function() {
if (td) {
$window.clearInterval(td);
td = null;
console.info("Stopped disconnector.");
return;
}
td = $window.setInterval(function() {
console.info("Test disconnect!");
connector.conn.close();
}, 10000);
console.info("Started disconnector.");
};
(function() {
var lastNonce = null;
var lastUserid = null;
$window.testAuthorize = function(userid) {
console.log("Testing authorize with userid", userid);
var url = mediaStream.url.api("sessions") + "/" + api.id + "/";
console.log("URL", url);
var data = {
Id: api.id,
Sid: api.sid,
Userid: userid
}
console.log("Data", data);
$.ajax({
type: "PATCH",
url: url,
contentType: "application/json",
dataType: "json",
data: JSON.stringify(data),
success: function(data) {
if (data.success) {
lastNonce = data.nonce;
lastUserid = userid;
console.log("Retrieved nonce", lastNonce, lastUserid);
}
},
error: function() {
console.log("error", arguments)
}
});
};
$window.testAuthenticate = function() {
if (!lastNonce || !lastUserid) {
console.log("Run testAuthorize first.");
return
}
api.requestAuthentication(lastUserid, lastNonce);
};
}())
var mediaStream = {
version: version,
ws: url,

Loading…
Cancel
Save