1
0
mirror of https://github.com/Ylianst/MeshCommander synced 2025-12-05 21:53:19 +00:00

first commit

This commit is contained in:
Ylian Saint-Hilaire
2020-03-09 11:11:06 -07:00
commit 3deafe34ef
130 changed files with 141283 additions and 0 deletions

293
.gitignore vendored Normal file
View File

@@ -0,0 +1,293 @@
## Ignore nodejs things
[Ii]mages-branded/
[Ii]mages-brandedcommander/
[Ii]mages-isdu
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017-2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

38
VSSolution.sln Normal file
View File

@@ -0,0 +1,38 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Source", ".", "{431BAB2D-8D6E-43E6-B9E9-A7A286AA1CF8}"
ProjectSection(WebsiteProperties) = preProject
TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0"
Debug.AspNetCompiler.VirtualPath = "/localhost_56807"
Debug.AspNetCompiler.PhysicalPath = "..\Source\"
Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_56807\"
Debug.AspNetCompiler.Updateable = "true"
Debug.AspNetCompiler.ForceOverwrite = "true"
Debug.AspNetCompiler.FixedNames = "false"
Debug.AspNetCompiler.Debug = "True"
Release.AspNetCompiler.VirtualPath = "/localhost_56807"
Release.AspNetCompiler.PhysicalPath = "..\Source\"
Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_56807\"
Release.AspNetCompiler.Updateable = "true"
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
VWDPort = "56807"
SlnRelativePath = "..\Source\"
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{431BAB2D-8D6E-43E6-B9E9-A7A286AA1CF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{431BAB2D-8D6E-43E6-B9E9-A7A286AA1CF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

781
agent-desktop-0.0.2.js Normal file
View File

@@ -0,0 +1,781 @@
/**
* @description Remote Desktop
* @author Ylian Saint-Hilaire
* @version v0.0.2g
*/
// Construct a MeshServer object
var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
var obj = {}
obj.CanvasId = canvasid;
if (typeof canvasid === 'string') obj.CanvasId = Q(canvasid);
obj.Canvas = obj.CanvasId.getContext("2d");
obj.scrolldiv = scrolldiv;
obj.State = 0;
obj.PendingOperations = [];
obj.tilesReceived = 0;
obj.TilesDrawn = 0;
obj.KillDraw = 0;
obj.ipad = false;
obj.tabletKeyboardVisible = false;
obj.LastX = 0;
obj.LastY = 0;
obj.touchenabled = 0;
obj.submenuoffset = 0;
obj.touchtimer = null;
obj.TouchArray = {};
obj.connectmode = 0; // 0 = HTTP, 1 = WebSocket, 2 = WebRTC
obj.connectioncount = 0;
obj.rotation = 0;
obj.protocol = 2; // KVM
obj.debugmode = 0;
obj.firstUpKeys = [];
obj.stopInput = false;
obj.localKeyMap = true;
obj.altPressed = false;
obj.ctrlPressed = false;
obj.shiftPressed = false;
obj.sessionid = 0;
obj.username;
obj.oldie = false;
obj.CompressionLevel = 50;
obj.ScalingLevel = 1024;
obj.FrameRateTimer = 50;
obj.FirstDraw = false;
obj.ScreenWidth = 960;
obj.ScreenHeight = 700;
obj.width = 960;
obj.height = 960;
obj.onScreenSizeChange = null;
obj.onMessage = null;
obj.onConnectCountChanged = null;
obj.onDebugMessage = null;
obj.onTouchEnabledChanged = null;
obj.onDisplayinfo = null;
obj.accumulator = null;
obj.Start = function () {
obj.State = 0;
obj.accumulator = null;
}
obj.Stop = function () {
obj.setRotation(0);
obj.UnGrabKeyInput();
obj.UnGrabMouseInput();
obj.touchenabled = 0;
if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
obj.Canvas.clearRect(0, 0, obj.CanvasId.width, obj.CanvasId.height);
}
obj.xxStateChange = function (newstate) {
if (obj.State == newstate) return;
obj.State = newstate;
obj.CanvasId.style.cursor = 'default';
//console.log('xxStateChange', newstate);
switch (newstate) {
case 0: {
// Disconnect
obj.Stop();
break;
}
case 3: {
// Websocket connected
break;
}
}
}
obj.send = function (x) {
if (obj.debugmode > 1) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); }
if (obj.parent != null) { obj.parent.send(x); }
}
// KVM Control.
// Routines for processing incoming packets from the AJAX server, and handling individual messages.
obj.ProcessPictureMsg = function (str, X, Y) {
//if (obj.targetnode != null) obj.Debug("ProcessPictureMsg " + X + "," + Y + " - " + obj.targetnode.substring(0, 8));
var tile = new Image();
tile.xcount = obj.tilesReceived++;
//console.log('Tile #' + tile.xcount);
var r = obj.tilesReceived;
tile.src = "data:image/jpeg;base64," + btoa(str.substring(4, str.length));
tile.onload = function () {
//console.log('DecodeTile #' + this.xcount);
if (obj.Canvas != null && obj.KillDraw < r && obj.State != 0) {
obj.PendingOperations.push([r, 2, tile, X, Y]);
while (obj.DoPendingOperations()) { }
}
}
tile.error = function () { console.log('DecodeTileError'); }
}
obj.DoPendingOperations = function () {
if (obj.PendingOperations.length == 0) return false;
for (var i = 0; i < obj.PendingOperations.length; i++) { // && KillDraw < tilesDrawn
var Msg = obj.PendingOperations[i];
if (Msg[0] == (obj.TilesDrawn + 1)) {
if (Msg[1] == 1) { obj.ProcessCopyRectMsg(Msg[2]); }
else if (Msg[1] == 2) { obj.Canvas.drawImage(Msg[2], obj.rotX(Msg[3], Msg[4]), obj.rotY(Msg[3], Msg[4])); delete Msg[2]; }
obj.PendingOperations.splice(i, 1);
delete Msg;
obj.TilesDrawn++;
if (obj.TilesDrawn == obj.tilesReceived && obj.KillDraw < obj.TilesDrawn) { obj.KillDraw = obj.TilesDrawn = obj.tilesReceived = 0; }
return true;
}
}
if (obj.oldie && obj.PendingOperations.length > 0) { obj.TilesDrawn++; }
return false;
}
obj.ProcessCopyRectMsg = function (str) {
var SX = ((str.charCodeAt(0) & 0xFF) << 8) + (str.charCodeAt(1) & 0xFF);
var SY = ((str.charCodeAt(2) & 0xFF) << 8) + (str.charCodeAt(3) & 0xFF);
var DX = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
var DY = ((str.charCodeAt(6) & 0xFF) << 8) + (str.charCodeAt(7) & 0xFF);
var WIDTH = ((str.charCodeAt(8) & 0xFF) << 8) + (str.charCodeAt(9) & 0xFF);
var HEIGHT = ((str.charCodeAt(10) & 0xFF) << 8) + (str.charCodeAt(11) & 0xFF);
obj.Canvas.drawImage(Canvas.canvas, SX, SY, WIDTH, HEIGHT, DX, DY, WIDTH, HEIGHT);
}
obj.SendUnPause = function () {
//obj.Debug("SendUnPause");
//obj.xxStateChange(3);
obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00));
}
obj.SendPause = function () {
//obj.Debug("SendPause");
//obj.xxStateChange(2);
obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01));
}
obj.SendCompressionLevel = function (type, level, scaling, frametimer) {
if (level) { obj.CompressionLevel = level; }
if (scaling) { obj.ScalingLevel = scaling; }
if (frametimer) { obj.FrameRateTimer = frametimer; }
obj.send(String.fromCharCode(0x00, 0x05, 0x00, 0x0A, type, obj.CompressionLevel) + obj.shortToStr(obj.ScalingLevel) + obj.shortToStr(obj.FrameRateTimer));
}
obj.SendRefresh = function () {
obj.send(String.fromCharCode(0x00, 0x06, 0x00, 0x04));
}
obj.ProcessScreenMsg = function (width, height) {
if (obj.debugmode > 0) { console.log("ScreenSize: " + width + " x " + height); }
obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
obj.rotation = 0;
obj.FirstDraw = true;
obj.ScreenWidth = obj.width = width;
obj.ScreenHeight = obj.height = height;
obj.KillDraw = obj.tilesReceived;
while (obj.PendingOperations.length > 0) { obj.PendingOperations.shift(); }
obj.SendCompressionLevel(1);
obj.SendUnPause();
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
}
obj.ProcessData = function (str) {
var ptr = 0;
//console.log('x0', str.length);
while (ptr < str.length) {
//console.log('x1', ptr, str.length);
ptr += obj.ProcessDataEx(str.substring(ptr));
//console.log('x2', ptr, str.length);
}
}
obj.ProcessDataEx = function (str) {
if (obj.accumulator != null) {
str = obj.accumulator + str;
//console.log('KVM using accumulated data, total size is now ' + str.length + ' bytes.');
obj.accumulator = null;
}
if (obj.debugmode > 1) { console.log("KRecv(" + str.length + "): " + rstr2hex(str.substring(0, Math.min(str.length, 40)))); }
if (str.length < 4) return;
var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2), jumboAdd = 0;
if ((command == 27) && (cmdsize == 8)) {
// Jumbo packet
if (str.length < 12) return;
command = ReadShort(str, 8)
cmdsize = ReadInt(str, 4);
//console.log('JUMBO cmd=' + command + ', cmdsize=' + cmdsize + ', data received=' + str.length);
if ((cmdsize + 8) > str.length) {
//console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.');
obj.accumulator = str;
return;
}
str = str.substring(8);
jumboAdd = 8;
}
if ((cmdsize != str.length) && (obj.debugmode > 0)) { console.log(cmdsize, str.length, cmdsize == str.length); }
if ((command >= 18) && (command != 65)) { console.error("Invalid KVM command " + command + " of size " + cmdsize); console.log("Invalid KVM data", str.length, rstr2hex(str.substring(0, 40)) + '...'); return; }
if (cmdsize > str.length) {
//console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.');
obj.accumulator = str;
return;
}
//console.log("KVM Command: " + command + " Len:" + cmdsize);
if (command == 3 || command == 4 || command == 7) {
cmdmsg = str.substring(4, cmdsize);
X = ((cmdmsg.charCodeAt(0) & 0xFF) << 8) + (cmdmsg.charCodeAt(1) & 0xFF);
Y = ((cmdmsg.charCodeAt(2) & 0xFF) << 8) + (cmdmsg.charCodeAt(3) & 0xFF);
if (obj.debugmode > 0) { console.log("CMD" + command + " at X=" + X + " Y=" + Y); }
}
switch (command) {
case 3: // Tile
if (obj.FirstDraw) obj.onResize();
obj.ProcessPictureMsg(cmdmsg, X, Y);
break;
case 4: // Tile Copy
if (obj.FirstDraw) obj.onResize();
if (obj.TilesDrawn == obj.tilesReceived) {
obj.ProcessCopyRectMsg(cmdmsg);
} else {
obj.PendingOperations.push([ ++tilesReceived, 1, cmdmsg ]);
}
break;
case 7: // Screen size
obj.ProcessScreenMsg(X, Y);
obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
obj.SendKeyMsgKC(obj.KeyAction.UP, 17); // Ctrl
obj.SendKeyMsgKC(obj.KeyAction.UP, 18); // Alt
obj.SendKeyMsgKC(obj.KeyAction.UP, 91); // Left-Windows
obj.SendKeyMsgKC(obj.KeyAction.UP, 92); // Right-Windows
obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
obj.send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04));
break;
case 11: // GetDisplays
var selectedDisplay = 0, displays = { }, dcount = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
if (dcount > 0) {
// Many displays present
selectedDisplay = ((str.charCodeAt(6 + (dcount * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (dcount * 2)) & 0xFF);
for (var i = 0; i < dcount; i++) {
var disp = ((str.charCodeAt(6 + (i * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (i * 2)) & 0xFF);
if (disp == 65535) { displays[disp] = 'All Displays'; } else { displays[disp] = 'Display ' + disp; }
}
}
//console.log('Get Displays', displays, selectedDisplay, rstr2hex(str));
if (obj.onDisplayinfo != null) { obj.onDisplayinfo(obj, displays, selectedDisplay); }
break;
case 12: // SetDisplay
//console.log('SetDisplayConfirmed');
break;
case 14: // KVM_INIT_TOUCH
obj.touchenabled = 1;
obj.TouchArray = {};
if (obj.onTouchEnabledChanged != null) obj.onTouchEnabledChanged(obj.touchenabled);
break;
case 15: // KVM_TOUCH
obj.TouchArray = {};
break;
case 16: // MNG_KVM_CONNECTCOUNT
obj.connectioncount = ReadInt(str, 4);
//obj.Debug("Got KVM Connect Count: " + obj.connectioncount);
if (obj.onConnectCountChanged != null) obj.onConnectCountChanged(obj.connectioncount, obj);
break;
case 17: // MNG_KVM_MESSAGE
//obj.Debug("Got KVM Message: " + str.substring(4, cmdsize));
if (obj.onMessage != null) obj.onMessage(str.substring(4, cmdsize), obj);
break;
case 65: // Alert
str = str.substring(4);
if (str[0] != '.') {
console.log(str); //alert('KVM: ' + str);
if (obj.parent != null) {
obj.parent.consoleMessage = str;
if (obj.parent.onConsoleMessageChange) { obj.parent.onConsoleMessageChange(obj.parent, str); }
}
} else {
console.log('KVM: ' + str.substring(1));
}
break;
}
return cmdsize + jumboAdd;
}
// Keyboard and Mouse I/O.
obj.MouseButton = { "NONE": 0x00, "LEFT": 0x02, "RIGHT": 0x08, "MIDDLE": 0x20 };
obj.KeyAction = { "NONE": 0, "DOWN": 1, "UP": 2, "SCROLL": 3, "EXUP": 4, "EXDOWN": 5, "DBLCLICK": 6 };
obj.InputType = { "KEY": 1, "MOUSE": 2, "CTRLALTDEL": 10, "TOUCH": 15 };
obj.Alternate = 0;
var convertKeyCodeTable = {
"Pause": 19,
"CapsLock": 20,
"Space": 32,
"Quote": 222,
"Minus": 189,
"NumpadMultiply": 106,
"NumpadAdd": 107,
"PrintScreen": 44,
"Comma": 188,
"NumpadSubtract": 109,
"NumpadDecimal": 110,
"Period": 190,
"Slash": 191,
"NumpadDivide": 111,
"Semicolon": 186,
"Equal": 187,
"OSLeft": 91,
"BracketLeft": 219,
"OSRight": 91,
"Backslash": 220,
"BracketRight": 221,
"ContextMenu": 93,
"Backquote": 192,
"NumLock": 144,
"ScrollLock": 145,
"Backspace": 8,
"Tab": 9,
"Enter": 13,
"NumpadEnter": 13,
"Escape": 27,
"Delete": 46,
"Home": 36,
"PageUp": 33,
"PageDown": 34,
"ArrowLeft": 37,
"ArrowUp": 38,
"ArrowRight": 39,
"ArrowDown": 40,
"End": 35,
"Insert": 45,
"F1": 112,
"F2": 113,
"F3": 114,
"F4": 115,
"F5": 116,
"F6": 117,
"F7": 118,
"F8": 119,
"F9": 120,
"F10": 121,
"F11": 122,
"F12": 123,
"ShiftLeft": 16,
"ShiftRight": 16,
"ControlLeft": 17,
"ControlRight": 17,
"AltLeft": 18,
"AltRight": 18,
"MetaLeft": 91,
"MetaRight": 92,
"VolumeMute": 181
//"LaunchMail":
//"LaunchApp1":
//"LaunchApp2":
//"BrowserStop":
//"MediaStop":
//"MediaTrackPrevious":
//"MediaTrackNext":
//"MediaPlayPause":
//"MediaSelect":
}
function convertKeyCode(e) {
if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3); }
if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6) + 48; }
return convertKeyCodeTable[e.code];
}
obj.SendKeyMsg = function (action, event) {
if (action == null) return;
if (!event) { event = window.event; }
if (event.code && (obj.localKeyMap == false)) {
// Convert "event.code" into a scancode. This works the same regardless of the keyboard language.
// Older browsers will not support this.
var kc = convertKeyCode(event);
if (kc != null) { obj.SendKeyMsgKC(action, kc); }
} else {
// Use this keycode, this works best with "US-EN" keyboards.
// Older browser support this.
var kc = event.keyCode;
if (kc == 0x3B) { kc = 0xBA; } // Fix the ';' key
else if (kc == 173) { kc = 189; } // Fix the '-' key for Firefox
else if (kc == 61) { kc = 187; } // Fix the '=' key for Firefox
obj.SendKeyMsgKC(action, kc);
}
}
obj.SendMessage = function (msg) {
if (obj.State == 3) obj.send(String.fromCharCode(0x00, 0x11) + obj.shortToStr(4 + msg.length) + msg); // 0x11 = 17 MNG_KVM_MESSAGE
}
obj.SendKeyMsgKC = function (action, kc) {
//console.log('SendKeyMsgKC', action, kc);
if (obj.State != 3) return;
if (typeof action == 'object') { for (var i in action) { obj.SendKeyMsgKC(action[i][0], action[i][1]); } }
else { obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, (action - 1), kc)); }
}
obj.sendcad = function() { obj.SendCtrlAltDelMsg(); }
obj.SendCtrlAltDelMsg = function () {
if (obj.State == 3) { obj.send(String.fromCharCode(0x00, obj.InputType.CTRLALTDEL, 0x00, 0x04)); }
}
obj.SendEscKey = function () {
if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, 0x00, 0x1B, 0x00, obj.InputType.KEY, 0x00, 0x06, 0x01, 0x1B));
}
obj.SendStartMsg = function () {
obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
}
obj.SendCharmsMsg = function () {
obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
obj.SendKeyMsgKC(obj.KeyAction.DOWN, 67); // C
obj.SendKeyMsgKC(obj.KeyAction.UP, 67); // C
obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
}
obj.SendTouchMsg1 = function (id, flags, x, y) {
if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(14) + String.fromCharCode(0x01, id) + obj.intToStr(flags) + obj.shortToStr(x) + obj.shortToStr(y));
}
obj.SendTouchMsg2 = function (id, flags) {
var msg = '';
var flags2;
var str = "TOUCHSEND: ";
for (var k in obj.TouchArray) {
if (k == id) { flags2 = flags; } else {
if (obj.TouchArray[k].f == 1) { flags2 = 0x00010000 | 0x00000002 | 0x00000004; obj.TouchArray[k].f = 3; str += "START" + k; } // POINTER_FLAG_DOWN
else if (obj.TouchArray[k].f == 2) { flags2 = 0x00040000; str += "STOP" + k; } // POINTER_FLAG_UP
else flags2 = 0x00000002 | 0x00000004 | 0x00020000; // POINTER_FLAG_UPDATE
}
msg += String.fromCharCode(k) + obj.intToStr(flags2) + obj.shortToStr(obj.TouchArray[k].x) + obj.shortToStr(obj.TouchArray[k].y);
if (obj.TouchArray[k].f == 2) delete obj.TouchArray[k];
}
if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(5 + msg.length) + String.fromCharCode(0x02) + msg);
if (Object.keys(obj.TouchArray).length == 0 && obj.touchtimer != null) { clearInterval(obj.touchtimer); obj.touchtimer = null; }
}
obj.SendMouseMsg = function (Action, event) {
if (obj.State != 3) return;
if (Action != null && obj.Canvas != null) {
if (!event) { var event = window.event; }
var ScaleFactorHeight = (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
var ScaleFactorWidth = (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
var Offsets = obj.GetPositionOfControl(obj.Canvas.canvas);
var X = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
var Y = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
if (event.addx) { X += event.addx; }
if (event.addy) { Y += event.addy; }
if (X >= 0 && X <= obj.Canvas.canvas.width && Y >= 0 && Y <= obj.Canvas.canvas.height) {
var Button = 0;
var Delta = 0;
if (Action == obj.KeyAction.UP || Action == obj.KeyAction.DOWN) {
if (event.which) { ((event.which == 1) ? (Button = obj.MouseButton.LEFT) : ((event.which == 2) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
else if (event.button) { ((event.button == 0) ? (Button = obj.MouseButton.LEFT) : ((event.button == 1) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
}
else if (Action == obj.KeyAction.SCROLL) {
if (event.detail) { Delta = (-1 * (event.detail * 120)); } else if (event.wheelDelta) { Delta = (event.wheelDelta * 3); }
}
var MouseMsg = "";
if (Action == obj.KeyAction.DBLCLICK) {
MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, 0x88, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
} else if (Action == obj.KeyAction.SCROLL) {
MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0C, 0x00, 0x00, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF), ((Delta / 256) & 0xFF), (Delta & 0xFF));
} else {
MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, ((Action == obj.KeyAction.DOWN) ? Button : ((Button * 2) & 0xFF)), ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
}
if (obj.Action == obj.KeyAction.NONE) {
if (obj.Alternate == 0 || obj.ipad) { obj.send(MouseMsg); obj.Alternate = 1; } else { obj.Alternate = 0; }
} else {
obj.send(MouseMsg);
}
}
}
}
obj.GetDisplayNumbers = function () { obj.send(String.fromCharCode(0x00, 0x0B, 0x00, 0x04)); } // Get Terminal display
obj.SetDisplay = function (number) { console.log('Set display', number); obj.send(String.fromCharCode(0x00, 0x0C, 0x00, 0x06, number >> 8, number & 0xFF)); } // Set Terminal display
obj.intToStr = function (x) { return String.fromCharCode((x >> 24) & 0xFF, (x >> 16) & 0xFF, (x >> 8) & 0xFF, x & 0xFF); }
obj.shortToStr = function (x) { return String.fromCharCode((x >> 8) & 0xFF, x & 0xFF); }
obj.onResize = function () {
if (obj.ScreenWidth == 0 || obj.ScreenHeight == 0) return;
if (obj.Canvas.canvas.width == obj.ScreenWidth && obj.Canvas.canvas.height == obj.ScreenHeight) return;
if (obj.FirstDraw) {
obj.Canvas.canvas.width = obj.ScreenWidth;
obj.Canvas.canvas.height = obj.ScreenHeight;
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
}
obj.FirstDraw = false;
//obj.Debug("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight);
}
obj.xxMouseInputGrab = false;
obj.xxKeyInputGrab = false;
obj.xxMouseMove = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.NONE, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
obj.xxMouseUp = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.UP, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
obj.xxMouseDown = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DOWN, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
obj.xxMouseDblClick = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DBLCLICK, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
obj.xxDOMMouseScroll = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
obj.xxMouseWheel = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
obj.xxKeyUp = function (e) { if (obj.State == 3) { obj.SendKeyMsg(obj.KeyAction.UP, e); } if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
obj.xxKeyDown = function (e) { if (obj.State == 3) { obj.SendKeyMsg(obj.KeyAction.DOWN, e); } if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
obj.xxKeyPress = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
// Key handlers
obj.handleKeys = function (e) { if (obj.stopInput == true || desktop.State != 3) return false; return obj.xxKeyPress(e); }
obj.handleKeyUp = function (e) {
if (obj.stopInput == true || desktop.State != 3) return false;
if (obj.firstUpKeys.length < 5) {
obj.firstUpKeys.push(e.keyCode);
if ((obj.firstUpKeys.length == 5)) { var j = obj.firstUpKeys.join(','); if ((j == '16,17,91,91,16') || (j == '16,17,18,91,92')) { obj.stopInput = true; } }
}
if (e.keyCode == 16) { obj.shiftPressed = false; }
if (e.keyCode == 17) { obj.ctrlPressed = false; }
if (e.keyCode == 18) { obj.altPressed = false; }
return obj.xxKeyUp(e);
}
obj.handleKeyDown = function (e) {
if (obj.stopInput == true || desktop.State != 3) return false;
if (e.keyCode == 16) { obj.shiftPressed = true; }
if (e.keyCode == 17) { obj.ctrlPressed = true; }
if (e.keyCode == 18) { obj.altPressed = true; }
return obj.xxKeyDown(e);
}
// Release the CTRL, ALT, SHIFT keys if they are pressed.
obj.handleReleaseKeys = function () {
if (obj.shiftPressed) { obj.SendKeyMsgKC(obj.KeyAction.UP, 16); } // Shift
if (obj.ctrlPressed) { obj.SendKeyMsgKC(obj.KeyAction.UP, 17); } // Ctrl
if (obj.altPressed) { obj.SendKeyMsgKC(obj.KeyAction.UP, 18); } // Alt
obj.shiftPressed = obj.ctrlPressed = obj.altPressed = false;
}
// Mouse handlers
obj.mousedblclick = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDblClick(e); }
obj.mousedown = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDown(e); }
obj.mouseup = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseUp(e); }
obj.mousemove = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseMove(e); }
obj.mousewheel = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseWheel(e); }
obj.xxMsTouchEvent = function (evt) {
if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok.
if (evt.preventDefault) evt.preventDefault();
if (evt.stopPropagation) evt.stopPropagation();
if (evt.type == 'MSPointerDown' || evt.type == 'MSPointerMove' || evt.type == 'MSPointerUp') {
var flags = 0;
var id = evt.originalEvent.pointerId % 256;
var X = evt.offsetX * (Canvas.canvas.width / obj.CanvasId.clientWidth);
var Y = evt.offsetY * (Canvas.canvas.height / obj.CanvasId.clientHeight);
if (evt.type == 'MSPointerDown') flags = 0x00010000 | 0x00000002 | 0x00000004; // POINTER_FLAG_DOWN
else if (evt.type == 'MSPointerMove') {
//if (obj.TouchArray[id] && MuchTheSame(obj.TouchArray[id].x, X) && MuchTheSame(obj.TouchArray[id].y, Y)) return;
flags = 0x00020000 | 0x00000002 | 0x00000004; // POINTER_FLAG_UPDATE
}
else if (evt.type == 'MSPointerUp') flags = 0x00040000; // POINTER_FLAG_UP
if (!obj.TouchArray[id]) obj.TouchArray[id] = { x: X, y : Y };
obj.SendTouchMsg2(id, flags)
if (evt.type == 'MSPointerUp') delete obj.TouchArray[id];
} else {
alert(evt.type);
}
return true;
}
obj.xxTouchStart = function (e) {
if (obj.State != 3) return;
if (e.preventDefault) e.preventDefault();
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
if (e.originalEvent.touches.length > 1) return;
var t = e.originalEvent.touches[0];
e.which = 1;
obj.LastX = e.pageX = t.pageX;
obj.LastY = e.pageY = t.pageY;
obj.SendMouseMsg(KeyAction.DOWN, e);
} else {
var Offsets = obj.GetPositionOfControl(Canvas.canvas);
for (var i in e.originalEvent.changedTouches) {
if (!e.originalEvent.changedTouches[i].identifier) continue;
var id = e.originalEvent.changedTouches[i].identifier % 256;
if (!obj.TouchArray[id]) { obj.TouchArray[id] = { x: (e.originalEvent.touches[i].pageX - Offsets[0]) * (Canvas.canvas.width / obj.CanvasId.clientWidth), y: (e.originalEvent.touches[i].pageY - Offsets[1]) * (Canvas.canvas.height / obj.CanvasId.clientHeight), f: 1 }; }
}
if (Object.keys(obj.TouchArray).length > 0 && touchtimer == null) { obj.touchtimer = setInterval(function () { obj.SendTouchMsg2(256, 0); }, 50); }
}
}
obj.xxTouchMove = function (e) {
if (obj.State != 3) return;
if (e.preventDefault) e.preventDefault();
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
if (e.originalEvent.touches.length > 1) return;
var t = e.originalEvent.touches[0];
e.which = 1;
obj.LastX = e.pageX = t.pageX;
obj.LastY = e.pageY = t.pageY;
obj.SendMouseMsg(obj.KeyAction.NONE, e);
} else {
var Offsets = obj.GetPositionOfControl(Canvas.canvas);
for (var i in e.originalEvent.changedTouches) {
if (!e.originalEvent.changedTouches[i].identifier) continue;
var id = e.originalEvent.changedTouches[i].identifier % 256;
if (obj.TouchArray[id]) {
obj.TouchArray[id].x = (e.originalEvent.touches[i].pageX - Offsets[0]) * (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
obj.TouchArray[id].y = (e.originalEvent.touches[i].pageY - Offsets[1]) * (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
}
}
}
}
obj.xxTouchEnd = function (e) {
if (obj.State != 3) return;
if (e.preventDefault) e.preventDefault();
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
if (e.originalEvent.touches.length > 1) return;
e.which = 1;
e.pageX = LastX;
e.pageY = LastY;
obj.SendMouseMsg(KeyAction.UP, e);
} else {
for (var i in e.originalEvent.changedTouches) {
if (!e.originalEvent.changedTouches[i].identifier) continue;
var id = e.originalEvent.changedTouches[i].identifier % 256;
if (obj.TouchArray[id]) obj.TouchArray[id].f = 2;
}
}
}
obj.GrabMouseInput = function () {
if (obj.xxMouseInputGrab == true) return;
var c = obj.CanvasId;
c.onmousemove = obj.xxMouseMove;
c.onmouseup = obj.xxMouseUp;
c.onmousedown = obj.xxMouseDown;
c.touchstart = obj.xxTouchStart;
c.touchmove = obj.xxTouchMove;
c.touchend = obj.xxTouchEnd;
c.MSPointerDown = obj.xxMsTouchEvent;
c.MSPointerMove = obj.xxMsTouchEvent;
c.MSPointerUp = obj.xxMsTouchEvent;
if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
obj.xxMouseInputGrab = true;
}
obj.UnGrabMouseInput = function () {
if (obj.xxMouseInputGrab == false) return;
var c = obj.CanvasId;
c.onmousemove = null;
c.onmouseup = null;
c.onmousedown = null;
c.touchstart = null;
c.touchmove = null;
c.touchend = null;
c.MSPointerDown = null;
c.MSPointerMove = null;
c.MSPointerUp = null;
if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
obj.xxMouseInputGrab = false;
}
obj.GrabKeyInput = function () {
if (obj.xxKeyInputGrab == true) return;
document.onkeyup = obj.xxKeyUp;
document.onkeydown = obj.xxKeyDown;
document.onkeypress = obj.xxKeyPress;
obj.xxKeyInputGrab = true;
}
obj.UnGrabKeyInput = function () {
if (obj.xxKeyInputGrab == false) return;
document.onkeyup = null;
document.onkeydown = null;
document.onkeypress = null;
obj.xxKeyInputGrab = false;
}
obj.GetPositionOfControl = function (Control) {
var Position = Array(2);
Position[0] = Position[1] = 0;
while (Control) { Position[0] += Control.offsetLeft; Position[1] += Control.offsetTop; Control = Control.offsetParent; }
return Position;
}
obj.crotX = function (x, y) {
if (obj.rotation == 0) return x;
if (obj.rotation == 1) return y;
if (obj.rotation == 2) return obj.Canvas.canvas.width - x;
if (obj.rotation == 3) return obj.Canvas.canvas.height - y;
}
obj.crotY = function (x, y) {
if (obj.rotation == 0) return y;
if (obj.rotation == 1) return obj.Canvas.canvas.width - x;
if (obj.rotation == 2) return obj.Canvas.canvas.height - y;
if (obj.rotation == 3) return x;
}
obj.rotX = function (x, y) {
if (obj.rotation == 0 || obj.rotation == 1) return x;
if (obj.rotation == 2) return x - obj.Canvas.canvas.width;
if (obj.rotation == 3) return x - obj.Canvas.canvas.height;
}
obj.rotY = function (x, y) {
if (obj.rotation == 0 || obj.rotation == 3) return y;
if (obj.rotation == 1) return y - obj.Canvas.canvas.width;
if (obj.rotation == 2) return y - obj.Canvas.canvas.height;
}
obj.tcanvas = null;
obj.setRotation = function (x) {
while (x < 0) { x += 4; }
var newrotation = x % 4;
if (newrotation == obj.rotation) return true;
var rw = obj.Canvas.canvas.width;
var rh = obj.Canvas.canvas.height;
if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.Canvas.canvas.height; rh = obj.Canvas.canvas.width; }
// Copy the canvas, put it back in the correct direction
if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
var tcanvasctx = obj.tcanvas.getContext('2d');
tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
tcanvasctx.canvas.width = rw;
tcanvasctx.canvas.height = rh;
tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
if (obj.rotation == 0) tcanvasctx.drawImage(obj.Canvas.canvas, 0, 0);
if (obj.rotation == 1) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, 0);
if (obj.rotation == 2) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, -obj.Canvas.canvas.height);
if (obj.rotation == 3) tcanvasctx.drawImage(obj.Canvas.canvas, 0, -obj.Canvas.canvas.height);
// Change the size and orientation and copy the canvas back into the rotation
if (obj.rotation == 0 || obj.rotation == 2) { obj.Canvas.canvas.height = rw; obj.Canvas.canvas.width = rh; }
if (obj.rotation == 1 || obj.rotation == 3) { obj.Canvas.canvas.height = rh; obj.Canvas.canvas.width = rw; }
obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
obj.Canvas.rotate((newrotation * 90) * Math.PI / 180);
obj.rotation = newrotation;
obj.Canvas.drawImage(obj.tcanvas, obj.rotX(0, 0), obj.rotY(0, 0));
obj.ScreenWidth = obj.Canvas.canvas.width;
obj.ScreenHeight = obj.Canvas.canvas.height;
if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
return true;
}
// Private method
obj.MuchTheSame = function (a, b) { return (Math.abs(a - b) < 4); }
obj.Debug = function (msg) { console.log(msg); }
obj.getIEVersion = function () { var r = -1; if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) r = parseFloat(RegExp.$1); } return r; }
obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
return obj;
}

145
agent-redir-rtc-0.1.0.js Normal file
View File

@@ -0,0 +1,145 @@
/**
* @description Mesh Agent Transport Module - using websocket relay
* @author Ylian Saint-Hilaire
* @version v0.0.1
*/
// Construct a MeshServer agent direction object
var CreateKvmDataChannel = function (webchannel, module, keepalive) {
var obj = {};
obj.m = module; // This is the inner module (Terminal or Desktop)
module.parent = obj;
obj.webchannel = webchannel;
obj.State = 0;
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
obj.onStateChanged = null;
obj.onControlMsg = null;
obj.debugmode = 0;
obj.keepalive = keepalive;
obj.rtcKeepAlive = null;
// Private method
//obj.debug = function (msg) { console.log(msg); }
obj.Start = function () {
if (obj.debugmode == 1) { console.log('start'); }
obj.xxStateChange(3);
obj.webchannel.onmessage = obj.xxOnMessage;
obj.rtcKeepAlive = setInterval(obj.xxSendRtcKeepAlive, 30000);
}
// Setup the file reader
var fileReader = new FileReader();
var fileReaderInuse = false, fileReaderAcc = [];
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
}
obj.xxOnMessage = function (e) {
//if (obj.debugmode == 1) { console.log('Recv', e.data); }
//if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Recv(' + obj.State + '): ', typeof e.data, e.data); }
if (typeof e.data == 'string') { if (obj.onControlMsg != null) { obj.onControlMsg(e.data); } return; } // If this is a control message, handle it here.
if (typeof e.data == 'object') {
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReaderInuse = true;
fileReader.readAsBinaryString(new Blob([e.data]));
} else if (f.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReaderInuse = true;
fileReader.readAsArrayBuffer(e.data);
} else {
// IE10, readAsBinaryString does not exist, use an alternative.
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
obj.xxOnSocketData(binary);
}
} else {
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
//obj.debug("Agent Redir Relay - OnData - " + typeof e.data + " - " + e.data.length);
obj.xxOnSocketData(e.data);
}
};
/*
obj.xxOnMessage = function (e) {
//if (obj.debugmode == 1) { console.log('Recv', e.data); }
if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Recv(' + obj.State + '): ', typeof e.data, e.data); }
if (typeof e.data == 'string') { if (obj.onControlMsg != null) { obj.onControlMsg(e.data); } return; } // If this is a control message, handle it here.
if (typeof e.data == 'object') {
var f = new FileReader();
if (f.readAsBinaryString) {
// Chrome & Firefox (Draft)
f.onload = function (e) { obj.xxOnSocketData(e.target.result); }
f.readAsBinaryString(new Blob([e.data]));
} else if (f.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
f.onloadend = function (e) { obj.xxOnSocketData(e.target.result); }
f.readAsArrayBuffer(e.data);
} else {
// IE10, readAsBinaryString does not exist, use an alternative.
var binary = '', bytes = new Uint8Array(e.data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
obj.xxOnSocketData(binary);
}
} else {
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
//obj.debug("Agent Redir Relay - OnData - " + typeof e.data + " - " + e.data.length);
obj.xxOnSocketData(e.data);
}
};
*/
obj.xxOnSocketData = function (data) {
if (!data) return;
if (typeof data === 'object') {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
data = binary;
}
else if (typeof data !== 'string') return;
//console.log("xxOnSocketData", rstr2hex(data));
return obj.m.ProcessData(data);
}
// Send a control message over the WebRTC data channel
obj.sendCtrlMsg = function (x) {
if (typeof x == 'string') {
obj.webchannel.send(x);
if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Send(' + obj.State + '): ', typeof x, x); }
if (obj.keepalive != null) obj.keepalive.sendKeepAlive();
}
}
// Send a binary message over the WebRTC data channel
obj.send = function (x) {
if (typeof x == 'string') { var b = new Uint8Array(x.length); for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); } x = b; }
if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Send(' + obj.State + '): ', typeof x, x); }
obj.webchannel.send(x);
}
obj.xxStateChange = function(newstate) {
if (obj.State == newstate) return;
obj.State = newstate;
obj.m.xxStateChange(obj.State);
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
}
obj.Stop = function () {
if (obj.debugmode == 1) { console.log('stop'); }
if (obj.rtcKeepAlive != null) { clearInterval(obj.rtcKeepAlive); obj.rtcKeepAlive = null; }
obj.xxStateChange(0);
}
obj.xxSendRtcKeepAlive = function () {
if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-SendKeepAlive()'); }
obj.sendCtrlMsg(JSON.stringify({ action: 'ping' }));
}
return obj;
}

987
amt-0.2.0.js Normal file
View File

@@ -0,0 +1,987 @@
/**
* @fileoverview Intel(r) AMT Communication StackXX
* @author Ylian Saint-Hilaire
* @version v0.2.0b
*/
/**
* Construct a AmtStackCreateService object, this ia the main Intel AMT communication stack.
* @constructor
*/
function AmtStackCreateService(wsmanStack) {
var obj = new Object();
obj.wsman = wsmanStack;
obj.pfx = ["http://intel.com/wbem/wscim/1/amt-schema/1/", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/", "http://intel.com/wbem/wscim/1/ips-schema/1/"];
obj.PendingEnums = [];
obj.PendingBatchOperations = 0;
obj.ActiveEnumsCount = 0;
obj.MaxActiveEnumsCount = 1; // Maximum number of enumerations that can be done at the same time.
obj.onProcessChanged = null;
var _MaxProcess = 0;
var _LastProcess = 0;
// Return the number of pending actions
obj.GetPendingActions = function () { return (obj.PendingEnums.length * 2) + (obj.ActiveEnumsCount) + obj.wsman.comm.PendingAjax.length + obj.wsman.comm.ActiveAjaxCount + obj.PendingBatchOperations; }
// Private Method, Update the current processing status, this gives the application an idea of what progress is being done by the WSMAN stack
function _up() {
var x = obj.GetPendingActions();
if (_MaxProcess < x) _MaxProcess = x;
if (obj.onProcessChanged != null && _LastProcess != x) {
//console.log("Process Old=" + _LastProcess + ", New=" + x + ", PEnums=" + obj.PendingEnums.length + ", AEnums=" + obj.ActiveEnumsCount + ", PAjax=" + obj.wsman.comm.PendingAjax.length + ", AAjax=" + obj.wsman.comm.ActiveAjaxCount + ", PBatch=" + obj.PendingBatchOperations);
_LastProcess = x;
obj.onProcessChanged(x, _MaxProcess);
}
if (x == 0) _MaxProcess = 0;
}
// Perform a WSMAN "SUBSCRIBE" operation.
obj.Subscribe = function (name, delivery, url, callback, tag, pri, selectors, opaque, user, pass) { obj.wsman.ExecSubscribe(obj.CompleteName(name), delivery, url, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors, opaque, user, pass); _up(); }
// Perform a WSMAN "UNSUBSCRIBE" operation.
obj.UnSubscribe = function (name, callback, tag, pri, selectors) { obj.wsman.ExecUnSubscribe(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
// Perform a WSMAN "GET" operation.
obj.Get = function (name, callback, tag, pri) { obj.wsman.ExecGet(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
// Perform a WSMAN "PUT" operation.
obj.Put = function (name, putobj, callback, tag, pri, selectors) { obj.wsman.ExecPut(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
// Perform a WSMAN "CREATE" operation.
obj.Create = function (name, putobj, callback, tag, pri) { obj.wsman.ExecCreate(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
// Perform a WSMAN "DELETE" operation.
obj.Delete = function (name, putobj, callback, tag, pri) { obj.wsman.ExecDelete(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
// Perform a WSMAN method call operation.
obj.Exec = function (name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethod(obj.CompleteName(name), method, args, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
// Perform a WSMAN method call operation.
obj.ExecWithXml = function (name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethodXml(obj.CompleteName(name), method, execArgumentsToXml(args), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
// Perform a WSMAN "ENUMERATE" operation.
obj.Enum = function (name, callback, tag, pri) {
if (obj.ActiveEnumsCount < obj.MaxActiveEnumsCount) {
obj.ActiveEnumsCount++; obj.wsman.ExecEnum(obj.CompleteName(name), function (ws, resuri, response, xstatus, tag0) { _up(); _EnumStartSink(name, response, callback, resuri, xstatus, tag0); }, tag, pri);
} else {
obj.PendingEnums.push([name, callback, tag, pri]);
}
_up();
}
// Private method
function _EnumStartSink(name, response, callback, resuri, status, tag, pri) {
if (status != 200) { callback(obj, name, null, status, tag); _EnumDoNext(1); return; }
if (response == null || response.Header["Method"] != "EnumerateResponse" || !response.Body["EnumerationContext"]) { callback(obj, name, null, 603, tag); _EnumDoNext(1); return; }
var enumctx = response.Body["EnumerationContext"];
obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, [], xstatus, tag, pri); });
}
// Private method
function _EnumContinueSink(name, response, callback, resuri, items, status, tag, pri) {
if (status != 200) { callback(obj, name, null, status, tag); _EnumDoNext(1); return; }
if (response == null || response.Header["Method"] != "PullResponse") { callback(obj, name, null, 604, tag); _EnumDoNext(1); return; }
for (var i in response.Body["Items"]) {
if (response.Body["Items"][i] instanceof Array) {
for (var j in response.Body["Items"][i]) { if (typeof response.Body["Items"][i][j] != 'function') { items.push(response.Body["Items"][i][j]); } }
} else {
if (typeof response.Body["Items"][i] != 'function') { items.push(response.Body["Items"][i]); }
}
}
if (response.Body["EnumerationContext"]) {
var enumctx = response.Body["EnumerationContext"];
obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, items, xstatus, tag, 1); });
} else {
_EnumDoNext(1);
callback(obj, name, items, status, tag);
_up();
}
}
// Private method
function _EnumDoNext(dec) {
obj.ActiveEnumsCount -= dec;
if (obj.ActiveEnumsCount >= obj.MaxActiveEnumsCount || obj.PendingEnums.length == 0) { _up(); return; }
var x = obj.PendingEnums.shift();
obj.Enum(x[0], x[1], x[2]);
_EnumDoNext(0);
}
// Perform a batch of WSMAN "ENUM" operations.
obj.BatchEnum = function (batchname, names, callback, tag, continueOnError, pri) {
obj.PendingBatchOperations += (names.length * 2);
_BatchNextEnum(batchname, Clone(names), callback, tag, {}, continueOnError, pri); _up();
}
// Request each enum in the batch, stopping if something does not return status 200
function _BatchNextEnum(batchname, names, callback, tag, results, continueOnError, pri) {
obj.PendingBatchOperations -= 2;
var n = names.shift(), f = obj.Enum;
if (n[0] == '*') { f = obj.Get; n = n.substring(1); } // If the name starts with a star, do a GET instead of an ENUM. This will reduce round trips.
//console.log((f == obj.Get?'Get ':'Enum ') + n);
// Perform a GET/ENUM action
f(n, function (stack, name, responses, status, tag0) {
tag0[2][name] = { response: (responses==null?null:responses.Body), responses: responses, status: status };
if (tag0[1].length == 0 || status == 401 || (continueOnError != true && status != 200 && status != 400)) { obj.PendingBatchOperations -= (names.length * 2); _up(); callback(obj, batchname, tag0[2], status, tag); }
else { _up(); _BatchNextEnum(batchname, names, callback, tag, tag0[2], pri); }
}, [batchname, names, results], pri);
_up();
}
// Perform a batch of WSMAN "GET" operations.
obj.BatchGet = function (batchname, names, callback, tag, pri) {
_FetchNext({ name: batchname, names: names, callback: callback, current: 0, responses: {}, tag: tag, pri: pri }); _up();
}
// Private method
function _FetchNext(batch) {
if (batch.names.length <= batch.current) {
batch.callback(obj, batch.name, batch.responses, 200, batch.tag);
} else {
obj.wsman.ExecGet(obj.CompleteName(batch.names[batch.current]), function (ws, resuri, response, xstatus) { _Fetched(batch, response, xstatus); }, batch.pri);
batch.current++;
}
_up();
}
// Private method
function _Fetched(batch, response, status) {
if (response == null || status != 200) {
batch.callback(obj, batch.name, null, status, batch.tag);
} else {
batch.responses[response.Header["Method"]] = response;
_FetchNext(batch);
}
}
// Private method
obj.CompleteName = function(name) {
if (name.indexOf("AMT_") == 0) return obj.pfx[0] + name;
if (name.indexOf("CIM_") == 0) return obj.pfx[1] + name;
if (name.indexOf("IPS_") == 0) return obj.pfx[2] + name;
}
obj.CompleteExecResponse = function (resp) {
if (resp && resp != null && resp.Body && (resp.Body["ReturnValue"] != undefined)) { resp.Body.ReturnValueStr = obj.AmtStatusToStr(resp.Body["ReturnValue"]); }
return resp;
}
obj.RequestPowerStateChange = function (PowerState, callback_func) {
obj.CIM_PowerManagementService_RequestPowerStateChange(PowerState, "<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"CreationClassName\">CIM_ComputerSystem</Selector><Selector Name=\"Name\">ManagedSystem</Selector></SelectorSet></ReferenceParameters>", null, null, callback_func);
}
obj.RequestOSPowerStateChange = function (PowerState, callback_func) {
obj.IPS_PowerManagementService_RequestOSPowerSavingStateChange(PowerState, "<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"CreationClassName\">CIM_ComputerSystem</Selector><Selector Name=\"Name\">ManagedSystem</Selector></SelectorSet></ReferenceParameters>", null, null, callback_func);
}
obj.SetBootConfigRole = function (Role, callback_func) {
obj.CIM_BootService_SetBootConfigRole("<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootConfigSetting</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"InstanceID\">Intel(r) AMT: Boot Configuration 0</Selector></SelectorSet></ReferenceParameters>", Role, callback_func);
}
// Cancel all pending queries with given status
obj.CancelAllQueries = function (s) {
obj.wsman.CancelAllQueries(s);
}
// Auto generated methods
obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func); }
obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
obj.AMT_AgentPresenceWatchdog_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func, tag, pri, selectors); }
obj.AMT_AgentPresenceWatchdog_DeleteAllActions = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "DeleteAllActions", {}, callback_func, tag, pri, selectors); }
obj.AMT_AgentPresenceWatchdogAction_GetActionEac = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogAction", "GetActionEac", {}, callback_func); }
obj.AMT_AgentPresenceWatchdogVA_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "RegisterAgent", {}, callback_func); }
obj.AMT_AgentPresenceWatchdogVA_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
obj.AMT_AgentPresenceWatchdogVA_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
obj.AMT_AgentPresenceWatchdogVA_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func); }
obj.AMT_AgentPresenceWatchdogVA_DeleteAllActions = function (_method_dummy, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "DeleteAllActions", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_AuditLog_ClearLog = function (callback_func) { obj.Exec("AMT_AuditLog", "ClearLog", {}, callback_func); }
obj.AMT_AuditLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_AuditLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.AMT_AuditLog_ReadRecords = function (StartIndex, callback_func, tag) { obj.Exec("AMT_AuditLog", "ReadRecords", { "StartIndex": StartIndex }, callback_func, tag); }
obj.AMT_AuditLog_SetAuditLock = function (LockTimeoutInSeconds, Flag, Handle, callback_func) { obj.Exec("AMT_AuditLog", "SetAuditLock", { "LockTimeoutInSeconds": LockTimeoutInSeconds, "Flag": Flag, "Handle": Handle }, callback_func); }
obj.AMT_AuditLog_ExportAuditLogSignature = function (SigningMechanism, callback_func) { obj.Exec("AMT_AuditLog", "ExportAuditLogSignature", { "SigningMechanism": SigningMechanism }, callback_func); }
obj.AMT_AuditLog_SetSigningKeyMaterial = function (SigningMechanismType, SigningKey, LengthOfCertificates, Certificates, callback_func) { obj.Exec("AMT_AuditLog", "SetSigningKeyMaterial", { "SigningMechanismType": SigningMechanismType, "SigningKey": SigningKey, "LengthOfCertificates": LengthOfCertificates, "Certificates": Certificates }, callback_func); }
obj.AMT_AuditPolicyRule_SetAuditPolicy = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicy", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
obj.AMT_AuditPolicyRule_SetAuditPolicyBulk = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicyBulk", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
obj.AMT_AuthorizationService_AddUserAclEntryEx = function (DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "AddUserAclEntryEx", { "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
obj.AMT_AuthorizationService_EnumerateUserAclEntries = function (StartIndex, callback_func) { obj.Exec("AMT_AuthorizationService", "EnumerateUserAclEntries", { "StartIndex": StartIndex }, callback_func); }
obj.AMT_AuthorizationService_GetUserAclEntryEx = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetUserAclEntryEx", { "Handle": Handle }, callback_func, tag); }
obj.AMT_AuthorizationService_UpdateUserAclEntryEx = function (Handle, DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "UpdateUserAclEntryEx", { "Handle": Handle, "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
obj.AMT_AuthorizationService_RemoveUserAclEntry = function (Handle, callback_func) { obj.Exec("AMT_AuthorizationService", "RemoveUserAclEntry", { "Handle": Handle }, callback_func); }
obj.AMT_AuthorizationService_SetAdminAclEntryEx = function (Username, DigestPassword, callback_func) { obj.Exec("AMT_AuthorizationService", "SetAdminAclEntryEx", { "Username": Username, "DigestPassword": DigestPassword }, callback_func); }
obj.AMT_AuthorizationService_GetAdminAclEntry = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntry", {}, callback_func); }
obj.AMT_AuthorizationService_GetAdminAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntryStatus", {}, callback_func); }
obj.AMT_AuthorizationService_GetAdminNetAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminNetAclEntryStatus", {}, callback_func); }
obj.AMT_AuthorizationService_SetAclEnabledState = function (Handle, Enabled, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "SetAclEnabledState", { "Handle": Handle, "Enabled": Enabled }, callback_func, tag); }
obj.AMT_AuthorizationService_GetAclEnabledState = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetAclEnabledState", { "Handle": Handle }, callback_func, tag); }
obj.AMT_EndpointAccessControlService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.AMT_EndpointAccessControlService_GetPosture = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPosture", { "PostureType": PostureType }, callback_func); }
obj.AMT_EndpointAccessControlService_GetPostureHash = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPostureHash", { "PostureType": PostureType }, callback_func); }
obj.AMT_EndpointAccessControlService_UpdatePostureState = function (UpdateType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "UpdatePostureState", { "UpdateType": UpdateType }, callback_func); }
obj.AMT_EndpointAccessControlService_GetEacOptions = function (callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetEacOptions", {}, callback_func); }
obj.AMT_EndpointAccessControlService_SetEacOptions = function (EacVendors, PostureHashAlgorithm, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "SetEacOptions", { "EacVendors": EacVendors, "PostureHashAlgorithm": PostureHashAlgorithm }, callback_func); }
obj.AMT_EnvironmentDetectionSettingData_SetSystemDefensePolicy = function (Policy, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "SetSystemDefensePolicy", { "Policy": Policy }, callback_func); }
obj.AMT_EnvironmentDetectionSettingData_EnableVpnRouting = function (Enable, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "EnableVpnRouting", { "Enable": Enable }, callback_func); }
obj.AMT_EthernetPortSettings_SetLinkPreference = function (LinkPreference, Timeout, callback_func) { obj.Exec("AMT_EthernetPortSettings", "SetLinkPreference", { "LinkPreference": LinkPreference, "Timeout": Timeout }, callback_func); }
obj.AMT_HeuristicPacketFilterStatistics_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("AMT_HeuristicPacketFilterStatistics", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
obj.AMT_KerberosSettingData_GetCredentialCacheState = function (callback_func) { obj.Exec("AMT_KerberosSettingData", "GetCredentialCacheState", {}, callback_func); }
obj.AMT_KerberosSettingData_SetCredentialCacheState = function (Enable, callback_func) { obj.Exec("AMT_KerberosSettingData", "SetCredentialCacheState", { "Enable": Enable }, callback_func); }
obj.AMT_MessageLog_CancelIteration = function (IterationIdentifier, callback_func) { obj.Exec("AMT_MessageLog", "CancelIteration", { "IterationIdentifier": IterationIdentifier }, callback_func); }
obj.AMT_MessageLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_MessageLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.AMT_MessageLog_ClearLog = function (callback_func) { obj.Exec("AMT_MessageLog", "ClearLog", { }, callback_func); }
obj.AMT_MessageLog_GetRecords = function (IterationIdentifier, MaxReadRecords, callback_func, tag) { obj.Exec("AMT_MessageLog", "GetRecords", { "IterationIdentifier": IterationIdentifier, "MaxReadRecords": MaxReadRecords }, callback_func, tag); }
obj.AMT_MessageLog_GetRecord = function (IterationIdentifier, PositionToNext, callback_func) { obj.Exec("AMT_MessageLog", "GetRecord", { "IterationIdentifier": IterationIdentifier, "PositionToNext": PositionToNext }, callback_func); }
obj.AMT_MessageLog_PositionAtRecord = function (IterationIdentifier, MoveAbsolute, RecordNumber, callback_func) { obj.Exec("AMT_MessageLog", "PositionAtRecord", { "IterationIdentifier": IterationIdentifier, "MoveAbsolute": MoveAbsolute, "RecordNumber": RecordNumber }, callback_func); }
obj.AMT_MessageLog_PositionToFirstRecord = function (callback_func, tag) { obj.Exec("AMT_MessageLog", "PositionToFirstRecord", {}, callback_func, tag); }
obj.AMT_MessageLog_FreezeLog = function (Freeze, callback_func) { obj.Exec("AMT_MessageLog", "FreezeLog", { "Freeze": Freeze }, callback_func); }
obj.AMT_PublicKeyManagementService_AddCRL = function (Url, SerialNumbers, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCRL", { "Url": Url, "SerialNumbers": SerialNumbers }, callback_func); }
obj.AMT_PublicKeyManagementService_ResetCRLList = function (_method_dummy, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "ResetCRLList", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_PublicKeyManagementService_AddCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
obj.AMT_PublicKeyManagementService_AddTrustedRootCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddTrustedRootCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
obj.AMT_PublicKeyManagementService_AddKey = function (KeyBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddKey", { "KeyBlob": KeyBlob }, callback_func); }
obj.AMT_PublicKeyManagementService_GeneratePKCS10Request = function (KeyPair, DNName, Usage, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10Request", { "KeyPair": KeyPair, "DNName": DNName, "Usage": Usage }, callback_func); }
obj.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx = function (KeyPair, SigningAlgorithm, NullSignedCertificateRequest, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10RequestEx", { "KeyPair": KeyPair, "SigningAlgorithm": SigningAlgorithm, "NullSignedCertificateRequest": NullSignedCertificateRequest }, callback_func); }
obj.AMT_PublicKeyManagementService_GenerateKeyPair = function (KeyAlgorithm, KeyLength, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GenerateKeyPair", { "KeyAlgorithm": KeyAlgorithm, "KeyLength": KeyLength }, callback_func); }
obj.AMT_RedirectionService_RequestStateChange = function (RequestedState, callback_func) { obj.Exec("AMT_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState }, callback_func); }
obj.AMT_RedirectionService_TerminateSession = function (SessionType, callback_func) { obj.Exec("AMT_RedirectionService", "TerminateSession", { "SessionType": SessionType }, callback_func); }
obj.AMT_RemoteAccessService_AddMpServer = function (AccessInfo, InfoFormat, Port, AuthMethod, Certificate, Username, Password, CN, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddMpServer", { "AccessInfo": AccessInfo, "InfoFormat": InfoFormat, "Port": Port, "AuthMethod": AuthMethod, "Certificate": Certificate, "Username": Username, "Password": Password, "CN": CN }, callback_func); }
obj.AMT_RemoteAccessService_AddRemoteAccessPolicyRule = function (Trigger, TunnelLifeTime, ExtendedData, MpServer, InternalMpServer, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddRemoteAccessPolicyRule", { "Trigger": Trigger, "TunnelLifeTime": TunnelLifeTime, "ExtendedData": ExtendedData, "MpServer": MpServer, "InternalMpServer": InternalMpServer }, callback_func); }
obj.AMT_RemoteAccessService_CloseRemoteAccessConnection = function (_method_dummy, callback_func) { obj.Exec("AMT_RemoteAccessService", "CloseRemoteAccessConnection", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_SetupAndConfigurationService_CommitChanges = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "CommitChanges", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_SetupAndConfigurationService_Unprovision = function (ProvisioningMode, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "Unprovision", { "ProvisioningMode": ProvisioningMode }, callback_func); }
obj.AMT_SetupAndConfigurationService_PartialUnprovision = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "PartialUnprovision", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_SetupAndConfigurationService_ResetFlashWearOutProtection = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ResetFlashWearOutProtection", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_SetupAndConfigurationService_ExtendProvisioningPeriod = function (Duration, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ExtendProvisioningPeriod", { "Duration": Duration }, callback_func); }
obj.AMT_SetupAndConfigurationService_SetMEBxPassword = function (Password, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetMEBxPassword", { "Password": Password }, callback_func); }
obj.AMT_SetupAndConfigurationService_SetTLSPSK = function (PID, PPS, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetTLSPSK", { "PID": PID, "PPS": PPS }, callback_func); }
obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecord = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecord", {}, callback_func); }
obj.AMT_SetupAndConfigurationService_GetUuid = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUuid", {}, callback_func); }
obj.AMT_SetupAndConfigurationService_GetUnprovisionBlockingComponents = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUnprovisionBlockingComponents", {}, callback_func); }
obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecordV2 = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecordV2", {}, callback_func); }
obj.AMT_SystemDefensePolicy_GetTimeout = function (callback_func) { obj.Exec("AMT_SystemDefensePolicy", "GetTimeout", {}, callback_func); }
obj.AMT_SystemDefensePolicy_SetTimeout = function (Timeout, callback_func) { obj.Exec("AMT_SystemDefensePolicy", "SetTimeout", { "Timeout": Timeout }, callback_func); }
obj.AMT_SystemDefensePolicy_UpdateStatistics = function (NetworkInterface, ResetOnRead, callback_func, tag, pri, selectors) { obj.Exec("AMT_SystemDefensePolicy", "UpdateStatistics", { "NetworkInterface": NetworkInterface, "ResetOnRead": ResetOnRead }, callback_func, tag, pri, selectors); }
obj.AMT_SystemPowerScheme_SetPowerScheme = function (callback_func, schemeInstanceId, tag) { obj.Exec("AMT_SystemPowerScheme", "SetPowerScheme", {}, callback_func, tag, 0, { "InstanceID": schemeInstanceId }); }
obj.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch = function (callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "GetLowAccuracyTimeSynch", {}, callback_func, tag); }
obj.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch = function (Ta0, Tm1, Tm2, callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "SetHighAccuracyTimeSynch", { "Ta0": Ta0, "Tm1": Tm1, "Tm2": Tm2 }, callback_func, tag); }
obj.AMT_UserInitiatedConnectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_UserInitiatedConnectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.AMT_WebUIService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_WebUIService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.AMT_WiFiPortConfigurationService_AddWiFiSettings = function (WiFiEndpoint, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "AddWiFiSettings", { "WiFiEndpoint": WiFiEndpoint, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
obj.AMT_WiFiPortConfigurationService_UpdateWiFiSettings = function (WiFiEndpointSettings, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "UpdateWiFiSettings", { "WiFiEndpointSettings": WiFiEndpointSettings, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
obj.AMT_WiFiPortConfigurationService_DeleteAllITProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllITProfiles", { "_method_dummy": _method_dummy }, callback_func); }
obj.AMT_WiFiPortConfigurationService_DeleteAllUserProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllUserProfiles", { "_method_dummy": _method_dummy }, callback_func); }
obj.CIM_Account_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Account", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_AccountManagementService_CreateAccount = function (System, AccountTemplate, callback_func) { obj.Exec("CIM_AccountManagementService", "CreateAccount", { "System": System, "AccountTemplate": AccountTemplate }, callback_func); }
obj.CIM_BootConfigSetting_ChangeBootOrder = function (Source, callback_func) { obj.Exec("CIM_BootConfigSetting", "ChangeBootOrder", { "Source": Source }, callback_func); }
obj.CIM_BootService_SetBootConfigRole = function (BootConfigSetting, Role, callback_func) { obj.Exec("CIM_BootService", "SetBootConfigRole", { "BootConfigSetting": BootConfigSetting, "Role": Role }, callback_func, 0, 1); }
obj.CIM_Card_ConnectorPower = function (Connector, PoweredOn, callback_func) { obj.Exec("CIM_Card", "ConnectorPower", { "Connector": Connector, "PoweredOn": PoweredOn }, callback_func); }
obj.CIM_Card_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Card", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
obj.CIM_Chassis_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Chassis", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
obj.CIM_Fan_SetSpeed = function (DesiredSpeed, callback_func) { obj.Exec("CIM_Fan", "SetSpeed", { "DesiredSpeed": DesiredSpeed }, callback_func); }
obj.CIM_KVMRedirectionSAP_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_KVMRedirectionSAP", "RequestStateChange", { "RequestedState": RequestedState/*, "TimeoutPeriod": TimeoutPeriod */}, callback_func); }
obj.CIM_MediaAccessDevice_LockMedia = function (Lock, callback_func) { obj.Exec("CIM_MediaAccessDevice", "LockMedia", { "Lock": Lock }, callback_func); }
obj.CIM_MediaAccessDevice_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_MediaAccessDevice", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
obj.CIM_MediaAccessDevice_Reset = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "Reset", {}, callback_func); }
obj.CIM_MediaAccessDevice_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_MediaAccessDevice", "EnableDevice", { "Enabled": Enabled }, callback_func); }
obj.CIM_MediaAccessDevice_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_MediaAccessDevice", "OnlineDevice", { "Online": Online }, callback_func); }
obj.CIM_MediaAccessDevice_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_MediaAccessDevice", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
obj.CIM_MediaAccessDevice_SaveProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "SaveProperties", {}, callback_func); }
obj.CIM_MediaAccessDevice_RestoreProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "RestoreProperties", {}, callback_func); }
obj.CIM_MediaAccessDevice_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_MediaAccessDevice", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_PhysicalFrame_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalFrame", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
obj.CIM_PhysicalPackage_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalPackage", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
obj.CIM_PowerManagementService_RequestPowerStateChange = function (PowerState, ManagedElement, Time, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerManagementService", "RequestPowerStateChange", { "PowerState": PowerState, "ManagedElement": ManagedElement, "Time": Time, "TimeoutPeriod": TimeoutPeriod }, callback_func, 0, 1); }
obj.CIM_PowerSupply_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_PowerSupply", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
obj.CIM_PowerSupply_Reset = function (callback_func) { obj.Exec("CIM_PowerSupply", "Reset", {}, callback_func); }
obj.CIM_PowerSupply_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_PowerSupply", "EnableDevice", { "Enabled": Enabled }, callback_func); }
obj.CIM_PowerSupply_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_PowerSupply", "OnlineDevice", { "Online": Online }, callback_func); }
obj.CIM_PowerSupply_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_PowerSupply", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
obj.CIM_PowerSupply_SaveProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "SaveProperties", {}, callback_func); }
obj.CIM_PowerSupply_RestoreProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "RestoreProperties", {}, callback_func); }
obj.CIM_PowerSupply_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerSupply", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_Processor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Processor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
obj.CIM_Processor_Reset = function (callback_func) { obj.Exec("CIM_Processor", "Reset", {}, callback_func); }
obj.CIM_Processor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Processor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
obj.CIM_Processor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Processor", "OnlineDevice", { "Online": Online }, callback_func); }
obj.CIM_Processor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Processor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
obj.CIM_Processor_SaveProperties = function (callback_func) { obj.Exec("CIM_Processor", "SaveProperties", {}, callback_func); }
obj.CIM_Processor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Processor", "RestoreProperties", {}, callback_func); }
obj.CIM_Processor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Processor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_RecordLog_ClearLog = function (callback_func) { obj.Exec("CIM_RecordLog", "ClearLog", {}, callback_func); }
obj.CIM_RecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_RedirectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_Sensor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Sensor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
obj.CIM_Sensor_Reset = function (callback_func) { obj.Exec("CIM_Sensor", "Reset", {}, callback_func); }
obj.CIM_Sensor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Sensor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
obj.CIM_Sensor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Sensor", "OnlineDevice", { "Online": Online }, callback_func); }
obj.CIM_Sensor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Sensor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
obj.CIM_Sensor_SaveProperties = function (callback_func) { obj.Exec("CIM_Sensor", "SaveProperties", {}, callback_func); }
obj.CIM_Sensor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Sensor", "RestoreProperties", {}, callback_func); }
obj.CIM_Sensor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Sensor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_StatisticalData_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("CIM_StatisticalData", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
obj.CIM_Watchdog_KeepAlive = function (callback_func) { obj.Exec("CIM_Watchdog", "KeepAlive", {}, callback_func); }
obj.CIM_Watchdog_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Watchdog", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
obj.CIM_Watchdog_Reset = function (callback_func) { obj.Exec("CIM_Watchdog", "Reset", {}, callback_func); }
obj.CIM_Watchdog_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Watchdog", "EnableDevice", { "Enabled": Enabled }, callback_func); }
obj.CIM_Watchdog_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Watchdog", "OnlineDevice", { "Online": Online }, callback_func); }
obj.CIM_Watchdog_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Watchdog", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
obj.CIM_Watchdog_SaveProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "SaveProperties", {}, callback_func); }
obj.CIM_Watchdog_RestoreProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "RestoreProperties", {}, callback_func); }
obj.CIM_Watchdog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Watchdog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.CIM_WiFiPort_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_WiFiPort", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
obj.CIM_WiFiPort_Reset = function (callback_func) { obj.Exec("CIM_WiFiPort", "Reset", {}, callback_func); }
obj.CIM_WiFiPort_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_WiFiPort", "EnableDevice", { "Enabled": Enabled }, callback_func); }
obj.CIM_WiFiPort_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_WiFiPort", "OnlineDevice", { "Online": Online }, callback_func); }
obj.CIM_WiFiPort_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_WiFiPort", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
obj.CIM_WiFiPort_SaveProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "SaveProperties", {}, callback_func); }
obj.CIM_WiFiPort_RestoreProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "RestoreProperties", {}, callback_func); }
obj.CIM_WiFiPort_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_WiFiPort", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.IPS_HostBasedSetupService_Setup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, Certificate, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "Setup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "Certificate": Certificate, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
obj.IPS_HostBasedSetupService_AddNextCertInChain = function (NextCertificate, IsLeafCertificate, IsRootCertificate, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AddNextCertInChain", { "NextCertificate": NextCertificate, "IsLeafCertificate": IsLeafCertificate, "IsRootCertificate": IsRootCertificate }, callback_func); }
obj.IPS_HostBasedSetupService_AdminSetup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AdminSetup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
obj.IPS_HostBasedSetupService_UpgradeClientToAdmin = function (McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "UpgradeClientToAdmin", { "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
obj.IPS_HostBasedSetupService_DisableClientControlMode = function (_method_dummy, callback_func) { obj.Exec("IPS_HostBasedSetupService", "DisableClientControlMode", { "_method_dummy": _method_dummy }, callback_func); }
obj.IPS_KVMRedirectionSettingData_TerminateSession = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "TerminateSession", {}, callback_func); }
obj.IPS_KVMRedirectionSettingData_DataChannelRead = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "DataChannelRead", {}, callback_func); }
obj.IPS_KVMRedirectionSettingData_DataChannelWrite = function (Data, callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "DataChannelWrite", { "DataMessage": Data }, callback_func); }
obj.IPS_OptInService_StartOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "StartOptIn", {}, callback_func); }
obj.IPS_OptInService_CancelOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "CancelOptIn", {}, callback_func); }
obj.IPS_OptInService_SendOptInCode = function (OptInCode, callback_func) { obj.Exec("IPS_OptInService", "SendOptInCode", { "OptInCode": OptInCode }, callback_func); }
obj.IPS_OptInService_StartService = function (callback_func) { obj.Exec("IPS_OptInService", "StartService", {}, callback_func); }
obj.IPS_OptInService_StopService = function (callback_func) { obj.Exec("IPS_OptInService", "StopService", {}, callback_func); }
obj.IPS_OptInService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_OptInService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.IPS_PowerManagementService_RequestOSPowerSavingStateChange = function (PowerState, ManagedElement, Time, TimeoutPeriod, callback_func) { obj.Exec("IPS_PowerManagementService", "RequestOSPowerSavingStateChange", { "OSPowerSavingState": PowerState, "ManagedElement": ManagedElement, "Time": Time, "TimeoutPeriod": TimeoutPeriod }, callback_func, 0, 1); }
obj.IPS_ProvisioningRecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.IPS_ProvisioningRecordLog_ClearLog = function (_method_dummy, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "ClearLog", { "_method_dummy": _method_dummy }, callback_func); }
obj.IPS_ScreenConfigurationService_SetSessionState = function (SessionState, ConsecutiveRebootsNum, callback_func) { obj.Exec("IPS_ScreenConfigurationService", "SetSessionState", { "SessionState": SessionState, "ConsecutiveRebootsNum": ConsecutiveRebootsNum }, callback_func); }
obj.IPS_SecIOService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_SecIOService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
obj.IPS_HTTPProxyService_AddProxyAccessPoint = function (AccessInfo, InfoFormat, Port, NetworkDnsSuffix, callback_func) { obj.Exec("IPS_HTTPProxyService", "AddProxyAccessPoint", { "AccessInfo": AccessInfo, "InfoFormat": InfoFormat, "Port": Port, "NetworkDnsSuffix": NetworkDnsSuffix }, callback_func); }
obj.AmtStatusToStr = function (code) { if (obj.AmtStatusCodes[code]) return obj.AmtStatusCodes[code]; else return "UNKNOWN_ERROR" }
obj.AmtStatusCodes = {
0x0000: "SUCCESS",
0x0001: "INTERNAL_ERROR",
0x0002: "NOT_READY",
0x0003: "INVALID_PT_MODE",
0x0004: "INVALID_MESSAGE_LENGTH",
0x0005: "TABLE_FINGERPRINT_NOT_AVAILABLE",
0x0006: "INTEGRITY_CHECK_FAILED",
0x0007: "UNSUPPORTED_ISVS_VERSION",
0x0008: "APPLICATION_NOT_REGISTERED",
0x0009: "INVALID_REGISTRATION_DATA",
0x000A: "APPLICATION_DOES_NOT_EXIST",
0x000B: "NOT_ENOUGH_STORAGE",
0x000C: "INVALID_NAME",
0x000D: "BLOCK_DOES_NOT_EXIST",
0x000E: "INVALID_BYTE_OFFSET",
0x000F: "INVALID_BYTE_COUNT",
0x0010: "NOT_PERMITTED",
0x0011: "NOT_OWNER",
0x0012: "BLOCK_LOCKED_BY_OTHER",
0x0013: "BLOCK_NOT_LOCKED",
0x0014: "INVALID_GROUP_PERMISSIONS",
0x0015: "GROUP_DOES_NOT_EXIST",
0x0016: "INVALID_MEMBER_COUNT",
0x0017: "MAX_LIMIT_REACHED",
0x0018: "INVALID_AUTH_TYPE",
0x0019: "AUTHENTICATION_FAILED",
0x001A: "INVALID_DHCP_MODE",
0x001B: "INVALID_IP_ADDRESS",
0x001C: "INVALID_DOMAIN_NAME",
0x001D: "UNSUPPORTED_VERSION",
0x001E: "REQUEST_UNEXPECTED",
0x001F: "INVALID_TABLE_TYPE",
0x0020: "INVALID_PROVISIONING_STATE",
0x0021: "UNSUPPORTED_OBJECT",
0x0022: "INVALID_TIME",
0x0023: "INVALID_INDEX",
0x0024: "INVALID_PARAMETER",
0x0025: "INVALID_NETMASK",
0x0026: "FLASH_WRITE_LIMIT_EXCEEDED",
0x0027: "INVALID_IMAGE_LENGTH",
0x0028: "INVALID_IMAGE_SIGNATURE",
0x0029: "PROPOSE_ANOTHER_VERSION",
0x002A: "INVALID_PID_FORMAT",
0x002B: "INVALID_PPS_FORMAT",
0x002C: "BIST_COMMAND_BLOCKED",
0x002D: "CONNECTION_FAILED",
0x002E: "CONNECTION_TOO_MANY",
0x002F: "RNG_GENERATION_IN_PROGRESS",
0x0030: "RNG_NOT_READY",
0x0031: "CERTIFICATE_NOT_READY",
0x0400: "DISABLED_BY_POLICY",
0x0800: "NETWORK_IF_ERROR_BASE",
0x0801: "UNSUPPORTED_OEM_NUMBER",
0x0802: "UNSUPPORTED_BOOT_OPTION",
0x0803: "INVALID_COMMAND",
0x0804: "INVALID_SPECIAL_COMMAND",
0x0805: "INVALID_HANDLE",
0x0806: "INVALID_PASSWORD",
0x0807: "INVALID_REALM",
0x0808: "STORAGE_ACL_ENTRY_IN_USE",
0x0809: "DATA_MISSING",
0x080A: "DUPLICATE",
0x080B: "EVENTLOG_FROZEN",
0x080C: "PKI_MISSING_KEYS",
0x080D: "PKI_GENERATING_KEYS",
0x080E: "INVALID_KEY",
0x080F: "INVALID_CERT",
0x0810: "CERT_KEY_NOT_MATCH",
0x0811: "MAX_KERB_DOMAIN_REACHED",
0x0812: "UNSUPPORTED",
0x0813: "INVALID_PRIORITY",
0x0814: "NOT_FOUND",
0x0815: "INVALID_CREDENTIALS",
0x0816: "INVALID_PASSPHRASE",
0x0818: "NO_ASSOCIATION",
0x081B: "AUDIT_FAIL",
0x081C: "BLOCKING_COMPONENT",
0x0821: "USER_CONSENT_REQUIRED",
0x1000: "APP_INTERNAL_ERROR",
0x1001: "NOT_INITIALIZED",
0x1002: "LIB_VERSION_UNSUPPORTED",
0x1003: "INVALID_PARAM",
0x1004: "RESOURCES",
0x1005: "HARDWARE_ACCESS_ERROR",
0x1006: "REQUESTOR_NOT_REGISTERED",
0x1007: "NETWORK_ERROR",
0x1008: "PARAM_BUFFER_TOO_SHORT",
0x1009: "COM_NOT_INITIALIZED_IN_THREAD",
0x100A: "URL_REQUIRED"
}
//
// Methods used for getting the event log
//
obj.GetMessageLog = function (func, tag) {
obj.AMT_MessageLog_PositionToFirstRecord(_GetMessageLog0, [func, tag, []]);
}
function _GetMessageLog0(stack, name, responses, status, tag) {
if (status != 200 || responses.Body['ReturnValue'] != '0') { tag[0](obj, null, tag[2]); return; }
obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, tag);
}
function _GetMessageLog1(stack, name, responses, status, tag) {
if (status != 200 || responses.Body['ReturnValue'] != '0') { tag[0](obj, null, tag[2]); return; }
var i, j, x, e, AmtMessages = tag[2], t = new Date(), TimeStamp, ra = responses.Body['RecordArray'];
if (typeof ra === 'string') { responses.Body['RecordArray'] = [responses.Body['RecordArray']]; }
for (i in ra) {
e = null;
try {
// ###BEGIN###{!Mode-MeshCentral2}
// NodeJS detection
var isNode = new Function('try {return this===global;}catch(e){return false;}');
if (isNode()) { e = require('atob')(ra[i]); } else { e = window.atob(ra[i]); }
// ###END###{!Mode-MeshCentral2}
// ###BEGIN###{Mode-MeshCentral2}
e = window.atob(ra[i]);
// ###END###{Mode-MeshCentral2}
} catch (ex) { }
if (e != null) {
TimeStamp = ReadIntX(e, 0);
if ((TimeStamp > 0) && (TimeStamp < 0xFFFFFFFF)) {
x = { 'DeviceAddress': e.charCodeAt(4), 'EventSensorType': e.charCodeAt(5), 'EventType': e.charCodeAt(6), 'EventOffset': e.charCodeAt(7), 'EventSourceType': e.charCodeAt(8), 'EventSeverity': e.charCodeAt(9), 'SensorNumber': e.charCodeAt(10), 'Entity': e.charCodeAt(11), 'EntityInstance': e.charCodeAt(12), 'EventData': [], 'Time': new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000) };
for (j = 13; j < 21; j++) { x['EventData'].push(e.charCodeAt(j)); }
x['EntityStr'] = _SystemEntityTypes[x['Entity']];
x['Desc'] = _GetEventDetailStr(x['EventSensorType'], x['EventOffset'], x['EventData'], x['Entity']);
if (!x['EntityStr']) x['EntityStr'] = "Unknown";
AmtMessages.push(x);
}
}
}
if (responses.Body["NoMoreRecords"] != true) { obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, [tag[0], AmtMessages, tag[2]]); } else { tag[0](obj, AmtMessages, tag[2]); }
}
var _EventTrapSourceTypes = "Platform firmware (e.g. BIOS)|SMI handler|ISV system management software|Alert ASIC|IPMI|BIOS vendor|System board set vendor|System integrator|Third party add-in|OSV|NIC|System management card".split('|');
var _SystemFirmwareError = "Unspecified.|No system memory is physically installed in the system.|No usable system memory, all installed memory has experienced an unrecoverable failure.|Unrecoverable hard-disk/ATAPI/IDE device failure.|Unrecoverable system-board failure.|Unrecoverable diskette subsystem failure.|Unrecoverable hard-disk controller failure.|Unrecoverable PS/2 or USB keyboard failure.|Removable boot media not found.|Unrecoverable video controller failure.|No video device detected.|Firmware (BIOS) ROM corruption detected.|CPU voltage mismatch (processors that share same supply have mismatched voltage requirements)|CPU speed matching failure".split('|');
var _SystemFirmwareProgress = "Unspecified.|Memory initialization.|Starting hard-disk initialization and test|Secondary processor(s) initialization|User authentication|User-initiated system setup|USB resource configuration|PCI resource configuration|Option ROM initialization|Video initialization|Cache initialization|SM Bus initialization|Keyboard controller initialization|Embedded controller/management controller initialization|Docking station attachment|Enabling docking station|Docking station ejection|Disabling docking station|Calling operating system wake-up vector|Starting operating system boot process|Baseboard or motherboard initialization|reserved|Floppy initialization|Keyboard test|Pointing device test|Primary processor initialization".split('|');
var _SystemEntityTypes = "Unspecified|Other|Unknown|Processor|Disk|Peripheral|System management module|System board|Memory module|Processor module|Power supply|Add in card|Front panel board|Back panel board|Power system board|Drive backplane|System internal expansion board|Other system board|Processor board|Power unit|Power module|Power management board|Chassis back panel board|System chassis|Sub chassis|Other chassis board|Disk drive bay|Peripheral bay|Device bay|Fan cooling|Cooling unit|Cable interconnect|Memory device|System management software|BIOS|Intel(r) ME|System bus|Group|Intel(r) ME|External environment|Battery|Processing blade|Connectivity switch|Processor/memory module|I/O module|Processor I/O module|Management controller firmware|IPMI channel|PCI bus|PCI express bus|SCSI bus|SATA/SAS bus|Processor front side bus".split('|');
obj.RealmNames = "||Redirection||Hardware Asset|Remote Control|Storage|Event Manager|Storage Admin|Agent Presence Local|Agent Presence Remote|Circuit Breaker|Network Time|General Information|Firmware Update|EIT|LocalUN|Endpoint Access Control|Endpoint Access Control Admin|Event Log Reader|Audit Log|ACL Realm|||Local System".split('|');
obj.WatchdogCurrentStates = { 1: 'Not Started', 2: 'Stopped', 4: 'Running', 8: 'Expired', 16: 'Suspended' };
function _GetEventDetailStr(eventSensorType, eventOffset, eventDataField, entity) {
if (eventSensorType == 15) {
if (eventDataField[0] == 235) return "Invalid Data";
if (eventOffset == 0) return _SystemFirmwareError[eventDataField[1]];
return _SystemFirmwareProgress[eventDataField[1]];
}
if ((eventSensorType == 18) && (eventDataField[0] == 170)) { // System watchdog event
return "Agent watchdog " + char2hex(eventDataField[4]) + char2hex(eventDataField[3]) + char2hex(eventDataField[2]) + char2hex(eventDataField[1]) + "-" + char2hex(eventDataField[6]) + char2hex(eventDataField[5]) + "-... changed to " + obj.WatchdogCurrentStates[eventDataField[7]];
}
if ((eventSensorType == 5) && (eventOffset == 0)) { // System chassis
return "Case intrusion";
}
if ((eventSensorType == 192) && (eventOffset == 0) && (eventDataField[0] == 170) && (eventDataField[1] == 48))
{
if (eventDataField[2] == 0) return "A remote Serial Over LAN session was established.";
if (eventDataField[2] == 1) return "Remote Serial Over LAN session finished. User control was restored.";
if (eventDataField[2] == 2) return "A remote IDE-Redirection session was established.";
if (eventDataField[2] == 3) return "Remote IDE-Redirection session finished. User control was restored.";
}
if (eventSensorType == 36) {
var handle = (eventDataField[1] << 24) + (eventDataField[2] << 16) + (eventDataField[3] << 8) + eventDataField[4];
var nic = "#" + eventDataField[0];
if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless *****
//if (eventDataField[0] == 0xAA) nic = "wireless";
if (handle == 4294967293) { return "All received packet filter was matched on " + nic + " interface."; }
if (handle == 4294967292) { return "All outbound packet filter was matched on " + nic + " interface."; }
if (handle == 4294967290) { return "Spoofed packet filter was matched on " + nic + " interface."; }
return "Filter " + handle + " was matched on " + nic + " interface.";
}
if (eventSensorType == 192) {
if (eventDataField[2] == 0) return "Security policy invoked. Some or all network traffic (TX) was stopped.";
if (eventDataField[2] == 2) return "Security policy invoked. Some or all network traffic (RX) was stopped.";
return "Security policy invoked.";
}
if (eventSensorType == 193) {
if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x30) && (eventDataField[2] == 0x00) && (eventDataField[3] == 0x00)) { return "User request for remote connection."; }
if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x20) && (eventDataField[2] == 0x03) && (eventDataField[3] == 0x01)) { return "EAC error: attempt to get posture while NAC in Intel<65> AMT is disabled."; } // eventDataField = 0xAA20030100000000
if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x20) && (eventDataField[2] == 0x04) && (eventDataField[3] == 0x00)) { return "HWA Error: general error"; } // Used to be "Certificate revoked." but don't know the source of this.
}
if (eventSensorType == 6) return "Authentication failed " + (eventDataField[1] + (eventDataField[2] << 8)) + " times. The system may be under attack.";
if (eventSensorType == 30) return "No bootable media";
if (eventSensorType == 32) return "Operating system lockup or power interrupt";
if (eventSensorType == 35) return "System boot failure";
if (eventSensorType == 37) return "System firmware started (at least one CPU is properly executing).";
return "Unknown Sensor Type #" + eventSensorType;
}
// ###BEGIN###{AuditLog}
// Useful link: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=WordDocuments%2Fsecurityadminevents.htm
var _AmtAuditStringTable =
{
16: 'Security Admin',
17: 'RCO',
18: 'Redirection Manager',
19: 'Firmware Update Manager',
20: 'Security Audit Log',
21: 'Network Time',
22: 'Network Administration',
23: 'Storage Administration',
24: 'Event Manager',
25: 'Circuit Breaker Manager',
26: 'Agent Presence Manager',
27: 'Wireless Configuration',
28: 'EAC',
29: 'KVM',
30: 'User Opt-In Events',
32: 'Screen Blanking',
33: 'Watchdog Events',
1600: 'Provisioning Started',
1601: 'Provisioning Completed',
1602: 'ACL Entry Added',
1603: 'ACL Entry Modified',
1604: 'ACL Entry Removed',
1605: 'ACL Access with Invalid Credentials',
1606: 'ACL Entry State',
1607: 'TLS State Changed',
1608: 'TLS Server Certificate Set',
1609: 'TLS Server Certificate Remove',
1610: 'TLS Trusted Root Certificate Added',
1611: 'TLS Trusted Root Certificate Removed',
1612: 'TLS Preshared Key Set',
1613: 'Kerberos Settings Modified',
1614: 'Kerberos Master Key Modified',
1615: 'Flash Wear out Counters Reset',
1616: 'Power Package Modified',
1617: 'Set Realm Authentication Mode',
1618: 'Upgrade Client to Admin Control Mode',
1619: 'Unprovisioning Started',
1700: 'Performed Power Up',
1701: 'Performed Power Down',
1702: 'Performed Power Cycle',
1703: 'Performed Reset',
1704: 'Set Boot Options',
1800: 'IDER Session Opened',
1801: 'IDER Session Closed',
1802: 'IDER Enabled',
1803: 'IDER Disabled',
1804: 'SoL Session Opened',
1805: 'SoL Session Closed',
1806: 'SoL Enabled',
1807: 'SoL Disabled',
1808: 'KVM Session Started',
1809: 'KVM Session Ended',
1810: 'KVM Enabled',
1811: 'KVM Disabled',
1812: 'VNC Password Failed 3 Times',
1900: 'Firmware Updated',
1901: 'Firmware Update Failed',
2000: 'Security Audit Log Cleared',
2001: 'Security Audit Policy Modified',
2002: 'Security Audit Log Disabled',
2003: 'Security Audit Log Enabled',
2004: 'Security Audit Log Exported',
2005: 'Security Audit Log Recovered',
2100: 'Intel&reg; ME Time Set',
2200: 'TCPIP Parameters Set',
2201: 'Host Name Set',
2202: 'Domain Name Set',
2203: 'VLAN Parameters Set',
2204: 'Link Policy Set',
2205: 'IPv6 Parameters Set',
2300: 'Global Storage Attributes Set',
2301: 'Storage EACL Modified',
2302: 'Storage FPACL Modified',
2303: 'Storage Write Operation',
2400: 'Alert Subscribed',
2401: 'Alert Unsubscribed',
2402: 'Event Log Cleared',
2403: 'Event Log Frozen',
2500: 'CB Filter Added',
2501: 'CB Filter Removed',
2502: 'CB Policy Added',
2503: 'CB Policy Removed',
2504: 'CB Default Policy Set',
2505: 'CB Heuristics Option Set',
2506: 'CB Heuristics State Cleared',
2600: 'Agent Watchdog Added',
2601: 'Agent Watchdog Removed',
2602: 'Agent Watchdog Action Set',
2700: 'Wireless Profile Added',
2701: 'Wireless Profile Removed',
2702: 'Wireless Profile Updated',
2800: 'EAC Posture Signer SET',
2801: 'EAC Enabled',
2802: 'EAC Disabled',
2803: 'EAC Posture State',
2804: 'EAC Set Options',
2900: 'KVM Opt-in Enabled',
2901: 'KVM Opt-in Disabled',
2902: 'KVM Password Changed',
2903: 'KVM Consent Succeeded',
2904: 'KVM Consent Failed',
3000: 'Opt-In Policy Change',
3001: 'Send Consent Code Event',
3002: 'Start Opt-In Blocked Event'
}
// Return human readable extended audit log data
// TODO: Just put some of them here, but many more still need to be added, helpful link here:
// https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=WordDocuments%2Fsecurityadminevents.htm
obj.GetAuditLogExtendedDataStr = function (id, data) {
if ((id == 1602 || id == 1604) && data.charCodeAt(0) == 0) { return data.substring(2, 2 + data.charCodeAt(1)); } // ACL Entry Added/Removed (Digest)
if (id == 1603) { if (data.charCodeAt(1) == 0) { return data.substring(3); } return null; } // ACL Entry Modified
if (id == 1605) { return ["Invalid ME access", "Invalid MEBx access"][data.charCodeAt(0)]; } // ACL Access with Invalid Credentials
if (id == 1606) { var r = ["Disabled", "Enabled"][data.charCodeAt(0)]; if (data.charCodeAt(1) == 0) { r += ", " + data.substring(3); } return r;} // ACL Entry State
if (id == 1607) { return "Remote " + ["NoAuth", "ServerAuth", "MutualAuth"][data.charCodeAt(0)] + ", Local " + ["NoAuth", "ServerAuth", "MutualAuth"][data.charCodeAt(1)]; } // TLS State Changed
if (id == 1617) { return obj.RealmNames[ReadInt(data, 0)] + ", " + ["NoAuth", "Auth", "Disabled"][data.charCodeAt(4)]; } // Set Realm Authentication Mode
if (id == 1619) { return ["BIOS", "MEBx", "Local MEI", "Local WSMAN", "Remote WSAMN"][data.charCodeAt(0)]; } // Intel AMT Unprovisioning Started
if (id == 1900) { return "From " + ReadShort(data, 0) + "." + ReadShort(data, 2) + "." + ReadShort(data, 4) + "." + ReadShort(data, 6) + " to " + ReadShort(data, 8) + "." + ReadShort(data, 10) + "." + ReadShort(data, 12) + "." + ReadShort(data, 14); } // Firmware Updated
if (id == 2100) { var t4 = new Date(); t4.setTime(ReadInt(data, 0) * 1000 + (new Date().getTimezoneOffset() * 60000)); return t4.toLocaleString(); } // Intel AMT Time Set
if (id == 3000) { return "From " + ["None", "KVM", "All"][data.charCodeAt(0)] + " to " + ["None", "KVM", "All"][data.charCodeAt(1)]; } // Opt-In Policy Change
if (id == 3001) { return ["Success", "Failed 3 times"][data.charCodeAt(0)]; } // Send Consent Code Event
return null;
}
obj.GetAuditLog = function (func) {
obj.AMT_AuditLog_ReadRecords(1, _GetAuditLog0, [func, []]);
}
function _GetAuditLog0(stack, name, responses, status, tag) {
if (status != 200) { tag[0](obj, [], status); return; }
var ptr, i, e, x, r = tag[1], t = new Date(), TimeStamp;
if (responses.Body['RecordsReturned'] > 0) {
responses.Body['EventRecords'] = MakeToArray(responses.Body['EventRecords']);
for (i in responses.Body['EventRecords']) {
e = null;
try {
e = window.atob(responses.Body['EventRecords'][i]);
} catch (e) {
console.log(e + " " + responses.Body['EventRecords'][i])
}
x = { 'AuditAppID': ReadShort(e, 0), 'EventID': ReadShort(e, 2), 'InitiatorType': e.charCodeAt(4) };
x['AuditApp'] = _AmtAuditStringTable[x['AuditAppID']];
x['Event'] = _AmtAuditStringTable[(x['AuditAppID'] * 100) + x['EventID']];
if (!x['Event']) x['Event'] = '#' + x['EventID'];
// Read and process the initiator
if (x['InitiatorType'] == 0) {
// HTTP digest
var userlen = e.charCodeAt(5);
x['Initiator'] = e.substring(6, 6 + userlen);
ptr = 6 + userlen;
}
if (x['InitiatorType'] == 1) {
// Kerberos
x['KerberosUserInDomain'] = ReadInt(e, 5);
var userlen = e.charCodeAt(9);
x['Initiator'] = GetSidString(e.substring(10, 10 + userlen));
ptr = 10 + userlen;
}
if (x['InitiatorType'] == 2) {
// Local
x['Initiator'] = '<i>Local</i>';
ptr = 5;
}
if (x['InitiatorType'] == 3) {
// KVM Default Port
x['Initiator'] = '<i>KVM Default Port</i>';
ptr = 5;
}
// Read timestamp
TimeStamp = ReadInt(e, ptr);
x['Time'] = new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000);
ptr += 4;
// Read network access
x['MCLocationType'] = e.charCodeAt(ptr++);
var netlen = e.charCodeAt(ptr++);
x['NetAddress'] = e.substring(ptr, ptr + netlen);
// Read extended data
ptr += netlen;
var exlen = e.charCodeAt(ptr++);
x['Ex'] = e.substring(ptr, ptr + exlen);
x['ExStr'] = obj.GetAuditLogExtendedDataStr((x['AuditAppID'] * 100) + x['EventID'], x['Ex']);
r.push(x);
}
}
if (responses.Body['TotalRecordCount'] > r.length) {
obj.AMT_AuditLog_ReadRecords(r.length + 1, _GetAuditLog0, [tag[0], r]);
} else {
tag[0](obj, r, status);
}
}
// ###END###{AuditLog}
return obj;
}
// ###BEGIN###{Certificates}
// Forge MD5
function hex_md5(str) { if (str == null) { str = ''; } return forge.md.md5.create().update(str).digest().toHex(); }
// ###END###{Certificates}
// ###BEGIN###{!Certificates}
// TinyMD5 from https://github.com/jbt/js-crypto
// Perform MD5 setup
var md5_k = [];
for (var i = 0; i < 64;) { md5_k[i] = 0 | (Math.abs(Math.sin(++i)) * 4294967296); }
// Perform MD5 on raw string and return hex
function hex_md5(str) {
if (str == null) { str = ''; }
var b, c, d, j,
x = [],
str2 = unescape(encodeURI(str)),
a = str2.length,
h = [b = 1732584193, c = -271733879, ~b, ~c],
i = 0;
for (; i <= a;) x[i >> 2] |= (str2.charCodeAt(i) || 128) << 8 * (i++ % 4);
x[str = (a + 8 >> 6) * 16 + 14] = a * 8;
i = 0;
for (; i < str; i += 16) {
a = h; j = 0;
for (; j < 64;) {
a = [
d = a[3],
((b = a[1] | 0) +
((d = (
(a[0] +
[
b & (c = a[2]) | ~b & d,
d & b | ~d & c,
b ^ c ^ d,
c ^ (b | ~d)
][a = j >> 4]
) +
(md5_k[j] +
(x[[
j,
5 * j + 1,
3 * j + 5,
7 * j
][a] % 16 + i] | 0)
)
)) << (a = [
7, 12, 17, 22,
5, 9, 14, 20,
4, 11, 16, 23,
6, 10, 15, 21
][4 * a + j++ % 4]) | d >>> 32 - a)
),
b,
c
];
}
for (j = 4; j;) h[--j] = h[j] + a[j];
}
str = '';
for (; j < 32;) str += ((h[j >> 3] >> ((1 ^ j++ & 7) * 4)) & 15).toString(16);
return str;
}
// ###END###{!Certificates}
// Perform MD5 on raw string and return raw string result
function rstr_md5(str) { return hex2rstr(hex_md5(str)); }
/*
Convert arguments into selector set and body XML. Used by AMT_WiFiPortConfigurationService_UpdateWiFiSettings.
args = {
"WiFiEndpoint": {
__parameterType: 'reference',
__resourceUri: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint',
Name: 'WiFi Endpoint 0'
},
"WiFiEndpointSettingsInput":
{
__parameterType: 'instance',
__namespace: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings',
ElementName: document.querySelector('#editProfile-profileName').value,
InstanceID: 'Intel(r) AMT:WiFi Endpoint Settings ' + document.querySelector('#editProfile-profileName').value,
AuthenticationMethod: document.querySelector('#editProfile-networkAuthentication').value,
//BSSType: 3, // Intel(r) AMT supports only infrastructure networks
EncryptionMethod: document.querySelector('#editProfile-encryption').value,
SSID: document.querySelector('#editProfile-networkName').value,
Priority: 100,
PSKPassPhrase: document.querySelector('#editProfile-passPhrase').value
},
"IEEE8021xSettingsInput": null,
"ClientCredential": null,
"CACredential": null
},
*/
function execArgumentsToXml(args) {
if(args === undefined || args === null) return null;
var result = '';
for(var argName in args) {
var arg = args[argName];
if(!arg) continue;
if(arg['__parameterType'] === 'reference') result += referenceToXml(argName, arg);
else result += instanceToXml(argName, arg);
//if(arg['__isInstance']) result += instanceToXml(argName, arg);
}
return result;
}
/**
* Convert JavaScript object into XML
<r:WiFiEndpointSettingsInput xmlns:q="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings">
<q:ElementName>Wireless-Profile-Admin</q:ElementName>
<q:InstanceID>Intel(r) AMT:WiFi Endpoint Settings Wireless-Profile-Admin</q:InstanceID>
<q:AuthenticationMethod>6</q:AuthenticationMethod>
<q:EncryptionMethod>4</q:EncryptionMethod>
<q:Priority>100</q:Priority>
<q:PSKPassPhrase>xxxxxxxx</q:PSKPassPhrase>
</r:WiFiEndpointSettingsInput>
*/
function instanceToXml(instanceName, inInstance) {
if(inInstance === undefined || inInstance === null) return null;
var hasNamespace = !!inInstance['__namespace'];
var startTag = hasNamespace ? '<q:' : '<';
var endTag = hasNamespace ? '</q:' : '</';
var namespaceDef = hasNamespace ? (' xmlns:q="' + inInstance['__namespace'] + '"' ): '';
var result = '<r:' + instanceName + namespaceDef + '>';
for(var prop in inInstance) {
if (!inInstance.hasOwnProperty(prop) || prop.indexOf('__') === 0) continue;
if (typeof inInstance[prop] === 'function' || Array.isArray(inInstance[prop]) ) continue;
if (typeof inInstance[prop] === 'object') {
//result += startTag + prop +'>' + instanceToXml('prop', inInstance[prop]) + endTag + prop +'>';
console.error('only convert one level down...');
}
else {
result += startTag + prop +'>' + inInstance[prop].toString() + endTag + prop +'>';
}
}
result += '</r:' + instanceName + '>';
return result;
}
/**
* Convert a selector set into XML. Expect no nesting.
* {
* selectorName : selectorValue,
* selectorName : selectorValue,
* ... ...
* }
<r:WiFiEndpoint>
<a:Address>http://192.168.1.103:16992/wsman</a:Address>
<a:ReferenceParameters>
<w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint</w:ResourceURI>
<w:SelectorSet>
<w:Selector Name="Name">WiFi Endpoint 0</w:Selector>
</w:SelectorSet>
</a:ReferenceParameters>
</r:WiFiEndpoint>
*/
function referenceToXml(referenceName, inReference) {
if(inReference === undefined || inReference === null ) return null;
var result = '<r:' + referenceName + '><a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>'+ inReference['__resourceUri']+'</w:ResourceURI><w:SelectorSet>';
for(var selectorName in inReference) {
if (!inReference.hasOwnProperty(selectorName) || selectorName.indexOf('__') === 0) continue;
if (typeof inReference[selectorName] === 'function' ||
typeof inReference[selectorName] === 'object' ||
Array.isArray(inReference[selectorName]) )
continue;
result += '<w:Selector Name="' + selectorName +'">' + inReference[selectorName].toString() + '</w:Selector>';
}
result += '</w:SelectorSet></a:ReferenceParameters></r:' + referenceName + '>';
return result;
}
// Convert a byte array of SID into string
function GetSidString(sid) {
var r = "S-" + sid.charCodeAt(0) + "-" + sid.charCodeAt(7);
for (var i = 2; i < (sid.length / 4) ; i++) r += "-" + ReadIntX(sid, i * 4);
return r;
}
// Convert a SID readable string into bytes
function GetSidByteArray(sidString) {
if (!sidString || sidString == null) return null;
var sidParts = sidString.split('-');
// Make sure the SID has at least 4 parts and starts with 'S'
if (sidParts.length < 4 || (sidParts[0] != 's' && sidParts[0] != 'S')) return null;
// Check that each part of the SID is really an integer
for (var i = 1; i < sidParts.length; i++) { var y = parseInt(sidParts[i]); if (y != sidParts[i]) return null; sidParts[i] = y; }
// Version (8 bit) + Id count (8 bit) + 48 bit in big endian -- DO NOT use bitwise right shift operator. JavaScript converts the number into a 32 bit integer before shifting. In real world, it's highly likely this part is always 0.
var r = String.fromCharCode(sidParts[1]) + String.fromCharCode(sidParts.length - 3) + ShortToStr(Math.floor(sidParts[2] / Math.pow(2, 32))) + IntToStr((sidParts[2]) & 0xFFFF);
// the rest are in 32 bit in little endian
for (var i = 3; i < sidParts.length; i++) r += IntToStrX(sidParts[i]);
return r;
}

181
amt-certificates-0.0.1.js Normal file
View File

@@ -0,0 +1,181 @@
/**
* @fileoverview Intel(r) AMT Certificate functions
* @author Ylian Saint-Hilaire
* @version v0.2.0b
*/
// Check which key pair matches the public key in the certificate
function amtcert_linkCertPrivateKey(certs, keys) {
for (var i in certs) {
var cert = certs[i];
try {
if (xxCertPrivateKeys.length == 0) return;
var publicKeyPEM = forge.pki.publicKeyToPem(forge.pki.certificateFromAsn1(forge.asn1.fromDer(cert.X509Certificate)).publicKey).substring(28 + 32).replace(/(\r\n|\n|\r)/gm, "");
for (var j = 0; j < keys.length; j++) {
if (publicKeyPEM === (keys[j]['DERKey'] + '-----END PUBLIC KEY-----')) {
keys[j].XCert = cert; // Link the key pair to the certificate
cert.XPrivateKey = keys[j]; // Link the certificate to the key pair
}
}
} catch (e) { console.log(e); }
}
}
// Load a P12 file, decodes it using the password and returns the private key handle
function amtcert_loadP12File(file, password, func) {
try {
// Encode in Base64 so Forge API can parse it.
var p12Der = window.forge.util.decode64(btoa(file));
var p12Asn1 = window.forge.asn1.fromDer(p12Der);
var p12 = window.forge.pkcs12.pkcs12FromAsn1(p12Asn1, password);
// Private key is stored in a shrouded key bag
var bags = p12.getBags({ bagType: window.forge.pki.oids.pkcs8ShroudedKeyBag });
console.assert(bags[window.forge.pki.oids.pkcs8ShroudedKeyBag] && bags[window.forge.pki.oids.pkcs8ShroudedKeyBag].length > 0);
// Import the Forge private key structure into Web Crypto
var privateKey = bags[window.forge.pki.oids.pkcs8ShroudedKeyBag][0].key;
var rsaPrivateKey = window.forge.pki.privateKeyToAsn1(privateKey);
var privateKeyInfo = window.forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
var pkcs8 = window.forge.asn1.toDer(privateKeyInfo).getBytes();
// Get the issuer attributes
var certBags = p12.getBags({ bagType: window.forge.pki.oids.certBag });
var issuerAttributes = certBags[window.forge.pki.oids.certBag][0].cert.subject.attributes;
var bags1 = p12.getBags({ bagType: forge.pki.oids.certBag });
var cert = bags1[forge.pki.oids.certBag][0].cert;
func(privateKey, issuerAttributes, cert);
return true;
} catch (ex) { }
return false;
}
function amtcert_signWithCaKey(DERKey, caPrivateKey, certAttributes, issuerAttributes, extKeyUsage) {
if (!caPrivateKey || caPrivateKey == null) {
var certAndKey = amtcert_createCertificate(issuerAttributes);
caPrivateKey = certAndKey.key;
}
return amtcert_createCertificate(certAttributes, caPrivateKey, DERKey, issuerAttributes, extKeyUsage);
}
// --- Extended Key Usage OID's ---
// 1.3.6.1.5.5.7.3.1 = TLS Server certificate
// 1.3.6.1.5.5.7.3.2 = TLS Client certificate
// 2.16.840.1.113741.1.2.1 = Intel AMT Remote Console
// 2.16.840.1.113741.1.2.2 = Intel AMT Local Console
// 2.16.840.1.113741.1.2.3 = Intel AMT Client Setup Certificate (Zero-Touch)
// Generate a certificate with a set of attributes signed by a rootCert. If the rootCert is obmitted, the generated certificate is self-signed.
function amtcert_createCertificate(certAttributes, caPrivateKey, DERKey, issuerAttributes, extKeyUsage) {
// Generate a keypair and create an X.509v3 certificate
var keys, cert = forge.pki.createCertificate();
if (!DERKey) {
keys = forge.pki.rsa.generateKeyPair(2048);
cert.publicKey = keys.publicKey;
} else {
cert.publicKey = forge.pki.publicKeyFromPem('-----BEGIN PUBLIC KEY-----' + DERKey + '-----END PUBLIC KEY-----');
}
cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1);
cert.validity.notBefore = new Date(2018, 0, 1);
//cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don't reject this cert.
cert.validity.notAfter = new Date(2049, 11, 31);
//cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 20);
var attrs = [];
if (certAttributes['CN']) attrs.push({ name: 'commonName', value: certAttributes['CN'] });
if (certAttributes['C']) attrs.push({ name: 'countryName', value: certAttributes['C'] });
if (certAttributes['ST']) attrs.push({ shortName: 'ST', value: certAttributes['ST'] });
if (certAttributes['O']) attrs.push({ name: 'organizationName', value: certAttributes['O'] });
cert.setSubject(attrs);
if (caPrivateKey) {
// Use root attributes
var rootattrs = [];
if (issuerAttributes['CN']) rootattrs.push({ name: 'commonName', value: issuerAttributes['CN'] });
if (issuerAttributes['C']) rootattrs.push({ name: 'countryName', value: issuerAttributes['C'] });
if (issuerAttributes['ST']) rootattrs.push({ shortName: 'ST', value: issuerAttributes['ST'] });
if (issuerAttributes['O']) rootattrs.push({ name: 'organizationName', value: issuerAttributes['O'] });
cert.setIssuer(rootattrs);
} else {
// Use our own attributes
cert.setIssuer(attrs);
}
if (caPrivateKey == undefined) {
// Create a root certificate
cert.setExtensions([{
name: 'basicConstraints',
cA: true
}, {
name: 'nsCertType',
sslCA: true,
emailCA: true,
objCA: true
}, {
name: 'subjectKeyIdentifier'
}]);
} else {
if (extKeyUsage == null) { extKeyUsage = { name: 'extKeyUsage', serverAuth: true, } } else { extKeyUsage.name = 'extKeyUsage'; }
/*
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true,
'2.16.840.1.113741.1.2.1': true
}
*/
// Create a leaf certificate
cert.setExtensions([{
name: 'basicConstraints'
}, {
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
}, extKeyUsage, {
name: 'nsCertType',
client: true,
server: true,
email: true,
objsign: true,
}, {
name: 'subjectKeyIdentifier'
}]);
}
// Self-sign certificate
if (caPrivateKey) {
cert.sign(caPrivateKey, forge.md.sha256.create());
} else {
cert.sign(keys.privateKey, forge.md.sha256.create());
}
if (DERKey) {
return cert;
} else {
return { 'cert': cert, 'key': keys.privateKey };
}
}
function _stringToArrayBuffer(str) {
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); }
return buf;
}
function _arrayBufferToString(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); }
return binary;
}

923
amt-desktop-0.0.2.js Normal file
View File

@@ -0,0 +1,923 @@
/**
* @description Remote Desktop
* @author Ylian Saint-Hilaire
* @version v0.0.2g
*/
// Construct a MeshServer object
var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
var obj = {};
obj.canvasid = divid;
obj.scrolldiv = scrolldiv;
obj.canvas = Q(divid).getContext("2d");
obj.protocol = 2; // KVM
obj.state = 0;
obj.acc = "";
obj.ScreenWidth = 960;
obj.ScreenHeight = 700;
obj.width = 0;
obj.height = 0;
obj.rwidth = 0;
obj.rheight = 0;
obj.bpp = 2; // Bytes per pixel (1 or 2 supported)
obj.graymode = 0; // 0 = Color, 1 = 256 grays, 2 = 16 grays. obj.bbp must be 1 in gray modes.
obj.useZRLE = true;
obj.showmouse = true;
obj.buttonmask = 0;
//obj.inbytes = 0;
//obj.outbytes = 0;
obj.spare = null;
obj.sparew = 0;
obj.spareh = 0;
obj.sparew2 = 0;
obj.spareh2 = 0;
obj.sparecache = {};
obj.ZRLEfirst = 1;
obj.onScreenSizeChange = null;
obj.frameRateDelay = 0;
// ###BEGIN###{DesktopRotation}
obj.noMouseRotate = false;
obj.rotation = 0;
// ###END###{DesktopRotation}
// ###BEGIN###{DesktopInband}
obj.kvmDataSupported = false;
obj.onKvmData = null;
obj.onKvmDataPending = [];
obj.onKvmDataAck = -1;
obj.holding = false;
obj.lastKeepAlive = Date.now();
// ###END###{DesktopInband}
obj.mNagleTimer = null; // Mouse motion slowdown timer
obj.mx = 0; // Last mouse x position
obj.my = 0; // Last mouse y position
// ###BEGIN###{DesktopFocus}
obj.ox = -1; // Old mouse x position
obj.oy = -1; // Old mouse y position
obj.focusmode = 0;
// ###END###{DesktopFocus}
// ###BEGIN###{Inflate}
obj.inflate = ZLIB.inflateInit(-15);
// ###END###{Inflate}
// Private method
obj.Debug = function (msg) { console.log(msg); }
obj.xxStateChange = function (newstate) {
if (newstate == 0) {
obj.canvas.fillStyle = '#000000';
obj.canvas.fillRect(0, 0, obj.width, obj.height);
obj.canvas.canvas.width = obj.rwidth = obj.width = 640;
obj.canvas.canvas.height = obj.rheight = obj.height = 400;
QS(obj.canvasid).cursor = 'auto';
// ###BEGIN###{Inflate}
obj.inflate = ZLIB.inflateInit(-15); // Reset inflate
// ###END###{Inflate}
} else {
if (!obj.showmouse) { QS(obj.canvasid).cursor = 'none'; }
}
}
obj.ProcessData = function (data) {
if (!data) return;
// obj.Debug("KRecv(" + data.length + "): " + rstr2hex(data));
//obj.inbytes += data.length;
//obj.Debug("KRecv(" + obj.inbytes + ")");
obj.acc += data;
while (obj.acc.length > 0) {
//obj.Debug("KAcc(" + obj.acc.length + "): " + rstr2hex(obj.acc));
var cmdsize = 0;
if (obj.state == 0 && obj.acc.length >= 12) {
// Getting handshake & version
cmdsize = 12;
//if (obj.acc.substring(0, 4) != "RFB ") { return obj.Stop(); }
//var version = parseFloat(obj.acc.substring(4, 11));
//obj.Debug("KVersion: " + version);
obj.state = 1;
obj.Send("RFB 003.008\n");
}
else if (obj.state == 1 && obj.acc.length >= 1) {
// Getting security options
cmdsize = obj.acc.charCodeAt(0) + 1;
obj.Send(String.fromCharCode(1)); // Send the "None" security type. Since we already authenticated using redirection digest auth, we don't need to do this again.
obj.state = 2;
}
else if (obj.state == 2 && obj.acc.length >= 4) {
// Getting security response
cmdsize = 4;
if (ReadInt(obj.acc, 0) != 0) { return obj.Stop(); }
obj.Send(String.fromCharCode(1)); // Send share desktop flag
obj.state = 3;
}
else if (obj.state == 3 && obj.acc.length >= 24) {
// Getting server init
// ###BEGIN###{DesktopRotation}
obj.rotation = 0; // We don't currently support screen init while rotated.
// ###END###{DesktopRotation}
var namelen = ReadInt(obj.acc, 20);
if (obj.acc.length < 24 + namelen) return;
cmdsize = 24 + namelen;
obj.canvas.canvas.width = obj.rwidth = obj.width = obj.ScreenWidth = ReadShort(obj.acc, 0);
obj.canvas.canvas.height = obj.rheight = obj.height = obj.ScreenHeight = ReadShort(obj.acc, 2);
// These are all values we don't really need, we are going to only run in RGB565 or RGB332 and not use the flexibility provided by these settings.
// Makes the javascript code smaller and maybe a bit faster.
/*
obj.xbpp = obj.acc.charCodeAt(4);
obj.depth = obj.acc.charCodeAt(5);
obj.bigend = obj.acc.charCodeAt(6);
obj.truecolor = obj.acc.charCodeAt(7);
obj.rmax = ReadShort(obj.acc, 8);
obj.gmax = ReadShort(obj.acc, 10);
obj.bmax = ReadShort(obj.acc, 12);
obj.rsh = obj.acc.charCodeAt(14);
obj.gsh = obj.acc.charCodeAt(15);
obj.bsh = obj.acc.charCodeAt(16);
var name = obj.acc.substring(24, 24 + namelen);
obj.Debug("name: " + name);
obj.Debug("width: " + obj.width + ", height: " + obj.height);
obj.Debug("bits-per-pixel: " + obj.xbpp);
obj.Debug("depth: " + obj.depth);
obj.Debug("big-endian-flag: " + obj.bigend);
obj.Debug("true-colour-flag: " + obj.truecolor);
obj.Debug("rgb max: " + obj.rmax + "," + obj.gmax + "," + obj.bmax);
obj.Debug("rgb shift: " + obj.rsh + "," + obj.gsh + "," + obj.bsh);
*/
// SetEncodings, with AMT we can't omit RAW, must be specified.
// Intel AMT supports encodings: RAW (0), ZRLE (16), Desktop Size (0xFFFFFF21, -223), KVM Data Channel (1092)
var supportedEncodings = '';
if (obj.useZRLE) supportedEncodings += IntToStr(16);
supportedEncodings += IntToStr(0);
// ###BEGIN###{DesktopInband}
supportedEncodings += IntToStr(1092);
// ###END###{DesktopInband}
obj.Send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size
if (obj.graymode == 0) {
// Set the pixel encoding to something much smaller
// obj.Send(String.fromCharCode(0, 0, 0, 0, 16, 16, 0, 1) + ShortToStr(31) + ShortToStr(63) + ShortToStr(31) + String.fromCharCode(11, 5, 0, 0, 0, 0)); // Setup 16 bit color RGB565 (This is the default, so we don't need to set it)
if (obj.bpp == 1) obj.Send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(7) + ShortToStr(7) + ShortToStr(3) + String.fromCharCode(5, 2, 0, 0, 0, 0)); // Setup 8 bit color RGB332
} else {
obj.bpp = 1;
if (obj.graymode == 1) { obj.Send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(255) + ShortToStr(0) + ShortToStr(0) + String.fromCharCode(0, 0, 0, 0, 0, 0)); } // Setup 8 bit gray RGB800
if (obj.graymode == 2) { obj.Send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(15) + ShortToStr(0) + ShortToStr(0) + String.fromCharCode(0, 0, 0, 0, 0, 0)); } // Setup 4 bit gray RGB400
}
obj.state = 4;
if (obj.parent) { obj.parent.xxStateChange(3); }
_SendRefresh();
//obj.timer = setInterval(obj.xxOnTimer, 50);
// ###BEGIN###{DesktopFocus}
obj.ox = -1; // Old mouse x position
// ###END###{DesktopFocus}
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
}
else if (obj.state == 4) {
switch (obj.acc.charCodeAt(0)) {
case 0: // FramebufferUpdate
if (obj.acc.length < 4) return;
obj.state = 100 + ReadShort(obj.acc, 2); // Read the number of tiles that are going to be sent, add 100 and use that as our protocol state.
cmdsize = 4;
break;
case 2: // This is the bell, do nothing.
cmdsize = 1;
break;
case 3: // This is ServerCutText
if (obj.acc.length < 8) return;
var len = ReadInt(obj.acc, 4) + 8;
if (obj.acc.length < len) return;
cmdsize = handleServerCutText(obj.acc);
break;
}
}
else if (obj.state > 100 && obj.acc.length >= 12) {
var x = ReadShort(obj.acc, 0),
y = ReadShort(obj.acc, 2),
width = ReadShort(obj.acc, 4),
height = ReadShort(obj.acc, 6),
s = width * height,
encoding = ReadInt(obj.acc, 8);
if (encoding < 17) {
if (width < 1 || width > 64 || height < 1 || height > 64) { console.log("Invalid tile size (" + width + "," + height + "), disconnecting."); return obj.Stop(); }
// Set the spare bitmap to the right size if it's not already. This allows us to recycle the spare most if not all the time.
if (obj.sparew != width || obj.spareh != height) {
obj.sparew = obj.sparew2 = width;
obj.spareh = obj.spareh2 = height;
// ###BEGIN###{DesktopRotation}
if (obj.rotation == 1 || obj.rotation == 3) { obj.sparew2 = height, obj.spareh2 = width; }
// ###END###{DesktopRotation}
var xspacecachename = obj.sparew2 + 'x' + obj.spareh2;
obj.spare = obj.sparecache[xspacecachename];
if (!obj.spare) {
obj.sparecache[xspacecachename] = obj.spare = obj.canvas.createImageData(obj.sparew2, obj.spareh2);
var j = (obj.sparew2 * obj.spareh2) << 2;
for (var i = 3; i < j; i += 4) { obj.spare.data[i] = 0xFF; } // Set alpha channel to opaque.
}
}
}
if (encoding == 0xFFFFFF21) {
// Desktop Size (0xFFFFFF21, -223)
obj.canvas.canvas.width = obj.ScreenWidth = obj.rwidth = obj.width = width;
obj.canvas.canvas.height = obj.ScreenHeight = obj.rheight = obj.height = height;
obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
cmdsize = 12;
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
// obj.Debug("New desktop width: " + obj.width + ", height: " + obj.height);
}
else if (encoding == 0) {
// RAW encoding
var ptr = 12, cs = 12 + (s * obj.bpp);
if (obj.acc.length < cs) return; // Check we have all the data needed and we can only draw 64x64 tiles.
cmdsize = cs;
// CRITICAL LOOP, optimize this as much as possible
if (obj.bpp == 2) {
for (var i = 0; i < s; i++) { _setPixel16(obj.acc.charCodeAt(ptr++) + (obj.acc.charCodeAt(ptr++) << 8), i); }
} else {
for (var i = 0; i < s; i++) { _setPixel8(obj.acc.charCodeAt(ptr++), i); }
}
_putImage(obj.spare, x, y);
}
else if (encoding == 16) {
// ZRLE encoding
if (obj.acc.length < 16) return;
var datalen = ReadInt(obj.acc, 12);
if (obj.acc.length < (16 + datalen)) return;
//obj.Debug("RECT ZRLE (" + x + "," + y + "," + width + "," + height + ") LEN = " + datalen);
//obj.Debug("RECT ZRLE LEN: " + ReadShortX(obj.acc, 17) + ", DATA: " + rstr2hex(obj.acc.substring(16)));
// Process the ZLib header if this is the first block
var ptr = 16, delta = 5, dx = 0;
if (datalen > 5 && obj.acc.charCodeAt(ptr) == 0 && ReadShortX(obj.acc, ptr + 1) == (datalen - delta)) {
// This is an uncompressed ZLib data block
_decodeLRE(obj.acc, ptr + 5, x, y, width, height, s, datalen);
}
// ###BEGIN###{Inflate}
else {
// This is compressed ZLib data, decompress and process it.
var arr = obj.inflate.inflate(obj.acc.substring(ptr, ptr + datalen - dx));
if (arr.length > 0) { _decodeLRE(arr, 0, x, y, width, height, s, arr.length); } else { obj.Debug("Invalid deflate data"); }
}
// ###END###{Inflate}
cmdsize = 16 + datalen;
} else {
obj.Debug('Unknown Encoding: ' + encoding + ', HEX: ' + rstr2hex(obj.acc));
return obj.Stop();
}
if (--obj.state == 100) {
obj.state = 4;
if (obj.frameRateDelay == 0) {
_SendRefresh(); // Ask for new frame
} else {
setTimeout(_SendRefresh, obj.frameRateDelay); // Hold x miliseconds before asking for a new frame
}
}
}
if (cmdsize == 0) return;
obj.acc = obj.acc.substring(cmdsize);
}
}
function _decodeLRE(data, ptr, x, y, width, height, s, datalen) {
var subencoding = data.charCodeAt(ptr++), index, v, runlengthdecode, palette = {}, rlecount = 0, runlength = 0, i;
// obj.Debug("RECT RLE (" + (datalen - 5) + ", " + subencoding + "):" + rstr2hex(data.substring(21, 21 + (datalen - 5))));
if (subencoding == 0) {
// RAW encoding
if (obj.bpp == 2) {
for (i = 0; i < s; i++) { _setPixel16(data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8), i); }
} else {
for (i = 0; i < s; i++) { _setPixel8(data.charCodeAt(ptr++), i); }
}
_putImage(obj.spare, x, y);
}
else if (subencoding == 1) {
// Solid color tile
v = data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0);
obj.canvas.fillStyle = 'rgb(' + ((obj.bpp == 1) ? ((v & 224) + ',' + ((v & 28) << 3) + ',' + _fixColor((v & 3) << 6)) : (((v >> 8) & 248) + ',' + ((v >> 3) & 252) + ',' + ((v & 31) << 3))) + ')';
// ###BEGIN###{DesktopRotation}
var xx = _rotX(x, y);
y = _rotY(x, y);
x = xx;
// ###END###{DesktopRotation}
obj.canvas.fillRect(x, y, width, height);
}
else if (subencoding > 1 && subencoding < 17) { // Packed palette encoded tile
// Read the palette
var br = 4, bm = 15; // br is BitRead and bm is BitMask. By adjusting these two we can support all the variations in this encoding.
if (obj.bpp == 2) {
for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); }
if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark
while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel16(palette[(v >> i) & bm], rlecount++); } } // Display all the bits
} else {
for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++); }
if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark
while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel8(palette[(v >> i) & bm], rlecount++); } } // Display all the bits
}
_putImage(obj.spare, x, y);
}
else if (subencoding == 128) { // RLE encoded tile
if (obj.bpp == 2) {
while (rlecount < s && ptr < data.length) {
// Get the run color
v = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8);
// Decode the run length. This is the fastest and most compact way I found to do this.
runlength = 1; do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255);
// Draw a run
if (obj.rotation == 0) {
_setPixel16run(v, rlecount, runlength); rlecount += runlength;
} else {
while (--runlength >= 0) { _setPixel16(v, rlecount++); }
}
}
} else {
while (rlecount < s && ptr < data.length) {
// Get the run color
v = data.charCodeAt(ptr++);
// Decode the run length. This is the fastest and most compact way I found to do this.
runlength = 1; do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255);
// Draw a run
if (obj.rotation == 0) {
_setPixel8run(v, rlecount, runlength); rlecount += runlength;
} else {
while (--runlength >= 0) { _setPixel8(v, rlecount++); }
}
}
}
_putImage(obj.spare, x, y);
}
else if (subencoding > 129) { // Palette RLE encoded tile
// Read the palette
if (obj.bpp == 2) {
for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); }
} else {
for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++); }
}
// Decode RLE on palette
while (rlecount < s && ptr < data.length) {
// Setup the run, get the color index and get the color from the palette.
runlength = 1; index = data.charCodeAt(ptr++); v = palette[index % 128];
// If the index starts with high order bit 1, this is a run and decode the run length.
if (index > 127) { do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255); }
// Draw a run
if (obj.rotation == 0) {
if (obj.bpp == 2) {
_setPixel16run(v, rlecount, runlength); rlecount += runlength;
} else {
_setPixel8run(v, rlecount, runlength); rlecount += runlength;
}
} else {
if (obj.bpp == 2) {
while (--runlength >= 0) { _setPixel16(v, rlecount++); }
} else {
while (--runlength >= 0) { _setPixel8(v, rlecount++); }
}
}
}
_putImage(obj.spare, x, y);
}
}
// ###BEGIN###{DesktopInband}
obj.hold = function (holding) {
if (obj.holding == holding) return;
obj.holding = holding;
obj.canvas.fillStyle = '#000000';
obj.canvas.fillRect(0, 0, obj.width, obj.height); // Paint black
if (obj.holding == false) {
// Go back to normal operations
// Set canvas size and ask for full screen refresh
if ((obj.canvas.canvas.width != obj.width) || (obj.canvas.canvas.height != obj.height)) {
obj.canvas.canvas.width = obj.width; obj.canvas.canvas.height = obj.height;
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } // ???
}
obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
} else {
obj.UnGrabMouseInput();
obj.UnGrabKeyInput();
}
}
// ###END###{DesktopInband}
function _putImage(i, x, y) {
// ###BEGIN###{DesktopInband}
if (obj.holding == true) return;
// ###END###{DesktopInband}
// ###BEGIN###{DesktopRotation}
var xx = _arotX(x, y);
y = _arotY(x, y);
x = xx;
// ###END###{DesktopRotation}
obj.canvas.putImageData(i, x, y);
}
// Set 8bit color RGB332
function _setPixel8(v, p) {
var pp = p << 2;
// ###BEGIN###{DesktopRotation}
if (obj.rotation > 0) {
if (obj.rotation == 1) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = (x * obj.sparew2) + (obj.sparew2 - 1 - y); pp = p << 2; }
else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; }
else if (obj.rotation == 3) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y); pp = p << 2; }
}
// ###END###{DesktopRotation}
if (obj.graymode == 0) {
obj.spare.data[pp] = v & 224;
obj.spare.data[pp + 1] = (v & 28) << 3;
obj.spare.data[pp + 2] = _fixColor((v & 3) << 6);
} else {
obj.spare.data[pp] = obj.spare.data[pp + 1] = obj.spare.data[pp + 2] = v;
}
}
// Set 16bit color RGB565
function _setPixel16(v, p) {
var pp = p << 2;
// ###BEGIN###{DesktopRotation}
if (obj.rotation > 0) {
if (obj.rotation == 1) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = (x * obj.sparew2) + (obj.sparew2 - 1 - y); pp = p << 2; }
else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; }
else if (obj.rotation == 3) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y); pp = p << 2; }
}
// ###END###{DesktopRotation}
obj.spare.data[pp] = (v >> 8) & 248;
obj.spare.data[pp + 1] = (v >> 3) & 252;
obj.spare.data[pp + 2] = (v & 31) << 3;
}
// Set a run of 8bit color RGB332
function _setPixel8run(v, p, run) {
var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6));
while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp+1] = g; obj.spare.data[pp+2] = b; pp += 4; }
}
// Set a run of 16bit color RGB565
function _setPixel16run(v, p, run) {
var pp = (p << 2), r = ((v >> 8) & 248), g = ((v >> 3) & 252), b = ((v & 31) << 3);
while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp+1] = g; obj.spare.data[pp+2] = b; pp += 4; }
}
// ###BEGIN###{DesktopRotation}
function _arotX(x, y) {
if (obj.rotation == 0) return x;
if (obj.rotation == 1) return obj.canvas.canvas.width - obj.sparew2 - y;
if (obj.rotation == 2) return obj.canvas.canvas.width - obj.sparew2 - x;
if (obj.rotation == 3) return y;
return 0;
}
function _arotY(x, y) {
if (obj.rotation == 0) return y;
if (obj.rotation == 1) return x;
if (obj.rotation == 2) return obj.canvas.canvas.height - obj.spareh2 - y;
if (obj.rotation == 3) return obj.canvas.canvas.height - obj.spareh - x;
return 0;
}
function _crotX(x, y) {
if (obj.rotation == 0) return x;
if (obj.rotation == 1) return y;
if (obj.rotation == 2) return obj.canvas.canvas.width - x;
if (obj.rotation == 3) return obj.canvas.canvas.height - y;
return 0;
}
function _crotY(x, y) {
if (obj.rotation == 0) return y;
if (obj.rotation == 1) return obj.canvas.canvas.width - x;
if (obj.rotation == 2) return obj.canvas.canvas.height - y;
if (obj.rotation == 3) return x;
return 0;
}
function _rotX(x, y) {
if (obj.rotation == 0) return x;
if (obj.rotation == 1) return x;
if (obj.rotation == 2) return x - obj.canvas.canvas.width;
if (obj.rotation == 3) return x - obj.canvas.canvas.height;
return 0;
}
function _rotY(x, y) {
if (obj.rotation == 0) return y;
if (obj.rotation == 1) return y - obj.canvas.canvas.width;
if (obj.rotation == 2) return y - obj.canvas.canvas.height;
if (obj.rotation == 3) return y;
return 0;
}
obj.tcanvas = null;
obj.setRotation = function (x) {
while (x < 0) { x += 4; }
var newrotation = x % 4;
//console.log('hard-rot: ' + newrotation);
// ###BEGIN###{DesktopInband}
if (obj.holding == true) { obj.rotation = newrotation; return; }
// ###END###{DesktopInband}
if (newrotation == obj.rotation) return true;
var rw = obj.canvas.canvas.width;
var rh = obj.canvas.canvas.height;
if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.canvas.canvas.height; rh = obj.canvas.canvas.width; }
// Copy the canvas, put it back in the correct direction
if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
var tcanvasctx = obj.tcanvas.getContext('2d');
tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
tcanvasctx.canvas.width = rw;
tcanvasctx.canvas.height = rh;
tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
if (obj.rotation == 0) tcanvasctx.drawImage(obj.canvas.canvas, 0, 0);
if (obj.rotation == 1) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, 0);
if (obj.rotation == 2) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, -obj.canvas.canvas.height);
if (obj.rotation == 3) tcanvasctx.drawImage(obj.canvas.canvas, 0, -obj.canvas.canvas.height);
// Change the size and orientation and copy the canvas back into the rotation
if (obj.rotation == 0 || obj.rotation == 2) { obj.canvas.canvas.height = rw; obj.canvas.canvas.width = rh; }
if (obj.rotation == 1 || obj.rotation == 3) { obj.canvas.canvas.height = rh; obj.canvas.canvas.width = rw; }
obj.canvas.setTransform(1, 0, 0, 1, 0, 0);
obj.canvas.rotate((newrotation * 90) * Math.PI / 180);
obj.rotation = newrotation;
obj.canvas.drawImage(obj.tcanvas, _rotX(0, 0), _rotY(0, 0));
obj.width = obj.canvas.canvas.width;
obj.height = obj.canvas.canvas.height;
if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.width, obj.height, obj.CanvasId);
return true;
}
// ###END###{DesktopRotation}
function _fixColor(c) { return (c > 127) ? (c + 32) : c; }
function _SendRefresh() {
// ###BEGIN###{DesktopInband}
if (obj.holding == true) return;
// ###END###{DesktopInband}
// ###BEGIN###{DesktopFocus}
if (obj.focusmode > 0) {
// Request only pixels around the last mouse position
var df = obj.focusmode * 2;
obj.Send(String.fromCharCode(3, 1) + ShortToStr(Math.max(Math.min(obj.ox, obj.mx) - obj.focusmode, 0)) + ShortToStr(Math.max(Math.min(obj.oy, obj.my) - obj.focusmode, 0)) + ShortToStr(df + Math.abs(obj.ox - obj.mx)) + ShortToStr(df + Math.abs(obj.oy - obj.my))); // FramebufferUpdateRequest
obj.ox = obj.mx;
obj.oy = obj.my;
} else {
// ###END###{DesktopFocus}
// Request the entire screen
obj.Send(String.fromCharCode(3, 1, 0, 0, 0, 0) + ShortToStr(obj.rwidth) + ShortToStr(obj.rheight)); // FramebufferUpdateRequest
// ###BEGIN###{DesktopFocus}
}
// ###END###{DesktopFocus}
}
obj.Start = function () {
//obj.Debug("KVM-Start");
obj.state = 0;
obj.acc = "";
obj.ZRLEfirst = 1;
//obj.inbytes = 0;
//obj.outbytes = 0;
// ###BEGIN###{Inflate}
obj.inflate.inflateReset();
// ###END###{Inflate}
// ###BEGIN###{DesktopInband}
obj.onKvmDataPending = [];
obj.onKvmDataAck = -1;
obj.kvmDataSupported = false;
// ###END###{DesktopInband}
for (var i in obj.sparecache) { delete obj.sparecache[i]; }
}
obj.Stop = function () {
obj.UnGrabMouseInput();
obj.UnGrabKeyInput();
if (obj.parent) { obj.parent.Stop(); }
}
obj.Send = function (x) {
//obj.Debug("KSend(" + x.length + "): " + rstr2hex(x));
//obj.outbytes += x.length;
if (obj.parent) { obj.parent.Send(x); }
}
var convertAmtKeyCodeTable = {
"Pause": 19,
"CapsLock": 20,
"Space": 32,
"Quote": 39,
"Minus": 45,
"NumpadMultiply": 42,
"NumpadAdd": 43,
"PrintScreen": 44,
"Comma": 44,
"NumpadSubtract": 45,
"NumpadDecimal": 46,
"Period": 46,
"Slash": 47,
"NumpadDivide": 47,
"Semicolon": 59,
"Equal": 61,
"OSLeft": 91,
"BracketLeft": 91,
"OSRight": 91,
"Backslash": 92,
"BracketRight": 93,
"ContextMenu": 93,
"Backquote": 96,
"NumLock": 144,
"ScrollLock": 145,
"Backspace": 0xff08,
"Tab": 0xff09,
"Enter": 0xff0d,
"NumpadEnter": 0xff0d,
"Escape": 0xff1b,
"Delete": 0xffff,
"Home": 0xff50,
"PageUp": 0xff55,
"PageDown": 0xff56,
"ArrowLeft": 0xff51,
"ArrowUp": 0xff52,
"ArrowRight": 0xff53,
"ArrowDown": 0xff54,
"End": 0xff57,
"Insert": 0xff63,
"F1": 0xffbe,
"F2": 0xffbf,
"F3": 0xffc0,
"F4": 0xffc1,
"F5": 0xffc2,
"F6": 0xffc3,
"F7": 0xffc4,
"F8": 0xffc5,
"F9": 0xffc6,
"F10": 0xffc7,
"F11": 0xffc8,
"F12": 0xffc9,
"ShiftLeft": 0xffe1,
"ShiftRight": 0xffe2,
"ControlLeft": 0xffe3,
"ControlRight": 0xffe4,
"AltLeft": 0xffe9,
"AltRight": 0xffea,
"MetaLeft": 0xffe7,
"MetaRight": 0xffe8
}
function convertAmtKeyCode(e) {
if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3) + ((e.shiftKey == false) ? 32 : 0); }
if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6); }
return convertAmtKeyCodeTable[e.code];
}
/*
Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you don<6F>t need to
implement all the languages (this is taken care by the USB Scancode Extension in RFB4.0 protocol).
The only subset recognized by the FW is the defined by the following sets : XK_LATIN1 , XK_MISCELLANY, XK_3270, XK_XKB_KEYS, XK_KATAKANA.
In addition to keysymdef.h symbols there are 6 japanese extra keys that we do support:
#define XK_Intel_EU_102kbd_backslash_pipe_45 0x17170056 // European 102-key: 45 (backslash/pipe), usb Usage: 0x64
#define XK_Intel_JP_106kbd_yen_pipe 0x1717007d // Japanese 106-key: 14 (Yen/pipe), usb Usage: 0x89
#define XK_Intel_JP_106kbd_backslash_underbar 0x17170073 // Japanese 106-key: 56 (backslash/underbar), usb Usage: 0x87
#define XK_Intel_JP_106kbd_NoConvert 0x1717007b // Japanese 106-key: 131 (NoConvert), usb Usage: 0x8b
#define XK_Intel_JP_106kbd_Convert 0x17170079 // Japanese 106-key: 132 (Convert), usb Usage: 0x8a
#define XK_Intel_JP_106kbd_Hirigana_Katakana 0x17170070 // Japanese 106-key: 133 (Hirigana/Katakana), usb Usage: 0x88
*/
function _keyevent(d, e) {
if (!e) { e = window.event; }
if (e.code) {
// For new browsers, this mapping is keyboard language independent
var k = convertAmtKeyCode(e);
if (k != null) { obj.sendkey(k, d); }
} else {
var k = e.keyCode;
if (k == 173) k = 189; // '-' key (Firefox)
if (k == 61) k = 187; // '=' key (Firefox)
var kk = k;
if (e.shiftKey == false && k >= 65 && k <= 90) kk = k + 32;
if (k >= 112 && k <= 124) kk = k + 0xFF4E;
if (k == 8) kk = 0xff08; // Backspace
if (k == 9) kk = 0xff09; // Tab
if (k == 13) kk = 0xff0d; // Return
if (k == 16) kk = 0xffe1; // Shift (Left)
if (k == 17) kk = 0xffe3; // Ctrl (Left)
if (k == 18) kk = 0xffe9; // Alt (Left)
if (k == 27) kk = 0xff1b; // ESC
if (k == 33) kk = 0xff55; // PageUp
if (k == 34) kk = 0xff56; // PageDown
if (k == 35) kk = 0xff57; // End
if (k == 36) kk = 0xff50; // Home
if (k == 37) kk = 0xff51; // Left
if (k == 38) kk = 0xff52; // Up
if (k == 39) kk = 0xff53; // Right
if (k == 40) kk = 0xff54; // Down
if (k == 45) kk = 0xff63; // Insert
if (k == 46) kk = 0xffff; // Delete
if (k >= 96 && k <= 105) kk = k - 48; // Key pad numbers
if (k == 106) kk = 42; // Pad *
if (k == 107) kk = 43; // Pad +
if (k == 109) kk = 45; // Pad -
if (k == 110) kk = 46; // Pad .
if (k == 111) kk = 47; // Pad /
if (k == 186) kk = 59; // ;
if (k == 187) kk = 61; // =
if (k == 188) kk = 44; // ,
if (k == 189) kk = 45; // -
if (k == 190) kk = 46; // .
if (k == 191) kk = 47; // /
if (k == 192) kk = 96; // `
if (k == 219) kk = 91; // [
if (k == 220) kk = 92; // \
if (k == 221) kk = 93; // ]t
if (k == 222) kk = 39; // '
//console.log('Key' + d + ": " + k + " = " + kk);
obj.sendkey(kk, d);
}
return obj.haltEvent(e);
}
obj.sendkey = function (k, d) {
if (typeof k == 'object') { for (var i in k) { obj.sendkey(k[i][0], k[i][1]); } }
else { obj.Send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); }
}
function handleServerCutText(acc) {
if (acc.length < 8) return 0;
var len = ReadInt(obj.acc, 4) + 8;
if (acc.length < len) return 0;
// ###BEGIN###{DesktopInband}
if (obj.onKvmData != null) {
var d = acc.substring(8, len);
if ((d.length >= 16) && (d.substring(0, 15) == '\0KvmDataChannel')) {
if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; console.log('KVM Data Channel Supported.'); }
if (((obj.onKvmDataAck == -1) && (d.length == 16)) || (d.charCodeAt(15) != 0)) { obj.onKvmDataAck = true; }
if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Recv(' + (d.length - 16) + '): ' + d.substring(16)); }
if (d.length > 16) { obj.onKvmData(d.substring(16)); } // Event the data and ack
if ((obj.onKvmDataAck == true) && (obj.onKvmDataPending.length > 0)) { obj.sendKvmData(obj.onKvmDataPending.shift()); } // Send pending data
}
}
// ###END###{DesktopInband}
return len;
}
// ###BEGIN###{DesktopInband}
obj.sendKvmData = function (x) {
if (obj.onKvmDataAck !== true) {
obj.onKvmDataPending.push(x);
} else {
if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Send(' + x.length + '): ' + x); }
x = '\0KvmDataChannel\0' + x;
obj.Send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x);
obj.onKvmDataAck = false;
}
}
// Send a HWKVM keep alive if it's not been sent in the last 5 seconds.
obj.sendKeepAlive = function () {
if (obj.lastKeepAlive < Date.now() - 5000) { obj.lastKeepAlive = Date.now(); obj.Send(String.fromCharCode(6, 0, 0, 0) + IntToStr(16) + '\0KvmDataChannel\0'); }
}
// ###END###{DesktopInband}
obj.SendCtrlAltDelMsg = function () { obj.sendcad(); }
obj.sendcad = function () {
obj.sendkey(0xFFE3, 1); // Control
obj.sendkey(0xFFE9, 1); // Alt
obj.sendkey(0xFFFF, 1); // Delete
obj.sendkey(0xFFFF, 0); // Delete
obj.sendkey(0xFFE9, 0); // Alt
obj.sendkey(0xFFE3, 0); // Control
}
var _MouseInputGrab = false;
var _KeyInputGrab = false;
obj.GrabMouseInput = function () {
if (_MouseInputGrab == true) return;
var c = obj.canvas.canvas;
c.onmouseup = obj.mouseup;
c.onmousedown = obj.mousedown;
c.onmousemove = obj.mousemove;
//if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
_MouseInputGrab = true;
}
obj.UnGrabMouseInput = function () {
if (_MouseInputGrab == false) return;
var c = obj.canvas.canvas;
c.onmousemove = null;
c.onmouseup = null;
c.onmousedown = null;
//if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
_MouseInputGrab = false;
}
obj.GrabKeyInput = function () {
if (_KeyInputGrab == true) return;
document.onkeyup = obj.handleKeyUp;
document.onkeydown = obj.handleKeyDown;
document.onkeypress = obj.handleKeys;
_KeyInputGrab = true;
}
obj.UnGrabKeyInput = function () {
if (_KeyInputGrab == false) return;
document.onkeyup = null;
document.onkeydown = null;
document.onkeypress = null;
_KeyInputGrab = false;
}
obj.handleKeys = function (e) { return obj.haltEvent(e); }
obj.handleKeyUp = function (e) { return _keyevent(0, e); }
obj.handleKeyDown = function (e) { return _keyevent(1, e); }
obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
// RFB "PointerEvent" and mouse handlers
obj.mousedown = function (e) { obj.buttonmask |= (1 << e.button); return obj.mousemove(e, 1); }
obj.mouseup = function (e) { obj.buttonmask &= (0xFFFF - (1 << e.button)); return obj.mousemove(e, 1); }
obj.mousemove = function (e, force) {
if (obj.state < 4) return true;
var pos = obj.getPositionOfControl(Q(obj.canvasid));
obj.mx = (e.pageX - pos[0]) * (obj.canvas.canvas.height / Q(obj.canvasid).offsetHeight);
obj.my = ((e.pageY - pos[1] + (scrolldiv ? scrolldiv.scrollTop : 0)) * (obj.canvas.canvas.width / Q(obj.canvasid).offsetWidth));
// ###BEGIN###{DesktopRotation}
if (obj.noMouseRotate != true) {
obj.mx2 = _crotX(obj.mx, obj.my);
obj.my = _crotY(obj.mx, obj.my);
obj.mx = obj.mx2;
}
// ###END###{DesktopRotation}
// This is the mouse motion nagle timer. Slow down the mouse motion event rate.
if (force == 1) {
obj.Send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
if (obj.mNagleTimer != null) { clearTimeout(obj.mNagleTimer); obj.mNagleTimer = null; }
} else {
if (obj.mNagleTimer == null) {
obj.mNagleTimer = setTimeout(function () {
obj.Send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
obj.mNagleTimer = null;
}, 50);
}
}
// ###BEGIN###{DesktopFocus}
// Update focus area if we are in focus mode
QV('DeskFocus', obj.focusmode);
if (obj.focusmode != 0) {
var x = Math.min(obj.mx, obj.canvas.canvas.width - obj.focusmode),
y = Math.min(obj.my, obj.canvas.canvas.height - obj.focusmode),
df = obj.focusmode * 2,
c = Q(obj.canvasid),
qx = c.offsetHeight / obj.canvas.canvas.height,
qy = c.offsetWidth / obj.canvas.canvas.width,
q = QS('DeskFocus'),
ppos = obj.getPositionOfControl(Q(obj.canvasid).parentElement);
q.left = (Math.max(((x - obj.focusmode) * qx), 0) + (pos[0] - ppos[0])) + 'px';
q.top = (Math.max(((y - obj.focusmode) * qy), 0) + (pos[1] - ppos[1])) + 'px';
q.width = ((df * qx) - 6) + 'px';
q.height = ((df * qx) - 6) + 'px';
}
// ###END###{DesktopFocus}
return obj.haltEvent(e);
}
obj.getPositionOfControl = function (Control) {
var Position = Array(2);
Position[0] = Position[1] = 0;
while (Control) {
Position[0] += Control.offsetLeft;
Position[1] += Control.offsetTop;
Control = Control.offsetParent;
}
return Position;
}
return obj;
}

509
amt-ider-node-0.0.1.js Normal file
View File

@@ -0,0 +1,509 @@
/**
* @description Meshcentral
* @author Ylian Saint-Hilaire
* @version v0.0.1
*/
function CreateIMRSDKWrapper() {
var obj = {};
var _IMRSDK;
var _ImrSdkVersion;
var _ref = require("ref");
var _ffi = require("ffi");
var _struct = require('ref-struct');
var _arrayType = require('ref-array');
obj.pendingData = {};
// Callback from the native lib back into js (StdCall)
var uintPtr = _ref.refType('uint');
var OpenHandlerCallBack = _ffi.Callback('int', ['uint', 'uint'], _ffi.FFI_STDCALL, function (clientID, conID) { obj.pendingData[conID] = ''; return 0; });
var CloseHandlerCallBack = _ffi.Callback('int', ['uint'], _ffi.FFI_STDCALL, function (conID) { obj.pendingData[conID] = ''; return 0; });
var ReceiveHandlerCallBack = _ffi.Callback('int', ['pointer', uintPtr, 'uint'], _ffi.FFI_STDCALL, function (bufferPtr, lengthPtr, conID) {
try {
var bufferLen = lengthPtr.readUInt32LE(0), buffer = _ref.reinterpret(bufferPtr, bufferLen);
lengthPtr.writeUInt32LE(obj.pendingData[conID].length, 0);
for (var i = 0; i < obj.pendingData[conID].length; i++) { buffer[i] = obj.pendingData[conID].charCodeAt(i); }
//console.log("ReceiveHandlerCallBack(conID: " + conID + ", Len: " + obj.pendingData[conID].length + ")");
obj.pendingData[conID] = '';
} catch (e) { console.log(e); }
return 0;
});
var SendHandlerCallBack = _ffi.Callback('int', ['pointer', 'uint', 'uint'], _ffi.FFI_STDCALL, function (ptr, length, conID) {
try {
var buffer = _ref.reinterpret(ptr, length), str = '';
for (var i = 0; i < length; i++) { str += String.fromCharCode(buffer[i]); }
//console.log("SendHandlerCallBack(conID: " + conID + ", Len: " + length + ")");
obj.client.write(str, 'binary');
} catch (e) { console.log(e); }
return 0;
});
var _IMRVersion = _struct({ 'major': 'ushort', 'minor': 'ushort' });
var _IMRVersionPtr = _ref.refType(_IMRVersion);
var _IMRClientInfo = _struct({ 'type': 'int', 'ip': _arrayType('char', 128), 'guid': _arrayType('char', 16)});
var _IMRClientInfoPtr = _ref.refType(_IMRClientInfo);
var _ProxySettings = _struct({ 'type': 'int', 'server': _arrayType('char', 128), 'port': 'int', 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128) });
var _TCPSessionParams = _struct({ 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128) });
var _TCPSessionParamsPtr = _ref.refType(_TCPSessionParams);
var _TCPSessionParamsEx = _struct({ 'version': 'int', 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128), 'proxy': _ProxySettings });
var _TCPSessionParamsExPtr = _ref.refType(_TCPSessionParamsEx);
var _TCPSessionParamsEx2 = _struct({ 'version': 'int', 'user': _arrayType('char', 128), 'pass': _arrayType('char', 128), 'domain': _arrayType('char', 254), 'proxy': _ProxySettings });
var _TCPSessionParamsEx2Ptr = _ref.refType(_TCPSessionParamsEx2);
var _FeaturesSupported = _struct({ 'ider_dev_pri': 'int', 'ider_dev_sec': 'int', 'reserved': _arrayType('char', 30) });
var _FeaturesSupportedPtr = _ref.refType(_FeaturesSupported);
var _IDERTout = _struct({ 'rx_timeout': 'ushort', 'tx_timeout': 'ushort', 'hb_timeout': 'ushort' });
var _IDERToutPtr = _ref.refType(_IDERTout);
var _IDERStatistics = _struct({
'error_state': 'int',
'data_transfer': 'int',
'num_reopen': 'ushort',
'num_error': 'int',
'num_reset': 'int',
'last_cmd_length': 'int',
'data_sent': 'int',
'data_received': 'int',
'packets_sent': 'int',
'packets_received': 'int'
});
var _IDERStatisticsPtr = _ref.refType(_IDERStatistics);
var _IDERDeviceState = _struct({ 'pri_default': 'int', 'pri_current': 'int', 'sec_default': 'int', 'sec_current': 'int' });
var _IDERDeviceStatePtr = _ref.refType(_IDERDeviceState);
var _IDERDeviceCmd = _struct({ 'pri_op': 'int', 'pri_timing': 'int', 'sec_op': 'int', 'sec_timing': 'int' });
var _IDERDeviceCmdPtr = _ref.refType(_IDERDeviceCmd);
var _IDERDeviceResult = _struct({ 'pri_res': 'int', 'sec_res': 'int' });
var _IDERDeviceResultPtr = _ref.refType(_IDERDeviceResult);
var _SockCallBacks = _struct({ 'OpenHandler': 'pointer', 'CloseHandler': 'pointer', 'ReceiveHandler': 'pointer', 'SendHandler': 'pointer' });
var _SockCallBacksPtr = _ref.refType(_SockCallBacks);
var _intPtr = _ref.refType('int');
function _Setup(lib) {
try {
_IMRSDK = _ffi.Library(lib, {
"IMR_Init": ['uint', [_IMRVersionPtr, 'string']], // IMRResult IMR_Init(IMRVersion *version, char *ini_file);
"IMR_InitEx": ['uint', [_IMRVersionPtr, 'string', _SockCallBacksPtr]], // IMRResult IMR_Init(IMRVersion *version, char *ini_file, void *funcPtrs);
"IMR_ReadyReadSock": ['uint', ['uint']], // IMRResult IMR_ReadyReadSock(uint conid);
"IMR_Close": ['uint', []], // IMRResult IMR_Close();
"IMR_GetErrorStringLen": ['uint', ['uint', _intPtr]], // IMRResult IMR_GetErrorStringLen(IMRResult, int * str_len);
"IMR_GetErrorString": ['uint', ['uint', 'pointer']], // IMRResult IMR_GetErrorString(IMRResult, char * str);
"IMR_SetCertificateInfo": ['uint', ['string', 'string', 'string']], // IMRResult IMR_SetCertificateInfo(const char *root_cert, const char *private_cert, const char *cert_pass);
"IMR_SetClientCertificate": ['uint', ['string']], // IMRResult IMR_SetClientCertificate(const char *common_name);
"IMR_AddClient": ['uint', ['int', 'string', 'pointer', _intPtr]], // IMRResult IMR_AddClient(ClientType new_client_type, char * client_ip, GUIDType client_guid, ClientID * new_client_id);
"IMR_RemoveClient": ['uint', ['int']], // IMRResult IMR_RemoveClient(ClientID client_id);
"IMR_RemoveAllClients": ['uint', []], // IMRResult IMR_RemoveAllClients();
"IMR_GetAllClients": ['uint', [_arrayType('int'), _intPtr]], // IMRResult IMR_GetAllClients(ClientID * client_list, int * client_list_size);
"IMR_GetClientInfo": ['uint', ['int', _IMRClientInfoPtr]], // IMRResult IMR_GetClientInfo(ClientID client_id, ClientInfo *);
"IMR_IDEROpenTCPSession": ['uint', ['int', _TCPSessionParamsPtr, _IDERToutPtr, 'string', 'string']], // IMRResult IMR_IDEROpenTCPSession(ClientID client_id, TCPSessionParams * params, IDERTout * touts, char * drive0, char * drive1);
"IMR_IDEROpenTCPSessionEx": ['uint', ['int', _TCPSessionParamsExPtr, _IDERToutPtr, 'string', 'string']], // IMRResult IMR_IDEROpenTCPSessionEx(ClientID client_id, TCPSessionParamsEx * params, IDERTout * touts, char * drive0, char * drive1);
"IMR_IDEROpenTCPSessionEx2": ['uint', ['int', _TCPSessionParamsEx2Ptr, _IDERToutPtr, 'string', 'string']], // IMRResult IMR_IDEROpenTCPSessionEx2(ClientID client_id, TCPSessionParamsEx2 * params, IDERTout * touts, char * drive0, char * drive1);
"IMR_IDERCloseSession": ['uint', ['int']], // IMRResult IMR_IDERCloseSession(ClientID client_id);
"IMR_IDERClientFeatureSupported" : ['uint', ['int', _FeaturesSupportedPtr]], // IMRResult IMR_IDERClientFeatureSupported(ClientID client_id, FeaturesSupported * supported);
"IMR_IDERGetDeviceState" : ['uint', ['int', _IDERDeviceStatePtr]], // IMRResult IMR_IDERGetDeviceState(ClientID client_id, IDERDeviceState * state);
"IMR_IDERSetDeviceState" : ['uint', ['int', _IDERDeviceCmdPtr, _IDERDeviceResultPtr]], // IMRResult IMR_IDERSetDeviceState(ClientID client_id, IDERDeviceCmd * cmd, IDERDeviceResult * result);
"IMR_IDERGetSessionStatistics": ['uint', ['int', _IDERStatisticsPtr]], // IMRResult IMR_IDERGetSessionStatistics(ClientID client_id, IDERStatistics * stat);
"IMR_SetOpt": ['uint', ['int', 'string', 'string', 'int']], // IMRResult IMR_SetOpt(/*IN*/ClientID id, /*IN*/int optname, /*IN*/ const char *optval, /*IN*/ int optlen);
"IMR_GetOpt": ['uint', ['int', 'string', 'pointer', _intPtr]], // IMRResult IMR_GetOpt(/*IN*/ClientID id, /*IN*/int optname, /*OUT*/ char *optval, /*OUT*/ int* optlen);
});
} catch (e) { return false; }
return true;
}
// Try to link to both 32bit and 64bit IMRSDK.dll's
if (_Setup('imrsdk') == false) { if (_Setup('imrsdk_x64') == false) { return null; } }
// IMR_Init
obj.Init = function() {
var version = new _IMRVersion();
var error = _IMRSDK.IMR_Init(version.ref(), "imrsdk.ini");
if (error == 4) return _ImrSdkVersion; // If already initialized, return previous version information.
if (error != 0) { throw obj.GetErrorString(error); }
_ImrSdkVersion = { major: version.major, minor: version.minor };
return _ImrSdkVersion;
}
// IMR_InitEx
obj.InitEx = function(client) {
var version = new _IMRVersion();
var callbacks = new _SockCallBacks();
callbacks.OpenHandler = OpenHandlerCallBack;
callbacks.CloseHandler = CloseHandlerCallBack;
callbacks.ReceiveHandler = ReceiveHandlerCallBack;
callbacks.SendHandler = SendHandlerCallBack;
obj.client = client;
var error = _IMRSDK.IMR_InitEx(version.ref(), "imrsdk.ini", callbacks.ref());
if (error == 4) return _ImrSdkVersion; // If already initialized, return previous version information.
if (error != 0) { throw obj.GetErrorString(error); }
_ImrSdkVersion = { major: version.major, minor: version.minor };
return _ImrSdkVersion;
}
// IMR_ReadyReadSock
obj.ReadyReadSock = function (connid, func) { _IMRSDK.IMR_ReadyReadSock.async(connid, func); }
// IMR_Close
obj.Close = function (func) { _IMRSDK.IMR_Close.async(func); }
// IMR_GetErrorString
obj.GetErrorString = function (v) {
var errorStringLen = _ref.alloc('int');
var error = _IMRSDK.IMR_GetErrorStringLen(v, errorStringLen); if (error != 0) return 'Unknown IMRSDK Error ' + v;
var errorStringLenEx = errorStringLen.deref();
var errorString = new Buffer(errorStringLenEx);
error = _IMRSDK.IMR_GetErrorString(v, errorString); if (error != 0) return 'Unknown IMRSDK Error ' + v;
//console.log('IMRSDK ' + errorString.toString().substring(0, errorStringLenEx));
return 'IMRSDK ' + errorString.toString().substring(0, errorStringLenEx);
}
// IMR_SetCertificateInfo
obj.SetCertificateInfo = function (root_cert, private_cert, cert_pass) {
var error = _IMRSDK.IMR_SetCertificateInfo(root_cert, private_cert, cert_pass); if (error != 0) { throw obj.GetErrorString(error); }
}
// IMR_SetClientCertificate
obj.SetClientCertificate = function (common_name) {
var error = _IMRSDK.IMR_SetClientCertificate(common_name); if (error != 0) { throw obj.GetErrorString(error); }
}
// IMR_AddClient
// Type: 1=TCP, 2=TLS
obj.AddClient = function (type, ip) {
var guid = new Buffer(16);
for (var i = 0; i < 16; i++) { guid[i] = Math.floor(Math.random() * 255); }
var clientid = _ref.alloc('int');
var error = _IMRSDK.IMR_AddClient(type, ip, guid, clientid); if (error != 0) { throw obj.GetErrorString(error); }
return clientid.deref();
}
// IMR_RemoveClient
obj.RemoveClient = function (client_id) {
var error = _IMRSDK.IMR_RemoveClient(client_id); if (error != 0) { throw obj.GetErrorString(error); }
}
// IMR_RemoveAllClients
obj.RemoveAllClients = function () {
var error = _IMRSDK.IMR_RemoveAllClients(); if (error != 0) { throw obj.GetErrorString(error); }
}
// IMR_GetAllClients
obj.GetAllClients = function () {
var clientListLength = _ref.alloc('int');
var error = _IMRSDK.IMR_GetAllClients(null, clientListLength); if (error != 0) { throw obj.GetErrorString(error); }
var clientListLengthEx = clientListLength.deref();
var IntArray = _arrayType(_ref.types.int)
var buf = new Buffer(4 * clientListLengthEx);
var error = _IMRSDK.IMR_GetAllClients(buf, clientListLength); if (error != 0) { throw obj.GetErrorString(error); }
clientListLengthEx = clientListLength.deref();
var array = IntArray.untilZeros(buf)
var clientids = [];
for (var i = 0; i < clientListLengthEx; i++) { clientids.push(array[i]); }
return clientids;
}
// IMR_GetClientInfo
obj.GetClientInfo = function (client_id) {
var client = new obj.IMRClientInfo();
var error = _IMRSDK.IMR_GetClientInfo(client_id, client.ref()); if (error != 0) { throw obj.GetErrorString(error); }
var ip = '', i = 0;
while (client.ip[i] != 0) { ip += String.fromCharCode(client.ip[i++]); }
return { type: client.type, ip: ip };
}
// IMR_IDEROpenTCPSession
obj.IDEROpenTCPSessionAsync = function (client_id, user, pass, driveimg, driveiso, func) {
//console.log('IDEROpenTCPSessionAsync', client_id, user, pass, driveimg, driveiso);
// Setup parameters
var params = new _TCPSessionParams();
var user2 = obj.decode_utf8(user);
var pass2 = obj.decode_utf8(pass);
for (var i = 0; i < 128; i++) { params.user[i] = 0; }
for (var i = 0; i < user2.length; i++) { params.user[i] = user2.charCodeAt(i); }
for (var i = 0; i < 128; i++) { params.pass[i] = 0; }
for (var i = 0; i < pass2.length; i++) { params.pass[i] = pass2.charCodeAt(i); }
// Setup
var IDERTout = new _IDERTout();
IDERTout.rx_timeout = 30000; // Default is 10000
IDERTout.tx_timeout = 0; // Default is 0
IDERTout.hb_timeout = 20000; // Default is 5000
// Make the call
_IMRSDK.IMR_IDEROpenTCPSession.async(client_id, params.ref(), IDERTout.ref(), driveimg, driveiso, function (x, error) {
if (error == 38) { return error; } // User consent required
//if (error != 0) { throw obj.GetErrorString(error); }
if (func != null) { func(error); }
});
}
// IMR_IDERCloseSession
obj.IDERCloseSessionAsync = function (client_id, func) { _IMRSDK.IMR_IDERCloseSession.async(client_id, func); }
// IMR_IDERClientFeatureSupported
obj.IDERClientFeatureSupported = function (client_id) {
var features = new _FeaturesSupported();
var error = _IMRSDK.IMR_IDERClientFeatureSupported(client_id, features.ref()); if (error != 0) { throw obj.GetErrorString(error); }
return { ider_dev_pri: features.ider_dev_pri, ider_dev_sec: features.ider_dev_sec }
}
// IMR_IDERGetDeviceState
obj.IDERGetDeviceState = function (client_id) {
var state = new _IDERDeviceState();
var error = _IMRSDK.IMR_IDERGetDeviceState(client_id, state.ref()); if (error != 0) { throw obj.GetErrorString(error); }
return { pri_default: state.pri_default, pri_current: state.pri_current, sec_default: state.sec_default, sec_current: state.sec_current }
}
// IMR_IDERSetDeviceState Async
obj.IDERSetDeviceStateAsync = function (client_id, pri_op, pri_timing, sec_op, sec_timing, func) {
var cmd = new _IDERDeviceCmd();
cmd.pri_op = pri_op;
cmd.pri_timing = pri_timing;
cmd.sec_op = sec_op;
cmd.sec_timing = sec_timing;
var result = new _IDERDeviceResult();
_IMRSDK.IMR_IDERSetDeviceState.async(client_id, cmd.ref(), result.ref(), function (x, error) {
if (func != null) func(error, { pri_res: result.pri_res, sec_res: result.sec_res });
});
}
// IMR_IDERGetSessionStatistics
obj.IDERGetSessionStatistics = function (client_id) {
var stats = new _IDERStatistics();
var error = _IMRSDK.IMR_IDERGetSessionStatistics(client_id, stats.ref()); if (error != 0) { throw obj.GetErrorString(error); }
return { error_state: stats.error_state, data_transfer: stats.data_transfer, num_reopen: stats.num_reopen, num_error: stats.num_error, num_reset: stats.num_reset, last_cmd_length: stats.last_cmd_length, data_sent: stats.data_sent, data_received: stats.data_received, packets_sent: stats.packets_sent, packets_received: stats.packets_received };
}
// IMR_SetOpt
obj.SetOpt = function (client_id, optname, optval) {
var error = _IMRSDK.IMR_SetOpt(client_id, optname, optval, optval.length); if (error != 0) { throw obj.GetErrorString(error); }
}
// IMR_GetOpt
obj.GetOpt = function (client_id, optname) {
var len = _ref.alloc('int');
var error = _IMRSDK.IMR_GetOpt(client_id, optname, null, len); if (error != 0) { throw obj.GetErrorString(error); }
var buf = new Buffer(len.deref() + 1);
var error = _IMRSDK.IMR_GetOpt(client_id, optname, buf, len); if (error != 0) { throw obj.GetErrorString(error); }
// console.log(buf);
return buf; // TODO: Return something correct after testing this call
}
// UTF-8 encoding & decoding functions
obj.encode_utf8 = function (s) { return unescape(encodeURIComponent(s)); }
obj.decode_utf8 = function (s) { return decodeURIComponent(escape(s)); }
return obj;
}
globalIderPendingCalls = 0;
// Construct a Intel AMT IDER object
var CreateAmtRemoteIderIMR = function () {
if (globalIderPendingCalls != 0) { console.log('Incomplete IDER cleanup (' + globalIderPendingCalls + ').'); return null; } // IDER is not ready.
var _net = require('net');
var _tls = require('tls');
var obj = {};
obj.onStateChanged = null;
obj.state = 0;
obj.m = {};
obj.m.protocol = 3; // IDER
obj.m.state = 0;
obj.m.bytesToAmt = 0;
obj.m.bytesFromAmt = 0;
obj.m.imrsdk = null;
obj.receivedCount = 0;
// Private method
obj.StateChange = function (newstate) { if (obj.state != newstate) { obj.state = newstate; obj.onStateChanged(obj, obj.state); } }
obj.m.Start = function (host, port, user, pass, tls, wsmanCert, tlsOptions) {
obj.constants = require('constants');
obj.m.host = host;
obj.m.port = port;
obj.m.user = user;
obj.m.pass = pass;
obj.m.tls = tls;
obj.m.xtlsoptions = tlsOptions;
obj.m.wsmanCert = wsmanCert;
obj.m.bytesToAmt = 0;
obj.m.bytesFromAmt = 0;
if (obj.m.onDialogPrompt) {
if (require('os').platform() == 'win32') {
obj.m.onDialogPrompt(obj.m, { 'html': '<br>Select a CDROM and Floppy disk image to start the disk redirection.<br><br><br><div style=height:26px;margin-bottom:6px><select style=float:right;width:300px id=storagesourceoption onchange=onIderSourceChange()><option value=0 selected>ISO & IMG from file</option><option value=1>ISO from drive, IMG from file</option><option value=2>ISO from file, IMG from drive</option><option value=3>ISO & IMG from drive</option></select><div>Source</div></div><div style=height:20px><input type=file id=iderisofile accept=.iso style=float:right;width:300px><select style=float:right;width:300px;display:none id=iderisodrive><option>D:</option><option>E:</option><option>F:</option><option>G:</option><option>H:</option><option>I:</option></select><div>.ISO file</div></div><br><div style=height:20px><input type=file id=iderimgfile accept=.img style=float:right;width:300px><select style=float:right;width:300px;display:none id=iderimgdrive><option>A:</option><option>B:</option></select><div>.IMG file</div></div><br /><div style=height:26px><select style=float:right;width:300px id=storageserveroption><option value=0>On Reset</option><option value=1 selected>Gracefully</option><option value=2>Immediately</option></select><div>Start</div></div>' });
} else {
obj.m.onDialogPrompt(obj.m, { 'html': '<br>Select a CDROM and Floppy disk image to start the disk redirection.<br><br><br><div style=height:20px><input type=file id=iderisofile accept=.iso style=float:right;width:300px><div>.ISO file</div></div><br><div style=height:20px><input type=file id=iderimgfile accept=.img style=float:right;width:300px><div>.IMG file</div></div><br /><div style=height:26px><select style=float:right;width:300px id=storageserveroption><option value=0>On Reset</option><option value=1 selected>Gracefully</option><option value=2>Immediately</option></select><div>Start</div></div>' });
}
}
}
obj.m.Stop = function () {
if (obj.m.client != null) {
//console.log('obj.m.Stop - client');
try { obj.m.client.destroy(); } catch (e) { console.log(e); }
delete obj.m.client;
}
if (obj.m.imrsdk) {
//console.log('obj.m.Stop - imrsdk', obj.m.clientid);
try
{
if (obj.m.clientid !== undefined) {
try { obj.m.imrsdk.IDERCloseSessionAsync(obj.m.clientid, function (error) { }); } catch (e) { }
delete obj.m.clientid;
}
obj.m.imrsdk.Close(function (error) { });
} catch (e) { }
delete obj.m.imrsdk;
}
obj.StateChange(0);
}
obj.m.Update = function () {
if ((obj.m.imrsdk != undefined) && (obj.m.clientid !== undefined)) {
try {
var stats = obj.m.imrsdk.IDERGetSessionStatistics(obj.m.clientid);
obj.m.bytesToAmt = stats.data_sent;
obj.m.bytesFromAmt = stats.data_received;
} catch (e) {
obj.m.bytesToAmt = -1;
obj.m.bytesFromAmt = -1;
}
} else {
obj.m.bytesToAmt = 0;
obj.m.bytesFromAmt = 0;
}
}
obj.m.dialogPrompt = function (userConsentFunc) {
try {
obj.m.Stop();
if (Q('storageserveroption')) {
var sel = 0;
if (require('os').platform() == 'win32') { sel = Q('storagesourceoption').value; }
if ((sel & 1) == 0) {
var x = Q('iderisofile');
if (!x || x.files.length != 1) { obj.m.isopath = 'empty.iso'; } else { obj.m.isopath = x.files[0].path; }
} else {
obj.m.isopath = Q('iderisodrive').value;
}
if ((sel & 2) == 0) {
var x = Q('iderimgfile');
if (!x || x.files.length != 1) { obj.m.imgpath = 'empty.img'; } else { obj.m.imgpath = x.files[0].path; }
} else {
obj.m.imgpath = Q('iderimgdrive').value;
}
obj.m.startoption = Q('storageserveroption').value;
startIderSession(userConsentFunc);
}
} catch (e) {
//console.log(e);
obj.m.onDialogPrompt(obj.m, { 'html': e }, 1);
}
}
function startIderSession(userConsentFunc) {
if (globalIderPendingCalls != 0) { console.log('Incomplete IDER cleanup (' + globalIderPendingCalls + ').'); return; }
try {
//console.log("IDER-Start");
if (obj.m.xtlsoptions && obj.m.xtlsoptions.meshServerConnect) {
// Open thru MeshCentral websocket wrapper
obj.m.client = CreateWebSocketWrapper(obj.m.xtlsoptions.host, obj.m.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.m.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.m.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.m.host) + '&p=2', obj.m.xtlsoptions.xtlsFingerprint);
obj.m.client.connect(function () {
//console.log('IDER Connected WS, ' + obj.m.xtlsoptions.host + ':' + obj.m.xtlsoptions.port + ' to ' + obj.m.host);
startIderSessionEx(userConsentFunc);
});
} else if (obj.m.tls == 0) {
// Open connection without TLS
obj.m.client = new _net.Socket();
obj.m.client.connect(obj.m.port, obj.m.host, function () {
//console.log('IDER Connected TCP, ' + obj.m.host + ':' + obj.m.port);
startIderSessionEx(userConsentFunc);
});
} else {
// Open connection with TLS
if (obj.m.xtlsoptions == null) { obj.m.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; }
obj.m.client = _tls.connect(obj.m.port, obj.m.host, obj.m.xtlsoptions, function () {
//console.log('IDER Connected TLS, ' + obj.m.host + ':' + obj.m.port);
// Check the Intel AMT certificate, it must be the same as the one used for WSMAN. If not, disconnect.
var iderTlsCertificate = obj.m.client.getPeerCertificate();
if ((iderTlsCertificate == null) || (obj.m.wsmanCert == null) || (iderTlsCertificate.fingerprint != obj.m.wsmanCert.fingerprint)) {
console.log("Invalid IDER certificate, disconnecting.", iderTlsCertificate);
obj.m.Stop();
} else {
startIderSessionEx(userConsentFunc)
}
});
}
obj.m.client.setEncoding('binary');
obj.m.client.on('data', function (data) {
//console.log("IDER-RECV(" + data.length + ", " + obj.receivedCount + "): " + rstr2hex(data));
if (obj.m.imrsdk == null) { return; }
if ((obj.receivedCount == 0) && (data.charCodeAt(0) == 0x11) && (data.charCodeAt(1) == 0x05)) {
// We got a user consent error, handle it now before IMRSDK.dll can get it.
console.log('IDER user consent required.');
obj.m.imrsdk.pendingData[obj.m.clientid] = String.fromCharCode(0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // Try to cause a fault.
obj.m.imrsdk.ReadyReadSock(0, function (x, error) { });
setTimeout(function () { obj.m.userConsentFunc(startIderSession); }, 500);
} else {
if (obj.m.imrsdk.pendingData[obj.m.clientid] == null) { obj.m.imrsdk.pendingData[obj.m.clientid] = data; } else { obj.m.imrsdk.pendingData[obj.m.clientid] += data; }
obj.m.imrsdk.ReadyReadSock(0, function (x, error) { });
}
obj.receivedCount += data.length;
});
obj.m.client.on('close', function () { obj.m.Stop(); });
// If the TCP connection causes an error, disconnect the associated web socket.
obj.m.client.on('error', function (err) { obj.m.Stop(); });
} catch (e) {
//console.log(e);
obj.m.Stop();
obj.m.onDialogPrompt(obj.m, { 'html': e }, 1);
}
}
function startIderSessionEx(userConsentFunc) {
try {
//console.log("IDER-StartEx");
obj.m.userConsentFunc = userConsentFunc;
obj.m.imrsdk = CreateIMRSDKWrapper();
obj.m.imrsdk.InitEx(obj.m.client);
obj.m.imrsdk.RemoveAllClients();
if (obj.m.amtcertpath) { obj.m.imrsdk.SetCertificateInfo(obj.m.amtcertpath, null, null); }
obj.m.clientid = obj.m.imrsdk.AddClient(obj.m.tls + 1, obj.m.host);
globalIderPendingCalls++;
//console.log('IDEROpenTCPSessionAsync-call');
var error = obj.m.imrsdk.IDEROpenTCPSessionAsync(obj.m.clientid, obj.m.user, obj.m.pass, obj.m.imgpath, obj.m.isopath, function (error) {
//console.log('IDEROpenTCPSessionAsync-callback(' + error + ')');
globalIderPendingCalls--;
if (obj.m.imrsdk == null) return; // We closed already, exit now.
if ((error == 38) && (userConsentFunc != undefined)) { obj.m.Stop(); userConsentFunc(startIderSession); return; }
if (error != 0) { console.log('IDER error ' + error); obj.m.Stop(); }
if (error == 0) {
globalIderPendingCalls++;
obj.m.imrsdk.IDERSetDeviceStateAsync(obj.m.clientid, 0, obj.m.startoption, 0, obj.m.startoption, function (error, result) {
globalIderPendingCalls--;
if (error != 0) {
obj.m.Stop();
obj.m.onDialogPrompt(obj.m, { 'html': e }, 1);
} else {
obj.StateChange(3);
}
});
}
});
} catch (e) {
//console.log(e);
obj.m.Stop();
obj.m.onDialogPrompt(obj.m, { 'html': e }, 1);
}
}
return obj;
}

100
amt-ider-server-ws-0.0.1.js Normal file
View File

@@ -0,0 +1,100 @@
/**
* @description IDER Handling Module
* @author Ylian Saint-Hilaire
* @version v0.0.2
*/
// Construct a Intel AMT Server IDER object
var CreateAmtRemoteServerIder = function () {
var obj = {};
obj.protocol = 4; // IDER-Server
obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
obj.floppy = null;
obj.cdrom = null;
obj.state = 0;
obj.onStateChanged = null;
obj.m = {
sectorStats: null,
onDialogPrompt: null,
dialogPrompt: function (data) { obj.socket.send(JSON.stringify({ action: 'dialogResponse', args: data })); },
bytesToAmt: 0,
bytesFromAmt: 0,
server: true,
Stop: function () { obj.Stop(); }
};
// Private method
function debug() { if (urlvars && urlvars['idertrace']) { console.log(...arguments); } }
// Private method, called by parent when it change state
obj.xxStateChange = function (newstate) {
if (obj.state == newstate) return;
debug("SIDER-StateChange", newstate);
obj.state = newstate;
if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.state); }
}
obj.Start = function (host, port, user, pass, tls) {
debug("SIDER-Start", host, port, user, pass, tls);
obj.host = host;
obj.port = port;
obj.user = user;
obj.pass = pass;
obj.connectstate = 0;
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webider.ashx?host=" + host + "&port=" + port + "&tls=" + tls + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "") + "&tls1only=" + obj.tlsv1only);
obj.socket.onopen = obj.xxOnSocketConnected;
obj.socket.onmessage = obj.xxOnMessage;
obj.socket.onclose = obj.xxOnSocketClosed;
obj.xxStateChange(1);
}
obj.Stop = function () {
debug("SIDER-Stop");
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
obj.xxStateChange(0);
}
obj.xxOnSocketConnected = function () {
obj.xxStateChange(2);
obj.socket.send(JSON.stringify({ action: 'start' }));
}
obj.xxOnMessage = function (data) {
var msg = null;
try { msg = JSON.parse(data.data); } catch (ex) { }
if ((msg == null) || (typeof msg.action != 'string')) return;
switch (msg.action) {
case 'dialog': {
if (obj.m.onDialogPrompt != null) { obj.m.onDialogPrompt(obj, msg.args, msg.buttons); }
break;
}
case 'state': {
if (msg.state == 2) { obj.xxStateChange(3); }
break;
}
case 'stats': {
obj.m.bytesToAmt = msg.toAmt;
obj.m.bytesFromAmt = msg.fromAmt;
if (obj.m.sectorStats) { obj.m.sectorStats(msg.mode, msg.dev, msg.total, msg.start, msg.len); }
break;
}
case 'error': {
var iderErrorStrings = ["", "Floppy disk image does not exist", "Invalid floppy disk image", "Unable to open floppy disk image", "CDROM disk image does not exist", "Invalid CDROM disk image", "Unable to open CDROM disk image", "Can't perform IDER with no disk images"];
console.log('IDER Error: ' + iderErrorStrings[msg.code]);
// TODO: Display dialog box this error.
break;
}
default: {
console.log('Unknown Server IDER action: ' + msg.action);
breal;
}
}
}
obj.xxOnSocketClosed = function () { obj.Stop(); }
return obj;
}

692
amt-ider-ws-0.0.1.js Normal file
View File

@@ -0,0 +1,692 @@
/**
* @description IDER Handling Module
* @author Ylian Saint-Hilaire
* @version v0.0.2
*/
// Construct a Intel AMT IDER object
var CreateAmtRemoteIder = function () {
var obj = {};
obj.protocol = 3; // IDER
obj.bytesToAmt = 0;
obj.bytesFromAmt = 0;
obj.rx_timeout = 30000; // Default 30000
obj.tx_timeout = 0; // Default 0
obj.heartbeat = 20000; // Default 20000
obj.version = 1;
obj.acc = "";
obj.inSequence = 0;
obj.outSequence = 0;
obj.iderinfo = null;
obj.enabled = false;
obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
obj.floppy = null;
obj.cdrom = null;
obj.floppyReady = false;
obj.cdromReady = false;
//obj.pingTimer = null;
// ###BEGIN###{IDERStats}
obj.sectorStats = null;
// ###END###{IDERStats}
// Private method
// ###BEGIN###{IDERDebug}
function debug() { if (urlvars && urlvars['idertrace']) { console.log(...arguments); } }
// ###END###{IDERDebug}
// Mode Sense
var IDE_ModeSence_LS120Disk_Page_Array = String.fromCharCode(0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00);
var IDE_ModeSence_3F_LS120_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31);
var IDE_ModeSence_FloppyDisk_Page_Array = String.fromCharCode(0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00);
var IDE_ModeSence_3F_Floppy_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31);
var IDE_ModeSence_CD_1A_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
//var IDE_ModeSence_CD_1B_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
var IDE_ModeSence_CD_1D_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
var IDE_ModeSence_CD_2A_Array = String.fromCharCode(0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
//var IDE_ModeSence_CD_01_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00);
var IDE_ModeSence_3F_CD_Array = String.fromCharCode(0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
// 0x46 constant data
var IDE_CD_ConfigArrayHeader = String.fromCharCode(0x00, 0x00,0x00, 0x28, 0x00, 0x00, 0x00, 0x08);
var IDE_CD_ConfigArrayProfileList = String.fromCharCode(0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00);
var IDE_CD_ConfigArrayCore = String.fromCharCode(0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02);
var IDE_CD_Morphing = String.fromCharCode(0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00);
var IDE_CD_ConfigArrayRemovable = String.fromCharCode(0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02);
var IDE_CD_ConfigArrayRandom = String.fromCharCode(0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00);
var IDE_CD_Read = String.fromCharCode(0x00, 0x1E, 0x03, 0x00);
var IDE_CD_PowerManagement = String.fromCharCode(0x01, 0x00, 0x03, 0x00);
var IDE_CD_Timeout = String.fromCharCode(0x01, 0x05, 0x03, 0x00);
// 0x01 constant data
var IDE_ModeSence_FloppyError_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
var IDE_ModeSence_Ls120Error_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
var IDE_ModeSence_CDError_Recovery_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00);
// Private method, called by parent when it change state
obj.xxStateChange = function (newstate) {
// ###BEGIN###{IDERDebug}
debug("IDER-StateChange", newstate);
// ###END###{IDERDebug}
if (newstate == 0) { obj.Stop(); }
if (newstate == 3) { obj.Start(); }
}
obj.Start = function () {
// ###BEGIN###{IDERDebug}
debug("IDER-Start");
debug(obj.floppy, obj.cdrom);
// ###END###{IDERDebug}
obj.bytesToAmt = 0;
obj.bytesFromAmt = 0;
obj.inSequence = 0;
obj.outSequence = 0;
g_readQueue = [];
// Send first command, OPEN_SESSION
obj.SendCommand(0x40, ShortToStrX(obj.rx_timeout) + ShortToStrX(obj.tx_timeout) + ShortToStrX(obj.heartbeat) + IntToStrX(obj.version));
// Send sector stats
// ###BEGIN###{IDERStats}
if (obj.sectorStats) {
obj.sectorStats(0, 0, obj.floppy?(obj.floppy.size >> 9):0);
obj.sectorStats(0, 1, obj.cdrom ? (obj.cdrom.size >> 11) : 0);
}
// ###END###{IDERStats}
// Setup the ping timer
//obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
}
obj.Stop = function () {
// ###BEGIN###{IDERDebug}
debug("IDER-Stop");
// ###END###{IDERDebug}
//if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
obj.parent.Stop();
}
// Private method
obj.ProcessData = function (data) {
obj.bytesFromAmt += data.length;
obj.acc += data;
// ###BEGIN###{IDERDebug}
debug('IDER-ProcessData', obj.acc.length, rstr2hex(obj.acc));
// ###END###{IDERDebug}
// Process as many commands as possible
while (true) {
var len = obj.ProcessDataEx();
if (len == 0) return;
if (obj.inSequence != ReadIntX(obj.acc, 4)) {
// ###BEGIN###{IDERDebug}
debug('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4));
// ###END###{IDERDebug}
obj.Stop();
return;
}
obj.inSequence++;
obj.acc = obj.acc.substring(len);
}
}
// Private method
obj.SendCommand = function (cmdid, data, completed, dma) {
if (data == null) { data = ''; }
var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
if (dma) { attributes += 1; }
var x = String.fromCharCode(cmdid, 0, 0, attributes) + IntToStrX(obj.outSequence++) + data;
obj.parent.xxSend(x);
obj.bytesToAmt += x.length;
// ###BEGIN###{IDERDebug}
if (cmdid != 0x4B) { debug('IDER-SendData', x.length, rstr2hex(x)); }
// ###END###{IDERDebug}
}
// CommandEndResponse (SCSI_SENSE)
obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
if (error) { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0), true); }
else { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq), true); }
}
// DataToHost (SCSI_READ)
obj.SendDataToHost = function (device, completed, data, dma) {
var dmalen = (dma) ? 0 : data.length;
if (completed == true) {
obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0) + data, completed, dma);
} else {
obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + data, completed, dma);
}
}
// GetDataFromHost (SCSI_CHUNK)
obj.SendGetDataFromHost = function (device, chunksize) {
obj.SendCommand(0x52, String.fromCharCode(0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), false);
}
// DisableEnableFeatures (STATUS_DATA)
// If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, String.fromCharCode(type) + data); }
// Private method
obj.ProcessDataEx = function () {
if (obj.acc.length < 8) return 0;
// First 8 bytes are the header
// CommandID + 0x000000 + Sequence Number
switch(obj.acc.charCodeAt(0)) {
case 0x41: // OPEN_SESSION
if (obj.acc.length < 30) return 0;
var len = obj.acc.charCodeAt(29);
if (obj.acc.length < (30 + len)) return 0;
obj.iderinfo = {};
obj.iderinfo.major = obj.acc.charCodeAt(8);
obj.iderinfo.minor = obj.acc.charCodeAt(9);
obj.iderinfo.fwmajor = obj.acc.charCodeAt(10);
obj.iderinfo.fwminor = obj.acc.charCodeAt(11);
obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
obj.iderinfo.proto = obj.acc.charCodeAt(21);
obj.iderinfo.iana = ReadIntX(obj.acc, 25);
// ###BEGIN###{IDERDebug}
debug(obj.iderinfo);
// ###END###{IDERDebug}
if (obj.iderinfo.proto != 0) {
// ###BEGIN###{IDERDebug}
debug("Unknown proto", obj.iderinfo.proto);
// ###END###{IDERDebug}
obj.Stop();
}
if (obj.iderinfo.readbfr > 8192) {
// ###BEGIN###{IDERDebug}
debug("Illegal read buffer size", obj.iderinfo.readbfr);
// ###END###{IDERDebug}
obj.Stop();
}
if (obj.iderinfo.writebfr > 8192) {
// ###BEGIN###{IDERDebug}
debug("Illegal write buffer size", obj.iderinfo.writebfr);
// ###END###{IDERDebug}
obj.Stop();
}
if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
//obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
return 30 + len;
case 0x43: // CLOSE
// ###BEGIN###{IDERDebug}
debug('CLOSE');
// ###END###{IDERDebug}
obj.Stop();
return 8;
case 0x44: // KEEPALIVEPING
obj.SendCommand(0x45); // Send PONG back
return 8;
case 0x45: // KEEPALIVEPONG
// ###BEGIN###{IDERDebug}
debug('PONG');
// ###END###{IDERDebug}
return 8;
case 0x46: // RESETOCCURED
if (obj.acc.length < 9) return 0;
var resetMask = obj.acc.charCodeAt(8);
if (g_media === null) {
// No operations are pending
obj.SendCommand(0x47); // Send ResetOccuredResponse
// ###BEGIN###{IDERDebug}
debug('RESETOCCURED1', resetMask);
// ###END###{IDERDebug}
} else {
// Operations are being done, sent the reset once completed.
g_reset = true;
// ###BEGIN###{IDERDebug}
debug('RESETOCCURED2', resetMask);
// ###END###{IDERDebug}
}
return 9;
case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
if (obj.acc.length < 13) return 0;
var type = obj.acc.charCodeAt(8);
var value = ReadIntX(obj.acc, 9);
// ###BEGIN###{IDERDebug}
debug('STATUS_DATA', type, value);
// ###END###{IDERDebug}
switch (type)
{
case 1: // REGS_AVAIL
if (value & 1) {
if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
}
break;
case 2: // REGS_STATUS
obj.enabled = (value & 2) ? true : false;
// ###BEGIN###{IDERDebug}
debug("IDER Status: " + obj.enabled);
// ###END###{IDERDebug}
break;
case 3: // REGS_TOGGLE
if (value != 1) {
// ###BEGIN###{IDERDebug}
debug("Register toggle failure");
// ###END###{IDERDebug}
} //else { obj.SendDisableEnableFeatures(2); }
break;
}
return 13;
case 0x4A: // ERROR OCCURED
if (obj.acc.length < 11) return 0;
// ###BEGIN###{IDERDebug}
debug('IDER: ABORT', obj.acc.charCodeAt(8));
// ###END###{IDERDebug}
//obj.Stop();
return 11;
case 0x4B: // HEARTBEAT
// ###BEGIN###{IDERDebug}
//debug('HEARTBEAT');
// ###END###{IDERDebug}
return 8;
case 0x50: // COMMAND WRITTEN
if (obj.acc.length < 28) return 0;
var device = (obj.acc.charCodeAt(14) & 0x10) ? 0xB0 : 0xA0;
var deviceFlags = obj.acc.charCodeAt(14);
var cdb = obj.acc.substring(16, 28);
var featureRegister = obj.acc.charCodeAt(9);
// ###BEGIN###{IDERDebug}
debug('SCSI_CMD', device, rstr2hex(cdb), featureRegister, deviceFlags);
// ###END###{IDERDebug}
handleSCSI(device, cdb, featureRegister, deviceFlags);
return 28;
case 0x53: // DATA FROM HOST
if (obj.acc.length < 14) return 0;
var len = ReadShortX(obj.acc, 9);
if (obj.acc.length < (14 + len)) return 0;
// ###BEGIN###{IDERDebug}
debug('SCSI_WRITE, len = ' + (14 + len));
// ###END###{IDERDebug}
obj.SendCommand(0x51, String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00), true);
return 14 + len;
default:
// ###BEGIN###{IDERDebug}
debug('Unknown IDER command', obj.acc[0]);
// ###END###{IDERDebug}
obj.Stop();
break;
}
return 0;
}
function handleSCSI(dev, cdb, featureRegister, deviceFlags)
{
var lba;
var len;
switch(cdb.charCodeAt(0))
{
case 0x00: // TEST_UNIT_READY:
// ###BEGIN###{IDERDebug}
debug("SCSI: TEST_UNIT_READY", dev);
// ###END###{IDERDebug}
switch (dev) {
case 0xA0: // DEV_FLOPPY
if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
break;
case 0xB0: // DEV_CDDVD
if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
break;
default:
// ###BEGIN###{IDERDebug}
debug("SCSI Internal error 3", dev);
// ###END###{IDERDebug}
return -1;
}
obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
break;
case 0x08: // READ_6
lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
len = cdb.charCodeAt(4);
if (len == 0) { len = 256; }
// ###BEGIN###{IDERDebug}
debug("SCSI: READ_6", dev, lba, len);
// ###END###{IDERDebug}
sendDiskData(dev, lba, len, featureRegister);
break;
case 0x0a: // WRITE_6
lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
len = cdb.charCodeAt(4);
if (len == 0) { len = 256; }
// ###BEGIN###{IDERDebug}
debug("SCSI: WRITE_6", dev, lba, len);
// ###END###{IDERDebug}
obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
return -1;
/*
case 0x15: // MODE_SELECT_6:
// ###BEGIN###{IDERDebug}
debug("SCSI ERROR: MODE_SELECT_6", dev);
// ###END###{IDERDebug}
obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
return -1;
*/
case 0x1a: // MODE_SENSE_6
// ###BEGIN###{IDERDebug}
debug("SCSI: MODE_SENSE_6", dev);
// ###END###{IDERDebug}
if ((cdb.charCodeAt(2) == 0x3f) && (cdb.charCodeAt(3) == 0x00)) {
var a = 0, b = 0;
switch (dev) {
case 0xA0: // DEV_FLOPPY
if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
a = 0x00;
b = 0x80; // Read only = 0x80, Read write = 0x00
break;
case 0xB0: // DEV_CDDVD
if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
a = 0x05;
b = 0x80;
break;
default:
// ###BEGIN###{IDERDebug}
debug("SCSI Internal error 6", dev);
// ###END###{IDERDebug}
return -1;
}
obj.SendDataToHost(dev, true, String.fromCharCode(0, a, b, 0), featureRegister & 1);
return;
}
obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
break;
case 0x1b: // START_STOP (Called when you eject the CDROM)
//var immediate = cdb.charCodeAt(1) & 0x01;
//var loej = cdb.charCodeAt(4) & 0x02;
//var start = cdb.charCodeAt(4) & 0x01;
obj.SendCommandEndResponse(1, 0, dev);
break;
case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
// ###BEGIN###{IDERDebug}
debug("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
// ###END###{IDERDebug}
if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
break;
case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
// ###BEGIN###{IDERDebug}
debug("SCSI: READ_FORMAT_CAPACITIES", dev);
// ###END###{IDERDebug}
var buflen = ReadShort(cdb, 7);
var mediaStatus = 0, sectors;
var mcSize = buflen / 8; // Capacity descriptor size is 8
switch (dev) {
case 0xA0: // DEV_FLOPPY
if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
sectors = (obj.floppy.size >> 9) - 1;
break;
case 0xB0: // DEV_CDDVD
if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
break;
default:
// ###BEGIN###{IDERDebug}
debug("SCSI Internal error 4", dev);
// ###END###{IDERDebug}
return -1;
}
obj.SendDataToHost(dev, true, IntToStr(8) + String.fromCharCode(0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00), featureRegister & 1);
break;
case 0x25: // READ_CAPACITY
// ###BEGIN###{IDERDebug}
debug("SCSI: READ_CAPACITY", dev);
// ###END###{IDERDebug}
var len = 0;
switch(dev)
{
case 0xA0: // DEV_FLOPPY
if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
// ###BEGIN###{IDERDebug}
debug('DEV_FLOPPY', len); // Number 512 byte blocks
// ###END###{IDERDebug}
break;
case 0xB0: // DEV_CDDVD
if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
// ###BEGIN###{IDERDebug}
debug('DEV_CDDVD', len);
// ###END###{IDERDebug}
break;
default:
// ###BEGIN###{IDERDebug}
debug("SCSI Internal error 4", dev);
// ###END###{IDERDebug}
return -1;
}
//if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
// ###BEGIN###{IDERDebug}
debug("SCSI: READ_CAPACITY2", dev, deviceFlags);
// ###END###{IDERDebug}
obj.SendDataToHost(deviceFlags, true, IntToStr(len) + String.fromCharCode(0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0), featureRegister & 1);
break;
case 0x28: // READ_10
lba = ReadInt(cdb, 2);
len = ReadShort(cdb, 7);
// ###BEGIN###{IDERDebug}
debug("SCSI: READ_10", dev, lba, len);
// ###END###{IDERDebug}
sendDiskData(dev, lba, len, featureRegister);
break;
case 0x2a: // WRITE_10 (Floppy only)
case 0x2e: // WRITE_AND_VERIFY (Floppy only)
lba = ReadInt(cdb, 2);
len = ReadShort(cdb, 7);
// ###BEGIN###{IDERDebug}
debug("SCSI: WRITE_10", dev, lba, len);
// ###END###{IDERDebug}
obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
break;
case 0x43: // READ_TOC (CD Audio only)
var buflen = ReadShort(cdb, 7);
var msf = cdb.charCodeAt(1) & 0x02;
var format = cdb.charCodeAt(2) & 0x07;
if (format == 0) { format = cdb.charCodeAt(9) >> 6; }
// ###BEGIN###{IDERDebug}
debug("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
// ###END###{IDERDebug}
switch (dev) {
case 0xA0: // DEV_FLOPPY
obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented
return -1;
case 0xB0: // DEV_CDDVD
// NOP
break;
default:
// ###BEGIN###{IDERDebug}
debug("SCSI Internal error 9", dev);
// ###END###{IDERDebug}
return -1;
}
if (format == 1) { obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1); }
else if (format == 0) {
if (msf) {
obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13), featureRegister & 1);
} else {
obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1);
}
}
break;
case 0x46: // GET_CONFIGURATION
var sendall = (cdb.charCodeAt(1) != 2);
var firstcode = ReadShort(cdb, 2);
var buflen = ReadShort(cdb, 7);
// ###BEGIN###{IDERDebug}
debug("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
// ###END###{IDERDebug}
if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
// Set the header
var r = IntToStr(0x0008);
// Add the data
if (firstcode == 0) { r += IDE_CD_ConfigArrayProfileList; }
if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r += IDE_CD_ConfigArrayCore; }
if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r += IDE_CD_Morphing; }
if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r += IDE_CD_ConfigArrayRemovable; }
if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r += IDE_CD_ConfigArrayRandom; }
if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r += IDE_CD_Read; }
if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r += IDE_CD_PowerManagement; }
if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r += IDE_CD_Timeout; }
// Set the length
r = IntToStr(r.length) + r;
// Cut the length to buflen if needed
if (r.length > buflen) { r = r.substring(0, buflen); }
obj.SendDataToHost(dev, true, r, featureRegister & 1);
return -1;
case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
//var buflen = (cdb.charCodeAt(7) << 8) + cdb.charCodeAt(8);
//if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
// ###BEGIN###{IDERDebug}
debug("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb.charCodeAt(1), cdb.charCodeAt(4), cdb.charCodeAt(9));
// ###END###{IDERDebug}
if ((cdb.charCodeAt(1) != 0x01) && (cdb.charCodeAt(4) != 0x10)) {
// ###BEGIN###{IDERDebug}
debug('SCSI ERROR');
// ###END###{IDERDebug}
obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01);
break;
}
var present = 0x00;
if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
obj.SendDataToHost(dev, true, String.fromCharCode(0x00, present, 0x80, 0x00), featureRegister & 1); // This is the original version, 4 bytes long
break;
case 0x4c:
obj.SendCommand(0x51, IntToStrX(0) + IntToStrX(0) + IntToStrX(0) + String.fromCharCode(0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00), true);
break;
case 0x51: // READ_DISC_INFO
// ###BEGIN###{IDERDebug}
debug("SCSI READ_DISC_INFO", dev);
// ###END###{IDERDebug}
obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
return -1;
case 0x55: // MODE_SELECT_10:
// ###BEGIN###{IDERDebug}
debug("SCSI ERROR: MODE_SELECT_10", dev);
// ###END###{IDERDebug}
obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
return -1;
case 0x5a: // MODE_SENSE_10
// ###BEGIN###{IDERDebug}
debug("SCSI: MODE_SENSE_10", dev, cdb.charCodeAt(2) & 0x3f);
// ###END###{IDERDebug}
var buflen = ReadShort(cdb, 7);
//var pc = cdb.charCodeAt(2) & 0xc0;
var r = null;
if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
// 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
var sectorCount = 0;
if (dev == 0xA0) {
if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
} else {
if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
}
switch (cdb.charCodeAt(2) & 0x3f) {
case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyError_Recovery_Array:IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break;
case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyDisk_Page_Array:IDE_ModeSence_LS120Disk_Page_Array; } break;
case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_3F_Floppy_Array:IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break;
case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
}
if (r == null) {
obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!!
} else {
// Set disk to read only (we don't support write).
//ms_data[3] = ms_data[3] | 0x80;
obj.SendDataToHost(dev, true, r, featureRegister & 1);
}
break;
default: // UNKNOWN COMMAND
// ###BEGIN###{IDERDebug}
debug("IDER: Unknown SCSI command", cdb.charCodeAt(0));
// ###END###{IDERDebug}
obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
return -1;
}
return 0;
}
function sendDiskData(dev, lba, len, featureRegister) {
var media = null;
var mediaBlocks = 0;
if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
if (media != null) {
// Send sector stats
// ###BEGIN###{IDERStats}
if (obj.sectorStats) { obj.sectorStats(1, (dev == 0xA0) ? 0 : 1, mediaBlocks, lba, len); }
// ###END###{IDERStats}
if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
if (g_media !== null) {
// Queue read operation
g_readQueue.push({ media: media, dev: dev, lba: lba, len: len, fr: featureRegister });
} else {
// obj.iderinfo.readbfr // TODO: MaxRead
g_media = media;
g_dev = dev;
g_lba = lba;
g_len = len;
sendDiskDataEx(featureRegister);
}
}
}
var g_readQueue = [];
var g_reset = false;
var g_media = null;
var g_dev;
var g_lba;
var g_len;
function sendDiskDataEx(featureRegister) {
var len = g_len, lba = g_lba;
if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
g_len -= len;
g_lba += len;
var fr = new FileReader();
fr.onload = function () {
obj.SendDataToHost(g_dev, (g_len == 0), this.result, featureRegister & 1);
if ((g_len > 0) && (g_reset == false)) {
sendDiskDataEx(featureRegister);
} else {
g_media = null;
if (g_reset) { obj.SendCommand(0x47); g_readQueue = []; g_reset = false; } // Send ResetOccuredResponse
else if (g_readQueue.length > 0) { var op = g_readQueue.shift(); g_media = op.media; g_dev = op.dev; g_lba = op.lba; g_len = op.len; sendDiskDataEx(op.fr); } // Un-queue read operation
}
};
//console.log('Read from ' + lba + ' to ' + (lba + len) + ', total of ' + len);
fr.readAsBinaryString(g_media.slice(lba, lba + len));
}
return obj;
}

104
amt-lms-0.0.1.js Normal file
View File

@@ -0,0 +1,104 @@
/**
* @description Intel AMT LMS control module - using websocket
* @author Ylian Saint-Hilaire
* @version v0.0.1
*/
// Construct a LMS control object
var CreateLmsControl = function () {
var obj = {};
var socket = null;
obj.State = 0;
obj.onStateChanged = null;
obj.onData = null;
// Private method
obj.Start = function () {
socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + "/lms.ashx");
socket.onopen = _OnSocketConnected;
socket.onmessage = _OnMessage;
socket.onclose = obj.Stop;
_StateChange(1);
}
function _OnSocketConnected() {
_StateChange(2);
}
// Setup the file reader
var fileReader = new FileReader();
var fileReaderInuse = false, fileReaderAcc = [];
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReader.onload = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReader.onloadend = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
}
function _OnMessage(e) {
if (typeof e.data == 'object') {
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReaderInuse = true;
fileReader.readAsBinaryString(new Blob([e.data]));
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReaderInuse = true;
fileReader.readAsArrayBuffer(e.data);
} else {
// IE10, readAsBinaryString does not exist, use an alternative.
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
_OnSocketData(binary);
}
} else {
_OnSocketData(e.data);
}
};
function _OnSocketData(data) {
if (!data) return;
if (typeof data === 'object') {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "";
var bytes = new Uint8Array(data);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
data = binary;
}
else if (typeof data !== 'string') { return; }
// Send the data up
if (obj.onData != null) obj.onData(obj, data);
}
function _Send(x) {
if (socket != null && socket.readyState == WebSocket.OPEN) {
var b = new Uint8Array(x.length);
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
socket.send(b.buffer);
}
}
obj.SendCmd = function (cmdid, data) {
if (socket == null || obj.State != 2) return;
if (!data || data == null) data = "";
_Send(ShortToStrX(cmdid) + data);
}
function _StateChange(newstate) {
if (obj.State == newstate) return;
obj.State = newstate;
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
}
obj.Stop = function () {
_StateChange(0);
if (socket != null) { socket.close(); socket = null; }
}
return obj;
}

323
amt-redir-node-0.1.0.js Normal file
View File

@@ -0,0 +1,323 @@
/**
* @description Intel AMT Redirection Transport Module - using Node
* @author Ylian Saint-Hilaire
* @version v0.0.1f
*/
// Construct a MeshServer object
var CreateAmtRedirect = function (module) {
var obj = {};
obj.m = module; // This is the inner module (Terminal or Desktop)
module.parent = obj;
obj.State = 0;
obj.net = require('net');
obj.tls = require('tls');
obj.crypto = require('crypto');
obj.constants = require('constants');
obj.socket = null;
obj.host = null;
obj.port = 0;
obj.user = null;
obj.pass = null;
obj.connectstate = 0;
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
obj.xtlsoptions = null;
obj.amtaccumulator = '';
obj.amtsequence = 1;
obj.amtkeepalivetimer = null;
obj.authuri = '/RedirectionService';
obj.digestRealmMatch = null;
obj.onStateChanged = null;
// Private method
//obj.Debug = function (msg) { console.log(msg); }
obj.Start = function (host, port, user, pass, tls, tlsFingerprint, tlsoptions) {
obj.host = host;
obj.port = port;
obj.user = user;
obj.pass = pass;
obj.xtls = tls;
obj.xtlsoptions = tlsoptions;
obj.xtlsFingerprint = tlsFingerprint;
obj.connectstate = 0;
if (obj.xtlsoptions && obj.xtlsoptions.meshServerConnect) {
// Use the websocket wrapper to connect to MeshServer server
obj.socket = CreateWebSocketWrapper(obj.xtlsoptions.host, obj.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.host) + '&p=2&tls=' + tlsFingerprint.xtls + '&tls1only=' + tlsFingerprint.xtlsMethod, obj.xtlsoptions.xtlsFingerprint);
obj.socket.setEncoding('binary');
obj.socket.ondata = obj.xxOnSocketData;
obj.socket.onclose = obj.xxOnSocketClosed;
obj.socket.connect(obj.xxOnSocketConnected);
} else if (tls == false) {
obj.socket = new obj.net.Socket();
obj.socket.setEncoding('binary');
obj.socket.connect(port, host, obj.xxOnSocketConnected);
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('error', obj.xxOnSocketClosed);
} else {
if (obj.xtlsoptions == null) { obj.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; }
obj.socket = obj.tls.connect(port, host, obj.xtlsoptions, obj.xxOnSocketConnected);
obj.socket.setEncoding('binary');
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('error', obj.xxOnSocketClosed);
}
obj.xxStateChange(1);
}
// Get the certificate of Intel AMT
obj.getPeerCertificate = function () { if (obj.xtls == true) { return obj.socket.getPeerCertificate(); } return null; }
obj.xxOnSocketConnected = function () {
if (obj.socket == null) return;
if (!obj.xtlsoptions || !obj.xtlsoptions.meshServerConnect) {
if (obj.xtls == true) {
obj.xtlsCertificate = obj.socket.getPeerCertificate();
if ((obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) { obj.Stop(); return; }
}
}
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CONNECTED'); }
//obj.Debug("Socket Connected");
obj.xxStateChange(2);
if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature if not compiled-in.
if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder);
}
obj.xxOnSocketData = function (data) {
if (!data || obj.connectstate == -1) return;
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-RECV(' + data.length + '): ' + rstr2hex(data)); }
//obj.Debug("Recv(" + data.length + "): " + rstr2hex(data));
if ((obj.protocol == 2 || obj.protocol == 3) && obj.connectstate == 1) { return obj.m.ProcessData(data); } // KVM traffic, forward it directly.
obj.amtaccumulator += data;
//obj.Debug("Recv(" + obj.amtaccumulator.length + "): " + rstr2hex(obj.amtaccumulator));
while (obj.amtaccumulator.length >= 1) {
var cmdsize = 0;
switch (obj.amtaccumulator.charCodeAt(0)) {
case 0x11: // StartRedirectionSessionReply (17)
if (obj.amtaccumulator.length < 4) return;
var statuscode = obj.amtaccumulator.charCodeAt(1);
switch (statuscode) {
case 0: // STATUS_SUCCESS
if (obj.amtaccumulator.length < 13) return;
var oemlen = obj.amtaccumulator.charCodeAt(12);
if (obj.amtaccumulator.length < 13 + oemlen) return;
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support
cmdsize = (13 + oemlen);
break;
default:
obj.Stop();
break;
}
break;
case 0x14: // AuthenticateSessionReply (20)
if (obj.amtaccumulator.length < 9) return;
var authDataLen = ReadIntX(obj.amtaccumulator, 5);
if (obj.amtaccumulator.length < 9 + authDataLen) return;
var status = obj.amtaccumulator.charCodeAt(1);
var authType = obj.amtaccumulator.charCodeAt(4);
var authData = [];
for (i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator.charCodeAt(9 + i)); }
var authDataBuf = obj.amtaccumulator.substring(9, 9 + authDataLen);
cmdsize = 9 + authDataLen;
if (authType == 0) {
// ###BEGIN###{Mode-NodeWebkit}
if (obj.user == '*') {
if (authData.indexOf(2) >= 0) {
// Kerberos Auth
var ticket;
if (kerberos && kerberos != null) {
var ticketReturn = kerberos.getTicket('HTTP' + ((obj.tls == 1)?'S':'') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass));
if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) {
ticket = ticketReturn.ticket;
if (process.platform.indexOf('win') >= 0) {
// Clear kerberos tickets on both 32 and 64bit Windows platforms
try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); }
}
} else {
console.error('Unexpected Kerberos error code: ' + ticketReturn.returnCode);
}
}
if (ticket) {
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x02) + IntToStrX(ticket.length) + ticket);
} else {
obj.Stop();
}
}
else obj.Stop();
} else {
// ###END###{Mode-NodeWebkit}
// Query
if (authData.indexOf(4) >= 0) {
// Good Digest Auth (With cnonce and all)
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04) + IntToStrX(obj.user.length + obj.authuri.length + 8) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00, 0x00));
}
else if (authData.indexOf(3) >= 0) {
// Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x03) + IntToStrX(obj.user.length + obj.authuri.length + 7) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00));
}
else if (authData.indexOf(1) >= 0) {
// Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + IntToStrX(obj.user.length + obj.pass.length + 2) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(obj.pass.length) + obj.pass);
}
else obj.Stop();
// ###BEGIN###{Mode-NodeWebkit}
}
// ###END###{Mode-NodeWebkit}
}
else if ((authType == 3 || authType == 4) && status == 1) {
var curptr = 0;
// Realm
var realmlen = authDataBuf.charCodeAt(curptr);
var realm = authDataBuf.substring(curptr + 1, curptr + 1 + realmlen);
curptr += (realmlen + 1);
// Check the digest realm. If it does not match, close the connection.
if (obj.digestRealmMatch && (obj.digestRealmMatch != realm)) { obj.Stop(); return; }
// Nonce
var noncelen = authDataBuf.charCodeAt(curptr);
var nonce = authDataBuf.substring(curptr + 1, curptr + 1 + noncelen);
curptr += (noncelen + 1);
// QOP
var qoplen = 0;
var qop = null;
var cnonce = obj.xxRandomValueHex(32);
var snc = '00000002';
var extra = '';
if (authType == 4) {
qoplen = authDataBuf.charCodeAt(curptr);
qop = authDataBuf.substring(curptr + 1, curptr + 1 + qoplen);
curptr += (qoplen + 1);
extra = snc + ':' + cnonce + ':' + qop + ':';
}
var digest = hex_md5(hex_md5(obj.user + ':' + realm + ':' + obj.pass) + ':' + nonce + ':' + extra + hex_md5('POST:' + obj.authuri));
var totallen = obj.user.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
if (authType == 4) totallen += (qop.length + 1);
var buf = String.fromCharCode(0x13, 0x00, 0x00, 0x00, authType) + IntToStrX(totallen) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(realm.length) + realm + String.fromCharCode(nonce.length) + nonce + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(cnonce.length) + cnonce + String.fromCharCode(snc.length) + snc + String.fromCharCode(digest.length) + digest;
if (authType == 4) buf += (String.fromCharCode(qop.length) + qop);
obj.xxSend(buf);
}
else if (status == 0) { // Success
if (obj.protocol == 1) {
// Serial-over-LAN: Send Intel AMT serial settings...
var MaxTxBuffer = 10000;
var TxTimeout = 100;
var TxOverflowTimeout = 0;
var RxTimeout = 10000;
var RxFlushTimeout = 100;
var Heartbeat = 0;//5000;
obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(MaxTxBuffer) + ToShortStr(TxTimeout) + ToShortStr(TxOverflowTimeout) + ToShortStr(RxTimeout) + ToShortStr(RxFlushTimeout) + ToShortStr(Heartbeat) + ToIntStr(0));
}
if (obj.protocol == 2) {
// Remote Desktop: Send traffic directly...
obj.xxSend(String.fromCharCode(0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
}
// ###BEGIN###{IDER}
if (obj.protocol == 3) {
// Remote IDER: Send traffic directly...
obj.connectstate = 1;
obj.xxStateChange(3);
}
// ###END###{IDER}
} else obj.Stop();
break;
case 0x21: // Response to settings (33)
if (obj.amtaccumulator.length < 23) break;
cmdsize = 23;
obj.xxSend(String.fromCharCode(0x27, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + String.fromCharCode(0x00, 0x00, 0x1B, 0x00, 0x00, 0x00));
if (obj.protocol == 1) { obj.amtkeepalivetimer = setInterval(obj.xxSendAmtKeepAlive, 2000); }
obj.connectstate = 1;
obj.xxStateChange(3);
break;
case 0x29: // Serial Settings (41)
if (obj.amtaccumulator.length < 10) break;
cmdsize = 10;
break;
case 0x2A: // Incoming display data (42)
if (obj.amtaccumulator.length < 10) break;
var cs = (10 + ((obj.amtaccumulator.charCodeAt(9) & 0xFF) << 8) + (obj.amtaccumulator.charCodeAt(8) & 0xFF));
if (obj.amtaccumulator.length < cs) break;
obj.m.ProcessData(obj.amtaccumulator.substring(10, cs));
cmdsize = cs;
break;
case 0x2B: // Keep alive message (43)
if (obj.amtaccumulator.length < 8) break;
cmdsize = 8;
break;
case 0x41:
if (obj.amtaccumulator.length < 8) break;
obj.connectstate = 1;
obj.m.Start();
// KVM traffic, forward rest of accumulator directly.
if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); }
cmdsize = obj.amtaccumulator.length;
break;
default:
console.log("Unknown Intel AMT command: " + obj.amtaccumulator.charCodeAt(0) + " acclen=" + obj.amtaccumulator.length);
obj.Stop();
return;
}
if (cmdsize == 0) return;
obj.amtaccumulator = obj.amtaccumulator.substring(cmdsize);
}
}
obj.xxSend = function (x) {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-SEND(' + x.length + '): ' + rstr2hex(x)); }
//obj.Debug("Send(" + x.length + "): " + rstr2hex(x));
obj.socket.write(new Buffer(x, 'binary'));
}
obj.Send = function (x) {
if (obj.socket == null || obj.connectstate != 1) return;
if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(x.length) + x); } else { obj.xxSend(x); }
}
obj.xxSendAmtKeepAlive = function () {
if (obj.socket == null) return;
obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++));
}
obj.xxRandomValueHex = function(len) { return obj.crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }
obj.xxOnSocketClosed = function () {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
//obj.Debug("Socket Closed");
obj.Stop();
}
obj.xxStateChange = function(newstate) {
if (obj.State == newstate) return;
obj.State = newstate;
obj.m.xxStateChange(obj.State);
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
}
obj.Stop = function () {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
//obj.Debug("Socket Stopped");
obj.xxStateChange(0);
obj.connectstate = -1;
obj.amtaccumulator = '';
if (obj.socket != null) { obj.socket.destroy(); obj.socket = null; }
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
}
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
return obj;
}
function ToIntStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF), ((v >> 16) & 0xFF), ((v >> 24) & 0xFF)); }
function ToShortStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF)); }

335
amt-redir-ws-0.1.0.js Normal file
View File

@@ -0,0 +1,335 @@
/**
* @description Intel AMT Redirection Transport Module - using websocket relay
* @author Ylian Saint-Hilaire
* @version v0.0.1f
*/
// Construct a MeshServer object
var CreateAmtRedirect = function (module) {
var obj = {};
obj.m = module; // This is the inner module (Terminal or Desktop)
module.parent = obj;
obj.State = 0;
obj.socket = null;
// ###BEGIN###{!Mode-Firmware}
obj.host = null;
obj.port = 0;
obj.user = null;
obj.pass = null;
obj.authuri = '/RedirectionService';
obj.tlsv1only = 0;
// ###END###{!Mode-Firmware}
obj.connectstate = 0;
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
obj.amtaccumulator = '';
obj.amtsequence = 1;
obj.amtkeepalivetimer = null;
obj.digestRealmMatch = null;
obj.onStateChanged = null;
// Private method
//obj.Debug = function (msg) { console.log(msg); }
// ###BEGIN###{!Mode-Firmware}
obj.Start = function (host, port, user, pass, tls) {
obj.host = host;
obj.port = port;
obj.user = user;
obj.pass = pass;
obj.connectstate = 0;
obj.socket = new WebSocket(window.location.protocol.replace('http', 'ws') + '//' + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + '/webrelay.ashx?p=2&host=' + host + '&port=' + port + '&tls=' + tls + ((user == '*') ? '&serverauth=1' : '') + ((typeof pass === 'undefined') ? ('&serverauth=1&user=' + user) : '') + '&tls1only=' + obj.tlsv1only); // The "p=2" indicates to the relay that this is a REDIRECTION session
obj.socket.onopen = obj.xxOnSocketConnected;
obj.socket.onmessage = obj.xxOnMessage;
obj.socket.onclose = obj.xxOnSocketClosed;
obj.xxStateChange(1);
}
// ###END###{!Mode-Firmware}
// ###BEGIN###{Mode-Firmware}
obj.Start = function () {
obj.connectstate = 0;
obj.socket = new WebSocket(window.location.protocol.replace('http', 'ws') + '//' + window.location.host + '/ws-redirection');
obj.socket.onopen = obj.xxOnSocketConnected;
obj.socket.onmessage = obj.xxOnMessage;
obj.socket.onclose = obj.xxOnSocketClosed;
obj.xxStateChange(1);
}
// ###END###{Mode-Firmware}
obj.xxOnSocketConnected = function() {
if (urlvars && urlvars['redirtrace']) console.log('REDIR-CONNECT');
obj.xxStateChange(2);
if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature is not compiled-in.
if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder);
}
// Setup the file reader
var fileReader = new FileReader();
var fileReaderInuse = false, fileReaderAcc = [];
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
}
obj.xxOnMessage = function (e) {
//if (obj.debugmode == 1) { console.log('Recv', e.data); }
obj.inDataCount++;
if (typeof e.data == 'object') {
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReaderInuse = true;
fileReader.readAsBinaryString(new Blob([e.data]));
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReaderInuse = true;
fileReader.readAsArrayBuffer(e.data);
} else {
// IE10, readAsBinaryString does not exist, use an alternative.
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
obj.xxOnSocketData(binary);
}
} else {
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
// obj.debug("MeshDataChannel - OnData - " + typeof e.data + " - " + e.data.length);
obj.xxOnSocketData(e.data);
}
};
obj.xxOnSocketData = function (data) {
if (!data || obj.connectstate == -1) return;
if (typeof data === 'object') {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "";
var bytes = new Uint8Array(data);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
data = binary;
}
else if (typeof data !== 'string') { return; }
if ((obj.protocol == 2 || obj.protocol == 3) && obj.connectstate == 1) { return obj.m.ProcessData(data); } // KVM traffic, forward it directly.
obj.amtaccumulator += data;
if (urlvars && urlvars['redirtrace']) console.log("REDIR-RECV(" + obj.amtaccumulator.length + "): " + rstr2hex(obj.amtaccumulator));
while (obj.amtaccumulator.length >= 1) {
var cmdsize = 0;
switch (obj.amtaccumulator.charCodeAt(0)) {
case 0x11: // StartRedirectionSessionReply (17)
if (obj.amtaccumulator.length < 4) return;
var statuscode = obj.amtaccumulator.charCodeAt(1);
switch (statuscode) {
case 0: // STATUS_SUCCESS
if (obj.amtaccumulator.length < 13) return;
var oemlen = obj.amtaccumulator.charCodeAt(12);
if (obj.amtaccumulator.length < 13 + oemlen) return;
// ###BEGIN###{!Mode-Firmware}
// Query for available authentication
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support
// ###END###{!Mode-Firmware}
// ###BEGIN###{Mode-Firmware}
// When using websocket, we are already authenticated. Send empty basic auth.
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00));
// ###END###{Mode-Firmware}
cmdsize = (13 + oemlen);
break;
default:
obj.Stop();
break;
}
break;
case 0x14: // AuthenticateSessionReply (20)
if (obj.amtaccumulator.length < 9) return;
var authDataLen = ReadIntX(obj.amtaccumulator, 5);
if (obj.amtaccumulator.length < 9 + authDataLen) return;
var status = obj.amtaccumulator.charCodeAt(1);
var authType = obj.amtaccumulator.charCodeAt(4);
var authData = [];
for (i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator.charCodeAt(9 + i)); }
var authDataBuf = obj.amtaccumulator.substring(9, 9 + authDataLen);
cmdsize = 9 + authDataLen;
// ###BEGIN###{!Mode-Firmware}
if (authType == 0) {
// Query
if (authData.indexOf(4) >= 0) {
// Good Digest Auth (With cnonce and all)
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04) + IntToStrX(obj.user.length + obj.authuri.length + 8) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00, 0x00));
}
else if (authData.indexOf(3) >= 0) {
// Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x03) + IntToStrX(obj.user.length + obj.authuri.length + 7) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00));
}
else if (authData.indexOf(1) >= 0) {
// Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + IntToStrX(obj.user.length + obj.pass.length + 2) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(obj.pass.length) + obj.pass);
}
else obj.Stop();
}
else if ((authType == 3 || authType == 4) && status == 1) {
var curptr = 0;
// Realm
var realmlen = authDataBuf.charCodeAt(curptr);
var realm = authDataBuf.substring(curptr + 1, curptr + 1 + realmlen);
curptr += (realmlen + 1);
// Check the digest realm. If it does not match, close the connection.
if (obj.digestRealmMatch && (obj.digestRealmMatch != realm)) { obj.Stop(); return; }
// Nonce
var noncelen = authDataBuf.charCodeAt(curptr);
var nonce = authDataBuf.substring(curptr + 1, curptr + 1 + noncelen);
curptr += (noncelen + 1);
// QOP
var qoplen = 0;
var qop = null;
var cnonce = obj.xxRandomNonce(32);
var snc = '00000002';
var extra = '';
if (authType == 4) {
qoplen = authDataBuf.charCodeAt(curptr);
qop = authDataBuf.substring(curptr + 1, curptr + 1 + qoplen);
curptr += (qoplen + 1);
extra = snc + ':' + cnonce + ':' + qop + ':';
}
var digest = hex_md5(hex_md5(obj.user + ':' + realm + ':' + obj.pass) + ':' + nonce + ':' + extra + hex_md5('POST:' + obj.authuri));
var totallen = obj.user.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
if (authType == 4) totallen += (qop.length + 1);
var buf = String.fromCharCode(0x13, 0x00, 0x00, 0x00, authType) + IntToStrX(totallen) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(realm.length) + realm + String.fromCharCode(nonce.length) + nonce + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(cnonce.length) + cnonce + String.fromCharCode(snc.length) + snc + String.fromCharCode(digest.length) + digest;
if (authType == 4) buf += (String.fromCharCode(qop.length) + qop);
obj.xxSend(buf);
}
else
// ###END###{!Mode-Firmware}
if (status == 0) { // Success
if (obj.protocol == 1) {
// Serial-over-LAN: Send Intel AMT serial settings...
var MaxTxBuffer = 10000;
var TxTimeout = 100;
var TxOverflowTimeout = 0;
var RxTimeout = 10000;
var RxFlushTimeout = 100;
var Heartbeat = 0;//5000;
obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + ShortToStrX(MaxTxBuffer) + ShortToStrX(TxTimeout) + ShortToStrX(TxOverflowTimeout) + ShortToStrX(RxTimeout) + ShortToStrX(RxFlushTimeout) + ShortToStrX(Heartbeat) + IntToStrX(0));
}
if (obj.protocol == 2) {
// Remote Desktop: Send traffic directly...
obj.xxSend(String.fromCharCode(0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
}
if (obj.protocol == 3) {
// Remote IDER: Send traffic directly...
obj.connectstate = 1;
obj.xxStateChange(3);
}
} else obj.Stop();
break;
case 0x21: // Response to settings (33)
if (obj.amtaccumulator.length < 23) break;
cmdsize = 23;
obj.xxSend(String.fromCharCode(0x27, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + String.fromCharCode(0x00, 0x00, 0x1B, 0x00, 0x00, 0x00));
if (obj.protocol == 1) { obj.amtkeepalivetimer = setInterval(obj.xxSendAmtKeepAlive, 2000); }
obj.connectstate = 1;
obj.xxStateChange(3);
break;
case 0x29: // Serial Settings (41)
if (obj.amtaccumulator.length < 10) break;
cmdsize = 10;
break;
case 0x2A: // Incoming display data (42)
if (obj.amtaccumulator.length < 10) break;
var cs = (10 + ((obj.amtaccumulator.charCodeAt(9) & 0xFF) << 8) + (obj.amtaccumulator.charCodeAt(8) & 0xFF));
if (obj.amtaccumulator.length < cs) break;
obj.m.ProcessData(obj.amtaccumulator.substring(10, cs));
cmdsize = cs;
break;
case 0x2B: // Keep alive message (43)
if (obj.amtaccumulator.length < 8) break;
cmdsize = 8;
break;
case 0x41:
if (obj.amtaccumulator.length < 8) break;
obj.connectstate = 1;
obj.m.Start();
// KVM traffic, forward rest of accumulator directly.
if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); }
cmdsize = obj.amtaccumulator.length;
break;
case 0xF0:
// console.log('Session is being recorded');
obj.serverIsRecording = true;
cmdsize = 1;
break;
default:
console.log("Unknown Intel AMT command: " + obj.amtaccumulator.charCodeAt(0) + " acclen=" + obj.amtaccumulator.length);
obj.Stop();
return;
}
if (cmdsize == 0) return;
obj.amtaccumulator = obj.amtaccumulator.substring(cmdsize);
}
}
obj.xxSend = function (x) {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-SEND(' + x.length + '): ' + rstr2hex(x)); }
//obj.Debug("Redir Send(" + x.length + "): " + rstr2hex(x));
if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
var b = new Uint8Array(x.length);
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
obj.socket.send(b.buffer);
}
}
obj.Send = function (x) {
if (obj.socket == null || obj.connectstate != 1) return;
if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + ShortToStrX(x.length) + x); } else { obj.xxSend(x); }
}
obj.xxSendAmtKeepAlive = function () {
if (obj.socket == null) return;
obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++));
}
obj.xxRandomNonceX = 'abcdef0123456789';
obj.xxRandomNonce = function (length) {
var r = "";
for (var i = 0; i < length; i++) { r += obj.xxRandomNonceX.charAt(Math.floor(Math.random() * obj.xxRandomNonceX.length)); }
return r;
}
obj.xxOnSocketClosed = function () {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
//obj.Debug("Redir Socket Closed");
obj.Stop();
}
obj.xxStateChange = function(newstate) {
if (obj.State == newstate) return;
obj.State = newstate;
obj.m.xxStateChange(obj.State);
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
}
obj.Stop = function () {
//obj.Debug("Redir Socket Stopped");
obj.xxStateChange(0);
obj.connectstate = -1;
obj.amtaccumulator = "";
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
}
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
return obj;
}

235
amt-scanner-0.1.0.js Normal file
View File

@@ -0,0 +1,235 @@
/**
* @description Meshcentral Intel AMT Local Scanner
* @author Ylian Saint-Hilaire
* @version v0.0.1
*/
// Construct a Intel AMT Scanner object
var CreateAmtScanner = function (func) {
var obj = {};
obj.active = false;
obj.dgram = require('dgram');
obj.servers = {};
obj.rserver = {};
obj.rpacket = null;
obj.tagToId = {}; // Tag --> { lastpong: time, id: NodeId }
obj.scanTable = {}; // Handle --> ScanInfo : { lastping: time, lastpong: time, nodeinfo:{node} }
obj.scanTableTags = {}; // Tag --> ScanInfo
obj.pendingSends = []; // We was to stagger the sends using a 10ms timer
obj.pendingSendTimer = null;
obj.mainTimer = null;
obj.nextTag = 0;
obj.computerPresenceFunc = func;
var PeriodicScanTime = 30000; // Interval between scan sweeps
var PeriodicScanTimeout = 65000; // After this time, timeout the device.
// Build a RMCP packet with a given tag field
obj.buildRmcpPing = function (tag) {
var packet = new Buffer(hex2rstr('06000006000011BE80000000'), 'ascii');
packet[9] = tag;
return packet;
}
// Start scanning for local network Intel AMT computers
obj.start = function () {
if (obj.active == false) {
obj.active = true;
obj.performScan();
obj.mainTimer = setInterval(obj.performScan, PeriodicScanTime);
}
}
// Stop scanning for local network Intel AMT computers
obj.stop = function () {
obj.active = false;
for (var i in obj.servers) { obj.servers[i].close(); } // Stop all servers
obj.servers = {};
if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; }
}
// Scan for Intel AMT computers using network multicast
obj.performRangeScan = function (id, rangestr, func) {
if (obj.rpacket == null) { obj.rpacket = obj.buildRmcpPing(0); }
var range = obj.parseIpv4Range(rangestr);
//console.log(obj.IPv4NumToStr(range.min), obj.IPv4NumToStr(range.max));
if (range == null || (range.min > range.max)) return false;
var rangeinfo = { id: id, range: rangestr, min: range.min, max: range.max, results: {}, resultCount: 0 };
obj.rserver[id] = rangeinfo;
rangeinfo.server = obj.dgram.createSocket("udp4");
rangeinfo.server.bind(0);
rangeinfo.server.on('error', function(err) { console.log(err); });
rangeinfo.server.on('message', function(data, rinfo) { obj.parseRmcpPacket(data, rinfo, 0, func, rangeinfo); });
rangeinfo.server.on('listening', function () {
for (var i = rangeinfo.min; i <= rangeinfo.max; i++) {
rangeinfo.server.send(obj.rpacket, 0, obj.rpacket.length, 623, obj.IPv4NumToStr(i));
rangeinfo.server.send(obj.rpacket, 0, obj.rpacket.length, 623, obj.IPv4NumToStr(i));
}
});
rangeinfo.timer = setTimeout(function () {
if (rangeinfo.resultCount == 0) { func(obj, null); }
rangeinfo.server.close();
delete rangeinfo.server;
delete rangeinfo;
}, 5000);
return true;
}
// Parse range, used to parse "ip", "ip/mask" or "ip-ip" notation.
// Return the start and end value of the scan
obj.parseIpv4Range = function (range) {
if (range == undefined || range == null) return null;
var x = range.split('-');
if (x.length == 2) { return { min: obj.parseIpv4Addr(x[0]), max: obj.parseIpv4Addr(x[1]) }; }
x = range.split('/');
if (x.length == 2) {
var ip = obj.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0;
if (masknum < 16) return null;
for (var i = 0; i < (32 - masknum) ; i++) { mask = (mask << 1); mask++; }
return { min: ip & (0xFFFFFFFF - mask), max: (ip & (0xFFFFFFFF - mask)) + mask };
}
x = obj.parseIpv4Addr(range);
if (x == null) return null;
return { min: x, max: x };
}
// Parse IP address. Takes a
obj.parseIpv4Addr = function (addr) {
var x = addr.split('.');
if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); }
return null;
}
// IP address number to string
obj.IPv4NumToStr = function (num) {
return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF);
}
// Scan the list of all computers
obj.performScan = function () {
if (obj.active == false || computerlist == undefined) return;
for (var i in obj.scanTable) { obj.scanTable[i].present = false; }
if (computerlist.length > 0) {
for (var i in computerlist) {
var computer = computerlist[i];
var host = computer.host.toLowerCase();
if ((host != '127.0.0.1') && (host != '::1') && (host != 'localhost') && (host.split(':').length == 1)) {
var scaninfo = obj.scanTable[computer.h];
if (scaninfo == undefined) {
var tag = obj.nextTag++;
obj.scanTableTags[tag] = obj.scanTable[computer.h] = scaninfo = { computer: computer, present: true, tag: tag, scanstate: -1, lastpong: Date.now() };
} else {
scaninfo.present = true;
var delta = Date.now() - scaninfo.lastpong;
if ((delta > PeriodicScanTimeout) && (scaninfo.scanstate != 0)) { // More than the timeout without a response, mark the node as unknown state
scaninfo.scanstate = 0;
if (obj.computerPresenceFunc) { obj.computerPresenceFunc(scaninfo.computer.h, false); }
}
}
// Start scanning this node
scaninfo.lastping = Date.now();
obj.checkAmtPresence(computer.host, scaninfo.tag);
}
}
}
for (var i in obj.scanTable) {
if (obj.scanTable[i].present == false) {
// Stop scanning this node
delete obj.scanTableTags[obj.scanTable[i].tag];
delete obj.scanTable[i];
}
}
}
// Check the presense of a specific Intel AMT computer
obj.checkAmtPresence = function (host, tag) {
var serverid = Math.floor(tag / 255);
var servertag = (tag % 255);
var packet = obj.buildRmcpPing(servertag);
var server = obj.servers[serverid];
if (server == undefined) {
// Start new server
server = obj.dgram.createSocket('udp4');
server.on('error', function(err) { });
server.on('message', function (data, rinfo) { obj.parseRmcpPacket(data, rinfo, serverid, obj.changeConnectState, null); });
server.on('listening', function() { obj.pendingSends.push([ server, packet, host ]); if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); } });
server.bind(0);
obj.servers[serverid] = server;
} else {
// Use existing server
obj.pendingSends.push([ server, packet, host ]);
if (obj.pendingSendTimer == null) { obj.pendingSendTimer = setInterval(obj.sendPendingPacket, 10); }
}
}
// Send a pending RMCP packet
obj.sendPendingPacket = function () {
try {
var p = obj.pendingSends.shift();
if (p != undefined) {
p[0].send(p[1], 0, p[1].length, 623, p[2]);
p[0].send(p[1], 0, p[1].length, 623, p[2]);
} else {
clearInterval(obj.pendingSendTimer);
obj.pendingSendTimer = null;
}
} catch (e) { }
}
// Parse RMCP packet
obj.parseRmcpPacket = function (data, rinfo, serverid, func, rangeinfo) {
if (data == null || data.length < 20) return;
if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) {
var result = { tag: (serverid * 255) + data[9], ver: ((data[18] >> 4) & 0x0F) + '.' + (data[18] & 0x0F), state: (data[19] & 0x03), port: ((data[16] * 256) + data[17]), dualport: (((data[19] & 0x04) != 0) ? true : false), rinfo: rinfo };
result.tls = (((result.openPort == 16993) || (result.dualPorts == true)) ? 1 : 0)
if (rangeinfo != null) {
if ((result.state <= 2) && (!rangeinfo.results[rinfo.address])) {
rangeinfo.results[rinfo.address] = result;
rangeinfo.resultCount++;
obj.reverseLookup(rinfo.address, function (err, domains) {
result.domains = domains;
func(obj, result, rangeinfo);
});
}
} else {
func(obj, result, rangeinfo);
}
}
}
// Use the RMCP packet to change the computer state
obj.changeConnectState = function (obj, result, rangeinfo) {
var scaninfo = obj.scanTableTags[result.tag];
if (scaninfo) {
scaninfo.lastpong = Date.now();
if (scaninfo.scanstate != 1) {
scaninfo.scanstate = 1;
if (obj.computerPresenceFunc) obj.computerPresenceFunc(scaninfo.computer.h, true);
}
}
}
// Perform DNS reverse lookup
obj.reverseLookup = function(ip, callback) {
var callbackCalled = false, timer = null;
var doCallback = function (err, domains) {
if (err) domains = [ip];
if (callbackCalled) return;
callbackCalled = true;
if (timer != null) {
// Cancelling timer to safe few seconds
clearTimeout(timer);
timer = null;
}
callback(err, domains);
}
// throw failed upon timeout
timer = setTimeout(function () {
timer = null; // Ensure timer is nullified
doCallback(new Error("Timeout exceeded"), null);
}, 3000);
require("dns").reverse(ip, doCallback);
}
return obj;
}

440
amt-script-0.2.0.js Normal file
View File

@@ -0,0 +1,440 @@
/**
* @fileoverview Script Compiler / Decompiler / Runner
* @author Ylian Saint-Hilaire
* @version v0.1.0e
*/
// Core functions
script_functionTable1 = ['nop', 'jump', 'set', 'print', 'dialog', 'getitem', 'substr', 'indexof', 'split', 'join', 'length', 'jsonparse', 'jsonstr', 'add', 'substract', 'parseint', 'wsbatchenum', 'wsput', 'wscreate', 'wsdelete', 'wsexec', 'scriptspeed', 'wssubscribe', 'wsunsubscribe', 'readchar', 'signwithdummyca'];
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
script_functionTable2 = ['encodeuri', 'decodeuri', 'passwordcheck', 'atob', 'btoa', 'hex2str', 'str2hex', 'random', 'md5', 'maketoarray', 'readshort', 'readshortx', 'readint', 'readsint', 'readintx', 'shorttostr', 'shorttostrx', 'inttostr', 'inttostrx'];
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
script_functionTableX2 = [encodeURI, decodeURI, passwordcheck, window.atob.bind(window), window.btoa.bind(window), hex2rstr, rstr2hex, random, rstr_md5, MakeToArray, ReadShort, ReadShortX, ReadInt, ReadSInt, ReadIntX, ShortToStr, ShortToStrX, IntToStr, IntToStrX];
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
script_functionTable3 = ['pullsystemstatus', 'pulleventlog', 'pullauditlog', 'pullcertificates', 'pullwatchdog', 'pullsystemdefense', 'pullhardware', 'pulluserinfo', 'pullremoteaccess', 'highlightblock', 'disconnect', 'getsidstring', 'getsidbytearray', 'pulleventsubscriptions'];
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
script_functionTableX3 = [
PullSystemStatus
,
// ###BEGIN###{EventLog}
PullEventLog
// ###END###{EventLog}
,
// ###BEGIN###{AuditLog}
PullAuditLog
// ###END###{AuditLog}
,
// ###BEGIN###{Certificates}
PullCertificates
// ###END###{Certificates}
,
// ###BEGIN###{AgentPresence}
PullWatchdog
// ###END###{AgentPresence}
,
// ###BEGIN###{SystemDefense}
PullSystemDefense
// ###END###{SystemDefense}
,
// ###BEGIN###{HardwareInfo}
PullHardware
// ###END###{HardwareInfo}
,
PullUserInfo
,
// ###BEGIN###{RemoteAccess}
PullRemoteAccess
// ###END###{RemoteAccess}
,
// ###BEGIN###{Scripting-Editor}
script_HighlightBlock
// ###END###{Scripting-Editor}
,
// ###BEGIN###{ComputerSelector}
disconnect
// ###END###{ComputerSelector}
,
function (runner, x) { return GetSidString(x); }
,
function (runner, x) { return GetSidByteArray(x); }
,
// ###BEGIN###{EventSubscriptions}
PullEventSubscriptions
// ###END###{EventSubscriptions}
];
// Setup the script state
function script_setup(binary, startvars) {
var obj = { startvars:startvars };
if (binary.length < 6) { console.error('Invalid script length'); return null; } // Script must have at least 6 byte header
if (ReadInt(binary, 0) != 0x247D2945) { console.error('Invalid binary script'); return null; } // Check the script magic header
if (ReadShort(binary, 4) > 1) { console.error('Unsupported script version'); return null; } // Check the script version
obj.script = binary.substring(6);
// obj.onStep;
// obj.onConsole;
// Reset the script to the start
obj.reset = function (stepspeed) {
obj.stop();
obj.ip = 0;
obj.variables = startvars;
obj.state = 1;
}
// Start the script
obj.start = function (stepspeed) {
obj.stop();
obj.stepspeed = stepspeed;
if (stepspeed > 0) { obj.timer = setInterval(function () { obj.step() }, stepspeed); }
}
// Stop the script
obj.stop = function () {
if (obj.timer != null) { clearInterval(obj.timer); }
obj.timer = null;
obj.stepspeed = 0;
}
// function used to load and store variable values
obj.getVar = function (name) { if (name == undefined) return undefined; return obj.getVarEx(name.split('.'), obj.variables); }
obj.getVarEx = function (name, val) { try { if (name == undefined) return undefined; if (name.length == 0) return val; return obj.getVarEx(name.slice(1), val[name[0]]); } catch (e) { return null; } }
obj.setVar = function (name, val) { obj.setVarEx(name.split('.'), obj.variables, val); }
obj.setVarEx = function (name, vars, val) { if (name.length == 1) { vars[name[0]] = val; } else { obj.setVarEx(name.slice(1), vars[name[0]], val); } }
// Run the script one step forward
obj.step = function () {
if (obj.state != 1) return;
if (obj.ip < obj.script.length) {
var cmdid = ReadShort(obj.script, obj.ip);
var cmdlen = ReadShort(obj.script, obj.ip + 2);
var argcount = ReadShort(obj.script, obj.ip + 4);
var argptr = obj.ip + 6;
var args = [];
// Clear all temp variables (This is optional)
for (var i in obj.variables) { if (i.startsWith('__')) { delete obj.variables[i]; } }
// Loop on each argument, moving forward by the argument length each time
for (var i = 0; i < argcount; i++) {
var arglen = ReadShort(obj.script, argptr);
var argval = obj.script.substring(argptr + 2, argptr + 2 + arglen);
var argtyp = argval.charCodeAt(0);
argval = argval.substring(1);
if (argtyp < 2) {
// Get the value and replace all {var} with variable values
while (argval.split("{").length > 1) { var t = argval.split("{").pop().split("}").shift(); argval = argval.replace('{' + t + '}', obj.getVar(t)); }
if (argtyp == 1) { obj.variables['__' + i] = decodeURI(argval); argval = '__' + i; } // If argtyp is 1, this is a literal. Store in temp variable.
args.push(argval);
}
if (argtyp == 2 || argtyp == 3) {
obj.variables['__' + i] = ReadSInt(argval, 0);
args.push('__' + i);
}
argptr += (2 + arglen);
}
// Move instruction pointer forward by command size
obj.ip += cmdlen;
// Get all variable values
var argsval = [];
for (var i = 0; i < 10; i++) { argsval.push(obj.getVar(args[i])); }
var storeInArg0;
try {
if (cmdid < 10000) {
// Lets run the actual command
switch (cmdid) {
case 0: // nop
break;
case 1: // jump(label) or jump(label, a, compare, b)
if (argsval[2]) {
if (
(argsval[2] == '<' && argsval[1] < argsval[3]) ||
(argsval[2] == '<=' && argsval[1] <= argsval[3]) ||
(argsval[2] == '!=' && argsval[1] != argsval[3]) ||
(argsval[2] == '=' && argsval[1] == argsval[3]) ||
(argsval[2] == '>=' && argsval[1] >= argsval[3]) ||
(argsval[2] == '>' && argsval[1] > argsval[3])
) { obj.ip = argsval[0]; }
} else {
obj.ip = argsval[0]; // Set the instruction pointer to the new location in the script
}
break;
case 2: // set(variable, value)
if (args[1] == undefined) delete obj.variables[args[0]]; else obj.setVar(args[0], argsval[1]);
break;
case 3: // print(message)
if (obj.onConsole) { obj.onConsole(obj.toString(argsval[0]), obj); } else { console.log(obj.toString(argsval[0])); }
// Q(obj.consoleid).value += () + '\n'); Q(obj.console).scrollTop = Q(obj.console).scrollHeight;
break;
case 4: // dialog(title, content, buttons)
obj.state = 2;
obj.dialog = true;
setDialogMode(11, argsval[0], argsval[2], obj.xxStepDialogOk, argsval[1], obj);
break;
case 5: // getitem(a, b, c)
for (var i in argsval[1]) { if (argsval[1][i][argsval[2]] == argsval[3]) { storeInArg0 = i; } };
break;
case 6: // substr(variable_dest, variable_src, index, len)
storeInArg0 = argsval[1].substr(argsval[2], argsval[3]);
break;
case 7: // indexOf(variable_dest, variable_src, index, len)
storeInArg0 = argsval[1].indexOf(argsval[2]);
break;
case 8: // split(variable_dest, variable_src, separator)
storeInArg0 = argsval[1].split(argsval[2]);
break;
case 9: // join(variable_dest, variable_src, separator)
storeInArg0 = argsval[1].join(argsval[2]);
break;
case 10: // length(variable_dest, variable_src)
storeInArg0 = argsval[1].length;
break;
case 11: // jsonparse(variable_dest, json)
storeInArg0 = JSON.parse(argsval[1]);
break;
case 12: // jsonstr(variable_dest, variable_src)
storeInArg0 = JSON.stringify(argsval[1]);
break;
case 13: // add(variable_dest, variable_src, value)
storeInArg0 = (argsval[1] + argsval[2]);
break;
case 14: // substract(variable_dest, variable_src, value)
storeInArg0 = (argsval[1] - argsval[2]);
break;
case 15: // parseInt(variable_dest, variable_src)
storeInArg0 = parseInt(argsval[1]);
break;
case 16: // wsbatchenum(name, objectList)
obj.state = 2;
obj.amtstack.BatchEnum(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
break;
case 17: // wsput(name, args)
obj.state = 2;
obj.amtstack.Put(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
break;
case 18: // wscreate(name, args)
obj.state = 2;
obj.amtstack.Create(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
break;
case 19: // wsdelete(name, args)
obj.state = 2;
obj.amtstack.Delete(argsval[0], argsval[1], obj.xxWsmanReturn, obj);
break;
case 20: // wsexec(name, method, args, selectors)
obj.state = 2;
obj.amtstack.Exec(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3]);
break;
case 21: // Script Speed
obj.stepspeed = argsval[0];
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = setInterval(function () { obj.step() }, obj.stepspeed); }
break;
case 22: // wssubscribe(name, delivery, url, selectors, opaque, user, pass)
obj.state = 2;
obj.amtstack.Subscribe(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3], argsval[4], argsval[5], argsval[6]);
break;
case 23: // wsunsubscribe(name, selectors)
obj.state = 2;
obj.amtstack.UnSubscribe(argsval[0], obj.xxWsmanReturn, obj, 0, argsval[1]);
break;
case 24: // readchar(str, pos)
console.log(argsval[1], argsval[2], argsval[1].charCodeAt(argsval[2]));
storeInArg0 = argsval[1].charCodeAt(argsval[2]);
break;
case 25: // signWithDummyCa
// ###BEGIN###{Certificates}
obj.state = 2;
// DERKey, xxCaPrivateKey, certattributes, issuerattributes
amtcert_signWithCaKey(argsval[0], null, argsval[1], { 'CN': 'Untrusted Root Certificate' }, obj.xxSignWithDummyCaReturn);
// ###END###{Certificates}
break;
default: {
obj.state = 9;
console.error("Script Error, unknown command: " + cmdid);
}
}
} else {
if (cmdid < 20000) {
// functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
storeInArg0 = script_functionTableX2[cmdid - 10000](argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]);
} else {
// Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6)
if (script_functionTableX3 && script_functionTableX3[cmdid - 20000]) {
storeInArg0 = script_functionTableX3[cmdid - 20000](obj, argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]); // Note that optional calls start with "obj" as first argument.
}
}
}
if (storeInArg0 != undefined) obj.setVar(args[0], storeInArg0);
} catch (e) {
if (typeof e == 'object') { e = e.message; }
obj.setVar('_exception', e);
}
}
if (obj.state == 1 && obj.ip >= obj.script.length) { obj.state = 0; obj.stop(); }
if (obj.onStep) obj.onStep(obj);
return obj;
}
obj.xxStepDialogOk = function (button) {
obj.variables['DialogSelect'] = button;
obj.state = 1;
obj.dialog = false;
if (obj.onStep) obj.onStep(obj);
}
// ###BEGIN###{**ClosureAdvancedMode}
obj.xxWsmanReturnFix = function (x) {
if (!x || x == null) return;
if (x.Header) { x['Header'] = x.Header; delete x.Header; }
if (x.Body) { x['Body'] = x.Body; delete x.Body; }
if (x.Responses) { x['Responses'] = x.Responses; delete x.Responses; }
if (x.Response) { x['Response'] = x.Response; delete x.Response; }
if (x.ReturnValueStr) { x['ReturnValueStr'] = x.ReturnValueStr; delete x.ReturnValueStr; }
}
// ###END###{**ClosureAdvancedMode}
obj.xxWsmanReturn = function (stack, name, responses, status) {
// ###BEGIN###{**ClosureAdvancedMode}
// This is required when Google Closure is used
if (responses) {
obj.xxWsmanReturnFix(responses);
for (var i in responses) {
obj.xxWsmanReturnFix(responses[i]);
for (var j in responses[i]) { obj.xxWsmanReturnFix(responses[i][j]); }
}
}
// ###END###{**ClosureAdvancedMode}
obj.setVar(name, responses);
obj.setVar('wsman_result', status);
obj.setVar('wsman_result_str', ((httpErrorTable[status]) ? (httpErrorTable[status]) : ('Error #' + status)));
obj.state = 1;
if (obj.onStep) obj.onStep(obj);
}
// ###BEGIN###{Certificates}
obj.xxSignWithDummyCaReturn = function (cert) {
obj.setVar('signed_cert', btoa(_arrayBufferToString(cert)));
obj.state = 1;
if (obj.onStep) obj.onStep(obj);
}
// ###END###{Certificates}
obj.toString = function (x) { if (typeof x == 'object') return JSON.stringify(x); return x; }
obj.reset();
return obj;
}
// Argument types: 0 = Variable, 1 = String, 2 = Integer, 3 = Label
function script_compile(script, onmsg) {
var r = '', scriptlines = script.split('\n'), labels = {}, labelswap = [], swaps = [];
// Go thru each script line and encode it
for (var i in scriptlines) {
var scriptline = scriptlines[i];
if (scriptline.startsWith('##SWAP ')) { var x = scriptline.split(' '); if (x.length == 3) { swaps[x[1]] = x[2]; } } // Add a swap instance
if (scriptline[0] == '#' || scriptline.length == 0) continue; // Skip comments & blank lines
for (var x in swaps) { scriptline = scriptline.split(x).join(swaps[x]); } // Apply all swaps
var keywords = scriptline.match(/"[^"]*"|[^\s"]+/g);
if (keywords.length == 0) continue; // Skip blank lines
if (scriptline[0] == ':') { labels[keywords[0].toUpperCase()] = r.length; continue; } // Mark a label position
var funcIndex = script_functionTable1.indexOf(keywords[0].toLowerCase());
if (funcIndex == -1) { funcIndex = script_functionTable2.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 10000; }
if (funcIndex == -1) { funcIndex = script_functionTable3.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 20000; } // Optional methods
if (funcIndex == -1) { if (onmsg) { onmsg("Unabled to compile, unknown command: " + keywords[0]); } return ''; }
// Encode CommandId, CmdSize, ArgCount, Arg1Len, Arg1, Arg2Len, Arg2...
var cmd = ShortToStr(keywords.length - 1);
for (var j in keywords) {
if (j == 0) continue;
if (keywords[j][0] == ':') {
labelswap.push([keywords[j], r.length + cmd.length + 7]); // Add a label swap
cmd += ShortToStr(5) + String.fromCharCode(3) + IntToStr(0xFFFFFFFF); // Put an empty label
} else {
var argint = parseInt(keywords[j]);
if (argint == keywords[j]) {
cmd += ShortToStr(5) + String.fromCharCode(2) + IntToStr(argint);
} else {
if (keywords[j][0] == '"' && keywords[j][keywords[j].length - 1] == '"') {
cmd += ShortToStr(keywords[j].length - 1) + String.fromCharCode(1) + keywords[j].substring(1, keywords[j].length - 1);
} else {
cmd += ShortToStr(keywords[j].length + 1) + String.fromCharCode(0) + keywords[j];
}
}
}
}
cmd = ShortToStr(funcIndex) + ShortToStr(cmd.length + 4) + cmd;
r += cmd;
}
// Perform all the needed label swaps
for (i in labelswap) {
var label = labelswap[i][0].toUpperCase(), position = labelswap[i][1], target = labels[label];
if (target == undefined) { if (onmsg) { onmsg("Unabled to compile, unknown label: " + label); } return ''; }
r = r.substr(0, position) + IntToStr(target) + r.substr(position + 4);
}
return IntToStr(0x247D2945) + ShortToStr(1) + r;
}
// Decompile the script, intended for debugging only
function script_decompile(binary, onecmd) {
var r = '', ptr = 6, labelcount = 0, labels = {};
if (onecmd >= 0) {
ptr = onecmd; // If we are decompiling just one command, set the ptr to that command.
} else {
if (binary.length < 6) { return '# Invalid script length'; }
var magic = ReadInt(binary, 0);
var version = ReadShort(binary, 4);
if (magic != 0x247D2945) { return '# Invalid binary script: ' + magic; }
if (version != 1) { return '# Invalid script version'; }
}
// Loop on each command, moving forward by the command length each time.
while (ptr < binary.length) {
var cmdid = ReadShort(binary, ptr);
var cmdlen = ReadShort(binary, ptr + 2);
var argcount = ReadShort(binary, ptr + 4);
var argptr = ptr + 6;
var argstr = '';
if (!(onecmd >= 0)) r += ":label" + (ptr - 6) + "\n";
// Loop on each argument, moving forward by the argument length each time
for (var i = 0; i < argcount; i++) {
var arglen = ReadShort(binary, argptr);
var argval = binary.substring(argptr + 2, argptr + 2 + arglen);
var argtyp = argval.charCodeAt(0);
if (argtyp == 0) { argstr += ' ' + argval.substring(1); } // Variable
else if (argtyp == 1) { argstr += ' \"' + argval.substring(1) + '\"'; } // String
else if (argtyp == 2) { argstr += ' ' + ReadInt(argval, 1); } // Integer
else if (argtyp == 3) { // Label
var target = ReadInt(argval, 1);
var label = labels[target];
if (!label) { label = ":label" + target; labels[label] = target; }
argstr += ' ' + label;
}
argptr += (2 + arglen);
}
// Go in the script function table to decode the function
if (cmdid < 10000) {
r += script_functionTable1[cmdid] + argstr + "\n";
} else {
if (cmdid >= 20000) {
r += script_functionTable3[cmdid - 20000] + argstr + "\n"; // Optional methods
} else {
r += script_functionTable2[cmdid - 10000] + argstr + "\n";
}
}
ptr += cmdlen;
if (onecmd >= 0) return r; // If we are decompiling just one command, exit now
}
// Remove all unused labels
var scriptlines = r.split('\n');
r = '';
for (var i in scriptlines) {
var line = scriptlines[i];
if (line[0] != ':') { r += line + '\n'; } else { if (labels[line]) { r += line + '\n'; } }
}
return r;
}

273
amt-setupbin-0.1.0.js Normal file
View File

@@ -0,0 +1,273 @@
/**
* @description Intel(R) AMT Setup.bin Parser
* @author Ylian Saint-Hilaire
* @version v0.1.0
*/
// Intel(R) AMT Setup.bin GUID's
var AmtSetupBinSetupGuids = [
"\xb5\x16\xfb\x71\x87\xcb\xf9\x4a\xb4\x41\xca\x7b\x38\x35\x78\xf9", // Version 1
"\x96\xb2\x81\x58\xcf\x6b\x72\x4c\x8b\x91\xa1\x5e\x51\x2e\x99\xc4", // Version 2
"\xa7\xf7\xf6\xc6\x89\xc4\xf6\x47\x93\xed\xe2\xe5\x02\x0d\xa5\x1d", // Version 3
"\xaa\xa9\x34\x52\xe1\x29\xa9\x44\x8d\x4d\x08\x1c\x07\xb9\x63\x53" // Version 4
];
// Notes about version 2 of setup.bin:
// - Default "admin" must be followed by a new MEBx password
// - ME_VARIABLE_IDENTIFIER_MANAGEABILITY_FEATURE_SELECTION may not appear after any CM settings
// - CM_VARIABLE_IDENTIFIER_USER_DEFINED_CERT_ADD must be preceded by setting CM_VARIABLE_IDENTIFIER_USER_DEFINED_CERTS_CONFIG to (TODO!)
// General notes:
// - Setup.bin should always start with "CurrentMEBx Pwd", "newMebx Pwd", "manageability selection" (if present).
// Intel(R) AMT variable identifiers
// Type: 0 = Binar Stringy, 1 = Char, 2 = Short, 3 = Int
var AmtSetupBinVarIds =
{
1: {
1: [0, "Current MEBx Password"],
2: [0, "New MEBx Password"],
3: [1, "Manageability Feature Selection",
{ 0: "None", 1: "Intel AMT" }],
4: [1, "Firmware Local Update", // 0 = Disabled, 1 = Enabled, 2 = Password Protected
{ 0: "Disabled", 1: "Enabled", 2: "Password Protected" }],
5: [1, "Firmware Update Qualifier", // 0 = Always, 1 = Never, 2 = Restricted
{ 0: "Always", 1: "Never", 2: "Restricted" }],
6: [4, "Power Package"] // GUID Length (16 bytes), Intel AMT version 2.1, 3 and 4
},
2: {
1: [0, "Provisioning Preshared Key ID (PID)"],
2: [0, "Provisioning Preshared Key (PPS)"],
3: [0, "PKI DNS Suffix"], // 255 bytes max length
4: [0, "Configuration Server FQDN"], // 255 bytes max length
5: [1, "Remote Configuration Enabled (RCFG)", // 0 = Off, 1 = On
{ 0: "Off", 1: "On" }],
6: [1, "Pre-Installed Certificates Enabled", // 0 = Off, 1 = On
{ 0: "Off", 1: "On" }],
7: [1, "User Defined Certificate Configuration", // 0 = Disabled, 1 = Enabled, 2 = Delete
{ 0: "Disabled", 1: "Enabled", 2: "Delete" }],
8: [0, "User Defined Certificate Addition"], // 1 byte hash algo, 20 to 48 bytes hash, 1 byte name length, up to 32 bytes friendly name, 1 = SHA1 (20 bytes), 2 = SHA256 (32 bytes), 3 = SHA384 (48 bytes). Algo 2 & 3 are for version 3 and up.
10: [1, "SOL/IDER Redirection Configuration", {
0: "None", 1: "SOL only - User/Pass Disabled", 2: "IDER only - User/Pass Disabled", 3: "SOL+IDER - User/Pass Disabled",
4: "None - User/Pass Enabled", 5: "SOL only - User/Pass Enabled", 6: "IDER only - User/Pass Enabled", 7: "SOL+IDER - User/Pass Enabled"
}],
11: [0, "Hostname"], // 63 bytes max length
12: [0, "Domain Name"], // 255 bytes max length
13: [1, "DHCP", { 1: "Disabled", 2: "Enabled" }],
14: [1, "Secure Firmware Update (SFWU)", // 0 = Disabled, 1 = Enabled
{ 0: "Disabled", 1: "Enabled" }],
15: [0, "ITO"],
16: [1, "Provisioning Mode (PM)", // 1 = Enterprise, 2 = Small Buisness (SMB)
{ 0: "Enterprise", 1: "Small Buisness"}],
17: [0, "Provisioning Server Address"],
18: [2, "Provision Server Port Number (PSPO)"],
19: [0, "Static IPv4 Parameters"],
20: [0, "VLAN"],
21: [0, "PASS Policy Flag"],
22: [0, "IPv6"], // Length is 204 bytes old format, 84 bytes new format, Version 3+ only
23: [1, "Shared/Dedicated FQDN", // 0 = Dedicated, 1 = Shared. This option is valid only if configuring the hostname as well
{ 0: "Dedicated", 1: "Shared" }],
24: [1, "Dynamic DNS Update", // 0 = Disabled, 1 = Enabled
{ 0: "Disabled", 1: "Enabled" }],
25: [1, "Remote Desktop (KVM) State", // 0 = Disabled, 1 = Enabled
{ 0: "Disabled", 1: "Enabled" }],
26: [1, "Opt-in User Consent Option", // 0 = Disabled, 1 = KVM, 0xFF = ALL
{ 0 : "Disabled", 1 : "KVM", 255 : "All" }],
27: [1, "Opt-in Remote IT Consent Policy", // 0 = Disabled, 1 = Enabled. Allows user consent to be configured remotely.
{ 0 : "Disabled", 1 : "Enabled"} ],
28: [1, "ME Provision Halt Active", // 0 = Stop, 1 = Start. The "ME provisioning Halt/Activate" command must appear in the file only after "PKIDNSSuffix", "ConfigServerFQDN" and "Provisioning Server Address"
{ 0 : "Stop", 1 : "Start"}],
29: [1, "Manual Setup and Configuration", // 0 = Automated, 1 = Manual
{ 0 : "Automated", 1 : "Manual"}],
30: [3, "Support Channel Identifier"], // 4 bytes length. Support channel identifier (valid values: 1-65535)
31: [0, "Support Channel Description"], // 60 bytes max. Friendly name used to describe the party representedby the support channel identifier.
32: [0, "Service Account Number"], // 32 bytes max. Unique string identifier given to the end user by the service provider.
33: [0, "Enrollement Passcode"], // 32 bytes max
34: [3, "Service Type"], // 4 bytes length. 1 = Reactive, 2 = Proactive, 4 = One Time Session
35: [0, "Service Provider Identifier"] // GUID Length (16 bytes)
}
}
// Parse the Setup.bin file
var AmtSetupBinCreate = function (version, flags) {
var obj = {};
obj.fileType = version;
obj.recordChunkCount = 0;
obj.recordHeaderByteCount = 0;
obj.recordNumber = 0;
obj.majorVersion = version;
obj.minorVersion = 0;
obj.flags = flags;
obj.dataRecordsConsumed = 0;
obj.dataRecordChunkCount = 0;
obj.records = [];
return obj;
}
// Parse the Setup.bin file
var AmtSetupBinDecode = function (file) {
// Format of the setup file header:
// FileTypeUUID(16) - uniquely identifies the file type. This identifier will remain valid and constant across all versions of the file type.
// RecordChunkCount(2) - indicates the number of 512-byte chunks occupied by this record, including all header, body, and reserved fields.
// RecordHeaderBytes(2) - indicates the length of the record header in bytes.
// RecordNumber(4) - uniquely identifies the record among all records in the file. The field contains a non-negative ordinal value. The value of this field is always zero in the Local Provisioning File Header Record.
// MajorVersion(1) - identifies the major version of the file format specification. This is a positive integer that is greater than or equal to 1. The Major Version number is incremented to indicate that changes have been introduced that will cause code written against a lower Major Version number to fail.
// MinorVersion(1) - identifies the minor version of the file format specification. This is an integer that is greater than or equal to 0. The Minor Version number is incremented to indicate that changes have been introduced that will not cause code written against the same Major Version and a lower Minor Version number to fail. The purpose of this behavior is to allow a single local provisioning file to be used for multiple generations of Intel<65> AMT platform.
// DataRecordCount(4) - indicates the total number of data records written in the file when it was created.
// DataRecordsConsumed(4) - is a counter value that begins at 0 and is incremented by 1 by each platform BIOS when it consumes a data record from the file. This value is used to determine the offset of the next data record in the file.
// DataRecordChunkCount(2) - contains the number of 512-byte chunks in each data record. All data records are the same length.
// ModuleList - contains a list of module identifiers. A module<6C>s identifier appears in the list if and only if the data records contain entries for that module. Each module identifier is two bytes in length. The list is terminated by an identifier value of 0.
var obj = {}, UUID = file.substring(0, 16);
obj.fileType = 0;
for (var i in AmtSetupBinSetupGuids) { if (UUID == AmtSetupBinSetupGuids[i]) obj.fileType = (+i + 1); }
if (obj.fileType == 0) return; // Bad header
obj.recordChunkCount = ReadShortX(file, 16);
obj.recordHeaderByteCount = ReadShortX(file, 18);
obj.recordNumber = ReadIntX(file, 20);
obj.majorVersion = file.charCodeAt(24);
obj.minorVersion = file.charCodeAt(25);
obj.flags = ReadShortX(file, 26); // Flags: 1 = Do not consume records
var dataRecordCount = ReadIntX(file, 28);
obj.dataRecordsConsumed = ReadIntX(file, 32);
obj.dataRecordChunkCount = ReadShortX(file, 36);
obj.records = [];
var ptr = 512;
while (ptr + 512 <= file.length) {
// Format of a data record header:
// RecordTypeIdentifier(4) - identifies the type of record (in this case a data record). Record Identifiers: Invalid - 0, Data Record - 1
// RecordFlags(4) - contains a set of bit flags that characterize the record.
// RecordChunkCount(2) - contains the number of 512-byte chunks occupied by the record including all header, body, and reserved fields.
// RecordHeaderByteCount(2) - indicates the length of the record header in bytes.
// RecordNumber(4) - uniquely identifies the record among all records in the file, including invalid as well as valid records. The identifier is a non-negative integer.
var r = {};
r.typeIdentifier = ReadIntX(file, ptr);
r.flags = ReadIntX(file, ptr + 4); // Flags: 1 = Valid, 2 = Scrambled
r.chunkCount = ReadShortX(file, ptr + 8);
r.headerByteCount = ReadShortX(file, ptr + 10);
r.number = ReadIntX(file, ptr + 12);
r.variables = [];
var ptr2 = 0, recbin = file.substring(ptr + 24, ptr + 512);
if ((r.flags & 2) != 0) { recbin = AmtSetupBinDescrambleRecordData(recbin); } // De-Scramble the record
while (1) {
// Format of a data record entry:
// ModuleIdentifier(2) - identifies the target ME module for the entry.
// VariableIdentifier(2) - an enumeration value that identifies the variable. Variable identifiers are unique to each ModuleIdentifier.
// VariableLength(2) - is the length of the variable value in bytes.
// VariableValue - is the value to be assigned to the variable.
var v = {};
v.moduleid = ReadShortX(recbin, ptr2);
v.varid = ReadShortX(recbin, ptr2 + 2);
if (v.moduleid == 0 || v.varid == 0) break;
if (AmtSetupBinVarIds[v.moduleid][v.varid]) {
v.length = ReadShortX(recbin, ptr2 + 4);
v.type = AmtSetupBinVarIds[v.moduleid][v.varid][0];
v.desc = AmtSetupBinVarIds[v.moduleid][v.varid][1];
v.value = recbin.substring(ptr2 + 8, ptr2 + 8 + v.length);
if (v.type == 1 && v.length == 1) v.value = v.value.charCodeAt(0); // 1 byte number
else if (v.type == 2 && v.length == 2) v.value = ReadShortX(v.value, 0); // 2 byte number
else if (v.type == 3 && v.length == 4) v.value = ReadIntX(v.value, 0); // 4 byte number
else if (v.type == 4) v.value = guidToStr(rstr2hex(v.value)); // GUID
r.variables.push(v);
}
ptr2 += (8 + (Math.floor((v.length + 3) / 4) * 4));
}
// Sort the variables
r.variables.sort(AmtSetupBinVariableCompare);
obj.records.push(r);
ptr += 512;
}
if (dataRecordCount != obj.records.length) return; // Mismatch record count
return obj;
}
// Construct a Setup.bin file
var AmtSetupBinEncode = function (obj) {
if (obj.fileType < 1 && obj.fileType > AmtSetupBinSetupGuids.length) return null;
var out = [], r = AmtSetupBinSetupGuids[obj.fileType - 1], reccount = 0;
r += ShortToStrX(obj.recordChunkCount);
r += ShortToStrX(obj.recordHeaderByteCount);
r += IntToStrX(obj.recordNumber);
r += String.fromCharCode(obj.majorVersion, obj.minorVersion);
r += ShortToStrX(obj.flags); // Flags: 1 = Do not consume records
r += IntToStrX(obj.records.length);
r += IntToStrX(obj.dataRecordsConsumed);
r += ShortToStrX(obj.dataRecordChunkCount);
while (r.length < 512) { r += "\0"; } // Pad the header
out.push(r);
// Write each record
for (var i in obj.records) {
var r2 = "", rec = obj.records[i];
r2 += IntToStrX(rec.typeIdentifier);
r2 += IntToStrX(rec.flags);
r2 += IntToStrX(0); // Reserved
r2 += IntToStrX(0); // Reserved
r2 += ShortToStrX(1); // rec.chunkCount
r2 += ShortToStrX(24); // rec.headerByteCount
r2 += IntToStrX(++reccount);
// Sort the variables
rec.variables.sort(AmtSetupBinVariableCompare);
/*
// Change variable priority
AmtSetupBinMoveToTop(r.variables, 1, 3); // Manageability Feature Selection
AmtSetupBinMoveToTop(r.variables, 1, 2); // New MEBx password
AmtSetupBinMoveToTop(r.variables, 1, 1); // Current MEBx password
*/
// Write each variable
for (var j in rec.variables) {
var r3 = "", v = rec.variables[j], data = v.value;
v.type = AmtSetupBinVarIds[v.moduleid][v.varid][0]; // Set the correct type if not alreay connect
if ((v.type > 0) && (v.type < 4)) { // If this is a numeric value, encode it correctly
data = parseInt(data);
if (v.type == 1) data = String.fromCharCode(data);
if (v.type == 2) data = ShortToStrX(data);
if (v.type == 3) data = IntToStrX(data);
}
if (v.type == 4) { data = hex2rstr(guidToStr(data.split('-').join('')).split('-').join('')); }
r3 += ShortToStrX(v.moduleid); // Module Identifier
r3 += ShortToStrX(v.varid); // Variable Identifier
r3 += ShortToStrX(data.length); // Variable Length
r3 += ShortToStrX(0); // Reserved
r3 += data; // Variable Data
while (r3.length % 4 != 0) { r3 += "\0"; } // Pad the variable
r2 += r3;
}
while (r2.length < 512) { r2 += "\0"; } // Pad the record
if ((rec.flags & 2) != 0) { r2 = r2.substring(0, 24) + AmtSetupBinScrambleRecordData(r2.substring(24)); } // Scramble the record starting at byte 24, after the header
out.push(r2);
}
return out.join('');
}
// Used to sort variables
function AmtSetupBinVariableCompare(a, b) {
if (a.moduleid > b.moduleid) return 1;
if (a.moduleid < b.moduleid) return -1;
if (a.varid > b.varid) return 1;
if (a.varid < b.varid) return -1;
return 0;
}
// Scramble and un-scramble records
function AmtSetupBinScrambleRecordData(data) { var out = ""; for (var i = 0; i < data.length; i++) { out += String.fromCharCode((data.charCodeAt(i) + 17) & 0xFF); } return out; }
function AmtSetupBinDescrambleRecordData(data) { var out = ""; for (var i = 0; i < data.length; i++) { out += String.fromCharCode((data.charCodeAt(i) + 0xEF) & 0xFF); } return out; }
// Find a moduleid/varid in the variable list, if found, move it to the top
//function AmtSetupBinMoveToTop(variables, moduleid, varid) { var i = -1; for (var j in variables) { if ((variables[j].moduleid == moduleid) && (variables[j].varid == varid)) { i = j; } } if (i > 1) { ArrayElementMove(variables, i, 0); } }

813
amt-terminal-0.0.2.js Normal file
View File

@@ -0,0 +1,813 @@
/**
* @description Remote Terminal
* @author Ylian Saint-Hilaire
* @version v0.0.2c
*/
// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
// https://www.x.org/docs/xterm/ctlseqs.pdf
// Construct a MeshServer object
var CreateAmtRemoteTerminal = function (divid, options) {
var obj = {};
obj.DivId = divid;
obj.DivElement = document.getElementById(divid);
obj.protocol = 1; // SOL
if (options && options.protocol) { obj.protocol = options.protocol; } // 1 = Normal, 6 = PowerShell
// ###BEGIN###{Terminal-Enumation-All}
obj.terminalEmulation = 1;
// ###END###{Terminal-Enumation-All}
obj.fxEmulation = 0;
obj.lineFeed = '\r\n';
obj.debugmode = 0;
obj.width = 80; // 80 or 100
obj.height = 25; // 25 or 30
obj.heightLock = 0;
var _Terminal_CellHeight = 21;
var _Terminal_CellWidth = 13;
var _TermColors = ['000000', 'BB0000', '00BB00', 'BBBB00', '0000BB', 'BB00BB', '00BBBB', 'BBBBBB', '555555', 'FF5555', '55FF55', 'FFFF55', '5555FF', 'FF55FF', '55FFFF', 'FFFFFF'];
var _TermCurrentReverse = 0;
var _TermCurrentFColor = 7;
var _TermCurrentBColor = 0;
var _TermLineWrap = true;
var _termx = 0;
var _termy = 0;
var _termsavex = 0;
var _termsavey = 0;
var _termstate = 0;
var _escNumber = [];
var _escNumberPtr = 0;
var _escNumberMode = 0;
var _scratt = [];
var _tscreen = [];
var _VTUNDERLINE = 1;
var _VTREVERSE = 2;
var _backSpaceErase = false;
var _cursorVisible = true;
var _scrollRegion;
var _altKeypadMode = false;
var scrollBackBuffer = [];
// ###BEGIN###{Terminal-Enumation-UTF8}
var utf8decodeBuffer = '';
// ###END###{Terminal-Enumation-UTF8}
// ###BEGIN###{Terminal-Enumation-All}
var utf8decodeBuffer = '';
// ###END###{Terminal-Enumation-All}
obj.title = null;
obj.onTitleChange = null;
obj.Start = function () { }
obj.Init = function (width, height) {
obj.width = width ? width : 80;
obj.height = height ? height : 25;
for (var y = 0; y < obj.height; y++) {
_tscreen[y] = [];
_scratt[y] = [];
for (var x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
}
obj.TermInit();
obj.TermDraw();
}
obj.xxStateChange = function (newstate) {
if ((newstate == 3) && (options != null) && (options.xterm == true)) { obj.TermSendKeys('stty rows ' + obj.height + ' cols ' + obj.width + '\nclear\n'); }
}
obj.ProcessData = function (str) {
if (obj.debugmode == 2) { console.log("TRecv(" + str.length + "): " + rstr2hex(str)); }
if (obj.capture != null) obj.capture += str;
// ###BEGIN###{Terminal-Enumation-UTF8}
try { str = decode_utf8(utf8decodeBuffer + str); } catch (ex) { utf8decodeBuffer += str; return; } // If we get data in the middle of a UTF-8 code, buffer it for next time.
utf8decodeBuffer = '';
// ###END###{Terminal-Enumation-UTF8}
// ###BEGIN###{Terminal-Enumation-All}
if (obj.terminalEmulation == 0) { try { str = decode_utf8(utf8decodeBuffer + str); } catch (ex) { utf8decodeBuffer += str; return; } } // If we get data in the middle of a UTF-8 code, buffer it for next time.
utf8decodeBuffer = '';
// ###END###{Terminal-Enumation-All}
_ProcessVt100EscString(str); obj.TermDraw();
}
function _ProcessVt100EscString(str) { for (var i = 0; i < str.length; i++) _ProcessVt100EscChar(String.fromCharCode(str.charCodeAt(i)), str.charCodeAt(i)); }
function _ProcessVt100EscChar(b, c) {
switch (_termstate) {
case 0: // Normal Term State
switch (c) {
case 27: // ESC
_termstate = 1;
_escNumber = [];
_escNumberPtr = 0;
_escNumberMode = 0;
break;
default:
// Process a single char
_ProcessVt100Char(b);
break;
}
break;
case 1:
switch (b) {
case '[':
_termstate = 2;
break;
case '(':
_termstate = 4;
break;
case ')':
_termstate = 5;
break;
case ']':
_termstate = 6; // xterm strings
break;
case '=':
// Set alternate keypad mode
_altKeypadMode = true;
_termstate = 0;
break;
case '>':
// Set numeric keypad mode
_altKeypadMode = false;
_termstate = 0;
break;
case '7':
// Save Cursor
_termsavex = _termx;
_termsavey = _termy;
_termstate = 0;
break;
case '8':
// Restore Cursor
_termx = _termsavex;
_termy = _termsavey;
_termstate = 0;
break;
case 'M':
// Scroll down one
var x = 1;
for (var y = _scrollRegion[1]; y >= _scrollRegion[0] + x; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
}
for (var y = _scrollRegion[0] + x - 1; y > _scrollRegion[0] - 1; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
_termstate = 0;
break;
default:
console.log('unknown terminal short code', b);
_termstate = 0;
break;
}
break;
case 2:
if (b >= '0' && b <= '9') {
// This is a number
if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = (b - '0'); }
else { _escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0')); }
break;
} else if (b == ';') {
// New number
_escNumberPtr++;
break;
} else if (b == '?') {
_escNumberMode = 1;
break;
} else {
// Process Escape Sequence
if (!_escNumber[0]) _escNumber[0] = 0;
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1, _escNumberMode);
_termstate = 0;
}
break;
case 4: // '(' Code
_termstate = 0;
break;
case 5: // ')' Code
_termstate = 0;
break;
case 6: // ']' Code, xterm
var bx = b.charCodeAt(0);
if (b == ';') {
_escNumberPtr++;
} else if (bx == 7) {
_ProcessXTermHandler(_escNumber);
_termstate = 0;
} else {
if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = b; }
else { _escNumber[_escNumberPtr] += b; }
}
break;
}
}
function _ProcessXTermHandler(_escNumber) {
if (_escNumber.length == 0) return;
var cmd = parseInt(_escNumber[0]);
if ((cmd == 0 || cmd == 2) && (_escNumber.length > 1) && (_escNumber[1] != '?')) {
if (obj.onTitleChange) { obj.onTitleChange(obj, obj.title = _escNumber[1]); }
}
}
function _ProcessEscapeHandler(code, args, argslen, mode) {
//console.log('process', code, args, mode);
if (mode == 1) {
switch (code) {
case 'l': // Hide the cursor
if (args[0] == 25) { _cursorVisible = false; }
break;
case 'h': // Show the cursor
if (args[0] == 25) { _cursorVisible = true; }
break;
}
} else if (mode == 0) {
var i;
switch (code) {
case 'c': // ResetDevice
// Reset
obj.TermResetScreen();
break;
case 'A': // Move cursor up n lines
if (argslen == 1) {
if (args[0] == 0) { _termy--; } else { _termy -= args[0]; }
if (_termy < 0) _termy = 0;
}
break;
case 'B': // Move cursor down n lines
if (argslen == 1) {
if (args[0] == 0) { _termy++; } else { _termy += args[0]; }
if (_termy > obj.height) _termy = obj.height;
}
break;
case 'C': // Move cursor right n lines
if (argslen == 1) {
if (args[0] == 0) { _termx++; } else { _termx += args[0]; }
if (_termx > obj.width) _termx = obj.width;
}
break;
case 'D': // Move cursor left n lines
if (argslen == 1) {
if (args[0] == 0) { _termx--; } else { _termx -= args[0]; }
if (_termx < 0) _termx = 0;
}
break;
case 'd': // Set cursor to line n
if (argslen == 1) {
_termy = args[0] - 1;
if (_termy > obj.height) _termy = obj.height;
if (_termy < 0) _termy = 0;
}
break;
case 'G': // Set cursor to col n
if (argslen == 1) {
_termx = args[0] - 1;
if (_termx < 0) _termx = 0;
if (_termx > (obj.width - 1)) _termx = (obj.width - 1);
}
break;
case 'P': // Delete X Character(s), default 1 char
var x = 1;
if (argslen == 1) { x = args[0]; }
for (i = _termx; i < obj.width - x; i++) { _tscreen[_termy][i] = _tscreen[_termy][i + x]; _scratt[_termy][i] = _scratt[_termy][i + x]; }
for (i = (obj.width - x); i < obj.width; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
break;
case 'L': // Insert X Line(s), default 1 char
var linecount = 1;
if (argslen == 1) { linecount = args[0]; }
if (linecount == 0) { linecount = 1; }
for (y = _scrollRegion[1]; y >= _termy + linecount; y--) {
_tscreen[y] = _tscreen[y - linecount];
_scratt[y] = _scratt[y - linecount];
}
for (y = _termy; y < _termy + linecount; y++) {
_tscreen[y] = [];
_scratt[y] = [];
for (x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
}
break;
case 'J': // ClearScreen:
if (argslen == 1 && args[0] == 2) {
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
_termx = 0;
_termy = 0;
scrollBackBuffer = [];
}
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
{
_EraseCursorToEol();
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
}
else if (argslen == 1 && args[0] == 1) // Erase cursor up
{
_EraseCursorToEol();
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
}
break;
case 'H': // MoveCursor:
if (argslen == 2) {
if (args[0] < 1) args[0] = 1;
if (args[1] < 1) args[1] = 1;
if (args[0] > obj.height) args[0] = obj.height;
if (args[1] > obj.width) args[1] = obj.width;
_termy = args[0] - 1;
_termx = args[1] - 1;
} else {
_termy = 0;
_termx = 0;
}
break;
case 'm': // ScreenAttribs:
// Change attributes
for (i = 0; i < argslen; i++) {
if (!args[i] || args[i] == 0) {
// Reset Attributes
_TermCurrentBColor = 0;
_TermCurrentFColor = 7;
_TermCurrentReverse = 0;
}
else if (args[i] == 1) {
// Bright
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
}
else if (args[i] == 2 || args[i] == 22) {
// Dim
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
}
else if (args[i] == 7) {
// Set Reverse attribute true
_TermCurrentReverse = 2;
}
else if (args[i] == 27) {
// Set Reverse attribute false
_TermCurrentReverse = 0;
}
else if (args[i] >= 30 && args[i] <= 37) {
// Set Foreground Color
var bright = (_TermCurrentFColor >= 8);
_TermCurrentFColor = (args[i] - 30);
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
}
else if (args[i] >= 40 && args[i] <= 47) {
// Set Background Color
_TermCurrentBColor = (args[i] - 40);
}
else if (args[i] >= 90 && args[i] <= 99) {
// Set Bright Foreground Color
_TermCurrentFColor = (args[i] - 82);
}
else if (args[i] >= 100 && args[i] <= 109) {
// Set Bright Background Color
_TermCurrentBColor = (args[i] - 92);
}
}
break;
case 'K': // EraseLine:
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
_EraseCursorToEol(); // Erase from the cursor to the end of the line
} else if (argslen == 1) {
if (args[0] == 1) { // Erase from the beginning of the line to the cursor
_EraseBolToCursor();
} else if (args[0] == 2) { // Erase the line with the cursor
_EraseLine(_termy);
}
}
break;
case 'h': // EnableLineWrap:
_TermLineWrap = true;
break;
case 'l': // DisableLineWrap:
_TermLineWrap = false;
break;
case 'r': // Set the scroll region
if (argslen == 2) { _scrollRegion = [args[0] - 1, args[1] - 1]; }
if (_scrollRegion[0] < 0) { _scrollRegion[0] = 0; }
if (_scrollRegion[0] > (obj.height - 1)) { _scrollRegion[0] = (obj.height - 1); }
if (_scrollRegion[1] < 0) { _scrollRegion[1] = 0; }
if (_scrollRegion[1] > (obj.height - 1)) { _scrollRegion[1] = (obj.height - 1); }
if (_scrollRegion[0] > _scrollRegion[1]) { _scrollRegion[0] = _scrollRegion[1]; }
break;
case 'S': // Scroll up the scroll region X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
for (var y = _scrollRegion[0]; y <= _scrollRegion[1] - x; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
}
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'M': // Delete X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
for (var y = _termy; y <= _scrollRegion[1] - x; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
}
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'T': // Scroll down the scroll region X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
for (var y = _scrollRegion[1]; y > _scrollRegion[0] + x; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
}
for (var y = _scrollRegion[0] + x; y > _scrollRegion[0]; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'X': // Erase X characters, default 1
var x = 1, xx = _termx, yy = _termy;
if (argslen == 1) { x = args[0] }
while ((x > 0) && (yy < obj.height)) { _tscreen[yy][xx] = ' '; xx++; x--; if (xx >= obj.width) { xx = 0; yy++; } }
break;
default:
//if (code != '@') alert(code);
console.log('unknown terminal code', code, args, mode);
break;
}
}
}
obj.ProcessVt100String = function (str) {
for (var i = 0; i < str.length; i++) _ProcessVt100Char(String.fromCharCode(str.charCodeAt(i)));
}
// ###BEGIN###{Terminal-Enumation-All}
var AsciiToUnicode = [
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
];
var AsciiToUnicodeIntel = [
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ae, 0x00bb,
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
];
// ###END###{Terminal-Enumation-All}
// ###BEGIN###{Terminal-Enumation-ASCII}
var AsciiToUnicode = [
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
];
// ###END###{Terminal-Enumation-ASCII}
// ###BEGIN###{Terminal-Enumation-Intel}
var AsciiToUnicodeIntel = [
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ae, 0x00bb,
0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
];
// ###END###{Terminal-Enumation-Intel}
function _ProcessVt100Char(c) {
if (c == '\0' || c.charCodeAt() == 7) return; // Ignore null & bell
var ch = c.charCodeAt();
//console.log('_ProcessVt100Char', ch, c);
// ###BEGIN###{Terminal-Enumation-All}
// UTF8 Terminal
if (obj.terminalEmulation == 1) {
// ANSI - Extended ASCII emulation.
if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicode[ch & 0x7F]); }
} else if (obj.terminalEmulation == 2) {
// ANSI - Intel Extended ASCII emulation.
if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicodeIntel[ch & 0x7F]); }
}
// ###END###{Terminal-Enumation-All}
// ###BEGIN###{Terminal-Enumation-ASCII}
// ANSI - Extended ASCII emulation.
//if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicode[ch & 0x7F]); }
// ###END###{Terminal-Enumation-ASCII}
// ###BEGIN###{Terminal-Enumation-Intel}
// ANSI - Intel Extended ASCII emulation.
//if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicodeIntel[ch & 0x7F]); }
// ###END###{Terminal-Enumation-Intel}
//if (ch < 32 && ch != 10 && ch != 13) alert(ch);
switch (ch) {
case 16: { c = ' '; break; } // This is an odd char that show up on Intel BIOS's.
case 24: { c = '↑'; break; }
case 25: { c = '↓'; break; }
}
if (_termx > obj.width) _termx = obj.width;
if (_termy > (obj.height - 1)) _termy = (obj.height - 1);
switch (c) {
case '\b': // Backspace
if (_termx > 0) {
_termx--;
if (_backSpaceErase) { _TermDrawChar(' '); }
}
break;
case '\t': // tab
var tab = 8 - (_termx % 8)
for (var x = 0; x < tab; x++) _ProcessVt100Char(" ");
break;
case '\n': // Linefeed
_termy++;
if (_termy > _scrollRegion[1]) {
// Move everything up one line
obj.recordLineTobackBuffer(0);
_TermMoveUp(1);
_termy = _scrollRegion[1];
}
if (obj.lineFeed = '\r') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
break;
case '\r': // Carriage Return
_termx = 0;
break;
default:
if (_termx >= obj.width) {
_termx = 0;
if (_TermLineWrap) { _termy++; }
if (_termy >= (obj.height - 1)) { _TermMoveUp(1); _termy = (obj.height - 1); }
}
_TermDrawChar(c);
_termx++;
break;
}
}
function _TermDrawChar(c) {
_tscreen[_termy][_termx] = c;
_scratt[_termy][_termx] = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
}
obj.TermClear = function(TermColor) {
for (var y = 0; y < obj.height; y++) {
for (var x = 0; x < obj.width; x++) {
_tscreen[y][x] = ' ';
_scratt[y][x] = TermColor;
}
}
scrollBackBuffer = [];
}
obj.TermResetScreen = function () {
_TermCurrentReverse = 0;
_TermCurrentFColor = 7;
_TermCurrentBColor = 0;
_TermLineWrap = _cursorVisible = true;
_termx = _termy = 0;
_backSpaceErase = false;
_scrollRegion = [0, (obj.height - 1)];
_altKeypadMode = false;
obj.TermClear(7 << 6);
// ###BEGIN###{Terminal-Enumation-UTF8}
//utf8decodeBuffer = '';
// ###END###{Terminal-Enumation-UTF8}
// ###BEGIN###{Terminal-Enumation-All}
utf8decodeBuffer = '';
// ###END###{Terminal-Enumation-All}
}
function _EraseCursorToEol() {
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
for (var x = _termx; x < obj.width; x++) {
_tscreen[_termy][x] = ' ';
_scratt[_termy][x] = t;
}
}
function _EraseBolToCursor() {
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
for (var x = 0; x < _termx; x++) {
_tscreen[_termy][x] = ' ';
_scratt[_termy][x] = t;
}
}
function _EraseLine(line) {
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
for (var x = 0; x < obj.width; x++) {
_tscreen[line][x] = ' ';
_scratt[line][x] = t;
}
}
obj.TermSendKeys = function (keys) { if (obj.debugmode == 2) { console.log("TSend(" + keys.length + "): " + rstr2hex(keys), keys); } if (obj.parent) { obj.parent.Send(keys); } }
obj.TermSendKey = function (key) { if (obj.debugmode == 2) { console.log("TSend(1): " + rstr2hex(String.fromCharCode(key)), key); } if (obj.parent) { obj.parent.Send(String.fromCharCode(key)); } }
function _TermMoveUp(linecount) {
var x, y;
for (y = _scrollRegion[0]; y <= _scrollRegion[1] - linecount; y++) {
_tscreen[y] = _tscreen[y + linecount];
_scratt[y] = _scratt[y + linecount];
}
for (y = _scrollRegion[1] - linecount + 1; y <= _scrollRegion[1]; y++) {
_tscreen[y] = [];
_scratt[y] = [];
for (x = 0; x < obj.width; x++) {
_tscreen[y][x] = ' ';
_scratt[y][x] = (7 << 6);
}
}
}
obj.TermHandleKeys = function (e) {
if (!e.ctrlKey) {
if (e.which == 127) obj.TermSendKey(8);
else if (e.which == 13) { obj.TermSendKeys(obj.lineFeed); }
else if (e.which != 0) obj.TermSendKey(e.which);
return false;
}
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
}
obj.TermHandleKeyUp = function (e) {
if ((e.which != 8) && (e.which != 32) && (e.which != 9)) return true;
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
return false;
}
obj.TermHandleKeyDown = function (e) {
if ((e.which >= 65) && (e.which <= 90) && (e.ctrlKey == true)) {
obj.TermSendKey(e.which - 64);
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
return;
}
if (e.which == 27) { obj.TermSendKeys(String.fromCharCode(27)); return true; }; // ESC
if (_altKeypadMode == true) {
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 79, 68)); return true; }; // Left
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 79, 65)); return true; }; // Up
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 79, 67)); return true; }; // Right
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 79, 66)); return true; }; // Down
} else {
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
}
if (e.which == 33) { obj.TermSendKeys(String.fromCharCode(27, 91, 53, 126)); return true; }; // PageUp
if (e.which == 34) { obj.TermSendKeys(String.fromCharCode(27, 91, 54, 126)); return true; }; // PageDown
if (e.which == 35) { obj.TermSendKeys(String.fromCharCode(27, 91, 70)); return true; }; // End
if (e.which == 36) { obj.TermSendKeys(String.fromCharCode(27, 91, 72)); return true; }; // Home
if (e.which == 45) { obj.TermSendKeys(String.fromCharCode(27, 91, 50, 126)); return true; }; // Insert
if (e.which == 46) { obj.TermSendKeys(String.fromCharCode(27, 91, 51, 126)); return true; }; // Delete
if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
// F1 to F12 keys
// ###BEGIN###{Terminal-FxEnumation-All}
var fx0 = [80, 81, 119, 120, 116, 117, 113, 114, 112, 77];
var fx1 = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 33, 64];
var fx2 = [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91];
if (e.which > 111 & e.which < 124 && e.repeat == false) { // F1 to F12 keys
if (obj.fxEmulation == 0 && e.which < 122) { obj.TermSendKeys(String.fromCharCode(27, 91, 79, fx0[e.which - 112])); return true; } // 'Intel (F10 = ESC+[OM)'
if (obj.fxEmulation == 1) { obj.TermSendKeys(String.fromCharCode(27, fx1[e.which - 112])); return true; } // 'Alternate (F10 = ESC+0)'
if (obj.fxEmulation == 2) { obj.TermSendKeys(String.fromCharCode(27, 79, fx2[e.which - 112])); return true; } // 'VT100+ (F10 = ESC+[OY)'
}
// ###END###{Terminal-FxEnumation-All}
// ###BEGIN###{Terminal-FxEnumation-Intel}
var fx0 = [80, 81, 119, 120, 116, 117, 113, 114, 112, 77];
if (e.which > 111 & e.which < 122 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, 91, 79, fx0[e.which - 112])); return true; } // 'Intel (F10 = ESC+[OM)'
// ###END###{Terminal-FxEnumation-Intel}
// ###BEGIN###{Terminal-FxEnumation-Alternate}
var fx1 = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 33, 64];
if (e.which > 111 & e.which < 124 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, fx1[e.which - 112])); return true; } // 'Alternate (F10 = ESC+0)'
// ###END###{Terminal-FxEnumation-Alternate}
// ###BEGIN###{Terminal-FxEnumation-VT100Plus}
var fx2 = [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91];
if (e.which > 111 & e.which < 124 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, 79, fx2[e.which - 112])); return true; } // 'VT100+ (F10 = ESC+[OY)'
// ###END###{Terminal-FxEnumation-VT100Plus}
if (e.which != 8 && e.which != 32 && e.which != 9) return true;
obj.TermSendKey(e.which);
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
return false;
}
obj.recordLineTobackBuffer = function(y) {
var closetag = '', buf = '';
var r = obj.TermDrawLine(buf, y, closetag);
buf = r[0];
closetag = r[1];
scrollBackBuffer.push(buf + closetag + '<br>');
}
obj.TermDrawLine = function (buf, y, closetag) {
var newat, c, oldat = 1, x1, x2;
for (var x = 0; x < obj.width; ++x) {
newat = _scratt[y][x];
if (_termx == x && _termy == y && _cursorVisible) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
if (newat != oldat) {
buf += closetag;
closetag = '';
x1 = 6; x2 = 12;
if (newat & _VTREVERSE) { x1 = 12; x2 = 6; }
buf += '<span style="color:#' + _TermColors[(newat >> x1) & 0x3F] + ';background-color:#' + _TermColors[(newat >> x2) & 0x3F];
if (newat & _VTUNDERLINE) buf += ';text-decoration:underline';
buf += ';">';
closetag = "</span>" + closetag;
oldat = newat;
}
c = _tscreen[y][x];
switch (c) {
case '&': buf += '&amp;'; break;
case '<': buf += '&lt;'; break;
case '>': buf += '&gt;'; break;
case ' ': buf += '&nbsp;'; break;
default: buf += c; break;
}
}
return [buf, closetag];
}
obj.TermDraw = function() {
var closetag = '', buf = '';
for (var y = 0; y < obj.height; ++y) {
var r = obj.TermDrawLine(buf, y, closetag);
buf = r[0];
closetag = r[1];
if (y != (obj.height - 1)) buf += '<br>';
}
if (scrollBackBuffer.length > 800) { scrollBackBuffer = scrollBackBuffer.slice(scrollBackBuffer.length - 800); }
var backbuffer = scrollBackBuffer.join('');
obj.DivElement.innerHTML = "<font size='4'><b>" + backbuffer + buf + closetag + "</b></font>";
obj.DivElement.scrollTop = obj.DivElement.scrollHeight;
//if (obj.heightLock == 0) { setTimeout(obj.TermLockHeight, 10); }
}
/*
obj.TermLockHeight = function () {
obj.heightLock = obj.DivElement.clientHeight;
obj.DivElement.style['height'] = obj.DivElement.parentNode.style['height'] = obj.heightLock + 'px';
obj.DivElement.style['overflow-y'] = 'scroll';
}
*/
obj.TermInit = function () { obj.TermResetScreen(); }
//obj.heightLock = 0;
//obj.DivElement.style['height'] = '';
if ((options != null) && (options.width != null) && (options.height != null)) { obj.Init(options.width, options.height); } else { obj.Init(); }
return obj;
}

336
amt-wsman-0.2.0.js Normal file
View File

@@ -0,0 +1,336 @@
/**
* @description Intel(r) AMT WSMAN Stack
* @author Ylian Saint-Hilaire
* @version v0.2.0
*/
// Construct a MeshServer object
var WsmanStackCreateService = function (host, port, user, pass, tls, extra) {
var obj = {};
//obj.onDebugMessage = null; // Set to a function if you want to get debug messages.
obj.NextMessageId = 1; // Next message number, used to label WSMAN calls.
obj.Address = '/wsman';
obj.comm = CreateWsmanComm(host, port, user, pass, tls, extra);
obj.PerformAjax = function (postdata, callback, tag, pri, namespaces) {
if (namespaces == null) namespaces = '';
obj.comm.PerformAjax('<?xml version=\"1.0\" encoding=\"utf-8\"?><Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns=\"http://www.w3.org/2003/05/soap-envelope\" ' + namespaces + '><Header><a:Action>' + postdata, function (data, status, tag) {
if (status != 200) { callback(obj, null, { Header: { HttpError: status } }, status, tag); return; }
var wsresponse = obj.ParseWsman(data);
if (!wsresponse || wsresponse == null) { callback(obj, null, { Header: { HttpError: status } }, 601, tag); } else { callback(obj, wsresponse.Header["ResourceURI"], wsresponse, 200, tag); }
}, tag, pri);
}
// Private method
//obj.Debug = function (msg) { /*console.log(msg);*/ }
// Cancel all pending queries with given status
obj.CancelAllQueries = function (s) { obj.comm.CancelAllQueries(s); }
// Get the last element of a URI string
obj.GetNameFromUrl = function (resuri) {
var x = resuri.lastIndexOf("/");
return (x == -1)?resuri:resuri.substring(x + 1);
}
// Perform a WSMAN Subscribe operation
obj.ExecSubscribe = function (resuri, delivery, url, callback, tag, pri, selectors, opaque, user, pass) {
var digest = "", digest2 = "", opaque = "";
if (user != null && pass != null) { digest = '<t:IssuedTokens xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><t:RequestSecurityTokenResponse><t:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</t:TokenType><t:RequestedSecurityToken><se:UsernameToken><se:Username>' + user + '</se:Username><se:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#PasswordText">' + pass + '</se:Password></se:UsernameToken></t:RequestedSecurityToken></t:RequestSecurityTokenResponse></t:IssuedTokens>'; digest2 = '<w:Auth Profile="http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/http/digest"/>'; }
if (opaque != null) { opaque = '<a:ReferenceParameters><m:arg>' + opaque + '</m:arg></a:ReferenceParameters>'; }
if (delivery == 'PushWithAck') { delivery = 'dmtf.org/wbem/wsman/1/wsman/PushWithAck'; } else if (delivery == 'Push') { delivery = 'xmlsoap.org/ws/2004/08/eventing/DeliveryModes/Push'; }
var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + digest + '</Header><Body><e:Subscribe><e:Delivery Mode="http://schemas.' + delivery + '"><e:NotifyTo><a:Address>' + url + '</a:Address>' + opaque + '</e:NotifyTo>' + digest2 + '</e:Delivery></e:Subscribe>';
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:m="http://x.com"');
}
// Perform a WSMAN UnSubscribe operation
obj.ExecUnSubscribe = function (resuri, callback, tag, pri, selectors) {
var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + '</Header><Body><e:Unsubscribe/>';
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"');
}
// Perform a WSMAN PUT operation
obj.ExecPut = function (resuri, putobj, callback, tag, pri, selectors) {
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60.000S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + '</Header><Body>' + _PutObjToBodyXml(resuri, putobj);
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri);
}
// Perform a WSMAN CREATE operation
obj.ExecCreate = function (resuri, putobj, callback, tag, pri, selectors) {
var objname = obj.GetNameFromUrl(resuri);
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><g:" + objname + " xmlns:g=\"" + resuri + "\">";
for (var n in putobj) { data += "<g:" + n + ">" + putobj[n] + "</g:" + n + ">" }
obj.PerformAjax(data + "</g:" + objname + "></Body></Envelope>", callback, tag, pri);
}
// Perform a WSMAN DELETE operation
obj.ExecDelete = function (resuri, putobj, callback, tag, pri) {
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(putobj) + "</Header><Body /></Envelope>";
obj.PerformAjax(data, callback, tag, pri);
}
// Perform a WSMAN GET operation
obj.ExecGet = function (resuri, callback, tag, pri) {
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body /></Envelope>", callback, tag, pri);
}
// Perform a WSMAN method call operation
obj.ExecMethod = function (resuri, method, args, callback, tag, pri, selectors) {
var argsxml = "";
for (var i in args) { if (args[i] != null) { if (Array.isArray(args[i])) { for (var x in args[i]) { argsxml += "<r:" + i + ">" + args[i][x] + "</r:" + i + ">"; } } else { argsxml += "<r:" + i + ">" + args[i] + "</r:" + i + ">"; } } }
obj.ExecMethodXml(resuri, method, argsxml, callback, tag, pri, selectors);
}
// Perform a WSMAN method call operation. The arguments are already formatted in XML.
obj.ExecMethodXml = function (resuri, method, argsxml, callback, tag, pri, selectors) {
obj.PerformAjax(resuri + "/" + method + "</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><r:" + method + '_INPUT' + " xmlns:r=\"" + resuri + "\">" + argsxml + "</r:" + method + "_INPUT></Body></Envelope>", callback, tag, pri);
}
// Perform a WSMAN ENUM operation
obj.ExecEnum = function (resuri, callback, tag, pri) {
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Enumerate xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\" /></Body></Envelope>", callback, tag, pri);
}
// Perform a WSMAN PULL operation
obj.ExecPull = function (resuri, enumctx, callback, tag, pri) {
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Pull xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\"><EnumerationContext>" + enumctx + "</EnumerationContext></Pull></Body></Envelope>", callback, tag, pri); // </EnumerationContext>--<MaxElements>999</MaxElements><MaxCharacters>99999</MaxCharacters>--</Pull>
}
// Private method
obj.ParseWsman = function (xml) {
try {
if (!xml.childNodes) xml = _turnToXml(xml);
var r = { Header:{} }, header = xml.getElementsByTagName("Header")[0], t;
if (!header) header = xml.getElementsByTagName("a:Header")[0];
if (!header) return null;
for (var i = 0; i < header.childNodes.length; i++) {
var child = header.childNodes[i];
r.Header[child.localName] = child.textContent;
}
var body = xml.getElementsByTagName("Body")[0];
if (!body) body = xml.getElementsByTagName("a:Body")[0];
if (!body) return null;
if (body.childNodes.length > 0) {
t = body.childNodes[0].localName;
if (t.indexOf("_OUTPUT") == t.length - 7) { t = t.substring(0, t.length - 7); }
r.Header['Method'] = t;
r.Body = _ParseWsmanRec(body.childNodes[0]);
}
return r;
} catch (e) {
console.log("Unable to parse XML: " + xml);
return null;
}
}
// Private method
function _ParseWsmanRec(node) {
var data, r = {};
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
if ((child.childElementCount == null) || (child.childElementCount == 0)) { data = child.textContent; } else { data = _ParseWsmanRec(child); }
if (data == 'true') data = true; // Convert 'true' into true
if (data == 'false') data = false; // Convert 'false' into false
if ((parseInt(data) + '') === data) data = parseInt(data); // Convert integers
var childObj = data;
if ((child.attributes != null) && (child.attributes.length > 0)) {
childObj = { 'Value': data };
for(var j = 0; j < child.attributes.length; j++) {
childObj['@' + child.attributes[j].name] = child.attributes[j].value;
}
}
if (r[child.localName] instanceof Array) { r[child.localName].push(childObj); }
else if (r[child.localName] == null) { r[child.localName] = childObj; }
else { r[child.localName] = [r[child.localName], childObj]; }
}
return r;
}
function _PutObjToBodyXml(resuri, putObj) {
if (!resuri || putObj == null) return '';
var objname = obj.GetNameFromUrl(resuri);
var result = '<r:' + objname + ' xmlns:r="' + resuri + '">';
for (var prop in putObj) {
if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue;
if (putObj[prop] == null || typeof putObj[prop] === 'function') continue;
if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) {
result += '<r:' + prop + '><a:Address>' + putObj[prop].Address + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + putObj[prop]['ReferenceParameters']["ResourceURI"] + '</w:ResourceURI><w:SelectorSet>';
var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector'];
if (Array.isArray(selectorArray)) {
for (var i=0; i< selectorArray.length; i++) {
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
}
}
else {
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
}
result += '</w:SelectorSet></a:ReferenceParameters></r:' + prop + '>';
}
else {
if (Array.isArray(putObj[prop])) {
for (var i = 0; i < putObj[prop].length; i++) {
result += '<r:' + prop + '>' + putObj[prop][i].toString() + '</r:' + prop + '>';
}
} else {
result += '<r:' + prop + '>' + putObj[prop].toString() + '</r:' + prop + '>';
}
}
}
result += '</r:' + objname + '>';
return result;
}
/*
convert
{ @Name: 'InstanceID', @AttrName: 'Attribute Value'}
into
' Name="InstanceID" AttrName="Attribute Value" '
*/
function _ObjectToXmlAttributes(objWithAttributes) {
if(!objWithAttributes) return '';
var result = '';
for (var propName in objWithAttributes) {
if (!objWithAttributes.hasOwnProperty(propName) || propName.indexOf('@') !== 0) continue;
result += ' ' + propName.substring(1) + '="' + objWithAttributes[propName] + '"';
}
return result;
}
function _PutObjToSelectorsXml(selectorSet) {
if (!selectorSet) return '';
if (typeof selectorSet == 'string') return selectorSet;
if (selectorSet['InstanceID']) return "<w:SelectorSet><w:Selector Name=\"InstanceID\">" + selectorSet['InstanceID'] + "</w:Selector></w:SelectorSet>";
var result = '<w:SelectorSet>';
for(var propName in selectorSet) {
if (!selectorSet.hasOwnProperty(propName)) continue;
result += '<w:Selector Name="' + propName + '">';
if (selectorSet[propName]['ReferenceParameters']) {
result += '<a:EndpointReference>';
result += '<a:Address>' + selectorSet[propName]['Address'] + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + selectorSet[propName]['ReferenceParameters']['ResourceURI'] + '</w:ResourceURI><w:SelectorSet>';
var selectorArray = selectorSet[propName]['ReferenceParameters']['SelectorSet']['Selector'];
if (Array.isArray(selectorArray)) {
for (var i = 0; i < selectorArray.length; i++) {
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
}
}
else {
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
}
result += '</w:SelectorSet></a:ReferenceParameters></a:EndpointReference>';
} else {
result += selectorSet[propName];
}
result += '</w:Selector>';
}
result += '</w:SelectorSet>';
return result;
}
function _turnToXml(text) {
// ###BEGIN###{!Mode-MeshCentral2}
// NodeJS detection
var isNode = new Function("try {return this===global;}catch(e){return false;}");
if (isNode()) {
var XDOMParser = require('xmldom').DOMParser;
return new XDOMParser().parseFromString(text, "text/xml");
}
// ###END###{!Mode-MeshCentral2}
if (window.DOMParser) {
return new DOMParser().parseFromString(text, "text/xml");
} else {
// Internet Explorer
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(text);
return xmlDoc;
}
}
/*
// This is a drop-in replacement to _turnToXml() that works without xml parser dependency.
Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : null); } });
function _treeBuilder() {
this.tree = [];
this.push = function (element) { this.tree.push(element); };
this.pop = function () { var element = this.tree.pop(); if (this.tree.length > 0) { var x = this.tree.peek(); x.childNodes.push(element); x.childElementCount = x.childNodes.length; } return (element); };
this.peek = function () { return (this.tree.peek()); }
this.addNamespace = function (prefix, namespace) { this.tree.peek().nsTable[prefix] = namespace; if (this.tree.peek().attributes.length > 0) { for (var i = 0; i < this.tree.peek().attributes; ++i) { var a = this.tree.peek().attributes[i]; if (prefix == '*' && a.name == a.localName) { a.namespace = namespace; } else if (prefix != '*' && a.name != a.localName) { var pfx = a.name.split(':')[0]; if (pfx == prefix) { a.namespace = namespace; } } } } }
this.getNamespace = function (prefix) { for (var i = this.tree.length - 1; i >= 0; --i) { if (this.tree[i].nsTable[prefix] != null) { return (this.tree[i].nsTable[prefix]); } } return null; }
}
function _turnToXml(text) { if (text == null) return null; return ({ childNodes: [_turnToXmlRec(text)], getElementsByTagName: _getElementsByTagName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS }); }
function _getElementsByTagNameNS(ns, name) { var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) { if (node.localName == name && (node.namespace == ns || ns == '*')) { ret.push(node); } }); return ret; }
function _getElementsByTagName(name) { var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) { if (node.localName == name) { ret.push(node); } }); return ret; }
function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name) { ret.push(this.childNodes[node]); } } } return (ret); }
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
function _turnToXmlRec(text) {
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
for (var i in x1) {
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
if ((elementName.length > 0) && (elementName[0] != '?')) {
if (elementName[0] != '/') {
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
Object.defineProperty(attributes, "get",
{
value: function () {
if (arguments.length == 1) {
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
}
else if (arguments.length == 2) {
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
}
else {
throw ('attributes.get(): Invalid number of parameters');
}
}
});
elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} });
// Parse Attributes
if (x3.length > 0) {
var skip = false;
for (var j in x3) {
if (x3[j] == '/') {
// This is an empty Element
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
elementStack.peek().textContent = '';
lastElement = elementStack.pop();
skip = true;
break;
}
var k = x3[j].indexOf('=');
if (k > 0) {
var attrName = x3[j].substring(0, k);
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
var attrNS = elementStack.getNamespace('*');
if (attrName == 'xmlns') {
elementStack.addNamespace('*', attrValue);
attrNS = attrValue;
} else if (attrName.startsWith('xmlns:')) {
elementStack.addNamespace(attrName.substring(6), attrValue);
} else {
var ax = attrName.split(':');
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
}
var x = { name: attrName, value: attrValue }
if (attrNS != null) x.namespace = attrNS;
elementStack.peek().attributes.push(x);
}
}
if (skip) { continue; }
}
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
if (x2[1]) { elementStack.peek().textContent = x2[1]; }
} else { lastElement = elementStack.pop(); }
}
}
return lastElement;
}
*/
return obj;
}

95
amt-wsman-ajax-0.2.0.js Normal file
View File

@@ -0,0 +1,95 @@
/**
* @description WSMAN communication using browser AJAX
* @author Ylian Saint-Hilaire
* @version v0.2.0c
*/
// Construct a WSMAN communication object
var CreateWsmanComm = function (url) {
var obj = {};
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time.
obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
obj.Url = url;
// Private method
// pri = priority, if set to 1, the call is high priority and put on top of the stack.
obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
if ((obj.ActiveAjaxCount == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && obj.PendingAjax.length == 0) {
// There are no pending AJAX calls, perform the call now.
obj.PerformAjaxEx(postdata, callback, tag, url, action);
} else {
// If this is a high priority call, put this call in front of the array, otherwise put it in the back.
if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
}
}
// Private method
obj.PerformNextAjax = function () {
if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
var x = obj.PendingAjax.shift();
obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
obj.PerformNextAjax();
}
// Private method
obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
if (obj.FailAllError != 0) { if (obj.FailAllError != 999) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag]); } return; }
// console.log("SEND: " + postdata); // DEBUG
// We are in a AJAX browser environment
obj.ActiveAjaxCount++;
if (!postdata) { postdata = ''; }
var xdr = null; // TODO: See if we should re-use this object and if we do, when should we do it.
try { xdr = new XDomainRequest(); } catch (e) { }
if (!xdr) xdr = new XMLHttpRequest();
xdr.open(action ? action : "POST", url ? url : obj.Url);
xdr.timeout = 15000;
xdr.onload = function () { obj.gotNextMessages(xdr.responseText, 'success', xdr, [postdata, callback, tag]); };
xdr.onerror = function () { obj.gotNextMessagesError(xdr, 'error', null, [postdata, callback, tag]); };
xdr.ontimeout = function () { obj.gotNextMessagesError(xdr, 'error', null, [postdata, callback, tag]); };
//xdr.send(postdata); // Works for text only, no binary.
// Send POST body, this work with binary.
if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-SEND(" + postdata.length + "): " + postdata); }
var b = new Uint8Array(postdata.length);
for (var i = 0; i < postdata.length; ++i) { b[i] = postdata.charCodeAt(i); }
xdr.send(b);
return xdr;
}
// AJAX specific private method
obj.pendingAjaxCall = [];
// Private method
obj.gotNextMessages = function (data, status, request, callArgs) {
if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-RECV(" + data.length + "): " + data); }
obj.ActiveAjaxCount--;
if (obj.FailAllError == 999) return;
// console.log("RECV: " + data); // DEBUG
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); obj.PerformNextAjax(); return; }
callArgs[1](data, 200, callArgs[2]);
obj.PerformNextAjax();
}
// Private method
obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
obj.ActiveAjaxCount--;
if (obj.FailAllError == 999) return;
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
// if (s != 200) { console.log("ERROR, status=" + status + "\r\n\r\nreq=" + callArgs[0]); } // Debug: Display the request & response if something did not work.
if (obj.FailAllError != 999) { callArgs[1]({ Header: { HttpError: request.status } }, request.status, callArgs[2]); }
obj.PerformNextAjax();
}
// Cancel all pending queries with given status
obj.CancelAllQueries = function (s) {
while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
}
return obj;
}

392
amt-wsman-node-0.2.0.js Normal file
View File

@@ -0,0 +1,392 @@
/**
* @description Intel(r) AMT WSMAN communication using Node.js TLS
* @author Ylian Saint-Hilaire
* @version v0.2.0b
*/
// Construct a MeshServer object
var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions) {
//console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions);
var obj = {};
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time.
obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
obj.challengeParams = null;
obj.noncecounter = 1;
obj.authcounter = 0;
obj.Address = '/wsman';
obj.challengeParams = null;
obj.noncecounter = 1;
obj.authcounter = 0;
obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce
obj.net = require('net');
obj.tls = require('tls');
obj.crypto = require('crypto');
obj.constants = require('constants');
obj.socket = null;
obj.socketState = 0;
obj.kerberosDone = 0;
obj.amtVersion = null;
obj.host = host;
obj.port = port;
obj.user = user;
obj.pass = pass;
obj.xtls = tls;
obj.xtlsoptions = tlsoptions;
obj.xtlsFingerprint;
obj.xtlsCertificate = null;
obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted
obj.xtlsSkipHostCheck = 0;
obj.xtlsMethod = 0;
obj.xtlsDataReceived = false;
obj.digestRealmMatch = null;
obj.digestRealm = null;
// Private method
//obj.Debug = function (msg) { console.log(msg); }
// Private method
// pri = priority, if set to 1, the call is high priority and put on top of the stack.
obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
if ((obj.ActiveAjaxCount == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && obj.PendingAjax.length == 0) {
// There are no pending AJAX calls, perform the call now.
obj.PerformAjaxEx(postdata, callback, tag, url, action);
} else {
// If this is a high priority call, put this call in front of the array, otherwise put it in the back.
if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
}
}
// Private method
obj.PerformNextAjax = function () {
if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
var x = obj.PendingAjax.shift();
obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
obj.PerformNextAjax();
}
// Private method
obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; }
if (!postdata) postdata = '';
//obj.Debug("SEND: " + postdata); // DEBUG
obj.ActiveAjaxCount++;
return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action);
}
// NODE.js specific private method
obj.pendingAjaxCall = [];
// NODE.js specific private method
obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 5); }
// NODE.js specific private method
obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) {
if (retry <= 0 || obj.FailAllError != 0) {
// Too many retry, fail here.
obj.ActiveAjaxCount--;
if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error
obj.PerformNextAjax();
return;
}
obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]);
if (obj.socketState == 0) { obj.xxConnectHttpSocket(); }
else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); }
}
// NODE.js specific private method
obj.sendRequest = function (postdata, url, action) {
url = url ? url : '/wsman';
action = action ? action : 'POST';
var h = action + ' ' + url + ' HTTP/1.1\r\n';
if (obj.challengeParams != null) {
obj.digestRealm = obj.challengeParams['realm'];
if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) {
obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error
obj.CancelAllQueries(997);
return;
}
}
if ((obj.user == '*') && (kerberos != null)) {
// Kerberos Auth
if (obj.kerberosDone == 0) {
var ticketName = 'HTTP' + ((obj.tls == 1) ? 'S' : '') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass);
// Ask for the new Kerberos ticket
//console.log('kerberos.getTicket', ticketName);
var ticketReturn = kerberos.getTicket(ticketName);
if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) {
h += 'Authorization: Negotiate ' + ticketReturn.ticket + '\r\n';
if (process.platform.indexOf('win') >= 0) {
// Clear kerberos tickets on both 32 and 64bit Windows platforms
try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); }
}
} else {
console.log('Unexpected Kerberos error code: ' + ticketReturn.returnCode);
}
obj.kerberosDone = 1;
}
} else if (obj.challengeParams != null) {
var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams['qop'] + ':' + hex_md5(action + ':' + url + ((obj.challengeParams['qop'] == 'auth-int')?(':' + hex_md5(postdata)):'')));
h += 'Authorization: ' + obj.renderDigest({ 'username': obj.user, 'realm': obj.challengeParams['realm'], 'nonce': obj.challengeParams['nonce'], 'uri': url, 'qop': obj.challengeParams['qop'], 'response': response, 'nc': obj.noncecounter++, 'cnonce': obj.cnonce }) + '\r\n';
}
h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length
//h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding
obj.xxSend(h);
//console.log("SEND: " + h); // Display send packet
}
// Parse the HTTP digest header and return a list of key & values.
obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) }
// Split a string on quotes but do not do it when in quotes
function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st }
// NODE.js specific private method
obj.renderDigest = function (params) {
var paramsnames = [];
for (i in params) { paramsnames.push(i); }
return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + ii + '="' + params[ii] + '"' }, '').substring(1);
}
// NODE.js specific private method
obj.xxConnectHttpSocket = function () {
//obj.Debug("xxConnectHttpSocket");
obj.socketParseState = 0;
obj.socketAccumulator = '';
obj.socketHeader = null;
obj.socketData = '';
obj.socketState = 1;
obj.kerberosDone = 0;
if (obj.xtlsoptions && obj.xtlsoptions.meshServerConnect) {
// Use the websocket wrapper to connect to MeshServer server
obj.socket = CreateWebSocketWrapper(obj.xtlsoptions.host, obj.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.host) + '&p=1&tls1only=' + obj.xtlsMethod, obj.xtlsoptions.xtlsFingerprint);
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.ondata = obj.xxOnSocketData;
obj.socket.onclose = function () { if (obj.xtlsDataReceived == false) { obj.xtlsMethod = 1 - obj.xtlsMethod; } obj.xxOnSocketClosed(); }
obj.socket.ontimeout = function () { if (obj.xtlsDataReceived == false) { obj.xtlsMethod = 1 - obj.xtlsMethod; } obj.xxOnSocketClosed(); }
obj.socket.connect(obj.xxOnSocketConnected);
} else if (obj.xtls != 1) {
// Connect without TLS
obj.socket = new obj.net.Socket();
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketClosed);
obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected);
} else {
// Connect with TLS
var options = { secureProtocol: ((obj.xtlsMethod == 0) ? 'SSLv23_method' : 'TLSv1_method'), ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
if (obj.xtlsoptions) {
if (obj.xtlsoptions.ca) options.ca = obj.xtlsoptions.ca;
if (obj.xtlsoptions.cert) options.cert = obj.xtlsoptions.cert;
if (obj.xtlsoptions.key) options.key = obj.xtlsoptions.key;
obj.xtlsoptions = options;
}
obj.socket = obj.tls.connect(obj.port, obj.host, obj.xtlsoptions, obj.xxOnSocketConnected);
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketClosed);
obj.socket.on('error', function (e) { if (e.message && e.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } });
}
obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining?
}
// Get the certificate of Intel AMT
obj.getPeerCertificate = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate(); } return null; }
obj.getPeerCertificateFingerprint = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate().fingerprint.split(':').join('').toLowerCase(); } return null; }
// NODE.js specific private method
obj.xxOnSocketConnected = function () {
if (obj.socket == null) return;
if (obj.xtls == 1) {
obj.xtlsCertificate = obj.socket.getPeerCertificate();
// ###BEGIN###{Certificates}
// Setup the forge certificate check
var camatch = 0;
if (obj.xtlsoptions.ca) {
var forgeCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(atob(obj.xtlsCertificate.raw.toString('base64'))));
var caStore = forge.pki.createCaStore(obj.xtlsoptions.ca);
// Got thru all certificates in the store and look for a match.
for (var i in caStore.certs) {
if (camatch == 0) {
var c = caStore.certs[i], verified = false;
try { verified = c.verify(forgeCert); } catch (e) { }
if (verified == true) { camatch = c; }
}
}
// We found a match, check that the CommonName matches the hostname
if ((obj.xtlsSkipHostCheck == 0) && (camatch != 0)) {
amtcertname = forgeCert.subject.getField('CN').value;
if (amtcertname.toLowerCase() != obj.host.toLowerCase()) { camatch = 0; }
}
}
if ((camatch == 0) && (obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) {
obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error
obj.CancelAllQueries(998);
return;
}
if ((obj.xtlsFingerprint == 0) && (camatch == 0)) { obj.xtlsCheck = 3; } else { obj.xtlsCheck = (camatch == 0) ? 2 : 1; }
// ###END###{Certificates}
// ###BEGIN###{!Certificates}
if ((obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) {
obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error
obj.CancelAllQueries(998);
return;
}
obj.xtlsCheck = 2;
// ###END###{!Certificates}
} else { obj.xtlsCheck = 0; }
obj.socketState = 2;
obj.socketParseState = 0;
for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); }
}
// NODE.js specific private method
obj.xxOnSocketData = function (data) {
obj.xtlsDataReceived = true;
if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-RECV(" + data.length + "): " + data); }
if (typeof data === 'object') {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
data = binary;
}
else if (typeof data !== 'string') return;
obj.socketAccumulator += data;
while (true) {
//console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator);
if (obj.socketParseState == 0) {
var headersize = obj.socketAccumulator.indexOf('\r\n\r\n');
if (headersize < 0) return;
//obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n');
if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } }
obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
obj.socketParseState = 1;
obj.socketData = '';
obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') };
for (i in obj.socketHeader) {
if (i != 0) {
var x2 = obj.socketHeader[i].indexOf(':');
obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2);
}
}
}
if (obj.socketParseState == 1) {
var csize = -1;
if ((obj.socketXHeader["connection"] != undefined) && (obj.socketXHeader["connection"].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
// The body ends with a close, in this case, we will only process the header
csize = 0;
} else if (obj.socketXHeader["content-length"] != undefined) {
// The body length is specified by the content-length
csize = parseInt(obj.socketXHeader["content-length"]);
if (obj.socketAccumulator.length < csize) return;
var data = obj.socketAccumulator.substring(0, csize);
obj.socketAccumulator = obj.socketAccumulator.substring(csize);
obj.socketData = data;
csize = 0;
} else {
// The body is chunked
var clen = obj.socketAccumulator.indexOf("\r\n");
if (clen < 0) return; // Chunk length not found, exit now and get more data.
// Chunk length if found, lets see if we can get the data.
csize = parseInt(obj.socketAccumulator.substring(0, clen), 16);
if (obj.socketAccumulator.length < clen + 2 + csize + 2) return;
// We got a chunk with all of the data, handle the chunck now.
var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize);
obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2);
obj.socketData += data;
}
if (csize == 0) {
//obj.Debug("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
obj.xxProcessHttpResponse(obj.socketXHeader, obj.socketData);
obj.socketParseState = 0;
obj.socketHeader = null;
}
}
}
}
// NODE.js specific private method
obj.xxProcessHttpResponse = function (header, data) {
//obj.Debug("xxProcessHttpResponse: " + header.Directive[1]);
var s = parseInt(header.Directive[1]);
if (isNaN(s)) s = 500;
if (s == 401 && ++(obj.authcounter) < 3) {
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
if (obj.challengeParams['qop'] != null) {
var qopList = obj.challengeParams['qop'].split(',');
for (var i in qopList) { qopList[i] = qopList[i].trim(); }
if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; }
}
obj.socket.end();
} else {
var r = obj.pendingAjaxCall.shift();
if (r == null || r.length < 1) { console.log("pendingAjaxCall error, " + r); return; }
//if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work.
obj.authcounter = 0;
obj.ActiveAjaxCount--;
obj.gotNextMessages(data, 'success', { status: s }, r);
obj.PerformNextAjax();
}
}
// NODE.js specific private method
obj.xxOnSocketClosed = function (data) {
//obj.Debug("xxOnSocketClosed");
obj.socketState = 0;
if (obj.socket != null) { obj.socket.destroy(); obj.socket = null; }
if (obj.pendingAjaxCall.length > 0) {
var r = obj.pendingAjaxCall.shift();
var retry = r[5];
setTimeout(function () { obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry) }, 500); // Wait half a second and try again
}
}
// NODE.js specific private method
obj.xxSend = function (x) {
if (obj.socketState == 2) {
if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-SEND(" + x.length + "): " + x); }
obj.socket.write(new Buffer(x, 'binary'));
}
}
// Cancel all pending queries with given status
obj.CancelAllQueries = function (s) {
obj.FailAllError = s;
while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
if (obj.socket != null) { obj.socket.end(); obj.socket = null; obj.socketState = 0; }
}
// Private method
obj.gotNextMessages = function (data, status, request, callArgs) {
if (obj.FailAllError == 999) return;
if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; }
if (request.status != 200) { try { callArgs[1](null, request.status, callArgs[2]); } catch (ex) { console.error(ex); } return; }
try { callArgs[1](data, 200, callArgs[2]); } catch (ex) { console.error(ex); }
}
// Private method
obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
if (obj.FailAllError == 999) return;
if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; }
try { callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]); } catch (ex) { console.error(ex); }
}
return obj;
}

315
amt-wsman-ws-0.2.0.js Normal file
View File

@@ -0,0 +1,315 @@
/**
* @description WSMAN communication using websocket
* @author Ylian Saint-Hilaire
* @version v0.2.0c
*/
// Construct a WSMAN communication object
var CreateWsmanComm = function (host, port, user, pass, tls) {
var obj = {};
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time.
obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
obj.challengeParams = null;
obj.noncecounter = 1;
obj.authcounter = 0;
obj.socket = null;
obj.socketState = 0;
obj.host = host;
obj.port = port;
obj.user = user;
obj.pass = pass;
obj.tls = tls;
obj.tlsv1only = 0;
obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce
obj.inDataCount = 0;
obj.amtVersion = null;
obj.digestRealmMatch = null;
obj.digestRealm = null;
// Private method
//obj.Debug = function (msg) { console.log(msg); }
// Private method
// pri = priority, if set to 1, the call is high priority and put on top of the stack.
obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
if (obj.ActiveAjaxCount < obj.MaxActiveAjaxCount && obj.PendingAjax.length == 0) {
// There are no pending AJAX calls, perform the call now.
obj.PerformAjaxEx(postdata, callback, tag, url, action);
} else {
// If this is a high priority call, put this call in front of the array, otherwise put it in the back.
if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
}
}
// Private method
obj.PerformNextAjax = function () {
if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
var x = obj.PendingAjax.shift();
obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
obj.PerformNextAjax();
}
// Private method
obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; }
if (!postdata) postdata = "";
//console.log("SEND: " + postdata); // DEBUG
// We are in a websocket relay environment
obj.ActiveAjaxCount++;
return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action);
}
// Websocket relay specific private method
obj.pendingAjaxCall = [];
// Websocket relay specific private method
obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 5); }
// Websocket relay specific private method
obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) {
//console.log('PerformAjaxExNodeJS2', postdata, retry);
if (retry <= 0 || obj.FailAllError != 0) {
// Too many retry, fail here.
obj.ActiveAjaxCount--;
if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error
obj.PerformNextAjax();
return;
}
obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]);
if (obj.socketState == 0) { obj.xxConnectHttpSocket(); }
else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); }
}
// Websocket relay specific private method (Content Length Encoding)
obj.sendRequest = function (postdata, url, action) {
url = url ? url : "/wsman";
action = action ? action : "POST";
var h = action + " " + url + " HTTP/1.1\r\n";
if (obj.challengeParams != null) {
obj.digestRealm = obj.challengeParams["realm"];
if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) {
obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error
obj.CancelAllQueries(997);
return;
}
var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams['qop'] + ':' + hex_md5(action + ':' + url + ((obj.challengeParams['qop'] == 'auth-int') ? (':' + hex_md5(postdata)) : '')));
h += 'Authorization: ' + obj.renderDigest({ 'username': obj.user, 'realm': obj.challengeParams['realm'], 'nonce': obj.challengeParams['nonce'], 'uri': url, 'qop': obj.challengeParams['qop'], 'response': response, 'nc': obj.noncecounter++, 'cnonce': obj.cnonce }) + '\r\n';
}
h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length
//h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding
_Send(h);
}
// Parse the HTTP digest header and return a list of key & values.
obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) }
// Split a string on quotes but do not do it when in quotes
function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st }
// Websocket relay specific private method
obj.renderDigest = function (params) {
var paramsnames = [];
for (i in params) { paramsnames.push(i); }
return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + ii + '="' + params[ii] + '"' }, '').substring(1);
}
// Websocket relay specific private method
obj.xxConnectHttpSocket = function () {
//obj.Debug("xxConnectHttpSocket");
obj.inDataCount = 0;
obj.socketState = 1;
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=1&host=" + obj.host + "&port=" + obj.port + "&tls=" + obj.tls + "&tls1only=" + obj.tlsv1only + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "")); // The "p=1" indicates to the relay that this is a WSMAN session
obj.socket.onopen = _OnSocketConnected;
obj.socket.onmessage = _OnMessage;
obj.socket.onclose = _OnSocketClosed;
}
// Websocket relay specific private method
function _OnSocketConnected() {
obj.socketState = 2;
obj.socketParseState = 0;
obj.socketAccumulator = '';
obj.socketHeader = null;
obj.socketData = '';
//console.log("xxOnSocketConnected");
for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); }
}
// Setup the file reader
var fileReader = new FileReader();
var fileReaderInuse = false, fileReaderAcc = [];
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReader.onload = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReader.onloadend = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
}
function _OnMessage(e) {
if (typeof e.data == 'object') {
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
if (fileReader.readAsBinaryString) {
// Chrome & Firefox (Draft)
fileReaderInuse = true;
fileReader.readAsBinaryString(new Blob([e.data]));
} else if (fileReader.readAsArrayBuffer) {
// Chrome & Firefox (Spec)
fileReaderInuse = true;
fileReader.readAsArrayBuffer(e.data);
} else {
// IE10, readAsBinaryString does not exist, use an alternative.
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
_OnSocketData(binary);
}
} else {
_OnSocketData(e.data);
}
};
// Websocket relay specific private method
function _OnSocketData(data) {
//obj.Debug("_OnSocketData (" + data.length + "): " + data);
if (typeof data === 'object') {
// This is an ArrayBuffer, convert it to a string array (used in IE)
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
data = binary;
}
else if (typeof data !== 'string') return;
//console.log("RECV(" + obj.socketParseState + "): " + data); // DEBUG
obj.socketAccumulator += data;
while (true) {
if (obj.socketParseState == 0) {
var headersize = obj.socketAccumulator.indexOf("\r\n\r\n");
if (headersize < 0) return;
//obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split("\r\n");
if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } }
obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
obj.socketParseState = 1;
obj.socketData = '';
obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') };
//console.log("Header", obj.socketXHeader);
for (i in obj.socketHeader) {
if (i != 0) {
var x2 = obj.socketHeader[i].indexOf(':');
obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2);
}
}
}
if (obj.socketParseState == 1) {
var csize = -1;
if ((obj.socketXHeader['connection'] != undefined) && (obj.socketXHeader['connection'].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
// The body ends with a close, in this case, we will only process the header
csize = 0;
} else if (obj.socketXHeader['content-length'] != undefined) {
// The body length is specified by the content-length
csize = parseInt(obj.socketXHeader['content-length']);
if (obj.socketAccumulator.length < csize) return;
var data = obj.socketAccumulator.substring(0, csize);
obj.socketAccumulator = obj.socketAccumulator.substring(csize);
obj.socketData = data;
csize = 0;
} else {
// The body is chunked
var clen = obj.socketAccumulator.indexOf('\r\n');
if (clen < 0) return; // Chunk length not found, exit now and get more data.
// Chunk length if found, lets see if we can get the data.
csize = parseInt(obj.socketAccumulator.substring(0, clen), 16);
if (isNaN(csize)) { if (obj.websocket) { obj.websocket.close(); } return; } // Critical error, close the socket and exit.
if (obj.socketAccumulator.length < clen + 2 + csize + 2) return;
// We got a chunk with all of the data, handle the chunck now.
var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize);
obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2);
obj.socketData += data;
}
if (csize == 0) {
//obj.Debug("_OnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
_ProcessHttpResponse(obj.socketXHeader, obj.socketData);
obj.socketParseState = 0;
obj.socketHeader = null;
}
}
}
}
// Websocket relay specific private method
function _ProcessHttpResponse(header, data) {
//obj.Debug("_ProcessHttpResponse: " + header.Directive[1]);
var s = parseInt(header.Directive[1]);
if (isNaN(s)) {
s = 602;
}
if (s == 401 && ++(obj.authcounter) < 3) {
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
if (obj.challengeParams['qop'] != null) {
var qopList = obj.challengeParams['qop'].split(',');
for (var i in qopList) { qopList[i] = qopList[i].trim(); }
if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; }
}
} else {
var r = obj.pendingAjaxCall.shift();
// if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work.
obj.authcounter = 0;
obj.ActiveAjaxCount--;
obj.gotNextMessages(data, 'success', { status: s }, r);
obj.PerformNextAjax();
}
}
// Websocket relay specific private method
function _OnSocketClosed(data) {
//console.log("_OnSocketClosed");
if (obj.inDataCount == 0) { obj.tlsv1only = (1 - obj.tlsv1only); }
obj.socketState = 0;
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
if (obj.pendingAjaxCall.length > 0) {
var r = obj.pendingAjaxCall.shift();
var retry = r[5];
obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry);
}
}
// Websocket relay specific private method
function _Send(x) {
//console.log("SEND: " + x); // DEBUG
if (obj.socketState == 2 && obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
var b = new Uint8Array(x.length);
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
try { obj.socket.send(b.buffer); } catch (e) { }
}
}
// Private method
obj.gotNextMessages = function (data, status, request, callArgs) {
if (obj.FailAllError == 999) return;
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; }
callArgs[1](data, 200, callArgs[2]);
}
// Private method
obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
if (obj.FailAllError == 999) return;
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]);
}
// Cancel all pending queries with given status
obj.CancelAllQueries = function (s) {
while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
if (obj.websocket != null) { obj.websocket.close(); obj.websocket = null; obj.socketState = 0; }
}
return obj;
}

BIN
certificate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

578
closure-externs/crypto.js Normal file
View File

@@ -0,0 +1,578 @@
/*
* Copyright 2012 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Definitions for node's crypto module. Depends on the buffer module.
* @see http://nodejs.org/api/crypto.html
* @see https://github.com/joyent/node/blob/master/lib/crypto.js
* @externs
* @author Daniel Wirtz <dcode@dcode.io>
*/
/**
BEGIN_NODE_INCLUDE
var crypto = require('crypto');
END_NODE_INCLUDE
*/
/**
* @type {Object.<string,*>}
*/
var crypto = {};
/**
* @type {string}
*/
crypto.DEFAULT_ENCODING;
/**
* @typedef {{pfx: (string|buffer.Buffer), key: (string|buffer.Buffer), passphrase: string, cert: (string|buffer.Buffer), ca: Array.<string|buffer.Buffer>, crl: (string|Array.<string>), ciphers: string}}
*/
crypto.Credentials;
/**
* @param {Object.<string,string>=} details
* @return {crypto.Credentials}
*/
crypto.createCredentials =
function(details) {};
/**
* @param {string} algorithm
* @return {crypto.Hash}
*/
crypto.createHash =
function(algorithm) {};
/**
* @param {string} algorithm
* @param {Object=} options
* @constructor
* @extends stream.Transform
*/
crypto.Hash =
function(algorithm, options) {};
/**
* @param {string|buffer.Buffer} data
* @param {string=} input_encoding
*/
crypto.Hash.prototype.update =
function(data, input_encoding) {};
/**
* @param {string=} encoding
* @return {string}
*/
crypto.Hash.prototype.digest =
function(encoding) {};
/**
* @param {string} algorithm
* @param {string|buffer.Buffer} key
* @return {crypto.Hmac}
*/
crypto.createHmac =
function(algorithm, key) {};
/**
* @param {string} hmac
* @param {string|buffer.Buffer} key
* @param {Object=} options
* @constructor
* @extends stream.Transform
*/
crypto.Hmac =
function(hmac, key, options) {};
/**
* @param {string|buffer.Buffer} data
*/
crypto.Hmac.prototype.update =
function(data) {};
/**
* @param {string} encoding
*/
crypto.Hmac.prototype.digest =
function(encoding) {};
/**
* @param {string} algorithm
* @param {string|buffer.Buffer} password
* @return {crypto.Cipher}
*/
crypto.createCipher =
function(algorithm, password) {};
/**
* @param {string} algorithm
* @param {string|buffer.Buffer} key
* @param {string|buffer.Buffer} iv
* @return {crypto.Cipheriv}
*/
crypto.createCipheriv =
function(algorithm, key, iv) {};
/**
* @param {string|buffer.Buffer} cipher
* @param {string} password
* @param {Object=} options
* @constructor
* @extends stream.Transform
*/
crypto.Cipher =
function(cipher, password, options) {};
/**
* @param {string|buffer.Buffer} data
* @param {string=} input_encoding
* @param {string=} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Cipher.prototype.update =
function(data, input_encoding, output_encoding) {};
/**
* @name crypto.Cipher.prototype.final
* @param {string} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Cipher.prototype['final'] =
function(output_encoding) {};
/**
* @param {boolean=} auto_padding
*/
crypto.Cipher.prototype.setAutoPadding =
function(auto_padding) {};
/**
* Note: Cipheriv mixes update, final, and setAutoPadding from Cipher but
* doesn't inherit directly from Cipher.
*
* @param {string} cipher
* @param {string|buffer.Buffer} key
* @param {string|buffer.Buffer} iv
* @constructor
* @extends stream.Transform
*/
crypto.Cipheriv =
function(cipher, key, iv) {};
/**
* @param {string|buffer.Buffer} data
* @param {string=} input_encoding
* @param {string=} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Cipheriv.prototype.update =
function(data, input_encoding, output_encoding) {};
/**
* @name crypto.Cipheriv.prototype.final
* @param {string} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Cipheriv.prototype['final'] =
function(output_encoding) {};
/**
* @param {boolean=} auto_padding
*/
crypto.Cipheriv.prototype.setAutoPadding =
function(auto_padding) {};
/**
* @param {string} algorithm
* @param {string|buffer.Buffer} password
* @return {crypto.Decipher}
*/
crypto.createDecipher =
function(algorithm, password) {};
/**
* @param {string} algorithm
* @param {string|buffer.Buffer} key
* @param {string|buffer.Buffer} iv
* @return {crypto.Decipheriv}
*/
crypto.createDecipheriv =
function(algorithm, key, iv) {};
/**
* Note: Decipher mixes update, final, and setAutoPadding from Cipher but
* doesn't inherit directly from Cipher.
*
* @param {string|buffer.Buffer} cipher
* @param {string|buffer.Buffer} password
* @param {Object=} options
* @constructor
* @extends stream.Transform
*/
crypto.Decipher =
function(cipher, password, options) {}
/**
* @param {string|buffer.Buffer} data
* @param {string=} input_encoding
* @param {string=} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Decipher.prototype.update =
function(data, input_encoding, output_encoding) {};
/**
* @name crypto.Decipher.prototype.final
* @param {string} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Decipher.prototype['final'] =
function(output_encoding) {};
/**
* @param {string} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Decipher.prototype.finaltol =
function(output_encoding) {};
/**
* @param {boolean=} auto_padding
*/
crypto.Decipher.prototype.setAutoPadding =
function(auto_padding) {};
/**
* Note: Decipheriv mixes update, final, and setAutoPadding from Cipher but
* doesn't inherit directly from Cipher.
*
* @param {string|buffer.Buffer|crypto.Decipheriv} cipher
* @param {string|buffer.Buffer} key
* @param {string|buffer.Buffer} iv
* @param {Object=} options
* @constructor
* @extends stream.Transform
*/
crypto.Decipheriv =
function(cipher, key, iv, options) {};
/**
* @param {string|buffer.Buffer} data
* @param {string=} input_encoding
* @param {string=} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Decipheriv.prototype.update =
function(data, input_encoding, output_encoding) {};
/**
* @name crypto.Decipheriv.prototype.final
* @param {string} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Decipheriv.prototype['final'] =
function(output_encoding) {};
/**
* @param {string} output_encoding
* @return {string|buffer.Buffer}
*/
crypto.Decipheriv.prototype.finaltol =
function(output_encoding) {};
/**
* @param {boolean=} auto_padding
*/
crypto.Decipheriv.prototype.setAutoPadding =
function(auto_padding) {};
/**
* @param {string} algorithm
* @return {crypto.Sign}
*/
crypto.createSign =
function(algorithm) {};
/**
* @param {string} algorithm
* @param {Object=} options
* @constructor
* @extends stream.Writable
*/
crypto.Sign =
function(algorithm, options) {};
/**
* @param {string|buffer.Buffer} data
*/
crypto.Sign.prototype.update =
function(data) {};
/**
* @param {string} private_key
* @param {string=} output_format
* @return {string|buffer.Buffer}
*/
crypto.Sign.prototype.sign =
function(private_key, output_format) {};
/**
* @param {string} algorithm
* @return crypto.Verify
*/
crypto.createVerify =
function(algorithm) {};
/**
* @param {string} algorithm
* @param {Object=} options
* @constructor
* @extends stream.Writable
*/
crypto.Verify =
function(algorithm, options) {};
/**
* @param {string|buffer.Buffer} data
*/
crypto.Verify.prototype.update =
function(data) {};
/**
* @param {string} object
* @param {string|buffer.Buffer} signature
* @param {string=} signature_format
* @return {boolean}
*/
crypto.Verify.prototype.verify =
function(object, signature, signature_format) {};
/**
* @param {number} prime
* @param {string=} encoding
* @return {crypto.DiffieHellman}
*/
crypto.createDiffieHellman =
function(prime, encoding) {};
/**
* @param {number} sizeOrKey
* @param {string=} encoding
* @constructor
*/
crypto.DiffieHellman =
function(sizeOrKey, encoding) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellman.prototype.generateKeys =
function(encoding) {};
/**
* @param {string|buffer.Buffer} key
* @param {string=} inEnc
* @param {string=} outEnc
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellman.prototype.computeSecret =
function(key, inEnc, outEnc) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellman.prototype.getPrime =
function(encoding) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellman.prototype.getGenerator =
function(encoding) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellman.prototype.getPublicKey =
function(encoding) {};
/**
* @param {string} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellman.prototype.getPrivateKey =
function(encoding) {}
/**
* @param {string|buffer.Buffer} key
* @param {string=} encoding
* @return {crypto.DiffieHellman}
*/
crypto.DiffieHellman.prototype.setPublicKey =
function(key, encoding) {};
/**
* @param {string|buffer.Buffer} key
* @param {string=} encoding
* @return {crypto.DiffieHellman}
*/
crypto.DiffieHellman.prototype.setPrivateKey =
function(key, encoding) {};
/**
* Note: DiffieHellmanGroup mixes DiffieHellman but doesn't inherit directly.
*
* @param {string} name
* @constructor
*/
crypto.DiffieHellmanGroup =
function(name) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellmanGroup.prototype.generateKeys =
function(encoding) {};
/**
* @param {string|buffer.Buffer} key
* @param {string=} inEnc
* @param {string=} outEnc
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellmanGroup.prototype.computeSecret =
function(key, inEnc, outEnc) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellmanGroup.prototype.getPrime =
function(encoding) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellmanGroup.prototype.getGenerator =
function(encoding) {};
/**
* @param {string=} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellmanGroup.prototype.getPublicKey =
function(encoding) {};
/**
* @param {string} encoding
* @return {string|buffer.Buffer}
*/
crypto.DiffieHellmanGroup.prototype.getPrivateKey =
function(encoding) {}
/**
* @param {string|buffer.Buffer} key
* @param {string=} encoding
* @return {crypto.DiffieHellmanGroup}
*/
crypto.DiffieHellmanGroup.prototype.setPublicKey =
function(key, encoding) {};
/**
* @param {string|buffer.Buffer} key
* @param {string=} encoding
* @return {crypto.DiffieHellmanGroup}
*/
crypto.DiffieHellmanGroup.prototype.setPrivateKey =
function(key, encoding) {};
/**
* @param {string} group_name
* @return {crypto.DiffieHellmanGroup}
*/
crypto.getDiffieHellman =
function(group_name) {};
/**
* @param {string|buffer.Buffer} password
* @param {string|buffer.Buffer} salt
* @param {number} iterations
* @param {number} keylen
* @param {
function(*, string)} callback
*/
crypto.pbkdf2 =
function(password, salt, iterations, keylen, callback) {};
/**
* @param {string|buffer.Buffer} password
* @param {string|buffer.Buffer} salt
* @param {number} iterations
* @param {number} keylen
*/
crypto.pbkdf2Sync =
function(password, salt, iterations, keylen) {};
/**
* @param {number} size
* @param {
function(Error, buffer.Buffer)=} callback
*/
crypto.randomBytes =
function(size, callback) {};
/**
* @param {number} size
* @param {
function(Error, buffer.Buffer)=} callback
*/
crypto.pseudoRandomBytes =
function(size, callback) {};
/**
* @param {number} size
* @param {
function(Error, buffer.Buffer)=} callback
*/
crypto.rng =
function(size, callback) {};
/**
* @param {number} size
* @param {
function(Error, buffer.Buffer)=} callback
*/
crypto.prng =
function(size, callback) {};
/**
* @return {Array.<string>}
*/
crypto.getCiphers =
function() {};
/**
* @return {Array.<string>}
*/
crypto.getHashes =
function() {};

249
closure-externs/net.js Normal file
View File

@@ -0,0 +1,249 @@
/*
* Copyright 2012 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Definitions for node's net module. Depends on the events and buffer modules.
* @see http://nodejs.org/api/net.html
* @see https://github.com/joyent/node/blob/master/lib/net.js
* @externs
* @author Daniel Wirtz <dcode@dcode.io>
*/
/**
BEGIN_NODE_INCLUDE
var net = require('net');
END_NODE_INCLUDE
*/
/**
* @type {Object.<string,*>}
*/
var net = {};
/**
* @typedef {{allowHalfOpen: ?boolean}}
*/
net.CreateOptions;
/**
* @param {(net.CreateOptions|
function(...))=} options
* @param {
function(...)=} connectionListener
* @return {net.Server}
*/
net.createServer =
function(options, connectionListener) {};
/**
* @typedef {{port: ?number, host: ?string, localAddress: ?string, path: ?string, allowHalfOpen: ?boolean}}
*/
net.ConnectOptions;
/**
* @param {net.ConnectOptions|number|string} arg1
* @param {(
function(...)|string)=} arg2
* @param {
function(...)=} arg3
*/
net.connect =
function(arg1, arg2, arg3) {};
/**
* @param {net.ConnectOptions|number|string} arg1
* @param {(
function(...)|string)=} arg2
* @param {
function(...)=} arg3
*/
net.createConnection =
function(arg1, arg2, arg3) {};
/**
* @constructor
* @extends events.EventEmitter
*/
net.Server =
function() {};
/**
*
* @param {number|*} port
* @param {(string|number|
function(...))=} host
* @param {(number|
function(...))=} backlog
* @param {
function(...)=} callback
*/
net.Server.prototype.listen =
function(port, host, backlog, callback) {};
/**
* @param {
function(...)=} callback
*/
net.Server.prototype.close =
function(callback) {};
/**
* @return {{port: number, family: string, address: string}}
*/
net.Server.prototype.address =
function() {};
/**
* @type {number}
*/
net.Server.prototype.maxConnectinos;
/**
* @type {number}
*/
net.Server.prototype.connections;
/**
* @constructor
* @param {{fd: ?*, type: ?string, allowHalfOpen: ?boolean}=} options
* @extends events.EventEmitter
*/
net.Socket =
function(options) {};
/**
* @param {number|string|
function(...)} port
* @param {(string|
function(...))=} host
* @param {
function(...)=} connectListener
*/
net.Socket.prototype.connect =
function(port, host, connectListener) {};
/**
* @type {number}
*/
net.Socket.prototype.bufferSize;
/**
* @param {?string=} encoding
*/
net.Socket.prototype.setEncoding =
function (encoding) { };
net.Socket.prototype.on =
function (str, func) { };
/**
* @param {string|buffer.Buffer} data
* @param {(string|
function(...))=}encoding
* @param {
function(...)=} callback
*/
net.Socket.prototype.write =
function(data, encoding, callback) {};
/**
* @param {(string|buffer.Buffer)=}data
* @param {string=} encoding
*/
net.Socket.prototype.end =
function(data, encoding) {};
/**
*/
net.Socket.prototype.destroy =
function() {};
/**
*/
net.Socket.prototype.pause =
function() {};
/**
*/
net.Socket.prototype.resume =
function() {};
/**
* @param {number} timeout
* @param {
function(...)=} callback
*/
net.Socket.prototype.setTimeout =
function(timeout, callback) {};
/**
* @param {boolean=} noDelay
*/
net.Socket.prototype.setNoDelay =
function(noDelay) {};
/**
* @param {(boolean|number)=} enable
* @param {number=} initialDelay
*/
net.Socket.prototype.setKeepAlive =
function(enable, initialDelay) {};
/**
* @return {string}
*/
net.Socket.prototype.address =
function() {};
/**
* @type {?string}
*/
net.Socket.prototype.remoteAddress;
/**
* @type {?number}
*/
net.Socket.prototype.remotePort;
/**
* @type {number}
*/
net.Socket.prototype.bytesRead;
/**
* @type {number}
*/
net.Socket.prototype.bytesWritten;
/**
* @param {*} input
* @return {number}
*/
net.isIP =
function(input) {};
/**
* @param {*} input
* @return {boolean}
*/
net.isIPv4 =
function(input) {};
/**
* @param {*} input
* @return {boolean}
*/
net.isIPv6 =
function(input) {};

144
closure-externs/tls.js Normal file
View File

@@ -0,0 +1,144 @@
/*
* Copyright 2012 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Definitions for node's tls module. Depends on the stream module.
* @see http://nodejs.org/api/tls.html
* @see https://github.com/joyent/node/blob/master/lib/tls.js
* @externs
* @author Daniel Wirtz <dcode@dcode.io>
*/
/**
BEGIN_NODE_INCLUDE
var tls = require('tls');
END_NODE_INCLUDE
*/
/**
* @type {Object.<string,*>}
*/
var tls = {};
/**
* @typedef {{pfx: (string|buffer.Buffer), key: (string|buffer.Buffer), passphrase: string, cert: (string|buffer.Buffer), ca: Array.<string|buffer.Buffer>, crl: (string|Array.<string>), ciphers: string, honorCipherOrder: boolean, requestCert: boolean, rejectUnauthorized: boolean, NPNProtocols: (Array|buffer.Buffer), SNICallback:
function(string), sessionIdContext: string}}
*/
tls.CreateOptions;
/**
*
* @param {tls.CreateOptions} options
* @param {
function(...)=} secureConnectionListener
* @return {tls.Server}
*/
tls.createServer =
function(options, secureConnectionListener) {};
/**
* @typedef {{host: string, port: number, socket: *, pfx: (string|buffer.Buffer), key: (string|buffer.Buffer), passphrase: string, cert: (string|buffer.Buffer), ca: Array.<string>, rejectUnauthorized: boolean, NPNProtocols: Array.<string|buffer.Buffer>, servername: string}}
*/
tls.ConnectOptions;
/**
*
* @param {number|tls.ConnectOptions} port
* @param {(string|tls.ConnectOptions|
function(...))=} host
* @param {(tls.ConnectOptions|
function(...))=} options
* @param {
function(...)=} callback
*/
tls.connect =
function(port, host, options, callback) {};
/**
* @param {crypto.Credentials=} credentials
* @param {boolean=} isServer
* @param {boolean=} requestCert
* @param {boolean=} rejectUnauthorized
* @return {tls.SecurePair}
*/
tls.createSecurePair =
function(credentials, isServer, requestCert, rejectUnauthorized) {};
/**
* @constructor
* @extends events.EventEmitter
*/
tls.SecurePair =
function() {};
/**
* @constructor
* @extends net.Server
*/
tls.Server =
function() {};
/**
* @param {string} hostname
* @param {string|buffer.Buffer} credentials
*/
tls.Server.prototype.addContext =
function(hostname, credentials) {};
/**
* @constructor
* @extends stream.Duplex
*/
tls.CleartextStream =
function() {};
/**
* @type {boolean}
*/
tls.CleartextStream.prototype.authorized;
/**
* @type {?string}
*/
tls.CleartextStream.prototype.authorizationError;
/**
* @return {Object.<string,(string|Object.<string,string>)>}
*/
tls.CleartextStream.prototype.getPeerCertificate =
function() {};
/**
* @return {{name: string, version: string}}
*/
tls.CleartextStream.prototype.getCipher =
function() {};
/**
* @return {{port: number, family: string, address: string}}
*/
tls.CleartextStream.prototype.address =
function() {};
/**
* @type {string}
*/
tls.CleartextStream.prototype.remoteAddress;
/**
* @type {number}
*/
tls.CleartextStream.prototype.remotePort;

105
common-0.0.1.js Normal file
View File

@@ -0,0 +1,105 @@
/**
* @description Set of short commonly used methods for handling HTML elements
* @author Ylian Saint-Hilaire
* @version v0.0.1b
*/
// Add startsWith for IE browser
if (!String.prototype.startsWith) { String.prototype.startsWith = function (str) { return this.lastIndexOf(str, 0) === 0; }; }
if (!String.prototype.endsWith) { String.prototype.endsWith = function (str) { return this.indexOf(str, this.length - str.length) !== -1; }; }
// Quick UI functions, a bit of a replacement for jQuery
//function Q(x) { if (document.getElementById(x) == null) { console.log('Invalid element: ' + x); } return document.getElementById(x); } // "Q"
function Q(x) { return document.getElementById(x); } // "Q"
function QS(x) { try { return Q(x).style; } catch (x) { } } // "Q" style
function QE(x, y) { try { Q(x).disabled = !y; } catch (x) { } } // "Q" enable
function QV(x, y) { try { QS(x).display = (y ? '' : 'none'); } catch (x) { } } // "Q" visible
function QA(x, y) { Q(x).innerHTML += y; } // "Q" append
function QH(x, y) { Q(x).innerHTML = y; } // "Q" html
// Move cursor to end of input box
function inputBoxFocus(x) { Q(x).focus(); var v = Q(x).value; Q(x).value = ''; Q(x).value = v; }
// Binary encoding and decoding functions
function ReadShort(v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); }
function ReadShortX(v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
function ReadSInt(v, p) { return (v.charCodeAt(p) << 24) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }
function ReadIntX(v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
function ShortToStr(v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }
function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }
function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
function MakeToArray(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; }
function SplitArray(v) { return v.split(','); }
function Clone(v) { return JSON.parse(JSON.stringify(v)); }
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;'); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
// Move an element from one position in an array to a new position
function ArrayElementMove(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
// Print object for HTML
function ObjectToStringEx(x, c) {
var r = "";
if (x != 0 && (!x || x == null)) return "(Null)";
if (x instanceof Array) { for (var i in x) { r += '<br />' + gap(c) + "Item #" + i + ": " + ObjectToStringEx(x[i], c + 1); } }
else if (x instanceof Object) { for (var i in x) { r += '<br />' + gap(c) + i + " = " + ObjectToStringEx(x[i], c + 1); } }
else { r += EscapeHtml(x); }
return r;
}
// Print object for console
function ObjectToStringEx2(x, c) {
var r = "";
if (x != 0 && (!x || x == null)) return "(Null)";
if (x instanceof Array) { for (var i in x) { r += '\r\n' + gap2(c) + "Item #" + i + ": " + ObjectToStringEx2(x[i], c + 1); } }
else if (x instanceof Object) { for (var i in x) { r += '\r\n' + gap2(c) + i + " = " + ObjectToStringEx2(x[i], c + 1); } }
else { r += EscapeHtml(x); }
return r;
}
// Create an ident gap
function gap(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += '&nbsp;'; } return x; }
function gap2(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
// Print an object in html
function ObjectToString(x) { return ObjectToStringEx(x, 0); }
function ObjectToString2(x) { return ObjectToStringEx2(x, 0); }
// Convert a hex string to a raw string
function hex2rstr(d) {
if (typeof d != "string" || d.length == 0) return '';
var r = '', m = ('' + d).match(/../g), t;
while (t = m.shift()) r += String.fromCharCode('0x' + t);
return r
}
// Convert decimal to hex
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
// Convert a raw string to a hex string
function rstr2hex(input) {
var r = '', i;
for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); }
return r;
}
// UTF-8 encoding & decoding functions
function encode_utf8(s) { return unescape(encodeURIComponent(s)); }
function decode_utf8(s) { return decodeURIComponent(escape(s)); }
// Convert a string into a blob
function data2blob(data) {
var bytes = new Array(data.length);
for (var i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i);
var blob = new Blob([new Uint8Array(bytes)]);
return blob;
}
// Generate random numbers
function random(max) { return Math.floor(Math.random() * max); }
// Trademarks
function trademarks(x) { return x.replace(/\(R\)/g, '&reg;').replace(/\(TM\)/g, '&trade;'); }

2
desktop.ini Normal file
View File

@@ -0,0 +1,2 @@
[LocalizedFileNames]
Changes.txt=@Changes.txt,0

BIN
favicon-intel.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
favicon-intel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

270
filesaver.1.1.20151003.js Normal file
View File

@@ -0,0 +1,270 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.1.20151003
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs || (function (view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function () {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function (node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
}
, is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent)
, webkit_req_fs = view.webkitRequestFileSystem
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
, throw_outside = function (ex) {
(view.setImmediate || view.setTimeout)(function () {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
// for the reasoning behind the timeout and revocation flow
, arbitrary_revoke_timeout = 500 // in ms
, revoke = function (file) {
var revoker = function () {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
if (view.chrome) {
revoker();
} else {
setTimeout(revoker, arbitrary_revoke_timeout);
}
}
, dispatch = function (filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, auto_bom = function (blob) {
// prepend BOM for UTF-8 XML and text types (including HTML)
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob(["\ufeff", blob], { type: blob.type });
}
return blob;
}
, FileSaver = function (blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, blob_changed = false
, object_url
, target_view
, dispatch_all = function () {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function () {
if (target_view && is_safari && typeof FileReader !== "undefined") {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function () {
var base64Data = reader.result;
target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));
filesaver.readyState = filesaver.DONE;
dispatch_all();
};
reader.readAsDataURL(blob);
filesaver.readyState = filesaver.INIT;
return;
}
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_URL().createObjectURL(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
var new_tab = view.open(object_url, "_blank");
if (new_tab == undefined && is_safari) {
//Apple do not allow window.open, see http://bit.ly/1kZffRI
view.location.href = object_url
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
, abortable = function (func) {
return function () {
if (filesaver.readyState !== filesaver.DONE) {
return func.apply(this, arguments);
}
};
}
, create_if_not_found = { create: true, exclusive: false }
, slice
;
filesaver.readyState = filesaver.INIT;
if (!name) {
name = "download";
}
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob);
save_link.href = object_url;
save_link.download = name;
setTimeout(function () {
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
});
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
// Update: Google errantly closed 91158, I submitted it again:
// https://code.google.com/p/chromium/issues/detail?id=389642
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
blob_changed = true;
}
// Since I can't be sure that the guessed media type will trigger a download
// in WebKit, I append .download to the filename.
// https://bugs.webkit.org/show_bug.cgi?id=65440
if (webkit_req_fs && name !== "download") {
name += ".download";
}
if (type === force_saveable_type || webkit_req_fs) {
target_view = view;
}
if (!req_fs) {
fs_error();
return;
}
fs_min_size += blob.size;
req_fs(view.TEMPORARY, fs_min_size, abortable(function (fs) {
fs.root.getDirectory("saved", create_if_not_found, abortable(function (dir) {
var save = function () {
dir.getFile(name, create_if_not_found, abortable(function (file) {
file.createWriter(abortable(function (writer) {
writer.onwriteend = function (event) {
target_view.location.href = file.toURL();
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
revoke(file);
};
writer.onerror = function () {
var error = writer.error;
if (error.code !== error.ABORT_ERR) {
fs_error();
}
};
"writestart progress write abort".split(" ").forEach(function (event) {
writer["on" + event] = filesaver["on" + event];
});
writer.write(blob);
filesaver.abort = function () {
writer.abort();
filesaver.readyState = filesaver.DONE;
};
filesaver.readyState = filesaver.WRITING;
}), fs_error);
}), fs_error);
};
dir.getFile(name, { create: false }, abortable(function (file) {
// delete file if it already exists
file.remove();
save();
}), abortable(function (ex) {
if (ex.code === ex.NOT_FOUND_ERR) {
save();
} else {
fs_error();
}
}));
}), fs_error);
}), fs_error);
}
, FS_proto = FileSaver.prototype
, saveAs = function (blob, name, no_auto_bom) {
return new FileSaver(blob, name, no_auto_bom);
}
;
// IE 10+ (native saveAs)
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function (blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
return navigator.msSaveOrOpenBlob(blob, name || "download");
};
}
FS_proto.abort = function () {
var filesaver = this;
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "abort");
};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
define([], function () {
return saveAs;
});
}

1147
forge.js/aes.js Normal file

File diff suppressed because it is too large Load Diff

338
forge.js/aesCipherSuites.js Normal file
View File

@@ -0,0 +1,338 @@
/**
* A Javascript implementation of AES Cipher Suites for TLS.
*
* @author Dave Longley
*
* Copyright (c) 2009-2015 Digital Bazaar, Inc.
*
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var tls = forge.tls;
/**
* Supported cipher suites.
*/
tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = {
id: [0x00,0x2f],
name: 'TLS_RSA_WITH_AES_128_CBC_SHA',
initSecurityParameters: function(sp) {
sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
sp.cipher_type = tls.CipherType.block;
sp.enc_key_length = 16;
sp.block_length = 16;
sp.fixed_iv_length = 16;
sp.record_iv_length = 16;
sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;
sp.mac_length = 20;
sp.mac_key_length = 20;
},
initConnectionState: initConnectionState
};
tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = {
id: [0x00,0x35],
name: 'TLS_RSA_WITH_AES_256_CBC_SHA',
initSecurityParameters: function(sp) {
sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
sp.cipher_type = tls.CipherType.block;
sp.enc_key_length = 32;
sp.block_length = 16;
sp.fixed_iv_length = 16;
sp.record_iv_length = 16;
sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;
sp.mac_length = 20;
sp.mac_key_length = 20;
},
initConnectionState: initConnectionState
};
function initConnectionState(state, c, sp) {
var client = (c.entity === forge.tls.ConnectionEnd.client);
// cipher setup
state.read.cipherState = {
init: false,
cipher: forge.cipher.createDecipher('AES-CBC', client ?
sp.keys.server_write_key : sp.keys.client_write_key),
iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV
};
state.write.cipherState = {
init: false,
cipher: forge.cipher.createCipher('AES-CBC', client ?
sp.keys.client_write_key : sp.keys.server_write_key),
iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV
};
state.read.cipherFunction = decrypt_aes_cbc_sha1;
state.write.cipherFunction = encrypt_aes_cbc_sha1;
// MAC setup
state.read.macLength = state.write.macLength = sp.mac_length;
state.read.macFunction = state.write.macFunction = tls.hmac_sha1;
}
/**
* Encrypts the TLSCompressed record into a TLSCipherText record using AES
* in CBC mode.
*
* @param record the TLSCompressed record to encrypt.
* @param s the ConnectionState to use.
*
* @return true on success, false on failure.
*/
function encrypt_aes_cbc_sha1(record, s) {
var rval = false;
// append MAC to fragment, update sequence number
var mac = s.macFunction(s.macKey, s.sequenceNumber, record);
record.fragment.putBytes(mac);
s.updateSequenceNumber();
// TLS 1.1+ use an explicit IV every time to protect against CBC attacks
var iv;
if(record.version.minor === tls.Versions.TLS_1_0.minor) {
// use the pre-generated IV when initializing for TLS 1.0, otherwise use
// the residue from the previous encryption
iv = s.cipherState.init ? null : s.cipherState.iv;
} else {
iv = forge.random.getBytesSync(16);
}
s.cipherState.init = true;
// start cipher
var cipher = s.cipherState.cipher;
cipher.start({iv: iv});
// TLS 1.1+ write IV into output
if(record.version.minor >= tls.Versions.TLS_1_1.minor) {
cipher.output.putBytes(iv);
}
// do encryption (default padding is appropriate)
cipher.update(record.fragment);
if(cipher.finish(encrypt_aes_cbc_sha1_padding)) {
// set record fragment to encrypted output
record.fragment = cipher.output;
record.length = record.fragment.length();
rval = true;
}
return rval;
}
/**
* Handles padding for aes_cbc_sha1 in encrypt mode.
*
* @param blockSize the block size.
* @param input the input buffer.
* @param decrypt true in decrypt mode, false in encrypt mode.
*
* @return true on success, false on failure.
*/
function encrypt_aes_cbc_sha1_padding(blockSize, input, decrypt) {
/* The encrypted data length (TLSCiphertext.length) is one more than the sum
of SecurityParameters.block_length, TLSCompressed.length,
SecurityParameters.mac_length, and padding_length.
The padding may be any length up to 255 bytes long, as long as it results in
the TLSCiphertext.length being an integral multiple of the block length.
Lengths longer than necessary might be desirable to frustrate attacks on a
protocol based on analysis of the lengths of exchanged messages. Each uint8
in the padding data vector must be filled with the padding length value.
The padding length should be such that the total size of the
GenericBlockCipher structure is a multiple of the cipher's block length.
Legal values range from zero to 255, inclusive. This length specifies the
length of the padding field exclusive of the padding_length field itself.
This is slightly different from PKCS#7 because the padding value is 1
less than the actual number of padding bytes if you include the
padding_length uint8 itself as a padding byte. */
if(!decrypt) {
// get the number of padding bytes required to reach the blockSize and
// subtract 1 for the padding value (to make room for the padding_length
// uint8)
var padding = blockSize - (input.length() % blockSize);
input.fillWithByte(padding - 1, padding);
}
return true;
}
/**
* Handles padding for aes_cbc_sha1 in decrypt mode.
*
* @param blockSize the block size.
* @param output the output buffer.
* @param decrypt true in decrypt mode, false in encrypt mode.
*
* @return true on success, false on failure.
*/
function decrypt_aes_cbc_sha1_padding(blockSize, output, decrypt) {
var rval = true;
if(decrypt) {
/* The last byte in the output specifies the number of padding bytes not
including itself. Each of the padding bytes has the same value as that
last byte (known as the padding_length). Here we check all padding
bytes to ensure they have the value of padding_length even if one of
them is bad in order to ward-off timing attacks. */
var len = output.length();
var paddingLength = output.last();
for(var i = len - 1 - paddingLength; i < len - 1; ++i) {
rval = rval && (output.at(i) == paddingLength);
}
if(rval) {
// trim off padding bytes and last padding length byte
output.truncate(paddingLength + 1);
}
}
return rval;
}
/**
* Decrypts a TLSCipherText record into a TLSCompressed record using
* AES in CBC mode.
*
* @param record the TLSCipherText record to decrypt.
* @param s the ConnectionState to use.
*
* @return true on success, false on failure.
*/
var count = 0;
function decrypt_aes_cbc_sha1(record, s) {
var rval = false;
++count;
var iv;
if(record.version.minor === tls.Versions.TLS_1_0.minor) {
// use pre-generated IV when initializing for TLS 1.0, otherwise use the
// residue from the previous decryption
iv = s.cipherState.init ? null : s.cipherState.iv;
} else {
// TLS 1.1+ use an explicit IV every time to protect against CBC attacks
// that is appended to the record fragment
iv = record.fragment.getBytes(16);
}
s.cipherState.init = true;
// start cipher
var cipher = s.cipherState.cipher;
cipher.start({iv: iv});
// do decryption
cipher.update(record.fragment);
rval = cipher.finish(decrypt_aes_cbc_sha1_padding);
// even if decryption fails, keep going to minimize timing attacks
// decrypted data:
// first (len - 20) bytes = application data
// last 20 bytes = MAC
var macLen = s.macLength;
// create a random MAC to check against should the mac length check fail
// Note: do this regardless of the failure to keep timing consistent
var mac = forge.random.getBytesSync(macLen);
// get fragment and mac
var len = cipher.output.length();
if(len >= macLen) {
record.fragment = cipher.output.getBytes(len - macLen);
mac = cipher.output.getBytes(macLen);
} else {
// bad data, but get bytes anyway to try to keep timing consistent
record.fragment = cipher.output.getBytes();
}
record.fragment = forge.util.createBuffer(record.fragment);
record.length = record.fragment.length();
// see if data integrity checks out, update sequence number
var mac2 = s.macFunction(s.macKey, s.sequenceNumber, record);
s.updateSequenceNumber();
rval = compareMacs(s.macKey, mac, mac2) && rval;
return rval;
}
/**
* Safely compare two MACs. This function will compare two MACs in a way
* that protects against timing attacks.
*
* TODO: Expose elsewhere as a utility API.
*
* See: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
*
* @param key the MAC key to use.
* @param mac1 as a binary-encoded string of bytes.
* @param mac2 as a binary-encoded string of bytes.
*
* @return true if the MACs are the same, false if not.
*/
function compareMacs(key, mac1, mac2) {
var hmac = forge.hmac.create();
hmac.start('SHA1', key);
hmac.update(mac1);
mac1 = hmac.digest().getBytes();
hmac.start(null, null);
hmac.update(mac2);
mac2 = hmac.digest().getBytes();
return mac1 === mac2;
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'aesCipherSuites';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './aes', './tls'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

1157
forge.js/asn1.js Normal file

File diff suppressed because it is too large Load Diff

286
forge.js/cipher.js Normal file
View File

@@ -0,0 +1,286 @@
/**
* Cipher base API.
*
* @author Dave Longley
*
* Copyright (c) 2010-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.cipher = forge.cipher || {};
// registered algorithms
forge.cipher.algorithms = forge.cipher.algorithms || {};
/**
* Creates a cipher object that can be used to encrypt data using the given
* algorithm and key. The algorithm may be provided as a string value for a
* previously registered algorithm or it may be given as a cipher algorithm
* API object.
*
* @param algorithm the algorithm to use, either a string or an algorithm API
* object.
* @param key the key to use, as a binary-encoded string of bytes or a
* byte buffer.
*
* @return the cipher.
*/
forge.cipher.createCipher = function(algorithm, key) {
var api = algorithm;
if(typeof api === 'string') {
api = forge.cipher.getAlgorithm(api);
if(api) {
api = api();
}
}
if(!api) {
throw new Error('Unsupported algorithm: ' + algorithm);
}
// assume block cipher
return new forge.cipher.BlockCipher({
algorithm: api,
key: key,
decrypt: false
});
};
/**
* Creates a decipher object that can be used to decrypt data using the given
* algorithm and key. The algorithm may be provided as a string value for a
* previously registered algorithm or it may be given as a cipher algorithm
* API object.
*
* @param algorithm the algorithm to use, either a string or an algorithm API
* object.
* @param key the key to use, as a binary-encoded string of bytes or a
* byte buffer.
*
* @return the cipher.
*/
forge.cipher.createDecipher = function(algorithm, key) {
var api = algorithm;
if(typeof api === 'string') {
api = forge.cipher.getAlgorithm(api);
if(api) {
api = api();
}
}
if(!api) {
throw new Error('Unsupported algorithm: ' + algorithm);
}
// assume block cipher
return new forge.cipher.BlockCipher({
algorithm: api,
key: key,
decrypt: true
});
};
/**
* Registers an algorithm by name. If the name was already registered, the
* algorithm API object will be overwritten.
*
* @param name the name of the algorithm.
* @param algorithm the algorithm API object.
*/
forge.cipher.registerAlgorithm = function(name, algorithm) {
name = name.toUpperCase();
forge.cipher.algorithms[name] = algorithm;
};
/**
* Gets a registered algorithm by name.
*
* @param name the name of the algorithm.
*
* @return the algorithm, if found, null if not.
*/
forge.cipher.getAlgorithm = function(name) {
name = name.toUpperCase();
if(name in forge.cipher.algorithms) {
return forge.cipher.algorithms[name];
}
return null;
};
var BlockCipher = forge.cipher.BlockCipher = function(options) {
this.algorithm = options.algorithm;
this.mode = this.algorithm.mode;
this.blockSize = this.mode.blockSize;
this._finish = false;
this._input = null;
this.output = null;
this._op = options.decrypt ? this.mode.decrypt : this.mode.encrypt;
this._decrypt = options.decrypt;
this.algorithm.initialize(options);
};
/**
* Starts or restarts the encryption or decryption process, whichever
* was previously configured.
*
* For non-GCM mode, the IV may be a binary-encoded string of bytes, an array
* of bytes, a byte buffer, or an array of 32-bit integers. If the IV is in
* bytes, then it must be Nb (16) bytes in length. If the IV is given in as
* 32-bit integers, then it must be 4 integers long.
*
* Note: an IV is not required or used in ECB mode.
*
* For GCM-mode, the IV must be given as a binary-encoded string of bytes or
* a byte buffer. The number of bytes should be 12 (96 bits) as recommended
* by NIST SP-800-38D but another length may be given.
*
* @param options the options to use:
* iv the initialization vector to use as a binary-encoded string of
* bytes, null to reuse the last ciphered block from a previous
* update() (this "residue" method is for legacy support only).
* additionalData additional authentication data as a binary-encoded
* string of bytes, for 'GCM' mode, (default: none).
* tagLength desired length of authentication tag, in bits, for
* 'GCM' mode (0-128, default: 128).
* tag the authentication tag to check if decrypting, as a
* binary-encoded string of bytes.
* output the output the buffer to write to, null to create one.
*/
BlockCipher.prototype.start = function(options) {
options = options || {};
var opts = {};
for(var key in options) {
opts[key] = options[key];
}
opts.decrypt = this._decrypt;
this._finish = false;
this._input = forge.util.createBuffer();
this.output = options.output || forge.util.createBuffer();
this.mode.start(opts);
};
/**
* Updates the next block according to the cipher mode.
*
* @param input the buffer to read from.
*/
BlockCipher.prototype.update = function(input) {
if(input) {
// input given, so empty it into the input buffer
this._input.putBuffer(input);
}
// do cipher operation until it needs more input and not finished
while(!this._op.call(this.mode, this._input, this.output, this._finish) &&
!this._finish) {}
// free consumed memory from input buffer
this._input.compact();
};
/**
* Finishes encrypting or decrypting.
*
* @param pad a padding function to use in CBC mode, null for default,
* signature(blockSize, buffer, decrypt).
*
* @return true if successful, false on error.
*/
BlockCipher.prototype.finish = function(pad) {
// backwards-compatibility w/deprecated padding API
// Note: will overwrite padding functions even after another start() call
if(pad && (this.mode.name === 'ECB' || this.mode.name === 'CBC')) {
this.mode.pad = function(input) {
return pad(this.blockSize, input, false);
};
this.mode.unpad = function(output) {
return pad(this.blockSize, output, true);
};
}
// build options for padding and afterFinish functions
var options = {};
options.decrypt = this._decrypt;
// get # of bytes that won't fill a block
options.overflow = this._input.length() % this.blockSize;
if(!this._decrypt && this.mode.pad) {
if(!this.mode.pad(this._input, options)) {
return false;
}
}
// do final update
this._finish = true;
this.update();
if(this._decrypt && this.mode.unpad) {
if(!this.mode.unpad(this.output, options)) {
return false;
}
}
if(this.mode.afterFinish) {
if(!this.mode.afterFinish(this.output, options)) {
return false;
}
}
return true;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'cipher';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

1049
forge.js/cipherModes.js Normal file

File diff suppressed because it is too large Load Diff

134
forge.js/debug.js Normal file
View File

@@ -0,0 +1,134 @@
/**
* Debugging support for web applications.
*
* @author David I. Lehn <dlehn@digitalbazaar.com>
*
* Copyright 2008-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/* DEBUG API */
forge.debug = forge.debug || {};
// Private storage for debugging.
// Useful to expose data that is otherwise unviewable behind closures.
// NOTE: remember that this can hold references to data and cause leaks!
// format is "forge._debug.<modulename>.<dataname> = data"
// Example:
// (function() {
// var cat = 'forge.test.Test'; // debugging category
// var sState = {...}; // local state
// forge.debug.set(cat, 'sState', sState);
// })();
forge.debug.storage = {};
/**
* Gets debug data. Omit name for all cat data Omit name and cat for
* all data.
*
* @param cat name of debugging category.
* @param name name of data to get (optional).
* @return object with requested debug data or undefined.
*/
forge.debug.get = function(cat, name) {
var rval;
if(typeof(cat) === 'undefined') {
rval = forge.debug.storage;
} else if(cat in forge.debug.storage) {
if(typeof(name) === 'undefined') {
rval = forge.debug.storage[cat];
} else {
rval = forge.debug.storage[cat][name];
}
}
return rval;
};
/**
* Sets debug data.
*
* @param cat name of debugging category.
* @param name name of data to set.
* @param data data to set.
*/
forge.debug.set = function(cat, name, data) {
if(!(cat in forge.debug.storage)) {
forge.debug.storage[cat] = {};
}
forge.debug.storage[cat][name] = data;
};
/**
* Clears debug data. Omit name for all cat data. Omit name and cat for
* all data.
*
* @param cat name of debugging category.
* @param name name of data to clear or omit to clear entire category.
*/
forge.debug.clear = function(cat, name) {
if(typeof(cat) === 'undefined') {
forge.debug.storage = {};
} else if(cat in forge.debug.storage) {
if(typeof(name) === 'undefined') {
delete forge.debug.storage[cat];
} else {
delete forge.debug.storage[cat][name];
}
}
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'debug';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

552
forge.js/des.js Normal file
View File

@@ -0,0 +1,552 @@
/**
* DES (Data Encryption Standard) implementation.
*
* This implementation supports DES as well as 3DES-EDE in ECB and CBC mode.
* It is based on the BSD-licensed implementation by Paul Tero:
*
* Paul Tero, July 2001
* http://www.tero.co.uk/des/
*
* Optimised for performance with large blocks by Michael Hayworth, November 2001
* http://www.netdealing.com
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @author Stefan Siegl
* @author Dave Longley
*
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
* Copyright (c) 2012-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/* DES API */
forge.des = forge.des || {};
/**
* Deprecated. Instead, use:
*
* var cipher = forge.cipher.createCipher('DES-<mode>', key);
* cipher.start({iv: iv});
*
* Creates an DES cipher object to encrypt data using the given symmetric key.
* The output will be stored in the 'output' member of the returned cipher.
*
* The key and iv may be given as binary-encoded strings of bytes or
* byte buffers.
*
* @param key the symmetric key to use (64 or 192 bits).
* @param iv the initialization vector to use.
* @param output the buffer to write to, null to create one.
* @param mode the cipher mode to use (default: 'CBC' if IV is
* given, 'ECB' if null).
*
* @return the cipher.
*/
forge.des.startEncrypting = function(key, iv, output, mode) {
var cipher = _createCipher({
key: key,
output: output,
decrypt: false,
mode: mode || (iv === null ? 'ECB' : 'CBC')
});
cipher.start(iv);
return cipher;
};
/**
* Deprecated. Instead, use:
*
* var cipher = forge.cipher.createCipher('DES-<mode>', key);
*
* Creates an DES cipher object to encrypt data using the given symmetric key.
*
* The key may be given as a binary-encoded string of bytes or a byte buffer.
*
* @param key the symmetric key to use (64 or 192 bits).
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
forge.des.createEncryptionCipher = function(key, mode) {
return _createCipher({
key: key,
output: null,
decrypt: false,
mode: mode
});
};
/**
* Deprecated. Instead, use:
*
* var decipher = forge.cipher.createDecipher('DES-<mode>', key);
* decipher.start({iv: iv});
*
* Creates an DES cipher object to decrypt data using the given symmetric key.
* The output will be stored in the 'output' member of the returned cipher.
*
* The key and iv may be given as binary-encoded strings of bytes or
* byte buffers.
*
* @param key the symmetric key to use (64 or 192 bits).
* @param iv the initialization vector to use.
* @param output the buffer to write to, null to create one.
* @param mode the cipher mode to use (default: 'CBC' if IV is
* given, 'ECB' if null).
*
* @return the cipher.
*/
forge.des.startDecrypting = function(key, iv, output, mode) {
var cipher = _createCipher({
key: key,
output: output,
decrypt: true,
mode: mode || (iv === null ? 'ECB' : 'CBC')
});
cipher.start(iv);
return cipher;
};
/**
* Deprecated. Instead, use:
*
* var decipher = forge.cipher.createDecipher('DES-<mode>', key);
*
* Creates an DES cipher object to decrypt data using the given symmetric key.
*
* The key may be given as a binary-encoded string of bytes or a byte buffer.
*
* @param key the symmetric key to use (64 or 192 bits).
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
forge.des.createDecryptionCipher = function(key, mode) {
return _createCipher({
key: key,
output: null,
decrypt: true,
mode: mode
});
};
/**
* Creates a new DES cipher algorithm object.
*
* @param name the name of the algorithm.
* @param mode the mode factory function.
*
* @return the DES algorithm object.
*/
forge.des.Algorithm = function(name, mode) {
var self = this;
self.name = name;
self.mode = new mode({
blockSize: 8,
cipher: {
encrypt: function(inBlock, outBlock) {
return _updateBlock(self._keys, inBlock, outBlock, false);
},
decrypt: function(inBlock, outBlock) {
return _updateBlock(self._keys, inBlock, outBlock, true);
}
}
});
self._init = false;
};
/**
* Initializes this DES algorithm by expanding its key.
*
* @param options the options to use.
* key the key to use with this algorithm.
* decrypt true if the algorithm should be initialized for decryption,
* false for encryption.
*/
forge.des.Algorithm.prototype.initialize = function(options) {
if(this._init) {
return;
}
var key = forge.util.createBuffer(options.key);
if(this.name.indexOf('3DES') === 0) {
if(key.length() !== 24) {
throw new Error('Invalid Triple-DES key size: ' + key.length() * 8);
}
}
// do key expansion to 16 or 48 subkeys (single or triple DES)
this._keys = _createKeys(key);
this._init = true;
};
/** Register DES algorithms **/
registerAlgorithm('DES-ECB', forge.cipher.modes.ecb);
registerAlgorithm('DES-CBC', forge.cipher.modes.cbc);
registerAlgorithm('DES-CFB', forge.cipher.modes.cfb);
registerAlgorithm('DES-OFB', forge.cipher.modes.ofb);
registerAlgorithm('DES-CTR', forge.cipher.modes.ctr);
registerAlgorithm('3DES-ECB', forge.cipher.modes.ecb);
registerAlgorithm('3DES-CBC', forge.cipher.modes.cbc);
registerAlgorithm('3DES-CFB', forge.cipher.modes.cfb);
registerAlgorithm('3DES-OFB', forge.cipher.modes.ofb);
registerAlgorithm('3DES-CTR', forge.cipher.modes.ctr);
function registerAlgorithm(name, mode) {
var factory = function() {
return new forge.des.Algorithm(name, mode);
};
forge.cipher.registerAlgorithm(name, factory);
}
/** DES implementation **/
var spfunction1 = [0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004];
var spfunction2 = [-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000];
var spfunction3 = [0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200];
var spfunction4 = [0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080];
var spfunction5 = [0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100];
var spfunction6 = [0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010];
var spfunction7 = [0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002];
var spfunction8 = [0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000];
/**
* Create necessary sub keys.
*
* @param key the 64-bit or 192-bit key.
*
* @return the expanded keys.
*/
function _createKeys(key) {
var pc2bytes0 = [0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204],
pc2bytes1 = [0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101],
pc2bytes2 = [0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808],
pc2bytes3 = [0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000],
pc2bytes4 = [0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010],
pc2bytes5 = [0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420],
pc2bytes6 = [0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002],
pc2bytes7 = [0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800],
pc2bytes8 = [0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002],
pc2bytes9 = [0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408],
pc2bytes10 = [0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020],
pc2bytes11 = [0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200],
pc2bytes12 = [0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010],
pc2bytes13 = [0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105];
// how many iterations (1 for des, 3 for triple des)
// changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
var iterations = key.length() > 8 ? 3 : 1;
// stores the return keys
var keys = [];
// now define the left shifts which need to be done
var shifts = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0];
var n = 0, tmp;
for(var j = 0; j < iterations; j ++) {
var left = key.getInt32();
var right = key.getInt32();
tmp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
right ^= tmp;
left ^= (tmp << 4);
tmp = ((right >>> -16) ^ left) & 0x0000ffff;
left ^= tmp;
right ^= (tmp << -16);
tmp = ((left >>> 2) ^ right) & 0x33333333;
right ^= tmp;
left ^= (tmp << 2);
tmp = ((right >>> -16) ^ left) & 0x0000ffff;
left ^= tmp;
right ^= (tmp << -16);
tmp = ((left >>> 1) ^ right) & 0x55555555;
right ^= tmp;
left ^= (tmp << 1);
tmp = ((right >>> 8) ^ left) & 0x00ff00ff;
left ^= tmp;
right ^= (tmp << 8);
tmp = ((left >>> 1) ^ right) & 0x55555555;
right ^= tmp;
left ^= (tmp << 1);
// right needs to be shifted and OR'd with last four bits of left
tmp = (left << 8) | ((right >>> 20) & 0x000000f0);
// left needs to be put upside down
left = ((right << 24) | ((right << 8) & 0xff0000) |
((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0));
right = tmp;
// now go through and perform these shifts on the left and right keys
for(var i = 0; i < shifts.length; ++i) {
//shift the keys either one or two bits to the left
if(shifts[i]) {
left = (left << 2) | (left >>> 26);
right = (right << 2) | (right >>> 26);
} else {
left = (left << 1) | (left >>> 27);
right = (right << 1) | (right >>> 27);
}
left &= -0xf;
right &= -0xf;
// now apply PC-2, in such a way that E is easier when encrypting or
// decrypting this conversion will look like PC-2 except only the last 6
// bits of each byte are used rather than 48 consecutive bits and the
// order of lines will be according to how the S selection functions will
// be applied: S2, S4, S6, S8, S1, S3, S5, S7
var lefttmp = (
pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] |
pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf] |
pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] |
pc2bytes6[(left >>> 4) & 0xf]);
var righttmp = (
pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] |
pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf] |
pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] |
pc2bytes13[(right >>> 4) & 0xf]);
tmp = ((righttmp >>> 16) ^ lefttmp) & 0x0000ffff;
keys[n++] = lefttmp ^ tmp;
keys[n++] = righttmp ^ (tmp << 16);
}
}
return keys;
}
/**
* Updates a single block (1 byte) using DES. The update will either
* encrypt or decrypt the block.
*
* @param keys the expanded keys.
* @param input the input block (an array of 32-bit words).
* @param output the updated output block.
* @param decrypt true to decrypt the block, false to encrypt it.
*/
function _updateBlock(keys, input, output, decrypt) {
// set up loops for single or triple DES
var iterations = keys.length === 32 ? 3 : 9;
var looping;
if(iterations === 3) {
looping = decrypt ? [30, -2, -2] : [0, 32, 2];
} else {
looping = (decrypt ?
[94, 62, -2, 32, 64, 2, 30, -2, -2] :
[0, 32, 2, 62, 30, -2, 64, 96, 2]);
}
var tmp;
var left = input[0];
var right = input[1];
// first each 64 bit chunk of the message must be permuted according to IP
tmp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
right ^= tmp;
left ^= (tmp << 4);
tmp = ((left >>> 16) ^ right) & 0x0000ffff;
right ^= tmp;
left ^= (tmp << 16);
tmp = ((right >>> 2) ^ left) & 0x33333333;
left ^= tmp;
right ^= (tmp << 2);
tmp = ((right >>> 8) ^ left) & 0x00ff00ff;
left ^= tmp;
right ^= (tmp << 8);
tmp = ((left >>> 1) ^ right) & 0x55555555;
right ^= tmp;
left ^= (tmp << 1);
// rotate left 1 bit
left = ((left << 1) | (left >>> 31));
right = ((right << 1) | (right >>> 31));
for(var j = 0; j < iterations; j += 3) {
var endloop = looping[j + 1];
var loopinc = looping[j + 2];
// now go through and perform the encryption or decryption
for(var i = looping[j]; i != endloop; i += loopinc) {
var right1 = right ^ keys[i];
var right2 = ((right >>> 4) | (right << 28)) ^ keys[i + 1];
// passing these bytes through the S selection functions
tmp = left;
left = right;
right = tmp ^ (
spfunction2[(right1 >>> 24) & 0x3f] |
spfunction4[(right1 >>> 16) & 0x3f] |
spfunction6[(right1 >>> 8) & 0x3f] |
spfunction8[right1 & 0x3f] |
spfunction1[(right2 >>> 24) & 0x3f] |
spfunction3[(right2 >>> 16) & 0x3f] |
spfunction5[(right2 >>> 8) & 0x3f] |
spfunction7[right2 & 0x3f]);
}
// unreverse left and right
tmp = left;
left = right;
right = tmp;
}
// rotate right 1 bit
left = ((left >>> 1) | (left << 31));
right = ((right >>> 1) | (right << 31));
// now perform IP-1, which is IP in the opposite direction
tmp = ((left >>> 1) ^ right) & 0x55555555;
right ^= tmp;
left ^= (tmp << 1);
tmp = ((right >>> 8) ^ left) & 0x00ff00ff;
left ^= tmp;
right ^= (tmp << 8);
tmp = ((right >>> 2) ^ left) & 0x33333333;
left ^= tmp;
right ^= (tmp << 2);
tmp = ((left >>> 16) ^ right) & 0x0000ffff;
right ^= tmp;
left ^= (tmp << 16);
tmp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
right ^= tmp;
left ^= (tmp << 4);
output[0] = left;
output[1] = right;
}
/**
* Deprecated. Instead, use:
*
* forge.cipher.createCipher('DES-<mode>', key);
* forge.cipher.createDecipher('DES-<mode>', key);
*
* Creates a deprecated DES cipher object. This object's mode will default to
* CBC (cipher-block-chaining).
*
* The key may be given as a binary-encoded string of bytes or a byte buffer.
*
* @param options the options to use.
* key the symmetric key to use (64 or 192 bits).
* output the buffer to write to.
* decrypt true for decryption, false for encryption.
* mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
function _createCipher(options) {
options = options || {};
var mode = (options.mode || 'CBC').toUpperCase();
var algorithm = 'DES-' + mode;
var cipher;
if(options.decrypt) {
cipher = forge.cipher.createDecipher(algorithm, options.key);
} else {
cipher = forge.cipher.createCipher(algorithm, options.key);
}
// backwards compatible start API
var start = cipher.start;
cipher.start = function(iv, options) {
// backwards compatibility: support second arg as output buffer
var output = null;
if(options instanceof forge.util.ByteBuffer) {
output = options;
options = {};
}
options = options || {};
options.output = output;
options.iv = iv;
start.call(cipher, options);
};
return cipher;
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'des';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(
['require', 'module', './cipher', './cipherModes', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

29553
forge.js/forge.bundle.js Normal file

File diff suppressed because it is too large Load Diff

92
forge.js/forge.js Normal file
View File

@@ -0,0 +1,92 @@
/**
* Node.js module for Forge.
*
* @author Dave Longley
*
* Copyright 2011-2014 Digital Bazaar, Inc.
*/
(function() {
var name = 'forge';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
// set to true to disable native code if even it's available
forge = {disableNativeCode: false};
}
return;
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
});
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge;
};
// set to true to disable native code if even it's available
module.exports.disableNativeCode = false;
module.exports(module.exports);
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./aes',
'./aesCipherSuites',
'./asn1',
'./cipher',
'./cipherModes',
'./debug',
'./des',
'./hmac',
'./kem',
'./log',
'./md',
'./mgf1',
'./pbkdf2',
'./pem',
'./pkcs7',
'./pkcs1',
'./pkcs12',
'./pki',
'./prime',
'./prng',
'./pss',
'./random',
'./rc2',
'./ssh',
'./task',
'./tls',
'./util'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

157
forge.js/form.js Normal file
View File

@@ -0,0 +1,157 @@
/**
* Functions for manipulating web forms.
*
* @author David I. Lehn <dlehn@digitalbazaar.com>
* @author Dave Longley
* @author Mike Johnson
*
* Copyright (c) 2011-2014 Digital Bazaar, Inc. All rights reserved.
*/
(function($) {
/**
* The form namespace.
*/
var form = {};
/**
* Regex for parsing a single name property (handles array brackets).
*/
var _regex = /(.*?)\[(.*?)\]/g;
/**
* Parses a single name property into an array with the name and any
* array indices.
*
* @param name the name to parse.
*
* @return the array of the name and its array indices in order.
*/
var _parseName = function(name) {
var rval = [];
var matches;
while(!!(matches = _regex.exec(name))) {
if(matches[1].length > 0) {
rval.push(matches[1]);
}
if(matches.length >= 2) {
rval.push(matches[2]);
}
}
if(rval.length === 0) {
rval.push(name);
}
return rval;
};
/**
* Adds a field from the given form to the given object.
*
* @param obj the object.
* @param names the field as an array of object property names.
* @param value the value of the field.
* @param dict a dictionary of names to replace.
*/
var _addField = function(obj, names, value, dict) {
// combine array names that fall within square brackets
var tmp = [];
for(var i = 0; i < names.length; ++i) {
// check name for starting square bracket but no ending one
var name = names[i];
if(name.indexOf('[') !== -1 && name.indexOf(']') === -1 &&
i < names.length - 1) {
do {
name += '.' + names[++i];
} while(i < names.length - 1 && names[i].indexOf(']') === -1);
}
tmp.push(name);
}
names = tmp;
// split out array indexes
var tmp = [];
$.each(names, function(n, name) {
tmp = tmp.concat(_parseName(name));
});
names = tmp;
// iterate over object property names until value is set
$.each(names, function(n, name) {
// do dictionary name replacement
if(dict && name.length !== 0 && name in dict) {
name = dict[name];
}
// blank name indicates appending to an array, set name to
// new last index of array
if(name.length === 0) {
name = obj.length;
}
// value already exists, append value
if(obj[name]) {
// last name in the field
if(n == names.length - 1) {
// more than one value, so convert into an array
if(!$.isArray(obj[name])) {
obj[name] = [obj[name]];
}
obj[name].push(value);
} else {
// not last name, go deeper into object
obj = obj[name];
}
} else if(n == names.length - 1) {
// new value, last name in the field, set value
obj[name] = value;
} else {
// new value, not last name, go deeper
// get next name
var next = names[n + 1];
// blank next value indicates array-appending, so create array
if(next.length === 0) {
obj[name] = [];
} else {
// if next name is a number create an array, otherwise a map
var isNum = ((next - 0) == next && next.length > 0);
obj[name] = isNum ? [] : {};
}
obj = obj[name];
}
});
};
/**
* Serializes a form to a JSON object. Object properties will be separated
* using the given separator (defaults to '.') and by square brackets.
*
* @param input the jquery form to serialize.
* @param sep the object-property separator (defaults to '.').
* @param dict a dictionary of names to replace (name=replace).
*
* @return the JSON-serialized form.
*/
form.serialize = function(input, sep, dict) {
var rval = {};
// add all fields in the form to the object
sep = sep || '.';
$.each(input.serializeArray(), function() {
_addField(rval, this.name.split(sep), this.value || '', dict);
});
return rval;
};
/**
* The forge namespace and form API.
*/
if(typeof forge === 'undefined') {
forge = {};
}
forge.form = form;
})(jQuery);

200
forge.js/hmac.js Normal file
View File

@@ -0,0 +1,200 @@
/**
* Hash-based Message Authentication Code implementation. Requires a message
* digest object that can be obtained, for example, from forge.md.sha1 or
* forge.md.md5.
*
* @author Dave Longley
*
* Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/* HMAC API */
var hmac = forge.hmac = forge.hmac || {};
/**
* Creates an HMAC object that uses the given message digest object.
*
* @return an HMAC object.
*/
hmac.create = function() {
// the hmac key to use
var _key = null;
// the message digest to use
var _md = null;
// the inner padding
var _ipadding = null;
// the outer padding
var _opadding = null;
// hmac context
var ctx = {};
/**
* Starts or restarts the HMAC with the given key and message digest.
*
* @param md the message digest to use, null to reuse the previous one,
* a string to use builtin 'sha1', 'md5', 'sha256'.
* @param key the key to use as a string, array of bytes, byte buffer,
* or null to reuse the previous key.
*/
ctx.start = function(md, key) {
if(md !== null) {
if(typeof md === 'string') {
// create builtin message digest
md = md.toLowerCase();
if(md in forge.md.algorithms) {
_md = forge.md.algorithms[md].create();
} else {
throw new Error('Unknown hash algorithm "' + md + '"');
}
} else {
// store message digest
_md = md;
}
}
if(key === null) {
// reuse previous key
key = _key;
} else {
if(typeof key === 'string') {
// convert string into byte buffer
key = forge.util.createBuffer(key);
} else if(forge.util.isArray(key)) {
// convert byte array into byte buffer
var tmp = key;
key = forge.util.createBuffer();
for(var i = 0; i < tmp.length; ++i) {
key.putByte(tmp[i]);
}
}
// if key is longer than blocksize, hash it
var keylen = key.length();
if(keylen > _md.blockLength) {
_md.start();
_md.update(key.bytes());
key = _md.digest();
}
// mix key into inner and outer padding
// ipadding = [0x36 * blocksize] ^ key
// opadding = [0x5C * blocksize] ^ key
_ipadding = forge.util.createBuffer();
_opadding = forge.util.createBuffer();
keylen = key.length();
for(var i = 0; i < keylen; ++i) {
var tmp = key.at(i);
_ipadding.putByte(0x36 ^ tmp);
_opadding.putByte(0x5C ^ tmp);
}
// if key is shorter than blocksize, add additional padding
if(keylen < _md.blockLength) {
var tmp = _md.blockLength - keylen;
for(var i = 0; i < tmp; ++i) {
_ipadding.putByte(0x36);
_opadding.putByte(0x5C);
}
}
_key = key;
_ipadding = _ipadding.bytes();
_opadding = _opadding.bytes();
}
// digest is done like so: hash(opadding | hash(ipadding | message))
// prepare to do inner hash
// hash(ipadding | message)
_md.start();
_md.update(_ipadding);
};
/**
* Updates the HMAC with the given message bytes.
*
* @param bytes the bytes to update with.
*/
ctx.update = function(bytes) {
_md.update(bytes);
};
/**
* Produces the Message Authentication Code (MAC).
*
* @return a byte buffer containing the digest value.
*/
ctx.getMac = function() {
// digest is done like so: hash(opadding | hash(ipadding | message))
// here we do the outer hashing
var inner = _md.digest().bytes();
_md.start();
_md.update(_opadding);
_md.update(inner);
return _md.digest();
};
// alias for getMac
ctx.digest = ctx.getMac;
return ctx;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'hmac';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './md', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

1369
forge.js/http.js Normal file

File diff suppressed because it is too large Load Diff

1321
forge.js/jsbn.js Normal file

File diff suppressed because it is too large Load Diff

221
forge.js/kem.js Normal file
View File

@@ -0,0 +1,221 @@
/**
* Javascript implementation of RSA-KEM.
*
* @author Lautaro Cozzani Rodriguez
* @author Dave Longley
*
* Copyright (c) 2014 Lautaro Cozzani <lautaro.cozzani@scytl.com>
* Copyright (c) 2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.kem = forge.kem || {};
var BigInteger = forge.jsbn.BigInteger;
/**
* The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
*/
forge.kem.rsa = {};
/**
* Creates an RSA KEM API object for generating a secret asymmetric key.
*
* The symmetric key may be generated via a call to 'encrypt', which will
* produce a ciphertext to be transmitted to the recipient and a key to be
* kept secret. The ciphertext is a parameter to be passed to 'decrypt' which
* will produce the same secret key for the recipient to use to decrypt a
* message that was encrypted with the secret key.
*
* @param kdf the KDF API to use (eg: new forge.kem.kdf1()).
* @param options the options to use.
* [prng] a custom crypto-secure pseudo-random number generator to use,
* that must define "getBytesSync".
*/
forge.kem.rsa.create = function(kdf, options) {
options = options || {};
var prng = options.prng || forge.random;
var kem = {};
/**
* Generates a secret key and its encapsulation.
*
* @param publicKey the RSA public key to encrypt with.
* @param keyLength the length, in bytes, of the secret key to generate.
*
* @return an object with:
* encapsulation: the ciphertext for generating the secret key, as a
* binary-encoded string of bytes.
* key: the secret key to use for encrypting a message.
*/
kem.encrypt = function(publicKey, keyLength) {
// generate a random r where 1 > r > n
var byteLength = Math.ceil(publicKey.n.bitLength() / 8);
var r;
do {
r = new BigInteger(
forge.util.bytesToHex(prng.getBytesSync(byteLength)),
16).mod(publicKey.n);
} while(r.equals(BigInteger.ZERO));
// prepend r with zeros
r = forge.util.hexToBytes(r.toString(16));
var zeros = byteLength - r.length;
if(zeros > 0) {
r = forge.util.fillString(String.fromCharCode(0), zeros) + r;
}
// encrypt the random
var encapsulation = publicKey.encrypt(r, 'NONE');
// generate the secret key
var key = kdf.generate(r, keyLength);
return {encapsulation: encapsulation, key: key};
};
/**
* Decrypts an encapsulated secret key.
*
* @param privateKey the RSA private key to decrypt with.
* @param encapsulation the ciphertext for generating the secret key, as
* a binary-encoded string of bytes.
* @param keyLength the length, in bytes, of the secret key to generate.
*
* @return the secret key as a binary-encoded string of bytes.
*/
kem.decrypt = function(privateKey, encapsulation, keyLength) {
// decrypt the encapsulation and generate the secret key
var r = privateKey.decrypt(encapsulation, 'NONE');
return kdf.generate(r, keyLength);
};
return kem;
};
// TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?
/**
* Creates a key derivation API object that implements KDF1 per ISO 18033-2.
*
* @param md the hash API to use.
* @param [digestLength] an optional digest length that must be positive and
* less than or equal to md.digestLength.
*
* @return a KDF1 API object.
*/
forge.kem.kdf1 = function(md, digestLength) {
_createKDF(this, md, 0, digestLength || md.digestLength);
};
/**
* Creates a key derivation API object that implements KDF2 per ISO 18033-2.
*
* @param md the hash API to use.
* @param [digestLength] an optional digest length that must be positive and
* less than or equal to md.digestLength.
*
* @return a KDF2 API object.
*/
forge.kem.kdf2 = function(md, digestLength) {
_createKDF(this, md, 1, digestLength || md.digestLength);
};
/**
* Creates a KDF1 or KDF2 API object.
*
* @param md the hash API to use.
* @param counterStart the starting index for the counter.
* @param digestLength the digest length to use.
*
* @return the KDF API object.
*/
function _createKDF(kdf, md, counterStart, digestLength) {
/**
* Generate a key of the specified length.
*
* @param x the binary-encoded byte string to generate a key from.
* @param length the number of bytes to generate (the size of the key).
*
* @return the key as a binary-encoded string.
*/
kdf.generate = function(x, length) {
var key = new forge.util.ByteBuffer();
// run counter from counterStart to ceil(length / Hash.len)
var k = Math.ceil(length / digestLength) + counterStart;
var c = new forge.util.ByteBuffer();
for(var i = counterStart; i < k; ++i) {
// I2OSP(i, 4): convert counter to an octet string of 4 octets
c.putInt32(i);
// digest 'x' and the counter and add the result to the key
md.start();
md.update(x + c.getBytes());
var hash = md.digest();
key.putBytes(hash.getBytes(digestLength));
}
// truncate to the correct key length
key.truncate(key.length() - length);
return key.getBytes();
};
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'kem';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util','./random','./jsbn'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

372
forge.js/log.js Normal file
View File

@@ -0,0 +1,372 @@
/**
* Cross-browser support for logging in a web application.
*
* @author David I. Lehn <dlehn@digitalbazaar.com>
*
* Copyright (c) 2008-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/* LOG API */
forge.log = forge.log || {};
/**
* Application logging system.
*
* Each logger level available as it's own function of the form:
* forge.log.level(category, args...)
* The category is an arbitrary string, and the args are the same as
* Firebug's console.log API. By default the call will be output as:
* 'LEVEL [category] <args[0]>, args[1], ...'
* This enables proper % formatting via the first argument.
* Each category is enabled by default but can be enabled or disabled with
* the setCategoryEnabled() function.
*/
// list of known levels
forge.log.levels = [
'none', 'error', 'warning', 'info', 'debug', 'verbose', 'max'];
// info on the levels indexed by name:
// index: level index
// name: uppercased display name
var sLevelInfo = {};
// list of loggers
var sLoggers = [];
/**
* Standard console logger. If no console support is enabled this will
* remain null. Check before using.
*/
var sConsoleLogger = null;
// logger flags
/**
* Lock the level at the current value. Used in cases where user config may
* set the level such that only critical messages are seen but more verbose
* messages are needed for debugging or other purposes.
*/
forge.log.LEVEL_LOCKED = (1 << 1);
/**
* Always call log function. By default, the logging system will check the
* message level against logger.level before calling the log function. This
* flag allows the function to do its own check.
*/
forge.log.NO_LEVEL_CHECK = (1 << 2);
/**
* Perform message interpolation with the passed arguments. "%" style
* fields in log messages will be replaced by arguments as needed. Some
* loggers, such as Firebug, may do this automatically. The original log
* message will be available as 'message' and the interpolated version will
* be available as 'fullMessage'.
*/
forge.log.INTERPOLATE = (1 << 3);
// setup each log level
for(var i = 0; i < forge.log.levels.length; ++i) {
var level = forge.log.levels[i];
sLevelInfo[level] = {
index: i,
name: level.toUpperCase()
};
}
/**
* Message logger. Will dispatch a message to registered loggers as needed.
*
* @param message message object
*/
forge.log.logMessage = function(message) {
var messageLevelIndex = sLevelInfo[message.level].index;
for(var i = 0; i < sLoggers.length; ++i) {
var logger = sLoggers[i];
if(logger.flags & forge.log.NO_LEVEL_CHECK) {
logger.f(message);
} else {
// get logger level
var loggerLevelIndex = sLevelInfo[logger.level].index;
// check level
if(messageLevelIndex <= loggerLevelIndex) {
// message critical enough, call logger
logger.f(logger, message);
}
}
}
};
/**
* Sets the 'standard' key on a message object to:
* "LEVEL [category] " + message
*
* @param message a message log object
*/
forge.log.prepareStandard = function(message) {
if(!('standard' in message)) {
message.standard =
sLevelInfo[message.level].name +
//' ' + +message.timestamp +
' [' + message.category + '] ' +
message.message;
}
};
/**
* Sets the 'full' key on a message object to the original message
* interpolated via % formatting with the message arguments.
*
* @param message a message log object.
*/
forge.log.prepareFull = function(message) {
if(!('full' in message)) {
// copy args and insert message at the front
var args = [message.message];
args = args.concat([] || message['arguments']);
// format the message
message.full = forge.util.format.apply(this, args);
}
};
/**
* Applies both preparseStandard() and prepareFull() to a message object and
* store result in 'standardFull'.
*
* @param message a message log object.
*/
forge.log.prepareStandardFull = function(message) {
if(!('standardFull' in message)) {
// FIXME implement 'standardFull' logging
forge.log.prepareStandard(message);
message.standardFull = message.standard;
}
};
// create log level functions
if(true) {
// levels for which we want functions
var levels = ['error', 'warning', 'info', 'debug', 'verbose'];
for(var i = 0; i < levels.length; ++i) {
// wrap in a function to ensure proper level var is passed
(function(level) {
// create function for this level
forge.log[level] = function(category, message/*, args...*/) {
// convert arguments to real array, remove category and message
var args = Array.prototype.slice.call(arguments).slice(2);
// create message object
// Note: interpolation and standard formatting is done lazily
var msg = {
timestamp: new Date(),
level: level,
category: category,
message: message,
'arguments': args
/*standard*/
/*full*/
/*fullMessage*/
};
// process this message
forge.log.logMessage(msg);
};
})(levels[i]);
}
}
/**
* Creates a new logger with specified custom logging function.
*
* The logging function has a signature of:
* function(logger, message)
* logger: current logger
* message: object:
* level: level id
* category: category
* message: string message
* arguments: Array of extra arguments
* fullMessage: interpolated message and arguments if INTERPOLATE flag set
*
* @param logFunction a logging function which takes a log message object
* as a parameter.
*
* @return a logger object.
*/
forge.log.makeLogger = function(logFunction) {
var logger = {
flags: 0,
f: logFunction
};
forge.log.setLevel(logger, 'none');
return logger;
};
/**
* Sets the current log level on a logger.
*
* @param logger the target logger.
* @param level the new maximum log level as a string.
*
* @return true if set, false if not.
*/
forge.log.setLevel = function(logger, level) {
var rval = false;
if(logger && !(logger.flags & forge.log.LEVEL_LOCKED)) {
for(var i = 0; i < forge.log.levels.length; ++i) {
var aValidLevel = forge.log.levels[i];
if(level == aValidLevel) {
// set level
logger.level = level;
rval = true;
break;
}
}
}
return rval;
};
/**
* Locks the log level at its current value.
*
* @param logger the target logger.
* @param lock boolean lock value, default to true.
*/
forge.log.lock = function(logger, lock) {
if(typeof lock === 'undefined' || lock) {
logger.flags |= forge.log.LEVEL_LOCKED;
} else {
logger.flags &= ~forge.log.LEVEL_LOCKED;
}
};
/**
* Adds a logger.
*
* @param logger the logger object.
*/
forge.log.addLogger = function(logger) {
sLoggers.push(logger);
};
// setup the console logger if possible, else create fake console.log
if(typeof(console) !== 'undefined' && 'log' in console) {
var logger;
if(console.error && console.warn && console.info && console.debug) {
// looks like Firebug-style logging is available
// level handlers map
var levelHandlers = {
error: console.error,
warning: console.warn,
info: console.info,
debug: console.debug,
verbose: console.debug
};
var f = function(logger, message) {
forge.log.prepareStandard(message);
var handler = levelHandlers[message.level];
// prepend standard message and concat args
var args = [message.standard];
args = args.concat(message['arguments'].slice());
// apply to low-level console function
handler.apply(console, args);
};
logger = forge.log.makeLogger(f);
} else {
// only appear to have basic console.log
var f = function(logger, message) {
forge.log.prepareStandardFull(message);
console.log(message.standardFull);
};
logger = forge.log.makeLogger(f);
}
forge.log.setLevel(logger, 'debug');
forge.log.addLogger(logger);
sConsoleLogger = logger;
} else {
// define fake console.log to avoid potential script errors on
// browsers that do not have console logging
console = {
log: function() {}
};
}
/*
* Check for logging control query vars.
*
* console.level=<level-name>
* Set's the console log level by name. Useful to override defaults and
* allow more verbose logging before a user config is loaded.
*
* console.lock=<true|false>
* Lock the console log level at whatever level it is set at. This is run
* after console.level is processed. Useful to force a level of verbosity
* that could otherwise be limited by a user config.
*/
if(sConsoleLogger !== null) {
var query = forge.util.getQueryVariables();
if('console.level' in query) {
// set with last value
forge.log.setLevel(
sConsoleLogger, query['console.level'].slice(-1)[0]);
}
if('console.lock' in query) {
// set with last value
var lock = query['console.lock'].slice(-1)[0];
if(lock == 'true') {
forge.log.lock(sConsoleLogger);
}
}
}
// provide public access to console logger
forge.log.consoleLogger = sConsoleLogger;
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'log';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

75
forge.js/md.js Normal file
View File

@@ -0,0 +1,75 @@
/**
* Node.js module for Forge message digests.
*
* @author Dave Longley
*
* Copyright 2011-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.md = forge.md || {};
forge.md.algorithms = {
md5: forge.md5,
sha1: forge.sha1,
sha256: forge.sha256
};
forge.md.md5 = forge.md5;
forge.md.sha1 = forge.sha1;
forge.md.sha256 = forge.sha256;
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'md';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(
['require', 'module', './md5', './sha1', './sha256', './sha512'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

345
forge.js/md5.js Normal file
View File

@@ -0,0 +1,345 @@
/**
* Message Digest Algorithm 5 with 128-bit digest (MD5) implementation.
*
* @author Dave Longley
*
* Copyright (c) 2010-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var md5 = forge.md5 = forge.md5 || {};
forge.md = forge.md || {};
forge.md.algorithms = forge.md.algorithms || {};
forge.md.md5 = forge.md.algorithms.md5 = md5;
/**
* Creates an MD5 message digest object.
*
* @return a message digest object.
*/
md5.create = function() {
// do initialization as necessary
if(!_initialized) {
_init();
}
// MD5 state contains four 32-bit integers
var _state = null;
// input buffer
var _input = forge.util.createBuffer();
// used for word storage
var _w = new Array(16);
// message digest object
var md = {
algorithm: 'md5',
blockLength: 64,
digestLength: 16,
// 56-bit length of message so far (does not including padding)
messageLength: 0,
// true message length
fullMessageLength: null,
// size of message length in bytes
messageLengthSize: 8
};
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
// up to 56-bit message length for convenience
md.messageLength = 0;
// full message length (set md.messageLength64 for backwards-compatibility)
md.fullMessageLength = md.messageLength64 = [];
var int32s = md.messageLengthSize / 4;
for(var i = 0; i < int32s; ++i) {
md.fullMessageLength.push(0);
}
_input = forge.util.createBuffer();
_state = {
h0: 0x67452301,
h1: 0xEFCDAB89,
h2: 0x98BADCFE,
h3: 0x10325476
};
return md;
};
// start digest automatically for first time
md.start();
/**
* Updates the digest with the given message input. The given input can
* treated as raw input (no encoding will be applied) or an encoding of
* 'utf8' maybe given to encode the input using UTF-8.
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
msg = forge.util.encodeUtf8(msg);
}
// update message length
var len = msg.length;
md.messageLength += len;
len = [(len / 0x100000000) >>> 0, len >>> 0];
for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
md.fullMessageLength[i] += len[1];
len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
len[0] = ((len[1] / 0x100000000) >>> 0);
}
// add bytes to input buffer
_input.putBytes(msg);
// process bytes
_update(_state, _w, _input);
// compact input buffer every 2K or if empty
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
* Produces the digest.
*
* @return a byte buffer containing the digest value.
*/
md.digest = function() {
/* Note: Here we copy the remaining bytes in the input buffer and
add the appropriate MD5 padding. Then we do the final update
on a copy of the state so that if the user wants to get
intermediate digests they can do so. */
/* Determine the number of bytes that must be added to the message
to ensure its length is congruent to 448 mod 512. In other words,
the data to be digested must be a multiple of 512 bits (or 128 bytes).
This data includes the message, some padding, and the length of the
message. Since the length of the message will be encoded as 8 bytes (64
bits), that means that the last segment of the data must have 56 bytes
(448 bits) of message and padding. Therefore, the length of the message
plus the padding must be congruent to 448 mod 512 because
512 - 128 = 448.
In order to fill up the message length it must be filled with
padding that begins with 1 bit followed by all 0 bits. Padding
must *always* be present, so if the message length is already
congruent to 448 mod 512, then 512 padding bits must be added. */
var finalBlock = forge.util.createBuffer();
finalBlock.putBytes(_input.bytes());
// compute remaining size to be digested (include message length size)
var remaining = (
md.fullMessageLength[md.fullMessageLength.length - 1] +
md.messageLengthSize);
// add padding for overflow blockSize - overflow
// _padding starts with 1 byte with first bit is set (byte value 128), then
// there may be up to (blockSize - 1) other pad bytes
var overflow = remaining & (md.blockLength - 1);
finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));
// serialize message length in bits in little-endian order; since length
// is stored in bytes we multiply by 8 and add carry
var bits, carry = 0;
for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
bits = md.fullMessageLength[i] * 8 + carry;
carry = (bits / 0x100000000) >>> 0;
finalBlock.putInt32Le(bits >>> 0);
}
var s2 = {
h0: _state.h0,
h1: _state.h1,
h2: _state.h2,
h3: _state.h3
};
_update(s2, _w, finalBlock);
var rval = forge.util.createBuffer();
rval.putInt32Le(s2.h0);
rval.putInt32Le(s2.h1);
rval.putInt32Le(s2.h2);
rval.putInt32Le(s2.h3);
return rval;
};
return md;
};
// padding, constant tables for calculating md5
var _padding = null;
var _g = null;
var _r = null;
var _k = null;
var _initialized = false;
/**
* Initializes the constant tables.
*/
function _init() {
// create padding
_padding = String.fromCharCode(128);
_padding += forge.util.fillString(String.fromCharCode(0x00), 64);
// g values
_g = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9];
// rounds table
_r = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];
// get the result of abs(sin(i + 1)) as a 32-bit integer
_k = new Array(64);
for(var i = 0; i < 64; ++i) {
_k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000);
}
// now initialized
_initialized = true;
}
/**
* Updates an MD5 state with the given byte buffer.
*
* @param s the MD5 state to update.
* @param w the array to use to store words.
* @param bytes the byte buffer to update with.
*/
function _update(s, w, bytes) {
// consume 512 bit (64 byte) chunks
var t, a, b, c, d, f, r, i;
var len = bytes.length();
while(len >= 64) {
// initialize hash value for this chunk
a = s.h0;
b = s.h1;
c = s.h2;
d = s.h3;
// round 1
for(i = 0; i < 16; ++i) {
w[i] = bytes.getInt32Le();
f = d ^ (b & (c ^ d));
t = (a + f + _k[i] + w[i]);
r = _r[i];
a = d;
d = c;
c = b;
b += (t << r) | (t >>> (32 - r));
}
// round 2
for(; i < 32; ++i) {
f = c ^ (d & (b ^ c));
t = (a + f + _k[i] + w[_g[i]]);
r = _r[i];
a = d;
d = c;
c = b;
b += (t << r) | (t >>> (32 - r));
}
// round 3
for(; i < 48; ++i) {
f = b ^ c ^ d;
t = (a + f + _k[i] + w[_g[i]]);
r = _r[i];
a = d;
d = c;
c = b;
b += (t << r) | (t >>> (32 - r));
}
// round 4
for(; i < 64; ++i) {
f = c ^ (b | ~d);
t = (a + f + _k[i] + w[_g[i]]);
r = _r[i];
a = d;
d = c;
c = b;
b += (t << r) | (t >>> (32 - r));
}
// update hash state
s.h0 = (s.h0 + a) | 0;
s.h1 = (s.h1 + b) | 0;
s.h2 = (s.h2 + c) | 0;
s.h3 = (s.h3 + d) | 0;
len -= 64;
}
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'md5';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

67
forge.js/mgf.js Normal file
View File

@@ -0,0 +1,67 @@
/**
* Node.js module for Forge mask generation functions.
*
* @author Stefan Siegl
*
* Copyright 2012 Stefan Siegl <stesie@brokenpipe.de>
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.mgf = forge.mgf || {};
forge.mgf.mgf1 = forge.mgf1;
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'mgf';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './mgf1'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

112
forge.js/mgf1.js Normal file
View File

@@ -0,0 +1,112 @@
/**
* Javascript implementation of mask generation function MGF1.
*
* @author Stefan Siegl
* @author Dave Longley
*
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
* Copyright (c) 2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.mgf = forge.mgf || {};
var mgf1 = forge.mgf.mgf1 = forge.mgf1 = forge.mgf1 || {};
/**
* Creates a MGF1 mask generation function object.
*
* @param md the message digest API to use (eg: forge.md.sha1.create()).
*
* @return a mask generation function object.
*/
mgf1.create = function(md) {
var mgf = {
/**
* Generate mask of specified length.
*
* @param {String} seed The seed for mask generation.
* @param maskLen Number of bytes to generate.
* @return {String} The generated mask.
*/
generate: function(seed, maskLen) {
/* 2. Let T be the empty octet string. */
var t = new forge.util.ByteBuffer();
/* 3. For counter from 0 to ceil(maskLen / hLen), do the following: */
var len = Math.ceil(maskLen / md.digestLength);
for(var i = 0; i < len; i++) {
/* a. Convert counter to an octet string C of length 4 octets */
var c = new forge.util.ByteBuffer();
c.putInt32(i);
/* b. Concatenate the hash of the seed mgfSeed and C to the octet
* string T: */
md.start();
md.update(seed + c.getBytes());
t.putBuffer(md.digest());
}
/* Output the leading maskLen octets of T as the octet string mask. */
t.truncate(t.length() - maskLen);
return t.getBytes();
}
};
return mgf;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'mgf1';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

269
forge.js/oids.js Normal file
View File

@@ -0,0 +1,269 @@
/**
* Object IDs for ASN.1.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.pki = forge.pki || {};
var oids = forge.pki.oids = forge.oids = forge.oids || {};
// algorithm OIDs
oids['1.2.840.113549.1.1.1'] = 'rsaEncryption';
oids['rsaEncryption'] = '1.2.840.113549.1.1.1';
// Note: md2 & md4 not implemented
//oids['1.2.840.113549.1.1.2'] = 'md2WithRSAEncryption';
//oids['md2WithRSAEncryption'] = '1.2.840.113549.1.1.2';
//oids['1.2.840.113549.1.1.3'] = 'md4WithRSAEncryption';
//oids['md4WithRSAEncryption'] = '1.2.840.113549.1.1.3';
oids['1.2.840.113549.1.1.4'] = 'md5WithRSAEncryption';
oids['md5WithRSAEncryption'] = '1.2.840.113549.1.1.4';
oids['1.2.840.113549.1.1.5'] = 'sha1WithRSAEncryption';
oids['sha1WithRSAEncryption'] = '1.2.840.113549.1.1.5';
oids['1.2.840.113549.1.1.7'] = 'RSAES-OAEP';
oids['RSAES-OAEP'] = '1.2.840.113549.1.1.7';
oids['1.2.840.113549.1.1.8'] = 'mgf1';
oids['mgf1'] = '1.2.840.113549.1.1.8';
oids['1.2.840.113549.1.1.9'] = 'pSpecified';
oids['pSpecified'] = '1.2.840.113549.1.1.9';
oids['1.2.840.113549.1.1.10'] = 'RSASSA-PSS';
oids['RSASSA-PSS'] = '1.2.840.113549.1.1.10';
oids['1.2.840.113549.1.1.11'] = 'sha256WithRSAEncryption';
oids['sha256WithRSAEncryption'] = '1.2.840.113549.1.1.11';
oids['1.2.840.113549.1.1.12'] = 'sha384WithRSAEncryption';
oids['sha384WithRSAEncryption'] = '1.2.840.113549.1.1.12';
oids['1.2.840.113549.1.1.13'] = 'sha512WithRSAEncryption';
oids['sha512WithRSAEncryption'] = '1.2.840.113549.1.1.13';
oids['1.3.14.3.2.7'] = 'desCBC';
oids['desCBC'] = '1.3.14.3.2.7';
oids['1.3.14.3.2.26'] = 'sha1';
oids['sha1'] = '1.3.14.3.2.26';
oids['2.16.840.1.101.3.4.2.1'] = 'sha256';
oids['sha256'] = '2.16.840.1.101.3.4.2.1';
oids['2.16.840.1.101.3.4.2.2'] = 'sha384';
oids['sha384'] = '2.16.840.1.101.3.4.2.2';
oids['2.16.840.1.101.3.4.2.3'] = 'sha512';
oids['sha512'] = '2.16.840.1.101.3.4.2.3';
oids['1.2.840.113549.2.5'] = 'md5';
oids['md5'] = '1.2.840.113549.2.5';
// pkcs#7 content types
oids['1.2.840.113549.1.7.1'] = 'data';
oids['data'] = '1.2.840.113549.1.7.1';
oids['1.2.840.113549.1.7.2'] = 'signedData';
oids['signedData'] = '1.2.840.113549.1.7.2';
oids['1.2.840.113549.1.7.3'] = 'envelopedData';
oids['envelopedData'] = '1.2.840.113549.1.7.3';
oids['1.2.840.113549.1.7.4'] = 'signedAndEnvelopedData';
oids['signedAndEnvelopedData'] = '1.2.840.113549.1.7.4';
oids['1.2.840.113549.1.7.5'] = 'digestedData';
oids['digestedData'] = '1.2.840.113549.1.7.5';
oids['1.2.840.113549.1.7.6'] = 'encryptedData';
oids['encryptedData'] = '1.2.840.113549.1.7.6';
// pkcs#9 oids
oids['1.2.840.113549.1.9.1'] = 'emailAddress';
oids['emailAddress'] = '1.2.840.113549.1.9.1';
oids['1.2.840.113549.1.9.2'] = 'unstructuredName';
oids['unstructuredName'] = '1.2.840.113549.1.9.2';
oids['1.2.840.113549.1.9.3'] = 'contentType';
oids['contentType'] = '1.2.840.113549.1.9.3';
oids['1.2.840.113549.1.9.4'] = 'messageDigest';
oids['messageDigest'] = '1.2.840.113549.1.9.4';
oids['1.2.840.113549.1.9.5'] = 'signingTime';
oids['signingTime'] = '1.2.840.113549.1.9.5';
oids['1.2.840.113549.1.9.6'] = 'counterSignature';
oids['counterSignature'] = '1.2.840.113549.1.9.6';
oids['1.2.840.113549.1.9.7'] = 'challengePassword';
oids['challengePassword'] = '1.2.840.113549.1.9.7';
oids['1.2.840.113549.1.9.8'] = 'unstructuredAddress';
oids['unstructuredAddress'] = '1.2.840.113549.1.9.8';
oids['1.2.840.113549.1.9.14'] = 'extensionRequest';
oids['extensionRequest'] = '1.2.840.113549.1.9.14';
oids['1.2.840.113549.1.9.20'] = 'friendlyName';
oids['friendlyName'] = '1.2.840.113549.1.9.20';
oids['1.2.840.113549.1.9.21'] = 'localKeyId';
oids['localKeyId'] = '1.2.840.113549.1.9.21';
oids['1.2.840.113549.1.9.22.1'] = 'x509Certificate';
oids['x509Certificate'] = '1.2.840.113549.1.9.22.1';
// pkcs#12 safe bags
oids['1.2.840.113549.1.12.10.1.1'] = 'keyBag';
oids['keyBag'] = '1.2.840.113549.1.12.10.1.1';
oids['1.2.840.113549.1.12.10.1.2'] = 'pkcs8ShroudedKeyBag';
oids['pkcs8ShroudedKeyBag'] = '1.2.840.113549.1.12.10.1.2';
oids['1.2.840.113549.1.12.10.1.3'] = 'certBag';
oids['certBag'] = '1.2.840.113549.1.12.10.1.3';
oids['1.2.840.113549.1.12.10.1.4'] = 'crlBag';
oids['crlBag'] = '1.2.840.113549.1.12.10.1.4';
oids['1.2.840.113549.1.12.10.1.5'] = 'secretBag';
oids['secretBag'] = '1.2.840.113549.1.12.10.1.5';
oids['1.2.840.113549.1.12.10.1.6'] = 'safeContentsBag';
oids['safeContentsBag'] = '1.2.840.113549.1.12.10.1.6';
// password-based-encryption for pkcs#12
oids['1.2.840.113549.1.5.13'] = 'pkcs5PBES2';
oids['pkcs5PBES2'] = '1.2.840.113549.1.5.13';
oids['1.2.840.113549.1.5.12'] = 'pkcs5PBKDF2';
oids['pkcs5PBKDF2'] = '1.2.840.113549.1.5.12';
oids['1.2.840.113549.1.12.1.1'] = 'pbeWithSHAAnd128BitRC4';
oids['pbeWithSHAAnd128BitRC4'] = '1.2.840.113549.1.12.1.1';
oids['1.2.840.113549.1.12.1.2'] = 'pbeWithSHAAnd40BitRC4';
oids['pbeWithSHAAnd40BitRC4'] = '1.2.840.113549.1.12.1.2';
oids['1.2.840.113549.1.12.1.3'] = 'pbeWithSHAAnd3-KeyTripleDES-CBC';
oids['pbeWithSHAAnd3-KeyTripleDES-CBC'] = '1.2.840.113549.1.12.1.3';
oids['1.2.840.113549.1.12.1.4'] = 'pbeWithSHAAnd2-KeyTripleDES-CBC';
oids['pbeWithSHAAnd2-KeyTripleDES-CBC'] = '1.2.840.113549.1.12.1.4';
oids['1.2.840.113549.1.12.1.5'] = 'pbeWithSHAAnd128BitRC2-CBC';
oids['pbeWithSHAAnd128BitRC2-CBC'] = '1.2.840.113549.1.12.1.5';
oids['1.2.840.113549.1.12.1.6'] = 'pbewithSHAAnd40BitRC2-CBC';
oids['pbewithSHAAnd40BitRC2-CBC'] = '1.2.840.113549.1.12.1.6';
// symmetric key algorithm oids
oids['1.2.840.113549.3.7'] = 'des-EDE3-CBC';
oids['des-EDE3-CBC'] = '1.2.840.113549.3.7';
oids['2.16.840.1.101.3.4.1.2'] = 'aes128-CBC';
oids['aes128-CBC'] = '2.16.840.1.101.3.4.1.2';
oids['2.16.840.1.101.3.4.1.22'] = 'aes192-CBC';
oids['aes192-CBC'] = '2.16.840.1.101.3.4.1.22';
oids['2.16.840.1.101.3.4.1.42'] = 'aes256-CBC';
oids['aes256-CBC'] = '2.16.840.1.101.3.4.1.42';
// certificate issuer/subject OIDs
oids['2.5.4.3'] = 'commonName';
oids['commonName'] = '2.5.4.3';
oids['2.5.4.5'] = 'serialName';
oids['serialName'] = '2.5.4.5';
oids['2.5.4.6'] = 'countryName';
oids['countryName'] = '2.5.4.6';
oids['2.5.4.7'] = 'localityName';
oids['localityName'] = '2.5.4.7';
oids['2.5.4.8'] = 'stateOrProvinceName';
oids['stateOrProvinceName'] = '2.5.4.8';
oids['2.5.4.10'] = 'organizationName';
oids['organizationName'] = '2.5.4.10';
oids['2.5.4.11'] = 'organizationalUnitName';
oids['organizationalUnitName'] = '2.5.4.11';
// X.509 extension OIDs
oids['2.16.840.1.113730.1.1'] = 'nsCertType';
oids['nsCertType'] = '2.16.840.1.113730.1.1';
oids['2.5.29.1'] = 'authorityKeyIdentifier'; // deprecated, use .35
oids['2.5.29.2'] = 'keyAttributes'; // obsolete use .37 or .15
oids['2.5.29.3'] = 'certificatePolicies'; // deprecated, use .32
oids['2.5.29.4'] = 'keyUsageRestriction'; // obsolete use .37 or .15
oids['2.5.29.5'] = 'policyMapping'; // deprecated use .33
oids['2.5.29.6'] = 'subtreesConstraint'; // obsolete use .30
oids['2.5.29.7'] = 'subjectAltName'; // deprecated use .17
oids['2.5.29.8'] = 'issuerAltName'; // deprecated use .18
oids['2.5.29.9'] = 'subjectDirectoryAttributes';
oids['2.5.29.10'] = 'basicConstraints'; // deprecated use .19
oids['2.5.29.11'] = 'nameConstraints'; // deprecated use .30
oids['2.5.29.12'] = 'policyConstraints'; // deprecated use .36
oids['2.5.29.13'] = 'basicConstraints'; // deprecated use .19
oids['2.5.29.14'] = 'subjectKeyIdentifier';
oids['subjectKeyIdentifier'] = '2.5.29.14';
oids['2.5.29.15'] = 'keyUsage';
oids['keyUsage'] = '2.5.29.15';
oids['2.5.29.16'] = 'privateKeyUsagePeriod';
oids['2.5.29.17'] = 'subjectAltName';
oids['subjectAltName'] = '2.5.29.17';
oids['2.5.29.18'] = 'issuerAltName';
oids['issuerAltName'] = '2.5.29.18';
oids['2.5.29.19'] = 'basicConstraints';
oids['basicConstraints'] = '2.5.29.19';
oids['2.5.29.20'] = 'cRLNumber';
oids['2.5.29.21'] = 'cRLReason';
oids['2.5.29.22'] = 'expirationDate';
oids['2.5.29.23'] = 'instructionCode';
oids['2.5.29.24'] = 'invalidityDate';
oids['2.5.29.25'] = 'cRLDistributionPoints'; // deprecated use .31
oids['2.5.29.26'] = 'issuingDistributionPoint'; // deprecated use .28
oids['2.5.29.27'] = 'deltaCRLIndicator';
oids['2.5.29.28'] = 'issuingDistributionPoint';
oids['2.5.29.29'] = 'certificateIssuer';
oids['2.5.29.30'] = 'nameConstraints';
oids['2.5.29.31'] = 'cRLDistributionPoints';
oids['2.5.29.32'] = 'certificatePolicies';
oids['2.5.29.33'] = 'policyMappings';
oids['2.5.29.34'] = 'policyConstraints'; // deprecated use .36
oids['2.5.29.35'] = 'authorityKeyIdentifier';
oids['2.5.29.36'] = 'policyConstraints';
oids['2.5.29.37'] = 'extKeyUsage';
oids['extKeyUsage'] = '2.5.29.37';
oids['2.5.29.46'] = 'freshestCRL';
oids['2.5.29.54'] = 'inhibitAnyPolicy';
// extKeyUsage purposes
oids['1.3.6.1.5.5.7.3.1'] = 'serverAuth';
oids['serverAuth'] = '1.3.6.1.5.5.7.3.1';
oids['1.3.6.1.5.5.7.3.2'] = 'clientAuth';
oids['clientAuth'] = '1.3.6.1.5.5.7.3.2';
oids['1.3.6.1.5.5.7.3.3'] = 'codeSigning';
oids['codeSigning'] = '1.3.6.1.5.5.7.3.3';
oids['1.3.6.1.5.5.7.3.4'] = 'emailProtection';
oids['emailProtection'] = '1.3.6.1.5.5.7.3.4';
oids['1.3.6.1.5.5.7.3.8'] = 'timeStamping';
oids['timeStamping'] = '1.3.6.1.5.5.7.3.8';
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'oids';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

975
forge.js/pbe.js Normal file
View File

@@ -0,0 +1,975 @@
/**
* Password-based encryption functions.
*
* @author Dave Longley
* @author Stefan Siegl <stesie@brokenpipe.de>
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
*
* An EncryptedPrivateKeyInfo:
*
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm EncryptionAlgorithmIdentifier,
* encryptedData EncryptedData }
*
* EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
*
* EncryptedData ::= OCTET STRING
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
if(typeof BigInteger === 'undefined') {
var BigInteger = forge.jsbn.BigInteger;
}
// shortcut for asn.1 API
var asn1 = forge.asn1;
/* Password-based encryption implementation. */
var pki = forge.pki = forge.pki || {};
pki.pbe = forge.pbe = forge.pbe || {};
var oids = pki.oids;
// validator for an EncryptedPrivateKeyInfo structure
// Note: Currently only works w/algorithm params
var encryptedPrivateKeyValidator = {
name: 'EncryptedPrivateKeyInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'EncryptedPrivateKeyInfo.encryptionAlgorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'AlgorithmIdentifier.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'encryptionOid'
}, {
name: 'AlgorithmIdentifier.parameters',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'encryptionParams'
}]
}, {
// encryptedData
name: 'EncryptedPrivateKeyInfo.encryptedData',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'encryptedData'
}]
};
// validator for a PBES2Algorithms structure
// Note: Currently only works w/PBKDF2 + AES encryption schemes
var PBES2AlgorithmsValidator = {
name: 'PBES2Algorithms',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'PBES2Algorithms.keyDerivationFunc',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'PBES2Algorithms.keyDerivationFunc.oid',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'kdfOid'
}, {
name: 'PBES2Algorithms.params',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'PBES2Algorithms.params.salt',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'kdfSalt'
}, {
name: 'PBES2Algorithms.params.iterationCount',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
onstructed: true,
capture: 'kdfIterationCount'
}]
}]
}, {
name: 'PBES2Algorithms.encryptionScheme',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'PBES2Algorithms.encryptionScheme.oid',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'encOid'
}, {
name: 'PBES2Algorithms.encryptionScheme.iv',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'encIv'
}]
}]
};
var pkcs12PbeParamsValidator = {
name: 'pkcs-12PbeParams',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'pkcs-12PbeParams.salt',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'salt'
}, {
name: 'pkcs-12PbeParams.iterations',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'iterations'
}]
};
/**
* Encrypts a ASN.1 PrivateKeyInfo object, producing an EncryptedPrivateKeyInfo.
*
* PBES2Algorithms ALGORITHM-IDENTIFIER ::=
* { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
*
* id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
*
* PBES2-params ::= SEQUENCE {
* keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}
* }
*
* PBES2-KDFs ALGORITHM-IDENTIFIER ::=
* { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
*
* PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
*
* PBKDF2-params ::= SEQUENCE {
* salt CHOICE {
* specified OCTET STRING,
* otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
* },
* iterationCount INTEGER (1..MAX),
* keyLength INTEGER (1..MAX) OPTIONAL,
* prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
* }
*
* @param obj the ASN.1 PrivateKeyInfo object.
* @param password the password to encrypt with.
* @param options:
* algorithm the encryption algorithm to use
* ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
* count the iteration count to use.
* saltSize the salt size to use.
*
* @return the ASN.1 EncryptedPrivateKeyInfo.
*/
pki.encryptPrivateKeyInfo = function(obj, password, options) {
// set default options
options = options || {};
options.saltSize = options.saltSize || 8;
options.count = options.count || 2048;
options.algorithm = options.algorithm || 'aes128';
// generate PBE params
var salt = forge.random.getBytesSync(options.saltSize);
var count = options.count;
var countBytes = asn1.integerToDer(count);
var dkLen;
var encryptionAlgorithm;
var encryptedData;
if(options.algorithm.indexOf('aes') === 0 || options.algorithm === 'des') {
// Do PBES2
var ivLen, encOid, cipherFn;
switch(options.algorithm) {
case 'aes128':
dkLen = 16;
ivLen = 16;
encOid = oids['aes128-CBC'];
cipherFn = forge.aes.createEncryptionCipher;
break;
case 'aes192':
dkLen = 24;
ivLen = 16;
encOid = oids['aes192-CBC'];
cipherFn = forge.aes.createEncryptionCipher;
break;
case 'aes256':
dkLen = 32;
ivLen = 16;
encOid = oids['aes256-CBC'];
cipherFn = forge.aes.createEncryptionCipher;
break;
case 'des':
dkLen = 8;
ivLen = 8;
encOid = oids['desCBC'];
cipherFn = forge.des.createEncryptionCipher;
break;
default:
var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');
error.algorithm = options.algorithm;
throw error;
}
// encrypt private key using pbe SHA-1 and AES/DES
var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen);
var iv = forge.random.getBytesSync(ivLen);
var cipher = cipherFn(dk);
cipher.start(iv);
cipher.update(asn1.toDer(obj));
cipher.finish();
encryptedData = cipher.output.getBytes();
encryptionAlgorithm = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(oids['pkcs5PBES2']).getBytes()),
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// keyDerivationFunc
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(oids['pkcs5PBKDF2']).getBytes()),
// PBKDF2-params
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// salt
asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
// iteration count
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
countBytes.getBytes())
])
]),
// encryptionScheme
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(encOid).getBytes()),
// iv
asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, iv)
])
])
]);
} else if(options.algorithm === '3des') {
// Do PKCS12 PBE
dkLen = 24;
var saltBytes = new forge.util.ByteBuffer(salt);
var dk = pki.pbe.generatePkcs12Key(password, saltBytes, 1, count, dkLen);
var iv = pki.pbe.generatePkcs12Key(password, saltBytes, 2, count, dkLen);
var cipher = forge.des.createEncryptionCipher(dk);
cipher.start(iv);
cipher.update(asn1.toDer(obj));
cipher.finish();
encryptedData = cipher.output.getBytes();
encryptionAlgorithm = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(oids['pbeWithSHAAnd3-KeyTripleDES-CBC']).getBytes()),
// pkcs-12PbeParams
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// salt
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
// iteration count
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
countBytes.getBytes())
])
]);
} else {
var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');
error.algorithm = options.algorithm;
throw error;
}
// EncryptedPrivateKeyInfo
var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// encryptionAlgorithm
encryptionAlgorithm,
// encryptedData
asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, encryptedData)
]);
return rval;
};
/**
* Decrypts a ASN.1 PrivateKeyInfo object.
*
* @param obj the ASN.1 EncryptedPrivateKeyInfo object.
* @param password the password to decrypt with.
*
* @return the ASN.1 PrivateKeyInfo on success, null on failure.
*/
pki.decryptPrivateKeyInfo = function(obj, password) {
var rval = null;
// get PBE params
var capture = {};
var errors = [];
if(!asn1.validate(obj, encryptedPrivateKeyValidator, capture, errors)) {
var error = new Error('Cannot read encrypted private key. ' +
'ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
error.errors = errors;
throw error;
}
// get cipher
var oid = asn1.derToOid(capture.encryptionOid);
var cipher = pki.pbe.getCipher(oid, capture.encryptionParams, password);
// get encrypted data
var encrypted = forge.util.createBuffer(capture.encryptedData);
cipher.update(encrypted);
if(cipher.finish()) {
rval = asn1.fromDer(cipher.output);
}
return rval;
};
/**
* Converts a EncryptedPrivateKeyInfo to PEM format.
*
* @param epki the EncryptedPrivateKeyInfo.
* @param maxline the maximum characters per line, defaults to 64.
*
* @return the PEM-formatted encrypted private key.
*/
pki.encryptedPrivateKeyToPem = function(epki, maxline) {
// convert to DER, then PEM-encode
var msg = {
type: 'ENCRYPTED PRIVATE KEY',
body: asn1.toDer(epki).getBytes()
};
return forge.pem.encode(msg, {maxline: maxline});
};
/**
* Converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format. Decryption
* is not performed.
*
* @param pem the EncryptedPrivateKeyInfo in PEM-format.
*
* @return the ASN.1 EncryptedPrivateKeyInfo.
*/
pki.encryptedPrivateKeyFromPem = function(pem) {
var msg = forge.pem.decode(pem)[0];
if(msg.type !== 'ENCRYPTED PRIVATE KEY') {
var error = new Error('Could not convert encrypted private key from PEM; ' +
'PEM header type is "ENCRYPTED PRIVATE KEY".');
error.headerType = msg.type;
throw error;
}
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
throw new Error('Could not convert encrypted private key from PEM; ' +
'PEM is encrypted.');
}
// convert DER to ASN.1 object
return asn1.fromDer(msg.body);
};
/**
* Encrypts an RSA private key. By default, the key will be wrapped in
* a PrivateKeyInfo and encrypted to produce a PKCS#8 EncryptedPrivateKeyInfo.
* This is the standard, preferred way to encrypt a private key.
*
* To produce a non-standard PEM-encrypted private key that uses encapsulated
* headers to indicate the encryption algorithm (old-style non-PKCS#8 OpenSSL
* private key encryption), set the 'legacy' option to true. Note: Using this
* option will cause the iteration count to be forced to 1.
*
* Note: The 'des' algorithm is supported, but it is not considered to be
* secure because it only uses a single 56-bit key. If possible, it is highly
* recommended that a different algorithm be used.
*
* @param rsaKey the RSA key to encrypt.
* @param password the password to use.
* @param options:
* algorithm: the encryption algorithm to use
* ('aes128', 'aes192', 'aes256', '3des', 'des').
* count: the iteration count to use.
* saltSize: the salt size to use.
* legacy: output an old non-PKCS#8 PEM-encrypted+encapsulated
* headers (DEK-Info) private key.
*
* @return the PEM-encoded ASN.1 EncryptedPrivateKeyInfo.
*/
pki.encryptRsaPrivateKey = function(rsaKey, password, options) {
// standard PKCS#8
options = options || {};
if(!options.legacy) {
// encrypt PrivateKeyInfo
var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));
rval = pki.encryptPrivateKeyInfo(rval, password, options);
return pki.encryptedPrivateKeyToPem(rval);
}
// legacy non-PKCS#8
var algorithm;
var iv;
var dkLen;
var cipherFn;
switch(options.algorithm) {
case 'aes128':
algorithm = 'AES-128-CBC';
dkLen = 16;
iv = forge.random.getBytesSync(16);
cipherFn = forge.aes.createEncryptionCipher;
break;
case 'aes192':
algorithm = 'AES-192-CBC';
dkLen = 24;
iv = forge.random.getBytesSync(16);
cipherFn = forge.aes.createEncryptionCipher;
break;
case 'aes256':
algorithm = 'AES-256-CBC';
dkLen = 32;
iv = forge.random.getBytesSync(16);
cipherFn = forge.aes.createEncryptionCipher;
break;
case '3des':
algorithm = 'DES-EDE3-CBC';
dkLen = 24;
iv = forge.random.getBytesSync(8);
cipherFn = forge.des.createEncryptionCipher;
break;
case 'des':
algorithm = 'DES-CBC';
dkLen = 8;
iv = forge.random.getBytesSync(8);
cipherFn = forge.des.createEncryptionCipher;
break;
default:
var error = new Error('Could not encrypt RSA private key; unsupported ' +
'encryption algorithm "' + options.algorithm + '".');
error.algorithm = options.algorithm;
throw error;
}
// encrypt private key using OpenSSL legacy key derivation
var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);
var cipher = cipherFn(dk);
cipher.start(iv);
cipher.update(asn1.toDer(pki.privateKeyToAsn1(rsaKey)));
cipher.finish();
var msg = {
type: 'RSA PRIVATE KEY',
procType: {
version: '4',
type: 'ENCRYPTED'
},
dekInfo: {
algorithm: algorithm,
parameters: forge.util.bytesToHex(iv).toUpperCase()
},
body: cipher.output.getBytes()
};
return forge.pem.encode(msg);
};
/**
* Decrypts an RSA private key.
*
* @param pem the PEM-formatted EncryptedPrivateKeyInfo to decrypt.
* @param password the password to use.
*
* @return the RSA key on success, null on failure.
*/
pki.decryptRsaPrivateKey = function(pem, password) {
var rval = null;
var msg = forge.pem.decode(pem)[0];
if(msg.type !== 'ENCRYPTED PRIVATE KEY' &&
msg.type !== 'PRIVATE KEY' &&
msg.type !== 'RSA PRIVATE KEY') {
var error = new Error('Could not convert private key from PEM; PEM header type ' +
'is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".');
error.headerType = error;
throw error;
}
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
var dkLen;
var cipherFn;
switch(msg.dekInfo.algorithm) {
case 'DES-CBC':
dkLen = 8;
cipherFn = forge.des.createDecryptionCipher;
break;
case 'DES-EDE3-CBC':
dkLen = 24;
cipherFn = forge.des.createDecryptionCipher;
break;
case 'AES-128-CBC':
dkLen = 16;
cipherFn = forge.aes.createDecryptionCipher;
break;
case 'AES-192-CBC':
dkLen = 24;
cipherFn = forge.aes.createDecryptionCipher;
break;
case 'AES-256-CBC':
dkLen = 32;
cipherFn = forge.aes.createDecryptionCipher;
break;
case 'RC2-40-CBC':
dkLen = 5;
cipherFn = function(key) {
return forge.rc2.createDecryptionCipher(key, 40);
};
break;
case 'RC2-64-CBC':
dkLen = 8;
cipherFn = function(key) {
return forge.rc2.createDecryptionCipher(key, 64);
};
break;
case 'RC2-128-CBC':
dkLen = 16;
cipherFn = function(key) {
return forge.rc2.createDecryptionCipher(key, 128);
};
break;
default:
var error = new Error('Could not decrypt private key; unsupported ' +
'encryption algorithm "' + msg.dekInfo.algorithm + '".');
error.algorithm = msg.dekInfo.algorithm;
throw error;
}
// use OpenSSL legacy key derivation
var iv = forge.util.hexToBytes(msg.dekInfo.parameters);
var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);
var cipher = cipherFn(dk);
cipher.start(iv);
cipher.update(forge.util.createBuffer(msg.body));
if(cipher.finish()) {
rval = cipher.output.getBytes();
} else {
return rval;
}
} else {
rval = msg.body;
}
if(msg.type === 'ENCRYPTED PRIVATE KEY') {
rval = pki.decryptPrivateKeyInfo(asn1.fromDer(rval), password);
} else {
// decryption already performed above
rval = asn1.fromDer(rval);
}
if(rval !== null) {
rval = pki.privateKeyFromAsn1(rval);
}
return rval;
};
/**
* Derives a PKCS#12 key.
*
* @param password the password to derive the key material from, null or
* undefined for none.
* @param salt the salt, as a ByteBuffer, to use.
* @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
* @param iter the iteration count.
* @param n the number of bytes to derive from the password.
* @param md the message digest to use, defaults to SHA-1.
*
* @return a ByteBuffer with the bytes derived from the password.
*/
pki.pbe.generatePkcs12Key = function(password, salt, id, iter, n, md) {
var j, l;
if(typeof md === 'undefined' || md === null) {
md = forge.md.sha1.create();
}
var u = md.digestLength;
var v = md.blockLength;
var result = new forge.util.ByteBuffer();
/* Convert password to Unicode byte buffer + trailing 0-byte. */
var passBuf = new forge.util.ByteBuffer();
if(password !== null && password !== undefined) {
for(l = 0; l < password.length; l++) {
passBuf.putInt16(password.charCodeAt(l));
}
passBuf.putInt16(0);
}
/* Length of salt and password in BYTES. */
var p = passBuf.length();
var s = salt.length();
/* 1. Construct a string, D (the "diversifier"), by concatenating
v copies of ID. */
var D = new forge.util.ByteBuffer();
D.fillWithByte(id, v);
/* 2. Concatenate copies of the salt together to create a string S of length
v * ceil(s / v) bytes (the final copy of the salt may be trunacted
to create S).
Note that if the salt is the empty string, then so is S. */
var Slen = v * Math.ceil(s / v);
var S = new forge.util.ByteBuffer();
for(l = 0; l < Slen; l ++) {
S.putByte(salt.at(l % s));
}
/* 3. Concatenate copies of the password together to create a string P of
length v * ceil(p / v) bytes (the final copy of the password may be
truncated to create P).
Note that if the password is the empty string, then so is P. */
var Plen = v * Math.ceil(p / v);
var P = new forge.util.ByteBuffer();
for(l = 0; l < Plen; l ++) {
P.putByte(passBuf.at(l % p));
}
/* 4. Set I=S||P to be the concatenation of S and P. */
var I = S;
I.putBuffer(P);
/* 5. Set c=ceil(n / u). */
var c = Math.ceil(n / u);
/* 6. For i=1, 2, ..., c, do the following: */
for(var i = 1; i <= c; i ++) {
/* a) Set Ai=H^r(D||I). (l.e. the rth hash of D||I, H(H(H(...H(D||I)))) */
var buf = new forge.util.ByteBuffer();
buf.putBytes(D.bytes());
buf.putBytes(I.bytes());
for(var round = 0; round < iter; round ++) {
md.start();
md.update(buf.getBytes());
buf = md.digest();
}
/* b) Concatenate copies of Ai to create a string B of length v bytes (the
final copy of Ai may be truncated to create B). */
var B = new forge.util.ByteBuffer();
for(l = 0; l < v; l ++) {
B.putByte(buf.at(l % u));
}
/* c) Treating I as a concatenation I0, I1, ..., Ik-1 of v-byte blocks,
where k=ceil(s / v) + ceil(p / v), modify I by setting
Ij=(Ij+B+1) mod 2v for each j. */
var k = Math.ceil(s / v) + Math.ceil(p / v);
var Inew = new forge.util.ByteBuffer();
for(j = 0; j < k; j ++) {
var chunk = new forge.util.ByteBuffer(I.getBytes(v));
var x = 0x1ff;
for(l = B.length() - 1; l >= 0; l --) {
x = x >> 8;
x += B.at(l) + chunk.at(l);
chunk.setAt(l, x & 0xff);
}
Inew.putBuffer(chunk);
}
I = Inew;
/* Add Ai to A. */
result.putBuffer(buf);
}
result.truncate(result.length() - n);
return result;
};
/**
* Get new Forge cipher object instance.
*
* @param oid the OID (in string notation).
* @param params the ASN.1 params object.
* @param password the password to decrypt with.
*
* @return new cipher object instance.
*/
pki.pbe.getCipher = function(oid, params, password) {
switch(oid) {
case pki.oids['pkcs5PBES2']:
return pki.pbe.getCipherForPBES2(oid, params, password);
case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
return pki.pbe.getCipherForPKCS12PBE(oid, params, password);
default:
var error = new Error('Cannot read encrypted PBE data block. Unsupported OID.');
error.oid = oid;
error.supportedOids = [
'pkcs5PBES2',
'pbeWithSHAAnd3-KeyTripleDES-CBC',
'pbewithSHAAnd40BitRC2-CBC'
];
throw error;
}
};
/**
* Get new Forge cipher object instance according to PBES2 params block.
*
* The returned cipher instance is already started using the IV
* from PBES2 parameter block.
*
* @param oid the PKCS#5 PBKDF2 OID (in string notation).
* @param params the ASN.1 PBES2-params object.
* @param password the password to decrypt with.
*
* @return new cipher object instance.
*/
pki.pbe.getCipherForPBES2 = function(oid, params, password) {
// get PBE params
var capture = {};
var errors = [];
if(!asn1.validate(params, PBES2AlgorithmsValidator, capture, errors)) {
var error = new Error('Cannot read password-based-encryption algorithm ' +
'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
error.errors = errors;
throw error;
}
// check oids
oid = asn1.derToOid(capture.kdfOid);
if(oid !== pki.oids['pkcs5PBKDF2']) {
var error = new Error('Cannot read encrypted private key. ' +
'Unsupported key derivation function OID.');
error.oid = oid;
error.supportedOids = ['pkcs5PBKDF2'];
throw error;
}
oid = asn1.derToOid(capture.encOid);
if(oid !== pki.oids['aes128-CBC'] &&
oid !== pki.oids['aes192-CBC'] &&
oid !== pki.oids['aes256-CBC'] &&
oid !== pki.oids['des-EDE3-CBC'] &&
oid !== pki.oids['desCBC']) {
var error = new Error('Cannot read encrypted private key. ' +
'Unsupported encryption scheme OID.');
error.oid = oid;
error.supportedOids = [
'aes128-CBC', 'aes192-CBC', 'aes256-CBC', 'des-EDE3-CBC', 'desCBC'];
throw error;
}
// set PBE params
var salt = capture.kdfSalt;
var count = forge.util.createBuffer(capture.kdfIterationCount);
count = count.getInt(count.length() << 3);
var dkLen;
var cipherFn;
switch(pki.oids[oid]) {
case 'aes128-CBC':
dkLen = 16;
cipherFn = forge.aes.createDecryptionCipher;
break;
case 'aes192-CBC':
dkLen = 24;
cipherFn = forge.aes.createDecryptionCipher;
break;
case 'aes256-CBC':
dkLen = 32;
cipherFn = forge.aes.createDecryptionCipher;
break;
case 'des-EDE3-CBC':
dkLen = 24;
cipherFn = forge.des.createDecryptionCipher;
break;
case 'desCBC':
dkLen = 8;
cipherFn = forge.des.createDecryptionCipher;
break;
}
// decrypt private key using pbe SHA-1 and AES/DES
var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen);
var iv = capture.encIv;
var cipher = cipherFn(dk);
cipher.start(iv);
return cipher;
};
/**
* Get new Forge cipher object instance for PKCS#12 PBE.
*
* The returned cipher instance is already started using the key & IV
* derived from the provided password and PKCS#12 PBE salt.
*
* @param oid The PKCS#12 PBE OID (in string notation).
* @param params The ASN.1 PKCS#12 PBE-params object.
* @param password The password to decrypt with.
*
* @return the new cipher object instance.
*/
pki.pbe.getCipherForPKCS12PBE = function(oid, params, password) {
// get PBE params
var capture = {};
var errors = [];
if(!asn1.validate(params, pkcs12PbeParamsValidator, capture, errors)) {
var error = new Error('Cannot read password-based-encryption algorithm ' +
'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
error.errors = errors;
throw error;
}
var salt = forge.util.createBuffer(capture.salt);
var count = forge.util.createBuffer(capture.iterations);
count = count.getInt(count.length() << 3);
var dkLen, dIvLen, cipherFn;
switch(oid) {
case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
dkLen = 24;
dIvLen = 8;
cipherFn = forge.des.startDecrypting;
break;
case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
dkLen = 5;
dIvLen = 8;
cipherFn = function(key, iv) {
var cipher = forge.rc2.createDecryptionCipher(key, 40);
cipher.start(iv, null);
return cipher;
};
break;
default:
var error = new Error('Cannot read PKCS #12 PBE data block. Unsupported OID.');
error.oid = oid;
throw error;
}
var key = pki.pbe.generatePkcs12Key(password, salt, 1, count, dkLen);
var iv = pki.pbe.generatePkcs12Key(password, salt, 2, count, dIvLen);
return cipherFn(key, iv);
};
/**
* OpenSSL's legacy key derivation function.
*
* See: http://www.openssl.org/docs/crypto/EVP_BytesToKey.html
*
* @param password the password to derive the key from.
* @param salt the salt to use, null for none.
* @param dkLen the number of bytes needed for the derived key.
* @param [options] the options to use:
* [md] an optional message digest object to use.
*/
pki.pbe.opensslDeriveBytes = function(password, salt, dkLen, md) {
if(typeof md === 'undefined' || md === null) {
md = forge.md.md5.create();
}
if(salt === null) {
salt = '';
}
var digests = [hash(md, password + salt)];
for(var length = 16, i = 1; length < dkLen; ++i, length += 16) {
digests.push(hash(md, digests[i - 1] + password + salt));
}
return digests.join('').substr(0, dkLen);
};
function hash(md, bytes) {
return md.start().update(bytes).digest().getBytes();
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pbe';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./aes',
'./asn1',
'./des',
'./md',
'./oids',
'./pem',
'./pbkdf2',
'./random',
'./rc2',
'./rsa',
'./util'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

264
forge.js/pbkdf2.js Normal file
View File

@@ -0,0 +1,264 @@
/**
* Password-Based Key-Derivation Function #2 implementation.
*
* See RFC 2898 for details.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var pkcs5 = forge.pkcs5 = forge.pkcs5 || {};
var _nodejs = (
typeof process !== 'undefined' && process.versions && process.versions.node);
var crypto;
if(_nodejs && !forge.disableNativeCode) {
crypto = require('crypto');
}
/**
* Derives a key from a password.
*
* @param p the password as a binary-encoded string of bytes.
* @param s the salt as a binary-encoded string of bytes.
* @param c the iteration count, a positive integer.
* @param dkLen the intended length, in bytes, of the derived key,
* (max: 2^32 - 1) * hash length of the PRF.
* @param [md] the message digest (or algorithm identifier as a string) to use
* in the PRF, defaults to SHA-1.
* @param [callback(err, key)] presence triggers asynchronous version, called
* once the operation completes.
*
* @return the derived key, as a binary-encoded string of bytes, for the
* synchronous version (if no callback is specified).
*/
forge.pbkdf2 = pkcs5.pbkdf2 = function(p, s, c, dkLen, md, callback) {
if(typeof md === 'function') {
callback = md;
md = null;
}
// use native implementation if possible and not disabled, note that
// some node versions only support SHA-1, others allow digest to be changed
if(_nodejs && !forge.disableNativeCode && crypto.pbkdf2 &&
(md === null || typeof md !== 'object') &&
(crypto.pbkdf2Sync.length > 4 || (!md || md === 'sha1'))) {
if(typeof md !== 'string') {
// default prf to SHA-1
md = 'sha1';
}
s = new Buffer(s, 'binary');
if(!callback) {
if(crypto.pbkdf2Sync.length === 4) {
return crypto.pbkdf2Sync(p, s, c, dkLen).toString('binary');
}
return crypto.pbkdf2Sync(p, s, c, dkLen, md).toString('binary');
}
if(crypto.pbkdf2Sync.length === 4) {
return crypto.pbkdf2(p, s, c, dkLen, function(err, key) {
if(err) {
return callback(err);
}
callback(null, key.toString('binary'));
});
}
return crypto.pbkdf2(p, s, c, dkLen, md, function(err, key) {
if(err) {
return callback(err);
}
callback(null, key.toString('binary'));
});
}
if(typeof md === 'undefined' || md === null) {
// default prf to SHA-1
md = forge.md.sha1.create();
}
if(typeof md === 'string') {
if(!(md in forge.md.algorithms)) {
throw new Error('Unknown hash algorithm: ' + md);
}
md = forge.md[md].create();
}
var hLen = md.digestLength;
/* 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
stop. */
if(dkLen > (0xFFFFFFFF * hLen)) {
var err = new Error('Derived key is too long.');
if(callback) {
return callback(err);
}
throw err;
}
/* 2. Let len be the number of hLen-octet blocks in the derived key,
rounding up, and let r be the number of octets in the last
block:
len = CEIL(dkLen / hLen),
r = dkLen - (len - 1) * hLen. */
var len = Math.ceil(dkLen / hLen);
var r = dkLen - (len - 1) * hLen;
/* 3. For each block of the derived key apply the function F defined
below to the password P, the salt S, the iteration count c, and
the block index to compute the block:
T_1 = F(P, S, c, 1),
T_2 = F(P, S, c, 2),
...
T_len = F(P, S, c, len),
where the function F is defined as the exclusive-or sum of the
first c iterates of the underlying pseudorandom function PRF
applied to the password P and the concatenation of the salt S
and the block index i:
F(P, S, c, i) = u_1 XOR u_2 XOR ... XOR u_c
where
u_1 = PRF(P, S || INT(i)),
u_2 = PRF(P, u_1),
...
u_c = PRF(P, u_{c-1}).
Here, INT(i) is a four-octet encoding of the integer i, most
significant octet first. */
var prf = forge.hmac.create();
prf.start(md, p);
var dk = '';
var xor, u_c, u_c1;
// sync version
if(!callback) {
for(var i = 1; i <= len; ++i) {
// PRF(P, S || INT(i)) (first iteration)
prf.start(null, null);
prf.update(s);
prf.update(forge.util.int32ToBytes(i));
xor = u_c1 = prf.digest().getBytes();
// PRF(P, u_{c-1}) (other iterations)
for(var j = 2; j <= c; ++j) {
prf.start(null, null);
prf.update(u_c1);
u_c = prf.digest().getBytes();
// F(p, s, c, i)
xor = forge.util.xorBytes(xor, u_c, hLen);
u_c1 = u_c;
}
/* 4. Concatenate the blocks and extract the first dkLen octets to
produce a derived key DK:
DK = T_1 || T_2 || ... || T_len<0..r-1> */
dk += (i < len) ? xor : xor.substr(0, r);
}
/* 5. Output the derived key DK. */
return dk;
}
// async version
var i = 1, j;
function outer() {
if(i > len) {
// done
return callback(null, dk);
}
// PRF(P, S || INT(i)) (first iteration)
prf.start(null, null);
prf.update(s);
prf.update(forge.util.int32ToBytes(i));
xor = u_c1 = prf.digest().getBytes();
// PRF(P, u_{c-1}) (other iterations)
j = 2;
inner();
}
function inner() {
if(j <= c) {
prf.start(null, null);
prf.update(u_c1);
u_c = prf.digest().getBytes();
// F(p, s, c, i)
xor = forge.util.xorBytes(xor, u_c, hLen);
u_c1 = u_c;
++j;
return forge.util.setImmediate(inner);
}
/* 4. Concatenate the blocks and extract the first dkLen octets to
produce a derived key DK:
DK = T_1 || T_2 || ... || T_len<0..r-1> */
dk += (i < len) ? xor : xor.substr(0, r);
++i;
outer();
}
outer();
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pbkdf2';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './hmac', './md', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

285
forge.js/pem.js Normal file
View File

@@ -0,0 +1,285 @@
/**
* Javascript implementation of basic PEM (Privacy Enhanced Mail) algorithms.
*
* See: RFC 1421.
*
* @author Dave Longley
*
* Copyright (c) 2013-2014 Digital Bazaar, Inc.
*
* A Forge PEM object has the following fields:
*
* type: identifies the type of message (eg: "RSA PRIVATE KEY").
*
* procType: identifies the type of processing performed on the message,
* it has two subfields: version and type, eg: 4,ENCRYPTED.
*
* contentDomain: identifies the type of content in the message, typically
* only uses the value: "RFC822".
*
* dekInfo: identifies the message encryption algorithm and mode and includes
* any parameters for the algorithm, it has two subfields: algorithm and
* parameters, eg: DES-CBC,F8143EDE5960C597.
*
* headers: contains all other PEM encapsulated headers -- where order is
* significant (for pairing data like recipient ID + key info).
*
* body: the binary-encoded body.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for pem API
var pem = forge.pem = forge.pem || {};
/**
* Encodes (serializes) the given PEM object.
*
* @param msg the PEM message object to encode.
* @param options the options to use:
* maxline the maximum characters per line for the body, (default: 64).
*
* @return the PEM-formatted string.
*/
pem.encode = function(msg, options) {
options = options || {};
var rval = '-----BEGIN ' + msg.type + '-----\r\n';
// encode special headers
var header;
if(msg.procType) {
header = {
name: 'Proc-Type',
values: [String(msg.procType.version), msg.procType.type]
};
rval += foldHeader(header);
}
if(msg.contentDomain) {
header = {name: 'Content-Domain', values: [msg.contentDomain]};
rval += foldHeader(header);
}
if(msg.dekInfo) {
header = {name: 'DEK-Info', values: [msg.dekInfo.algorithm]};
if(msg.dekInfo.parameters) {
header.values.push(msg.dekInfo.parameters);
}
rval += foldHeader(header);
}
if(msg.headers) {
// encode all other headers
for(var i = 0; i < msg.headers.length; ++i) {
rval += foldHeader(msg.headers[i]);
}
}
// terminate header
if(msg.procType) {
rval += '\r\n';
}
// add body
rval += forge.util.encode64(msg.body, options.maxline || 64) + '\r\n';
rval += '-----END ' + msg.type + '-----\r\n';
return rval;
};
/**
* Decodes (deserializes) all PEM messages found in the given string.
*
* @param str the PEM-formatted string to decode.
*
* @return the PEM message objects in an array.
*/
pem.decode = function(str) {
var rval = [];
// split string into PEM messages (be lenient w/EOF on BEGIN line)
var rMessage = /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g;
var rHeader = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/;
var rCRLF = /\r?\n/;
var match;
while(true) {
match = rMessage.exec(str);
if(!match) {
break;
}
var msg = {
type: match[1],
procType: null,
contentDomain: null,
dekInfo: null,
headers: [],
body: forge.util.decode64(match[3])
};
rval.push(msg);
// no headers
if(!match[2]) {
continue;
}
// parse headers
var lines = match[2].split(rCRLF);
var li = 0;
while(match && li < lines.length) {
// get line, trim any rhs whitespace
var line = lines[li].replace(/\s+$/, '');
// RFC2822 unfold any following folded lines
for(var nl = li + 1; nl < lines.length; ++nl) {
var next = lines[nl];
if(!/\s/.test(next[0])) {
break;
}
line += next;
li = nl;
}
// parse header
match = line.match(rHeader);
if(match) {
var header = {name: match[1], values: []};
var values = match[2].split(',');
for(var vi = 0; vi < values.length; ++vi) {
header.values.push(ltrim(values[vi]));
}
// Proc-Type must be the first header
if(!msg.procType) {
if(header.name !== 'Proc-Type') {
throw new Error('Invalid PEM formatted message. The first ' +
'encapsulated header must be "Proc-Type".');
} else if(header.values.length !== 2) {
throw new Error('Invalid PEM formatted message. The "Proc-Type" ' +
'header must have two subfields.');
}
msg.procType = {version: values[0], type: values[1]};
} else if(!msg.contentDomain && header.name === 'Content-Domain') {
// special-case Content-Domain
msg.contentDomain = values[0] || '';
} else if(!msg.dekInfo && header.name === 'DEK-Info') {
// special-case DEK-Info
if(header.values.length === 0) {
throw new Error('Invalid PEM formatted message. The "DEK-Info" ' +
'header must have at least one subfield.');
}
msg.dekInfo = {algorithm: values[0], parameters: values[1] || null};
} else {
msg.headers.push(header);
}
}
++li;
}
if(msg.procType === 'ENCRYPTED' && !msg.dekInfo) {
throw new Error('Invalid PEM formatted message. The "DEK-Info" ' +
'header must be present if "Proc-Type" is "ENCRYPTED".');
}
}
if(rval.length === 0) {
throw new Error('Invalid PEM formatted message.');
}
return rval;
};
function foldHeader(header) {
var rval = header.name + ': ';
// ensure values with CRLF are folded
var values = [];
var insertSpace = function(match, $1) {
return ' ' + $1;
};
for(var i = 0; i < header.values.length; ++i) {
values.push(header.values[i].replace(/^(\S+\r\n)/, insertSpace));
}
rval += values.join(',') + '\r\n';
// do folding
var length = 0;
var candidate = -1;
for(var i = 0; i < rval.length; ++i, ++length) {
if(length > 65 && candidate !== -1) {
var insert = rval[candidate];
if(insert === ',') {
++candidate;
rval = rval.substr(0, candidate) + '\r\n ' + rval.substr(candidate);
} else {
rval = rval.substr(0, candidate) +
'\r\n' + insert + rval.substr(candidate + 1);
}
length = (i - candidate - 1);
candidate = -1;
++i;
} else if(rval[i] === ' ' || rval[i] === '\t' || rval[i] === ',') {
candidate = i;
}
}
return rval;
}
function ltrim(str) {
return str.replace(/^\s+/, '');
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pem';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

329
forge.js/pkcs1.js Normal file
View File

@@ -0,0 +1,329 @@
/**
* Partial implementation of PKCS#1 v2.2: RSA-OEAP
*
* Modified but based on the following MIT and BSD licensed code:
*
* https://github.com/kjur/jsjws/blob/master/rsa.js:
*
* The 'jsjws'(JSON Web Signature JavaScript Library) License
*
* Copyright (c) 2012 Kenji Urushima
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* http://webrsa.cvs.sourceforge.net/viewvc/webrsa/Client/RSAES-OAEP.js?content-type=text%2Fplain:
*
* RSAES-OAEP.js
* $Id: RSAES-OAEP.js,v 1.1.1.1 2003/03/19 15:37:20 ellispritchard Exp $
* JavaScript Implementation of PKCS #1 v2.1 RSA CRYPTOGRAPHY STANDARD (RSA Laboratories, June 14, 2002)
* Copyright (C) Ellis Pritchard, Guardian Unlimited 2003.
* Contact: ellis@nukinetics.com
* Distributed under the BSD License.
*
* Official documentation: http://www.rsa.com/rsalabs/node.asp?id=2125
*
* @author Evan Jones (http://evanjones.ca/)
* @author Dave Longley
*
* Copyright (c) 2013-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for PKCS#1 API
var pkcs1 = forge.pkcs1 = forge.pkcs1 || {};
/**
* Encode the given RSAES-OAEP message (M) using key, with optional label (L)
* and seed.
*
* This method does not perform RSA encryption, it only encodes the message
* using RSAES-OAEP.
*
* @param key the RSA key to use.
* @param message the message to encode.
* @param options the options to use:
* label an optional label to use.
* seed the seed to use.
* md the message digest object to use, undefined for SHA-1.
* mgf1 optional mgf1 parameters:
* md the message digest object to use for MGF1.
*
* @return the encoded message bytes.
*/
pkcs1.encode_rsa_oaep = function(key, message, options) {
// parse arguments
var label;
var seed;
var md;
var mgf1Md;
// legacy args (label, seed, md)
if(typeof options === 'string') {
label = options;
seed = arguments[3] || undefined;
md = arguments[4] || undefined;
} else if(options) {
label = options.label || undefined;
seed = options.seed || undefined;
md = options.md || undefined;
if(options.mgf1 && options.mgf1.md) {
mgf1Md = options.mgf1.md;
}
}
// default OAEP to SHA-1 message digest
if(!md) {
md = forge.md.sha1.create();
} else {
md.start();
}
// default MGF-1 to same as OAEP
if(!mgf1Md) {
mgf1Md = md;
}
// compute length in bytes and check output
var keyLength = Math.ceil(key.n.bitLength() / 8);
var maxLength = keyLength - 2 * md.digestLength - 2;
if(message.length > maxLength) {
var error = new Error('RSAES-OAEP input message length is too long.');
error.length = message.length;
error.maxLength = maxLength;
throw error;
}
if(!label) {
label = '';
}
md.update(label, 'raw');
var lHash = md.digest();
var PS = '';
var PS_length = maxLength - message.length;
for (var i = 0; i < PS_length; i++) {
PS += '\x00';
}
var DB = lHash.getBytes() + PS + '\x01' + message;
if(!seed) {
seed = forge.random.getBytes(md.digestLength);
} else if(seed.length !== md.digestLength) {
var error = new Error('Invalid RSAES-OAEP seed. The seed length must ' +
'match the digest length.')
error.seedLength = seed.length;
error.digestLength = md.digestLength;
throw error;
}
var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, mgf1Md);
var maskedDB = forge.util.xorBytes(DB, dbMask, DB.length);
var seedMask = rsa_mgf1(maskedDB, md.digestLength, mgf1Md);
var maskedSeed = forge.util.xorBytes(seed, seedMask, seed.length);
// return encoded message
return '\x00' + maskedSeed + maskedDB;
};
/**
* Decode the given RSAES-OAEP encoded message (EM) using key, with optional
* label (L).
*
* This method does not perform RSA decryption, it only decodes the message
* using RSAES-OAEP.
*
* @param key the RSA key to use.
* @param em the encoded message to decode.
* @param options the options to use:
* label an optional label to use.
* md the message digest object to use for OAEP, undefined for SHA-1.
* mgf1 optional mgf1 parameters:
* md the message digest object to use for MGF1.
*
* @return the decoded message bytes.
*/
pkcs1.decode_rsa_oaep = function(key, em, options) {
// parse args
var label;
var md;
var mgf1Md;
// legacy args
if(typeof options === 'string') {
label = options;
md = arguments[3] || undefined;
} else if(options) {
label = options.label || undefined;
md = options.md || undefined;
if(options.mgf1 && options.mgf1.md) {
mgf1Md = options.mgf1.md;
}
}
// compute length in bytes
var keyLength = Math.ceil(key.n.bitLength() / 8);
if(em.length !== keyLength) {
var error = new Error('RSAES-OAEP encoded message length is invalid.');
error.length = em.length;
error.expectedLength = keyLength;
throw error;
}
// default OAEP to SHA-1 message digest
if(md === undefined) {
md = forge.md.sha1.create();
} else {
md.start();
}
// default MGF-1 to same as OAEP
if(!mgf1Md) {
mgf1Md = md;
}
if(keyLength < 2 * md.digestLength + 2) {
throw new Error('RSAES-OAEP key is too short for the hash function.');
}
if(!label) {
label = '';
}
md.update(label, 'raw');
var lHash = md.digest().getBytes();
// split the message into its parts
var y = em.charAt(0);
var maskedSeed = em.substring(1, md.digestLength + 1);
var maskedDB = em.substring(1 + md.digestLength);
var seedMask = rsa_mgf1(maskedDB, md.digestLength, mgf1Md);
var seed = forge.util.xorBytes(maskedSeed, seedMask, maskedSeed.length);
var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, mgf1Md);
var db = forge.util.xorBytes(maskedDB, dbMask, maskedDB.length);
var lHashPrime = db.substring(0, md.digestLength);
// constant time check that all values match what is expected
var error = (y !== '\x00');
// constant time check lHash vs lHashPrime
for(var i = 0; i < md.digestLength; ++i) {
error |= (lHash.charAt(i) !== lHashPrime.charAt(i));
}
// "constant time" find the 0x1 byte separating the padding (zeros) from the
// message
// TODO: It must be possible to do this in a better/smarter way?
var in_ps = 1;
var index = md.digestLength;
for(var j = md.digestLength; j < db.length; j++) {
var code = db.charCodeAt(j);
var is_0 = (code & 0x1) ^ 0x1;
// non-zero if not 0 or 1 in the ps section
var error_mask = in_ps ? 0xfffe : 0x0000;
error |= (code & error_mask);
// latch in_ps to zero after we find 0x1
in_ps = in_ps & is_0;
index += in_ps;
}
if(error || db.charCodeAt(index) !== 0x1) {
throw new Error('Invalid RSAES-OAEP padding.');
}
return db.substring(index + 1);
};
function rsa_mgf1(seed, maskLength, hash) {
// default to SHA-1 message digest
if(!hash) {
hash = forge.md.sha1.create();
}
var t = '';
var count = Math.ceil(maskLength / hash.digestLength);
for(var i = 0; i < count; ++i) {
var c = String.fromCharCode(
(i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
hash.start();
hash.update(seed + c);
t += hash.digest().getBytes();
}
return t.substring(0, maskLength);
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pkcs1';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util', './random', './sha1'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

1133
forge.js/pkcs12.js Normal file

File diff suppressed because it is too large Load Diff

1301
forge.js/pkcs7.js Normal file

File diff suppressed because it is too large Load Diff

463
forge.js/pkcs7asn1.js Normal file
View File

@@ -0,0 +1,463 @@
/**
* Javascript implementation of ASN.1 validators for PKCS#7 v1.5.
*
* @author Dave Longley
* @author Stefan Siegl
*
* Copyright (c) 2012-2015 Digital Bazaar, Inc.
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
*
* The ASN.1 representation of PKCS#7 is as follows
* (see RFC #2315 for details, http://www.ietf.org/rfc/rfc2315.txt):
*
* A PKCS#7 message consists of a ContentInfo on root level, which may
* contain any number of further ContentInfo nested into it.
*
* ContentInfo ::= SEQUENCE {
* contentType ContentType,
* content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
* }
*
* ContentType ::= OBJECT IDENTIFIER
*
* EnvelopedData ::= SEQUENCE {
* version Version,
* recipientInfos RecipientInfos,
* encryptedContentInfo EncryptedContentInfo
* }
*
* EncryptedData ::= SEQUENCE {
* version Version,
* encryptedContentInfo EncryptedContentInfo
* }
*
* id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
* us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
*
* SignedData ::= SEQUENCE {
* version INTEGER,
* digestAlgorithms DigestAlgorithmIdentifiers,
* contentInfo ContentInfo,
* certificates [0] IMPLICIT Certificates OPTIONAL,
* crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
* signerInfos SignerInfos
* }
*
* SignerInfos ::= SET OF SignerInfo
*
* SignerInfo ::= SEQUENCE {
* version Version,
* issuerAndSerialNumber IssuerAndSerialNumber,
* digestAlgorithm DigestAlgorithmIdentifier,
* authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
* digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
* encryptedDigest EncryptedDigest,
* unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
* }
*
* EncryptedDigest ::= OCTET STRING
*
* Attributes ::= SET OF Attribute
*
* Attribute ::= SEQUENCE {
* attrType OBJECT IDENTIFIER,
* attrValues SET OF AttributeValue
* }
*
* AttributeValue ::= ANY
*
* Version ::= INTEGER
*
* RecipientInfos ::= SET OF RecipientInfo
*
* EncryptedContentInfo ::= SEQUENCE {
* contentType ContentType,
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
* }
*
* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
*
* The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
* for the algorithm, if any. In the case of AES and DES3, there is only one,
* the IV.
*
* AlgorithmIdentifer ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
*
* EncryptedContent ::= OCTET STRING
*
* RecipientInfo ::= SEQUENCE {
* version Version,
* issuerAndSerialNumber IssuerAndSerialNumber,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* encryptedKey EncryptedKey
* }
*
* IssuerAndSerialNumber ::= SEQUENCE {
* issuer Name,
* serialNumber CertificateSerialNumber
* }
*
* CertificateSerialNumber ::= INTEGER
*
* KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
*
* EncryptedKey ::= OCTET STRING
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for ASN.1 API
var asn1 = forge.asn1;
// shortcut for PKCS#7 API
var p7v = forge.pkcs7asn1 = forge.pkcs7asn1 || {};
forge.pkcs7 = forge.pkcs7 || {};
forge.pkcs7.asn1 = p7v;
var contentInfoValidator = {
name: 'ContentInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'ContentInfo.ContentType',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'contentType'
}, {
name: 'ContentInfo.content',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 0,
constructed: true,
optional: true,
captureAsn1: 'content'
}]
};
p7v.contentInfoValidator = contentInfoValidator;
var encryptedContentInfoValidator = {
name: 'EncryptedContentInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'EncryptedContentInfo.contentType',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'contentType'
}, {
name: 'EncryptedContentInfo.contentEncryptionAlgorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'EncryptedContentInfo.contentEncryptionAlgorithm.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'encAlgorithm'
}, {
name: 'EncryptedContentInfo.contentEncryptionAlgorithm.parameter',
tagClass: asn1.Class.UNIVERSAL,
captureAsn1: 'encParameter'
}]
}, {
name: 'EncryptedContentInfo.encryptedContent',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 0,
/* The PKCS#7 structure output by OpenSSL somewhat differs from what
* other implementations do generate.
*
* OpenSSL generates a structure like this:
* SEQUENCE {
* ...
* [0]
* 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
* C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
* ...
* }
*
* Whereas other implementations (and this PKCS#7 module) generate:
* SEQUENCE {
* ...
* [0] {
* OCTET STRING
* 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
* C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
* ...
* }
* }
*
* In order to support both, we just capture the context specific
* field here. The OCTET STRING bit is removed below.
*/
capture: 'encryptedContent',
captureAsn1: 'encryptedContentAsn1'
}]
};
p7v.envelopedDataValidator = {
name: 'EnvelopedData',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'EnvelopedData.Version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'version'
}, {
name: 'EnvelopedData.RecipientInfos',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SET,
constructed: true,
captureAsn1: 'recipientInfos'
}].concat(encryptedContentInfoValidator)
};
p7v.encryptedDataValidator = {
name: 'EncryptedData',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'EncryptedData.Version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'version'
}].concat(encryptedContentInfoValidator)
};
var signerValidator = {
name: 'SignerInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'SignerInfo.version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false
}, {
name: 'SignerInfo.issuerAndSerialNumber',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'SignerInfo.issuerAndSerialNumber.issuer',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'issuer'
}, {
name: 'SignerInfo.issuerAndSerialNumber.serialNumber',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'serial'
}]
}, {
name: 'SignerInfo.digestAlgorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'SignerInfo.digestAlgorithm.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'digestAlgorithm'
}, {
name: 'SignerInfo.digestAlgorithm.parameter',
tagClass: asn1.Class.UNIVERSAL,
constructed: false,
captureAsn1: 'digestParameter',
optional: true
}]
}, {
name: 'SignerInfo.authenticatedAttributes',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 0,
constructed: true,
optional: true,
capture: 'authenticatedAttributes'
}, {
name: 'SignerInfo.digestEncryptionAlgorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
capture: 'signatureAlgorithm'
}, {
name: 'SignerInfo.encryptedDigest',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'signature'
}, {
name: 'SignerInfo.unauthenticatedAttributes',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 1,
constructed: true,
optional: true,
capture: 'unauthenticatedAttributes'
}]
};
p7v.signedDataValidator = {
name: 'SignedData',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'SignedData.Version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'version'
}, {
name: 'SignedData.DigestAlgorithms',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SET,
constructed: true,
captureAsn1: 'digestAlgorithms'
},
contentInfoValidator,
{
name: 'SignedData.Certificates',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 0,
optional: true,
captureAsn1: 'certificates'
}, {
name: 'SignedData.CertificateRevocationLists',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 1,
optional: true,
captureAsn1: 'crls'
}, {
name: 'SignedData.SignerInfos',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SET,
capture: 'signerInfos',
optional: true,
value: [signerValidator]
}]
};
p7v.recipientInfoValidator = {
name: 'RecipientInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'RecipientInfo.version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'version'
}, {
name: 'RecipientInfo.issuerAndSerial',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'RecipientInfo.issuerAndSerial.issuer',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'issuer'
}, {
name: 'RecipientInfo.issuerAndSerial.serialNumber',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'serial'
}]
}, {
name: 'RecipientInfo.keyEncryptionAlgorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'RecipientInfo.keyEncryptionAlgorithm.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'encAlgorithm'
}, {
name: 'RecipientInfo.keyEncryptionAlgorithm.parameter',
tagClass: asn1.Class.UNIVERSAL,
constructed: false,
captureAsn1: 'encParameter'
}]
}, {
name: 'RecipientInfo.encryptedKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'encKey'
}]
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pkcs7asn1';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './asn1', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

161
forge.js/pki.js Normal file
View File

@@ -0,0 +1,161 @@
/**
* Javascript implementation of a basic Public Key Infrastructure, including
* support for RSA public and private keys.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for asn.1 API
var asn1 = forge.asn1;
/* Public Key Infrastructure (PKI) implementation. */
var pki = forge.pki = forge.pki || {};
/**
* NOTE: THIS METHOD IS DEPRECATED. Use pem.decode() instead.
*
* Converts PEM-formatted data to DER.
*
* @param pem the PEM-formatted data.
*
* @return the DER-formatted data.
*/
pki.pemToDer = function(pem) {
var msg = forge.pem.decode(pem)[0];
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
throw new Error('Could not convert PEM to DER; PEM is encrypted.');
}
return forge.util.createBuffer(msg.body);
};
/**
* Converts an RSA private key from PEM format.
*
* @param pem the PEM-formatted private key.
*
* @return the private key.
*/
pki.privateKeyFromPem = function(pem) {
var msg = forge.pem.decode(pem)[0];
if(msg.type !== 'PRIVATE KEY' && msg.type !== 'RSA PRIVATE KEY') {
var error = new Error('Could not convert private key from PEM; PEM ' +
'header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".');
error.headerType = msg.type;
throw error;
}
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
throw new Error('Could not convert private key from PEM; PEM is encrypted.');
}
// convert DER to ASN.1 object
var obj = asn1.fromDer(msg.body);
return pki.privateKeyFromAsn1(obj);
};
/**
* Converts an RSA private key to PEM format.
*
* @param key the private key.
* @param maxline the maximum characters per line, defaults to 64.
*
* @return the PEM-formatted private key.
*/
pki.privateKeyToPem = function(key, maxline) {
// convert to ASN.1, then DER, then PEM-encode
var msg = {
type: 'RSA PRIVATE KEY',
body: asn1.toDer(pki.privateKeyToAsn1(key)).getBytes()
};
return forge.pem.encode(msg, {maxline: maxline});
};
/**
* Converts a PrivateKeyInfo to PEM format.
*
* @param pki the PrivateKeyInfo.
* @param maxline the maximum characters per line, defaults to 64.
*
* @return the PEM-formatted private key.
*/
pki.privateKeyInfoToPem = function(pki, maxline) {
// convert to DER, then PEM-encode
var msg = {
type: 'PRIVATE KEY',
body: asn1.toDer(pki).getBytes()
};
return forge.pem.encode(msg, {maxline: maxline});
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pki';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./asn1',
'./oids',
'./pbe',
'./pem',
'./pbkdf2',
'./pkcs12',
'./pss',
'./rsa',
'./util',
'./x509'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

337
forge.js/prime.js Normal file
View File

@@ -0,0 +1,337 @@
/**
* Prime number generation API.
*
* @author Dave Longley
*
* Copyright (c) 2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// forge.prime already defined
if(forge.prime) {
return;
}
/* PRIME API */
var prime = forge.prime = forge.prime || {};
var BigInteger = forge.jsbn.BigInteger;
// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
var THIRTY = new BigInteger(null);
THIRTY.fromInt(30);
var op_or = function(x, y) {return x|y;};
/**
* Generates a random probable prime with the given number of bits.
*
* Alternative algorithms can be specified by name as a string or as an
* object with custom options like so:
*
* {
* name: 'PRIMEINC',
* options: {
* maxBlockTime: <the maximum amount of time to block the main
* thread before allowing I/O other JS to run>,
* millerRabinTests: <the number of miller-rabin tests to run>,
* workerScript: <the worker script URL>,
* workers: <the number of web workers (if supported) to use,
* -1 to use estimated cores minus one>.
* workLoad: the size of the work load, ie: number of possible prime
* numbers for each web worker to check per work assignment,
* (default: 100).
* }
* }
*
* @param bits the number of bits for the prime number.
* @param options the options to use.
* [algorithm] the algorithm to use (default: 'PRIMEINC').
* [prng] a custom crypto-secure pseudo-random number generator to use,
* that must define "getBytesSync".
*
* @return callback(err, num) called once the operation completes.
*/
prime.generateProbablePrime = function(bits, options, callback) {
if(typeof options === 'function') {
callback = options;
options = {};
}
options = options || {};
// default to PRIMEINC algorithm
var algorithm = options.algorithm || 'PRIMEINC';
if(typeof algorithm === 'string') {
algorithm = {name: algorithm};
}
algorithm.options = algorithm.options || {};
// create prng with api that matches BigInteger secure random
var prng = options.prng || forge.random;
var rng = {
// x is an array to fill with bytes
nextBytes: function(x) {
var b = prng.getBytesSync(x.length);
for(var i = 0; i < x.length; ++i) {
x[i] = b.charCodeAt(i);
}
}
};
if(algorithm.name === 'PRIMEINC') {
return primeincFindPrime(bits, rng, algorithm.options, callback);
}
throw new Error('Invalid prime generation algorithm: ' + algorithm.name);
};
function primeincFindPrime(bits, rng, options, callback) {
if('workers' in options) {
return primeincFindPrimeWithWorkers(bits, rng, options, callback);
}
return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
}
function primeincFindPrimeWithoutWorkers(bits, rng, options, callback) {
// initialize random number
var num = generateRandom(bits, rng);
/* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The
number we are given is always aligned at 30k + 1. Each time the number is
determined not to be prime we add to get to the next 'i', eg: if the number
was at 30k + 1 we add 6. */
var deltaIdx = 0;
// get required number of MR tests
var mrTests = getMillerRabinTests(num.bitLength());
if('millerRabinTests' in options) {
mrTests = options.millerRabinTests;
}
// find prime nearest to 'num' for maxBlockTime ms
// 10 ms gives 5ms of leeway for other calculations before dropping
// below 60fps (1000/60 == 16.67), but in reality, the number will
// likely be higher due to an 'atomic' big int modPow
var maxBlockTime = 10;
if('maxBlockTime' in options) {
maxBlockTime = options.maxBlockTime;
}
var start = +new Date();
do {
// overflow, regenerate random number
if(num.bitLength() > bits) {
num = generateRandom(bits, rng);
}
// do primality test
if(num.isProbablePrime(mrTests)) {
return callback(null, num);
}
// get next potential prime
num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
} while(maxBlockTime < 0 || (+new Date() - start < maxBlockTime));
// keep trying (setImmediate would be better here)
forge.util.setImmediate(function() {
primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
});
}
function primeincFindPrimeWithWorkers(bits, rng, options, callback) {
// web workers unavailable
if(typeof Worker === 'undefined') {
return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
}
// initialize random number
var num = generateRandom(bits, rng);
// use web workers to generate keys
var numWorkers = options.workers;
var workLoad = options.workLoad || 100;
var range = workLoad * 30 / 8;
var workerScript = options.workerScript || 'forge/prime.worker.js';
if(numWorkers === -1) {
return forge.util.estimateCores(function(err, cores) {
if(err) {
// default to 2
cores = 2;
}
numWorkers = cores - 1;
generate();
});
}
generate();
function generate() {
// require at least 1 worker
numWorkers = Math.max(1, numWorkers);
// TODO: consider optimizing by starting workers outside getPrime() ...
// note that in order to clean up they will have to be made internally
// asynchronous which may actually be slower
// start workers immediately
var workers = [];
for(var i = 0; i < numWorkers; ++i) {
// FIXME: fix path or use blob URLs
workers[i] = new Worker(workerScript);
}
var running = numWorkers;
// listen for requests from workers and assign ranges to find prime
for(var i = 0; i < numWorkers; ++i) {
workers[i].addEventListener('message', workerMessage);
}
/* Note: The distribution of random numbers is unknown. Therefore, each
web worker is continuously allocated a range of numbers to check for a
random number until one is found.
Every 30 numbers will be checked just 8 times, because prime numbers
have the form:
30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)
Therefore, if we want a web worker to run N checks before asking for
a new range of numbers, each range must contain N*30/8 numbers.
For 100 checks (workLoad), this is a range of 375. */
var found = false;
function workerMessage(e) {
// ignore message, prime already found
if(found) {
return;
}
--running;
var data = e.data;
if(data.found) {
// terminate all workers
for(var i = 0; i < workers.length; ++i) {
workers[i].terminate();
}
found = true;
return callback(null, new BigInteger(data.prime, 16));
}
// overflow, regenerate random number
if(num.bitLength() > bits) {
num = generateRandom(bits, rng);
}
// assign new range to check
var hex = num.toString(16);
// start prime search
e.target.postMessage({
hex: hex,
workLoad: workLoad
});
num.dAddOffset(range, 0);
}
}
}
/**
* Generates a random number using the given number of bits and RNG.
*
* @param bits the number of bits for the number.
* @param rng the random number generator to use.
*
* @return the random number.
*/
function generateRandom(bits, rng) {
var num = new BigInteger(bits, rng);
// force MSB set
var bits1 = bits - 1;
if(!num.testBit(bits1)) {
num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);
}
// align number on 30k+1 boundary
num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);
return num;
}
/**
* Returns the required number of Miller-Rabin tests to generate a
* prime with an error probability of (1/2)^80.
*
* See Handbook of Applied Cryptography Chapter 4, Table 4.4.
*
* @param bits the bit size.
*
* @return the required number of iterations.
*/
function getMillerRabinTests(bits) {
if(bits <= 100) return 27;
if(bits <= 150) return 18;
if(bits <= 200) return 15;
if(bits <= 250) return 12;
if(bits <= 300) return 9;
if(bits <= 350) return 8;
if(bits <= 400) return 7;
if(bits <= 500) return 6;
if(bits <= 600) return 5;
if(bits <= 800) return 4;
if(bits <= 1250) return 3;
return 2;
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'prime';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util', './jsbn', './random'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

165
forge.js/prime.worker.js Normal file
View File

@@ -0,0 +1,165 @@
/**
* RSA Key Generation Worker.
*
* @author Dave Longley
*
* Copyright (c) 2013 Digital Bazaar, Inc.
*/
importScripts('jsbn.js');
// prime constants
var LOW_PRIMES = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
var LP_LIMIT = (1 << 26) / LOW_PRIMES[LOW_PRIMES.length - 1];
var BigInteger = forge.jsbn.BigInteger;
var BIG_TWO = new BigInteger(null);
BIG_TWO.fromInt(2);
self.addEventListener('message', function(e) {
var result = findPrime(e.data);
self.postMessage(result);
});
// start receiving ranges to check
self.postMessage({found: false});
// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
function findPrime(data) {
// TODO: abstract based on data.algorithm (PRIMEINC vs. others)
// create BigInteger from given random bytes
var num = new BigInteger(data.hex, 16);
/* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The
number we are given is always aligned at 30k + 1. Each time the number is
determined not to be prime we add to get to the next 'i', eg: if the number
was at 30k + 1 we add 6. */
var deltaIdx = 0;
// find nearest prime
var workLoad = data.workLoad;
for(var i = 0; i < workLoad; ++i) {
// do primality test
if(isProbablePrime(num)) {
return {found: true, prime: num.toString(16)};
}
// get next potential prime
num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
}
return {found: false};
}
function isProbablePrime(n) {
// divide by low primes, ignore even checks, etc (n alread aligned properly)
var i = 1;
while(i < LOW_PRIMES.length) {
var m = LOW_PRIMES[i];
var j = i + 1;
while(j < LOW_PRIMES.length && m < LP_LIMIT) {
m *= LOW_PRIMES[j++];
}
m = n.modInt(m);
while(i < j) {
if(m % LOW_PRIMES[i++] === 0) {
return false;
}
}
}
return runMillerRabin(n);
}
// HAC 4.24, Miller-Rabin
function runMillerRabin(n) {
// n1 = n - 1
var n1 = n.subtract(BigInteger.ONE);
// get s and d such that n1 = 2^s * d
var s = n1.getLowestSetBit();
if(s <= 0) {
return false;
}
var d = n1.shiftRight(s);
var k = _getMillerRabinTests(n.bitLength());
var prng = getPrng();
var a;
for(var i = 0; i < k; ++i) {
// select witness 'a' at random from between 1 and n - 1
do {
a = new BigInteger(n.bitLength(), prng);
} while(a.compareTo(BigInteger.ONE) <= 0 || a.compareTo(n1) >= 0);
/* See if 'a' is a composite witness. */
// x = a^d mod n
var x = a.modPow(d, n);
// probably prime
if(x.compareTo(BigInteger.ONE) === 0 || x.compareTo(n1) === 0) {
continue;
}
var j = s;
while(--j) {
// x = x^2 mod a
x = x.modPowInt(2, n);
// 'n' is composite because no previous x == -1 mod n
if(x.compareTo(BigInteger.ONE) === 0) {
return false;
}
// x == -1 mod n, so probably prime
if(x.compareTo(n1) === 0) {
break;
}
}
// 'x' is first_x^(n1/2) and is not +/- 1, so 'n' is not prime
if(j === 0) {
return false;
}
}
return true;
}
// get pseudo random number generator
function getPrng() {
// create prng with api that matches BigInteger secure random
return {
// x is an array to fill with bytes
nextBytes: function(x) {
for(var i = 0; i < x.length; ++i) {
x[i] = Math.floor(Math.random() * 0xFF);
}
}
};
}
/**
* Returns the required number of Miller-Rabin tests to generate a
* prime with an error probability of (1/2)^80.
*
* See Handbook of Applied Cryptography Chapter 4, Table 4.4.
*
* @param bits the bit size.
*
* @return the required number of iterations.
*/
function _getMillerRabinTests(bits) {
if(bits <= 100) return 27;
if(bits <= 150) return 18;
if(bits <= 200) return 15;
if(bits <= 250) return 12;
if(bits <= 300) return 9;
if(bits <= 350) return 8;
if(bits <= 400) return 7;
if(bits <= 500) return 6;
if(bits <= 600) return 5;
if(bits <= 800) return 4;
if(bits <= 1250) return 3;
return 2;
}

458
forge.js/prng.js Normal file
View File

@@ -0,0 +1,458 @@
/**
* A javascript implementation of a cryptographically-secure
* Pseudo Random Number Generator (PRNG). The Fortuna algorithm is followed
* here though the use of SHA-256 is not enforced; when generating an
* a PRNG context, the hashing algorithm and block cipher used for
* the generator are specified via a plugin.
*
* @author Dave Longley
*
* Copyright (c) 2010-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var _nodejs = (
typeof process !== 'undefined' && process.versions && process.versions.node);
var _crypto = null;
if(!forge.disableNativeCode && _nodejs && !process.versions['node-webkit']) {
_crypto = require('crypto');
}
/* PRNG API */
var prng = forge.prng = forge.prng || {};
/**
* Creates a new PRNG context.
*
* A PRNG plugin must be passed in that will provide:
*
* 1. A function that initializes the key and seed of a PRNG context. It
* will be given a 16 byte key and a 16 byte seed. Any key expansion
* or transformation of the seed from a byte string into an array of
* integers (or similar) should be performed.
* 2. The cryptographic function used by the generator. It takes a key and
* a seed.
* 3. A seed increment function. It takes the seed and returns seed + 1.
* 4. An api to create a message digest.
*
* For an example, see random.js.
*
* @param plugin the PRNG plugin to use.
*/
prng.create = function(plugin) {
var ctx = {
plugin: plugin,
key: null,
seed: null,
time: null,
// number of reseeds so far
reseeds: 0,
// amount of data generated so far
generated: 0
};
// create 32 entropy pools (each is a message digest)
var md = plugin.md;
var pools = new Array(32);
for(var i = 0; i < 32; ++i) {
pools[i] = md.create();
}
ctx.pools = pools;
// entropy pools are written to cyclically, starting at index 0
ctx.pool = 0;
/**
* Generates random bytes. The bytes may be generated synchronously or
* asynchronously. Web workers must use the asynchronous interface or
* else the behavior is undefined.
*
* @param count the number of random bytes to generate.
* @param [callback(err, bytes)] called once the operation completes.
*
* @return count random bytes as a string.
*/
ctx.generate = function(count, callback) {
// do synchronously
if(!callback) {
return ctx.generateSync(count);
}
// simple generator using counter-based CBC
var cipher = ctx.plugin.cipher;
var increment = ctx.plugin.increment;
var formatKey = ctx.plugin.formatKey;
var formatSeed = ctx.plugin.formatSeed;
var b = forge.util.createBuffer();
// reset key for every request
ctx.key = null;
generate();
function generate(err) {
if(err) {
return callback(err);
}
// sufficient bytes generated
if(b.length() >= count) {
return callback(null, b.getBytes(count));
}
// if amount of data generated is greater than 1 MiB, trigger reseed
if(ctx.generated > 0xfffff) {
ctx.key = null;
}
if(ctx.key === null) {
// prevent stack overflow
return forge.util.nextTick(function() {
_reseed(generate);
});
}
// generate the random bytes
var bytes = cipher(ctx.key, ctx.seed);
ctx.generated += bytes.length;
b.putBytes(bytes);
// generate bytes for a new key and seed
ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
forge.util.setImmediate(generate);
}
};
/**
* Generates random bytes synchronously.
*
* @param count the number of random bytes to generate.
*
* @return count random bytes as a string.
*/
ctx.generateSync = function(count) {
// simple generator using counter-based CBC
var cipher = ctx.plugin.cipher;
var increment = ctx.plugin.increment;
var formatKey = ctx.plugin.formatKey;
var formatSeed = ctx.plugin.formatSeed;
// reset key for every request
ctx.key = null;
var b = forge.util.createBuffer();
while(b.length() < count) {
// if amount of data generated is greater than 1 MiB, trigger reseed
if(ctx.generated > 0xfffff) {
ctx.key = null;
}
if(ctx.key === null) {
_reseedSync();
}
// generate the random bytes
var bytes = cipher(ctx.key, ctx.seed);
ctx.generated += bytes.length;
b.putBytes(bytes);
// generate bytes for a new key and seed
ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
}
return b.getBytes(count);
};
/**
* Private function that asynchronously reseeds a generator.
*
* @param callback(err) called once the operation completes.
*/
function _reseed(callback) {
if(ctx.pools[0].messageLength >= 32) {
_seed();
return callback();
}
// not enough seed data...
var needed = (32 - ctx.pools[0].messageLength) << 5;
ctx.seedFile(needed, function(err, bytes) {
if(err) {
return callback(err);
}
ctx.collect(bytes);
_seed();
callback();
});
}
/**
* Private function that synchronously reseeds a generator.
*/
function _reseedSync() {
if(ctx.pools[0].messageLength >= 32) {
return _seed();
}
// not enough seed data...
var needed = (32 - ctx.pools[0].messageLength) << 5;
ctx.collect(ctx.seedFileSync(needed));
_seed();
}
/**
* Private function that seeds a generator once enough bytes are available.
*/
function _seed() {
// create a plugin-based message digest
var md = ctx.plugin.md.create();
// digest pool 0's entropy and restart it
md.update(ctx.pools[0].digest().getBytes());
ctx.pools[0].start();
// digest the entropy of other pools whose index k meet the
// condition '2^k mod n == 0' where n is the number of reseeds
var k = 1;
for(var i = 1; i < 32; ++i) {
// prevent signed numbers from being used
k = (k === 31) ? 0x80000000 : (k << 2);
if(k % ctx.reseeds === 0) {
md.update(ctx.pools[i].digest().getBytes());
ctx.pools[i].start();
}
}
// get digest for key bytes and iterate again for seed bytes
var keyBytes = md.digest().getBytes();
md.start();
md.update(keyBytes);
var seedBytes = md.digest().getBytes();
// update
ctx.key = ctx.plugin.formatKey(keyBytes);
ctx.seed = ctx.plugin.formatSeed(seedBytes);
ctx.reseeds = (ctx.reseeds === 0xffffffff) ? 0 : ctx.reseeds + 1;
ctx.generated = 0;
}
/**
* The built-in default seedFile. This seedFile is used when entropy
* is needed immediately.
*
* @param needed the number of bytes that are needed.
*
* @return the random bytes.
*/
function defaultSeedFile(needed) {
// use window.crypto.getRandomValues strong source of entropy if available
var getRandomValues = null;
if(typeof window !== 'undefined') {
var _crypto = window.crypto || window.msCrypto;
if(_crypto && _crypto.getRandomValues) {
getRandomValues = function(arr) {
return _crypto.getRandomValues(arr);
};
}
}
var b = forge.util.createBuffer();
if(getRandomValues) {
while(b.length() < needed) {
// max byte length is 65536 before QuotaExceededError is thrown
// http://www.w3.org/TR/WebCryptoAPI/#RandomSource-method-getRandomValues
var count = Math.max(1, Math.min(needed - b.length(), 65536) / 4);
var entropy = new Uint32Array(Math.floor(count));
try {
getRandomValues(entropy);
for(var i = 0; i < entropy.length; ++i) {
b.putInt32(entropy[i]);
}
} catch(e) {
/* only ignore QuotaExceededError */
if(!(typeof QuotaExceededError !== 'undefined' &&
e instanceof QuotaExceededError)) {
throw e;
}
}
}
}
// be sad and add some weak random data
if(b.length() < needed) {
/* Draws from Park-Miller "minimal standard" 31 bit PRNG,
implemented with David G. Carta's optimization: with 32 bit math
and without division (Public Domain). */
var hi, lo, next;
var seed = Math.floor(Math.random() * 0x010000);
while(b.length() < needed) {
lo = 16807 * (seed & 0xFFFF);
hi = 16807 * (seed >> 16);
lo += (hi & 0x7FFF) << 16;
lo += hi >> 15;
lo = (lo & 0x7FFFFFFF) + (lo >> 31);
seed = lo & 0xFFFFFFFF;
// consume lower 3 bytes of seed
for(var i = 0; i < 3; ++i) {
// throw in more pseudo random
next = seed >>> (i << 3);
next ^= Math.floor(Math.random() * 0x0100);
b.putByte(String.fromCharCode(next & 0xFF));
}
}
}
return b.getBytes(needed);
}
// initialize seed file APIs
if(_crypto) {
// use nodejs async API
ctx.seedFile = function(needed, callback) {
_crypto.randomBytes(needed, function(err, bytes) {
if(err) {
return callback(err);
}
callback(null, bytes.toString());
});
};
// use nodejs sync API
ctx.seedFileSync = function(needed) {
return _crypto.randomBytes(needed).toString();
};
} else {
ctx.seedFile = function(needed, callback) {
try {
callback(null, defaultSeedFile(needed));
} catch(e) {
callback(e);
}
};
ctx.seedFileSync = defaultSeedFile;
}
/**
* Adds entropy to a prng ctx's accumulator.
*
* @param bytes the bytes of entropy as a string.
*/
ctx.collect = function(bytes) {
// iterate over pools distributing entropy cyclically
var count = bytes.length;
for(var i = 0; i < count; ++i) {
ctx.pools[ctx.pool].update(bytes.substr(i, 1));
ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;
}
};
/**
* Collects an integer of n bits.
*
* @param i the integer entropy.
* @param n the number of bits in the integer.
*/
ctx.collectInt = function(i, n) {
var bytes = '';
for(var x = 0; x < n; x += 8) {
bytes += String.fromCharCode((i >> x) & 0xFF);
}
ctx.collect(bytes);
};
/**
* Registers a Web Worker to receive immediate entropy from the main thread.
* This method is required until Web Workers can access the native crypto
* API. This method should be called twice for each created worker, once in
* the main thread, and once in the worker itself.
*
* @param worker the worker to register.
*/
ctx.registerWorker = function(worker) {
// worker receives random bytes
if(worker === self) {
ctx.seedFile = function(needed, callback) {
function listener(e) {
var data = e.data;
if(data.forge && data.forge.prng) {
self.removeEventListener('message', listener);
callback(data.forge.prng.err, data.forge.prng.bytes);
}
}
self.addEventListener('message', listener);
self.postMessage({forge: {prng: {needed: needed}}});
};
} else {
// main thread sends random bytes upon request
var listener = function(e) {
var data = e.data;
if(data.forge && data.forge.prng) {
ctx.seedFile(data.forge.prng.needed, function(err, bytes) {
worker.postMessage({forge: {prng: {err: err, bytes: bytes}}});
});
}
};
// TODO: do we need to remove the event listener when the worker dies?
worker.addEventListener('message', listener);
}
};
return ctx;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'prng';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './md', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

295
forge.js/pss.js Normal file
View File

@@ -0,0 +1,295 @@
/**
* Javascript implementation of PKCS#1 PSS signature padding.
*
* @author Stefan Siegl
*
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for PSS API
var pss = forge.pss = forge.pss || {};
/**
* Creates a PSS signature scheme object.
*
* There are several ways to provide a salt for encoding:
*
* 1. Specify the saltLength only and the built-in PRNG will generate it.
* 2. Specify the saltLength and a custom PRNG with 'getBytesSync' defined that
* will be used.
* 3. Specify the salt itself as a forge.util.ByteBuffer.
*
* @param options the options to use:
* md the message digest object to use, a forge md instance.
* mgf the mask generation function to use, a forge mgf instance.
* [saltLength] the length of the salt in octets.
* [prng] the pseudo-random number generator to use to produce a salt.
* [salt] the salt to use when encoding.
*
* @return a signature scheme object.
*/
pss.create = function(options) {
// backwards compatibility w/legacy args: hash, mgf, sLen
if(arguments.length === 3) {
options = {
md: arguments[0],
mgf: arguments[1],
saltLength: arguments[2]
};
}
var hash = options.md;
var mgf = options.mgf;
var hLen = hash.digestLength;
var salt_ = options.salt || null;
if(typeof salt_ === 'string') {
// assume binary-encoded string
salt_ = forge.util.createBuffer(salt_);
}
var sLen;
if('saltLength' in options) {
sLen = options.saltLength;
} else if(salt_ !== null) {
sLen = salt_.length();
} else {
throw new Error('Salt length not specified or specific salt not given.');
}
if(salt_ !== null && salt_.length() !== sLen) {
throw new Error('Given salt length does not match length of given salt.');
}
var prng = options.prng || forge.random;
var pssobj = {};
/**
* Encodes a PSS signature.
*
* This function implements EMSA-PSS-ENCODE as per RFC 3447, section 9.1.1.
*
* @param md the message digest object with the hash to sign.
* @param modsBits the length of the RSA modulus in bits.
*
* @return the encoded message as a binary-encoded string of length
* ceil((modBits - 1) / 8).
*/
pssobj.encode = function(md, modBits) {
var i;
var emBits = modBits - 1;
var emLen = Math.ceil(emBits / 8);
/* 2. Let mHash = Hash(M), an octet string of length hLen. */
var mHash = md.digest().getBytes();
/* 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. */
if(emLen < hLen + sLen + 2) {
throw new Error('Message is too long to encrypt.');
}
/* 4. Generate a random octet string salt of length sLen; if sLen = 0,
* then salt is the empty string. */
var salt;
if(salt_ === null) {
salt = prng.getBytesSync(sLen);
} else {
salt = salt_.bytes();
}
/* 5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; */
var m_ = new forge.util.ByteBuffer();
m_.fillWithByte(0, 8);
m_.putBytes(mHash);
m_.putBytes(salt);
/* 6. Let H = Hash(M'), an octet string of length hLen. */
hash.start();
hash.update(m_.getBytes());
var h = hash.digest().getBytes();
/* 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
* zero octets. The length of PS may be 0. */
var ps = new forge.util.ByteBuffer();
ps.fillWithByte(0, emLen - sLen - hLen - 2);
/* 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
* emLen - hLen - 1. */
ps.putByte(0x01);
ps.putBytes(salt);
var db = ps.getBytes();
/* 9. Let dbMask = MGF(H, emLen - hLen - 1). */
var maskLen = emLen - hLen - 1;
var dbMask = mgf.generate(h, maskLen);
/* 10. Let maskedDB = DB \xor dbMask. */
var maskedDB = '';
for(i = 0; i < maskLen; i ++) {
maskedDB += String.fromCharCode(db.charCodeAt(i) ^ dbMask.charCodeAt(i));
}
/* 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
* maskedDB to zero. */
var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
maskedDB = String.fromCharCode(maskedDB.charCodeAt(0) & ~mask) +
maskedDB.substr(1);
/* 12. Let EM = maskedDB || H || 0xbc.
* 13. Output EM. */
return maskedDB + h + String.fromCharCode(0xbc);
};
/**
* Verifies a PSS signature.
*
* This function implements EMSA-PSS-VERIFY as per RFC 3447, section 9.1.2.
*
* @param mHash the message digest hash, as a binary-encoded string, to
* compare against the signature.
* @param em the encoded message, as a binary-encoded string
* (RSA decryption result).
* @param modsBits the length of the RSA modulus in bits.
*
* @return true if the signature was verified, false if not.
*/
pssobj.verify = function(mHash, em, modBits) {
var i;
var emBits = modBits - 1;
var emLen = Math.ceil(emBits / 8);
/* c. Convert the message representative m to an encoded message EM
* of length emLen = ceil((modBits - 1) / 8) octets, where modBits
* is the length in bits of the RSA modulus n */
em = em.substr(-emLen);
/* 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. */
if(emLen < hLen + sLen + 2) {
throw new Error('Inconsistent parameters to PSS signature verification.');
}
/* 4. If the rightmost octet of EM does not have hexadecimal value
* 0xbc, output "inconsistent" and stop. */
if(em.charCodeAt(emLen - 1) !== 0xbc) {
throw new Error('Encoded message does not end in 0xBC.');
}
/* 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
* let H be the next hLen octets. */
var maskLen = emLen - hLen - 1;
var maskedDB = em.substr(0, maskLen);
var h = em.substr(maskLen, hLen);
/* 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
* maskedDB are not all equal to zero, output "inconsistent" and stop. */
var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
if((maskedDB.charCodeAt(0) & mask) !== 0) {
throw new Error('Bits beyond keysize not zero as expected.');
}
/* 7. Let dbMask = MGF(H, emLen - hLen - 1). */
var dbMask = mgf.generate(h, maskLen);
/* 8. Let DB = maskedDB \xor dbMask. */
var db = '';
for(i = 0; i < maskLen; i ++) {
db += String.fromCharCode(maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i));
}
/* 9. Set the leftmost 8emLen - emBits bits of the leftmost octet
* in DB to zero. */
db = String.fromCharCode(db.charCodeAt(0) & ~mask) + db.substr(1);
/* 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
* or if the octet at position emLen - hLen - sLen - 1 (the leftmost
* position is "position 1") does not have hexadecimal value 0x01,
* output "inconsistent" and stop. */
var checkLen = emLen - hLen - sLen - 2;
for(i = 0; i < checkLen; i ++) {
if(db.charCodeAt(i) !== 0x00) {
throw new Error('Leftmost octets not zero as expected');
}
}
if(db.charCodeAt(checkLen) !== 0x01) {
throw new Error('Inconsistent PSS signature, 0x01 marker not found');
}
/* 11. Let salt be the last sLen octets of DB. */
var salt = db.substr(-sLen);
/* 12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
var m_ = new forge.util.ByteBuffer();
m_.fillWithByte(0, 8);
m_.putBytes(mHash);
m_.putBytes(salt);
/* 13. Let H' = Hash(M'), an octet string of length hLen. */
hash.start();
hash.update(m_.getBytes());
var h_ = hash.digest().getBytes();
/* 14. If H = H', output "consistent." Otherwise, output "inconsistent." */
return h === h_;
};
return pssobj;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pss';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './random', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

237
forge.js/random.js Normal file
View File

@@ -0,0 +1,237 @@
/**
* An API for getting cryptographically-secure random bytes. The bytes are
* generated using the Fortuna algorithm devised by Bruce Schneier and
* Niels Ferguson.
*
* Getting strong random bytes is not yet easy to do in javascript. The only
* truish random entropy that can be collected is from the mouse, keyboard, or
* from timing with respect to page loads, etc. This generator makes a poor
* attempt at providing random bytes when those sources haven't yet provided
* enough entropy to initially seed or to reseed the PRNG.
*
* @author Dave Longley
*
* Copyright (c) 2009-2014 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// forge.random already defined
if(forge.random && forge.random.getBytes) {
return;
}
(function(jQuery) {
// the default prng plugin, uses AES-128
var prng_aes = {};
var _prng_aes_output = new Array(4);
var _prng_aes_buffer = forge.util.createBuffer();
prng_aes.formatKey = function(key) {
// convert the key into 32-bit integers
var tmp = forge.util.createBuffer(key);
key = new Array(4);
key[0] = tmp.getInt32();
key[1] = tmp.getInt32();
key[2] = tmp.getInt32();
key[3] = tmp.getInt32();
// return the expanded key
return forge.aes._expandKey(key, false);
};
prng_aes.formatSeed = function(seed) {
// convert seed into 32-bit integers
var tmp = forge.util.createBuffer(seed);
seed = new Array(4);
seed[0] = tmp.getInt32();
seed[1] = tmp.getInt32();
seed[2] = tmp.getInt32();
seed[3] = tmp.getInt32();
return seed;
};
prng_aes.cipher = function(key, seed) {
forge.aes._updateBlock(key, seed, _prng_aes_output, false);
_prng_aes_buffer.putInt32(_prng_aes_output[0]);
_prng_aes_buffer.putInt32(_prng_aes_output[1]);
_prng_aes_buffer.putInt32(_prng_aes_output[2]);
_prng_aes_buffer.putInt32(_prng_aes_output[3]);
return _prng_aes_buffer.getBytes();
};
prng_aes.increment = function(seed) {
// FIXME: do we care about carry or signed issues?
++seed[3];
return seed;
};
prng_aes.md = forge.md.sha256;
/**
* Creates a new PRNG.
*/
function spawnPrng() {
var ctx = forge.prng.create(prng_aes);
/**
* Gets random bytes. If a native secure crypto API is unavailable, this
* method tries to make the bytes more unpredictable by drawing from data that
* can be collected from the user of the browser, eg: mouse movement.
*
* If a callback is given, this method will be called asynchronously.
*
* @param count the number of random bytes to get.
* @param [callback(err, bytes)] called once the operation completes.
*
* @return the random bytes in a string.
*/
ctx.getBytes = function(count, callback) {
return ctx.generate(count, callback);
};
/**
* Gets random bytes asynchronously. If a native secure crypto API is
* unavailable, this method tries to make the bytes more unpredictable by
* drawing from data that can be collected from the user of the browser,
* eg: mouse movement.
*
* @param count the number of random bytes to get.
*
* @return the random bytes in a string.
*/
ctx.getBytesSync = function(count) {
return ctx.generate(count);
};
return ctx;
}
// create default prng context
var _ctx = spawnPrng();
// add other sources of entropy only if window.crypto.getRandomValues is not
// available -- otherwise this source will be automatically used by the prng
var _nodejs = (
typeof process !== 'undefined' && process.versions && process.versions.node);
var getRandomValues = null;
if(typeof window !== 'undefined') {
var _crypto = window.crypto || window.msCrypto;
if(_crypto && _crypto.getRandomValues) {
getRandomValues = function(arr) {
return _crypto.getRandomValues(arr);
};
}
}
if(forge.disableNativeCode || (!_nodejs && !getRandomValues)) {
// if this is a web worker, do not use weak entropy, instead register to
// receive strong entropy asynchronously from the main thread
if(typeof window === 'undefined' || window.document === undefined) {
// FIXME:
}
// get load time entropy
_ctx.collectInt(+new Date(), 32);
// add some entropy from navigator object
if(typeof(navigator) !== 'undefined') {
var _navBytes = '';
for(var key in navigator) {
try {
if(typeof(navigator[key]) == 'string') {
_navBytes += navigator[key];
}
} catch(e) {
/* Some navigator keys might not be accessible, e.g. the geolocation
attribute throws an exception if touched in Mozilla chrome://
context.
Silently ignore this and just don't use this as a source of
entropy. */
}
}
_ctx.collect(_navBytes);
_navBytes = null;
}
// add mouse and keyboard collectors if jquery is available
if(jQuery) {
// set up mouse entropy capture
jQuery().mousemove(function(e) {
// add mouse coords
_ctx.collectInt(e.clientX, 16);
_ctx.collectInt(e.clientY, 16);
});
// set up keyboard entropy capture
jQuery().keypress(function(e) {
_ctx.collectInt(e.charCode, 8);
});
}
}
/* Random API */
if(!forge.random) {
forge.random = _ctx;
} else {
// extend forge.random with _ctx
for(var key in _ctx) {
forge.random[key] = _ctx[key];
}
}
// expose spawn PRNG
forge.random.createInstance = spawnPrng;
})(typeof(jQuery) !== 'undefined' ? jQuery : null);
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'random';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './aes', './md', './prng', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

470
forge.js/rc2.js Normal file
View File

@@ -0,0 +1,470 @@
/**
* RC2 implementation.
*
* @author Stefan Siegl
*
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
*
* Information on the RC2 cipher is available from RFC #2268,
* http://www.ietf.org/rfc/rfc2268.txt
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var piTable = [
0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad
];
var s = [1, 2, 3, 5];
/**
* Rotate a word left by given number of bits.
*
* Bits that are shifted out on the left are put back in on the right
* hand side.
*
* @param word The word to shift left.
* @param bits The number of bits to shift by.
* @return The rotated word.
*/
var rol = function(word, bits) {
return ((word << bits) & 0xffff) | ((word & 0xffff) >> (16 - bits));
};
/**
* Rotate a word right by given number of bits.
*
* Bits that are shifted out on the right are put back in on the left
* hand side.
*
* @param word The word to shift right.
* @param bits The number of bits to shift by.
* @return The rotated word.
*/
var ror = function(word, bits) {
return ((word & 0xffff) >> bits) | ((word << (16 - bits)) & 0xffff);
};
/* RC2 API */
forge.rc2 = forge.rc2 || {};
/**
* Perform RC2 key expansion as per RFC #2268, section 2.
*
* @param key variable-length user key (between 1 and 128 bytes)
* @param effKeyBits number of effective key bits (default: 128)
* @return the expanded RC2 key (ByteBuffer of 128 bytes)
*/
forge.rc2.expandKey = function(key, effKeyBits) {
if(typeof key === 'string') {
key = forge.util.createBuffer(key);
}
effKeyBits = effKeyBits || 128;
/* introduce variables that match the names used in RFC #2268 */
var L = key;
var T = key.length();
var T1 = effKeyBits;
var T8 = Math.ceil(T1 / 8);
var TM = 0xff >> (T1 & 0x07);
var i;
for(i = T; i < 128; i ++) {
L.putByte(piTable[(L.at(i - 1) + L.at(i - T)) & 0xff]);
}
L.setAt(128 - T8, piTable[L.at(128 - T8) & TM]);
for(i = 127 - T8; i >= 0; i --) {
L.setAt(i, piTable[L.at(i + 1) ^ L.at(i + T8)]);
}
return L;
};
/**
* Creates a RC2 cipher object.
*
* @param key the symmetric key to use (as base for key generation).
* @param bits the number of effective key bits.
* @param encrypt false for decryption, true for encryption.
*
* @return the cipher.
*/
var createCipher = function(key, bits, encrypt) {
var _finish = false, _input = null, _output = null, _iv = null;
var mixRound, mashRound;
var i, j, K = [];
/* Expand key and fill into K[] Array */
key = forge.rc2.expandKey(key, bits);
for(i = 0; i < 64; i ++) {
K.push(key.getInt16Le());
}
if(encrypt) {
/**
* Perform one mixing round "in place".
*
* @param R Array of four words to perform mixing on.
*/
mixRound = function(R) {
for(i = 0; i < 4; i++) {
R[i] += K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +
((~R[(i + 3) % 4]) & R[(i + 1) % 4]);
R[i] = rol(R[i], s[i]);
j ++;
}
};
/**
* Perform one mashing round "in place".
*
* @param R Array of four words to perform mashing on.
*/
mashRound = function(R) {
for(i = 0; i < 4; i ++) {
R[i] += K[R[(i + 3) % 4] & 63];
}
};
} else {
/**
* Perform one r-mixing round "in place".
*
* @param R Array of four words to perform mixing on.
*/
mixRound = function(R) {
for(i = 3; i >= 0; i--) {
R[i] = ror(R[i], s[i]);
R[i] -= K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +
((~R[(i + 3) % 4]) & R[(i + 1) % 4]);
j --;
}
};
/**
* Perform one r-mashing round "in place".
*
* @param R Array of four words to perform mashing on.
*/
mashRound = function(R) {
for(i = 3; i >= 0; i--) {
R[i] -= K[R[(i + 3) % 4] & 63];
}
};
}
/**
* Run the specified cipher execution plan.
*
* This function takes four words from the input buffer, applies the IV on
* it (if requested) and runs the provided execution plan.
*
* The plan must be put together in form of a array of arrays. Where the
* outer one is simply a list of steps to perform and the inner one needs
* to have two elements: the first one telling how many rounds to perform,
* the second one telling what to do (i.e. the function to call).
*
* @param {Array} plan The plan to execute.
*/
var runPlan = function(plan) {
var R = [];
/* Get data from input buffer and fill the four words into R */
for(i = 0; i < 4; i ++) {
var val = _input.getInt16Le();
if(_iv !== null) {
if(encrypt) {
/* We're encrypting, apply the IV first. */
val ^= _iv.getInt16Le();
} else {
/* We're decryption, keep cipher text for next block. */
_iv.putInt16Le(val);
}
}
R.push(val & 0xffff);
}
/* Reset global "j" variable as per spec. */
j = encrypt ? 0 : 63;
/* Run execution plan. */
for(var ptr = 0; ptr < plan.length; ptr ++) {
for(var ctr = 0; ctr < plan[ptr][0]; ctr ++) {
plan[ptr][1](R);
}
}
/* Write back result to output buffer. */
for(i = 0; i < 4; i ++) {
if(_iv !== null) {
if(encrypt) {
/* We're encrypting in CBC-mode, feed back encrypted bytes into
IV buffer to carry it forward to next block. */
_iv.putInt16Le(R[i]);
} else {
R[i] ^= _iv.getInt16Le();
}
}
_output.putInt16Le(R[i]);
}
};
/* Create cipher object */
var cipher = null;
cipher = {
/**
* Starts or restarts the encryption or decryption process, whichever
* was previously configured.
*
* To use the cipher in CBC mode, iv may be given either as a string
* of bytes, or as a byte buffer. For ECB mode, give null as iv.
*
* @param iv the initialization vector to use, null for ECB mode.
* @param output the output the buffer to write to, null to create one.
*/
start: function(iv, output) {
if(iv) {
/* CBC mode */
if(typeof iv === 'string') {
iv = forge.util.createBuffer(iv);
}
}
_finish = false;
_input = forge.util.createBuffer();
_output = output || new forge.util.createBuffer();
_iv = iv;
cipher.output = _output;
},
/**
* Updates the next block.
*
* @param input the buffer to read from.
*/
update: function(input) {
if(!_finish) {
// not finishing, so fill the input buffer with more input
_input.putBuffer(input);
}
while(_input.length() >= 8) {
runPlan([
[ 5, mixRound ],
[ 1, mashRound ],
[ 6, mixRound ],
[ 1, mashRound ],
[ 5, mixRound ]
]);
}
},
/**
* Finishes encrypting or decrypting.
*
* @param pad a padding function to use, null for PKCS#7 padding,
* signature(blockSize, buffer, decrypt).
*
* @return true if successful, false on error.
*/
finish: function(pad) {
var rval = true;
if(encrypt) {
if(pad) {
rval = pad(8, _input, !encrypt);
} else {
// add PKCS#7 padding to block (each pad byte is the
// value of the number of pad bytes)
var padding = (_input.length() === 8) ? 8 : (8 - _input.length());
_input.fillWithByte(padding, padding);
}
}
if(rval) {
// do final update
_finish = true;
cipher.update();
}
if(!encrypt) {
// check for error: input data not a multiple of block size
rval = (_input.length() === 0);
if(rval) {
if(pad) {
rval = pad(8, _output, !encrypt);
} else {
// ensure padding byte count is valid
var len = _output.length();
var count = _output.at(len - 1);
if(count > len) {
rval = false;
} else {
// trim off padding bytes
_output.truncate(count);
}
}
}
}
return rval;
}
};
return cipher;
};
/**
* Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the
* given symmetric key. The output will be stored in the 'output' member
* of the returned cipher.
*
* The key and iv may be given as a string of bytes or a byte buffer.
* The cipher is initialized to use 128 effective key bits.
*
* @param key the symmetric key to use.
* @param iv the initialization vector to use.
* @param output the buffer to write to, null to create one.
*
* @return the cipher.
*/
forge.rc2.startEncrypting = function(key, iv, output) {
var cipher = forge.rc2.createEncryptionCipher(key, 128);
cipher.start(iv, output);
return cipher;
};
/**
* Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the
* given symmetric key.
*
* The key may be given as a string of bytes or a byte buffer.
*
* To start encrypting call start() on the cipher with an iv and optional
* output buffer.
*
* @param key the symmetric key to use.
*
* @return the cipher.
*/
forge.rc2.createEncryptionCipher = function(key, bits) {
return createCipher(key, bits, true);
};
/**
* Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the
* given symmetric key. The output will be stored in the 'output' member
* of the returned cipher.
*
* The key and iv may be given as a string of bytes or a byte buffer.
* The cipher is initialized to use 128 effective key bits.
*
* @param key the symmetric key to use.
* @param iv the initialization vector to use.
* @param output the buffer to write to, null to create one.
*
* @return the cipher.
*/
forge.rc2.startDecrypting = function(key, iv, output) {
var cipher = forge.rc2.createDecryptionCipher(key, 128);
cipher.start(iv, output);
return cipher;
};
/**
* Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the
* given symmetric key.
*
* The key may be given as a string of bytes or a byte buffer.
*
* To start decrypting call start() on the cipher with an iv and optional
* output buffer.
*
* @param key the symmetric key to use.
*
* @return the cipher.
*/
forge.rc2.createDecryptionCipher = function(key, bits) {
return createCipher(key, bits, false);
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'rc2';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

1714
forge.js/rsa.js Normal file

File diff suppressed because it is too large Load Diff

369
forge.js/sha1.js Normal file
View File

@@ -0,0 +1,369 @@
/**
* Secure Hash Algorithm with 160-bit digest (SHA-1) implementation.
*
* @author Dave Longley
*
* Copyright (c) 2010-2015 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var sha1 = forge.sha1 = forge.sha1 || {};
forge.md = forge.md || {};
forge.md.algorithms = forge.md.algorithms || {};
forge.md.sha1 = forge.md.algorithms.sha1 = sha1;
/**
* Creates a SHA-1 message digest object.
*
* @return a message digest object.
*/
sha1.create = function() {
// do initialization as necessary
if(!_initialized) {
_init();
}
// SHA-1 state contains five 32-bit integers
var _state = null;
// input buffer
var _input = forge.util.createBuffer();
// used for word storage
var _w = new Array(80);
// message digest object
var md = {
algorithm: 'sha1',
blockLength: 64,
digestLength: 20,
// 56-bit length of message so far (does not including padding)
messageLength: 0,
// true message length
fullMessageLength: null,
// size of message length in bytes
messageLengthSize: 8
};
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
// up to 56-bit message length for convenience
md.messageLength = 0;
// full message length (set md.messageLength64 for backwards-compatibility)
md.fullMessageLength = md.messageLength64 = [];
var int32s = md.messageLengthSize / 4;
for(var i = 0; i < int32s; ++i) {
md.fullMessageLength.push(0);
}
_input = forge.util.createBuffer();
_state = {
h0: 0x67452301,
h1: 0xEFCDAB89,
h2: 0x98BADCFE,
h3: 0x10325476,
h4: 0xC3D2E1F0
};
return md;
};
// start digest automatically for first time
md.start();
/**
* Updates the digest with the given message input. The given input can
* treated as raw input (no encoding will be applied) or an encoding of
* 'utf8' maybe given to encode the input using UTF-8.
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
msg = forge.util.encodeUtf8(msg);
}
// update message length
var len = msg.length;
md.messageLength += len;
len = [(len / 0x100000000) >>> 0, len >>> 0];
for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
md.fullMessageLength[i] += len[1];
len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
len[0] = ((len[1] / 0x100000000) >>> 0);
}
// add bytes to input buffer
_input.putBytes(msg);
// process bytes
_update(_state, _w, _input);
// compact input buffer every 2K or if empty
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
* Produces the digest.
*
* @return a byte buffer containing the digest value.
*/
md.digest = function() {
/* Note: Here we copy the remaining bytes in the input buffer and
add the appropriate SHA-1 padding. Then we do the final update
on a copy of the state so that if the user wants to get
intermediate digests they can do so. */
/* Determine the number of bytes that must be added to the message
to ensure its length is congruent to 448 mod 512. In other words,
the data to be digested must be a multiple of 512 bits (or 128 bytes).
This data includes the message, some padding, and the length of the
message. Since the length of the message will be encoded as 8 bytes (64
bits), that means that the last segment of the data must have 56 bytes
(448 bits) of message and padding. Therefore, the length of the message
plus the padding must be congruent to 448 mod 512 because
512 - 128 = 448.
In order to fill up the message length it must be filled with
padding that begins with 1 bit followed by all 0 bits. Padding
must *always* be present, so if the message length is already
congruent to 448 mod 512, then 512 padding bits must be added. */
var finalBlock = forge.util.createBuffer();
finalBlock.putBytes(_input.bytes());
// compute remaining size to be digested (include message length size)
var remaining = (
md.fullMessageLength[md.fullMessageLength.length - 1] +
md.messageLengthSize);
// add padding for overflow blockSize - overflow
// _padding starts with 1 byte with first bit is set (byte value 128), then
// there may be up to (blockSize - 1) other pad bytes
var overflow = remaining & (md.blockLength - 1);
finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));
// serialize message length in bits in big-endian order; since length
// is stored in bytes we multiply by 8 and add carry from next int
var messageLength = forge.util.createBuffer();
var next, carry;
var bits = md.fullMessageLength[0] * 8;
for(var i = 0; i < md.fullMessageLength.length; ++i) {
next = md.fullMessageLength[i + 1] * 8;
carry = (next / 0x100000000) >>> 0;
bits += carry;
finalBlock.putInt32(bits >>> 0);
bits = next;
}
var s2 = {
h0: _state.h0,
h1: _state.h1,
h2: _state.h2,
h3: _state.h3,
h4: _state.h4
};
_update(s2, _w, finalBlock);
var rval = forge.util.createBuffer();
rval.putInt32(s2.h0);
rval.putInt32(s2.h1);
rval.putInt32(s2.h2);
rval.putInt32(s2.h3);
rval.putInt32(s2.h4);
return rval;
};
return md;
};
// sha-1 padding bytes not initialized yet
var _padding = null;
var _initialized = false;
/**
* Initializes the constant tables.
*/
function _init() {
// create padding
_padding = String.fromCharCode(128);
_padding += forge.util.fillString(String.fromCharCode(0x00), 64);
// now initialized
_initialized = true;
}
/**
* Updates a SHA-1 state with the given byte buffer.
*
* @param s the SHA-1 state to update.
* @param w the array to use to store words.
* @param bytes the byte buffer to update with.
*/
function _update(s, w, bytes) {
// consume 512 bit (64 byte) chunks
var t, a, b, c, d, e, f, i;
var len = bytes.length();
while(len >= 64) {
// the w array will be populated with sixteen 32-bit big-endian words
// and then extended into 80 32-bit words according to SHA-1 algorithm
// and for 32-79 using Max Locktyukhin's optimization
// initialize hash value for this chunk
a = s.h0;
b = s.h1;
c = s.h2;
d = s.h3;
e = s.h4;
// round 1
for(i = 0; i < 16; ++i) {
t = bytes.getInt32();
w[i] = t;
f = d ^ (b & (c ^ d));
t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
for(; i < 20; ++i) {
t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]);
t = (t << 1) | (t >>> 31);
w[i] = t;
f = d ^ (b & (c ^ d));
t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
// round 2
for(; i < 32; ++i) {
t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]);
t = (t << 1) | (t >>> 31);
w[i] = t;
f = b ^ c ^ d;
t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
for(; i < 40; ++i) {
t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
t = (t << 2) | (t >>> 30);
w[i] = t;
f = b ^ c ^ d;
t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
// round 3
for(; i < 60; ++i) {
t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
t = (t << 2) | (t >>> 30);
w[i] = t;
f = (b & c) | (d & (b ^ c));
t = ((a << 5) | (a >>> 27)) + f + e + 0x8F1BBCDC + t;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
// round 4
for(; i < 80; ++i) {
t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
t = (t << 2) | (t >>> 30);
w[i] = t;
f = b ^ c ^ d;
t = ((a << 5) | (a >>> 27)) + f + e + 0xCA62C1D6 + t;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
// update hash state
s.h0 = (s.h0 + a) | 0;
s.h1 = (s.h1 + b) | 0;
s.h2 = (s.h2 + c) | 0;
s.h3 = (s.h3 + d) | 0;
s.h4 = (s.h4 + e) | 0;
len -= 64;
}
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'sha1';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

379
forge.js/sha256.js Normal file
View File

@@ -0,0 +1,379 @@
/**
* Secure Hash Algorithm with 256-bit digest (SHA-256) implementation.
*
* See FIPS 180-2 for details.
*
* @author Dave Longley
*
* Copyright (c) 2010-2015 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var sha256 = forge.sha256 = forge.sha256 || {};
forge.md = forge.md || {};
forge.md.algorithms = forge.md.algorithms || {};
forge.md.sha256 = forge.md.algorithms.sha256 = sha256;
/**
* Creates a SHA-256 message digest object.
*
* @return a message digest object.
*/
sha256.create = function() {
// do initialization as necessary
if(!_initialized) {
_init();
}
// SHA-256 state contains eight 32-bit integers
var _state = null;
// input buffer
var _input = forge.util.createBuffer();
// used for word storage
var _w = new Array(64);
// message digest object
var md = {
algorithm: 'sha256',
blockLength: 64,
digestLength: 32,
// 56-bit length of message so far (does not including padding)
messageLength: 0,
// true message length
fullMessageLength: null,
// size of message length in bytes
messageLengthSize: 8
};
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
// up to 56-bit message length for convenience
md.messageLength = 0;
// full message length (set md.messageLength64 for backwards-compatibility)
md.fullMessageLength = md.messageLength64 = [];
var int32s = md.messageLengthSize / 4;
for(var i = 0; i < int32s; ++i) {
md.fullMessageLength.push(0);
}
_input = forge.util.createBuffer();
_state = {
h0: 0x6A09E667,
h1: 0xBB67AE85,
h2: 0x3C6EF372,
h3: 0xA54FF53A,
h4: 0x510E527F,
h5: 0x9B05688C,
h6: 0x1F83D9AB,
h7: 0x5BE0CD19
};
return md;
};
// start digest automatically for first time
md.start();
/**
* Updates the digest with the given message input. The given input can
* treated as raw input (no encoding will be applied) or an encoding of
* 'utf8' maybe given to encode the input using UTF-8.
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
msg = forge.util.encodeUtf8(msg);
}
// update message length
var len = msg.length;
md.messageLength += len;
len = [(len / 0x100000000) >>> 0, len >>> 0];
for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
md.fullMessageLength[i] += len[1];
len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
len[0] = ((len[1] / 0x100000000) >>> 0);
}
// add bytes to input buffer
_input.putBytes(msg);
// process bytes
_update(_state, _w, _input);
// compact input buffer every 2K or if empty
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
* Produces the digest.
*
* @return a byte buffer containing the digest value.
*/
md.digest = function() {
/* Note: Here we copy the remaining bytes in the input buffer and
add the appropriate SHA-256 padding. Then we do the final update
on a copy of the state so that if the user wants to get
intermediate digests they can do so. */
/* Determine the number of bytes that must be added to the message
to ensure its length is congruent to 448 mod 512. In other words,
the data to be digested must be a multiple of 512 bits (or 128 bytes).
This data includes the message, some padding, and the length of the
message. Since the length of the message will be encoded as 8 bytes (64
bits), that means that the last segment of the data must have 56 bytes
(448 bits) of message and padding. Therefore, the length of the message
plus the padding must be congruent to 448 mod 512 because
512 - 128 = 448.
In order to fill up the message length it must be filled with
padding that begins with 1 bit followed by all 0 bits. Padding
must *always* be present, so if the message length is already
congruent to 448 mod 512, then 512 padding bits must be added. */
var finalBlock = forge.util.createBuffer();
finalBlock.putBytes(_input.bytes());
// compute remaining size to be digested (include message length size)
var remaining = (
md.fullMessageLength[md.fullMessageLength.length - 1] +
md.messageLengthSize);
// add padding for overflow blockSize - overflow
// _padding starts with 1 byte with first bit is set (byte value 128), then
// there may be up to (blockSize - 1) other pad bytes
var overflow = remaining & (md.blockLength - 1);
finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));
// serialize message length in bits in big-endian order; since length
// is stored in bytes we multiply by 8 and add carry from next int
var messageLength = forge.util.createBuffer();
var next, carry;
var bits = md.fullMessageLength[0] * 8;
for(var i = 0; i < md.fullMessageLength.length; ++i) {
next = md.fullMessageLength[i + 1] * 8;
carry = (next / 0x100000000) >>> 0;
bits += carry;
finalBlock.putInt32(bits >>> 0);
bits = next;
}
var s2 = {
h0: _state.h0,
h1: _state.h1,
h2: _state.h2,
h3: _state.h3,
h4: _state.h4,
h5: _state.h5,
h6: _state.h6,
h7: _state.h7
};
_update(s2, _w, finalBlock);
var rval = forge.util.createBuffer();
rval.putInt32(s2.h0);
rval.putInt32(s2.h1);
rval.putInt32(s2.h2);
rval.putInt32(s2.h3);
rval.putInt32(s2.h4);
rval.putInt32(s2.h5);
rval.putInt32(s2.h6);
rval.putInt32(s2.h7);
return rval;
};
return md;
};
// sha-256 padding bytes not initialized yet
var _padding = null;
var _initialized = false;
// table of constants
var _k = null;
/**
* Initializes the constant tables.
*/
function _init() {
// create padding
_padding = String.fromCharCode(128);
_padding += forge.util.fillString(String.fromCharCode(0x00), 64);
// create K table for SHA-256
_k = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
// now initialized
_initialized = true;
}
/**
* Updates a SHA-256 state with the given byte buffer.
*
* @param s the SHA-256 state to update.
* @param w the array to use to store words.
* @param bytes the byte buffer to update with.
*/
function _update(s, w, bytes) {
// consume 512 bit (64 byte) chunks
var t1, t2, s0, s1, ch, maj, i, a, b, c, d, e, f, g, h;
var len = bytes.length();
while(len >= 64) {
// the w array will be populated with sixteen 32-bit big-endian words
// and then extended into 64 32-bit words according to SHA-256
for(i = 0; i < 16; ++i) {
w[i] = bytes.getInt32();
}
for(; i < 64; ++i) {
// XOR word 2 words ago rot right 17, rot right 19, shft right 10
t1 = w[i - 2];
t1 =
((t1 >>> 17) | (t1 << 15)) ^
((t1 >>> 19) | (t1 << 13)) ^
(t1 >>> 10);
// XOR word 15 words ago rot right 7, rot right 18, shft right 3
t2 = w[i - 15];
t2 =
((t2 >>> 7) | (t2 << 25)) ^
((t2 >>> 18) | (t2 << 14)) ^
(t2 >>> 3);
// sum(t1, word 7 ago, t2, word 16 ago) modulo 2^32
w[i] = (t1 + w[i - 7] + t2 + w[i - 16]) | 0;
}
// initialize hash value for this chunk
a = s.h0;
b = s.h1;
c = s.h2;
d = s.h3;
e = s.h4;
f = s.h5;
g = s.h6;
h = s.h7;
// round function
for(i = 0; i < 64; ++i) {
// Sum1(e)
s1 =
((e >>> 6) | (e << 26)) ^
((e >>> 11) | (e << 21)) ^
((e >>> 25) | (e << 7));
// Ch(e, f, g) (optimized the same way as SHA-1)
ch = g ^ (e & (f ^ g));
// Sum0(a)
s0 =
((a >>> 2) | (a << 30)) ^
((a >>> 13) | (a << 19)) ^
((a >>> 22) | (a << 10));
// Maj(a, b, c) (optimized the same way as SHA-1)
maj = (a & b) | (c & (a ^ b));
// main algorithm
t1 = h + s1 + ch + _k[i] + w[i];
t2 = s0 + maj;
h = g;
g = f;
f = e;
e = (d + t1) | 0;
d = c;
c = b;
b = a;
a = (t1 + t2) | 0;
}
// update hash state
s.h0 = (s.h0 + a) | 0;
s.h1 = (s.h1 + b) | 0;
s.h2 = (s.h2 + c) | 0;
s.h3 = (s.h3 + d) | 0;
s.h4 = (s.h4 + e) | 0;
s.h5 = (s.h5 + f) | 0;
s.h6 = (s.h6 + g) | 0;
s.h7 = (s.h7 + h) | 0;
len -= 64;
}
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'sha256';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

603
forge.js/sha512.js Normal file
View File

@@ -0,0 +1,603 @@
/**
* Secure Hash Algorithm with a 1024-bit block size implementation.
*
* This includes: SHA-512, SHA-384, SHA-512/224, and SHA-512/256. For
* SHA-256 (block size 512 bits), see sha256.js.
*
* See FIPS 180-4 for details.
*
* @author Dave Longley
*
* Copyright (c) 2014-2015 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var sha512 = forge.sha512 = forge.sha512 || {};
forge.md = forge.md || {};
forge.md.algorithms = forge.md.algorithms || {};
// SHA-512
forge.md.sha512 = forge.md.algorithms.sha512 = sha512;
// SHA-384
var sha384 = forge.sha384 = forge.sha512.sha384 = forge.sha512.sha384 || {};
sha384.create = function() {
return sha512.create('SHA-384');
};
forge.md.sha384 = forge.md.algorithms.sha384 = sha384;
// SHA-512/256
forge.sha512.sha256 = forge.sha512.sha256 || {
create: function() {
return sha512.create('SHA-512/256');
}
};
forge.md['sha512/256'] = forge.md.algorithms['sha512/256'] =
forge.sha512.sha256;
// SHA-512/224
forge.sha512.sha224 = forge.sha512.sha224 || {
create: function() {
return sha512.create('SHA-512/224');
}
};
forge.md['sha512/224'] = forge.md.algorithms['sha512/224'] =
forge.sha512.sha224;
/**
* Creates a SHA-2 message digest object.
*
* @param algorithm the algorithm to use (SHA-512, SHA-384, SHA-512/224,
* SHA-512/256).
*
* @return a message digest object.
*/
sha512.create = function(algorithm) {
// do initialization as necessary
if(!_initialized) {
_init();
}
if(typeof algorithm === 'undefined') {
algorithm = 'SHA-512';
}
if(!(algorithm in _states)) {
throw new Error('Invalid SHA-512 algorithm: ' + algorithm);
}
// SHA-512 state contains eight 64-bit integers (each as two 32-bit ints)
var _state = _states[algorithm];
var _h = null;
// input buffer
var _input = forge.util.createBuffer();
// used for 64-bit word storage
var _w = new Array(80);
for(var wi = 0; wi < 80; ++wi) {
_w[wi] = new Array(2);
}
// message digest object
var md = {
// SHA-512 => sha512
algorithm: algorithm.replace('-', '').toLowerCase(),
blockLength: 128,
digestLength: 64,
// 56-bit length of message so far (does not including padding)
messageLength: 0,
// true message length
fullMessageLength: null,
// size of message length in bytes
messageLengthSize: 16
};
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
// up to 56-bit message length for convenience
md.messageLength = 0;
// full message length (set md.messageLength128 for backwards-compatibility)
md.fullMessageLength = md.messageLength128 = [];
var int32s = md.messageLengthSize / 4;
for(var i = 0; i < int32s; ++i) {
md.fullMessageLength.push(0);
}
_input = forge.util.createBuffer();
_h = new Array(_state.length);
for(var i = 0; i < _state.length; ++i) {
_h[i] = _state[i].slice(0);
}
return md;
};
// start digest automatically for first time
md.start();
/**
* Updates the digest with the given message input. The given input can
* treated as raw input (no encoding will be applied) or an encoding of
* 'utf8' maybe given to encode the input using UTF-8.
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
msg = forge.util.encodeUtf8(msg);
}
// update message length
var len = msg.length;
md.messageLength += len;
len = [(len / 0x100000000) >>> 0, len >>> 0];
for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {
md.fullMessageLength[i] += len[1];
len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);
md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;
len[0] = ((len[1] / 0x100000000) >>> 0);
}
// add bytes to input buffer
_input.putBytes(msg);
// process bytes
_update(_h, _w, _input);
// compact input buffer every 2K or if empty
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
* Produces the digest.
*
* @return a byte buffer containing the digest value.
*/
md.digest = function() {
/* Note: Here we copy the remaining bytes in the input buffer and
add the appropriate SHA-512 padding. Then we do the final update
on a copy of the state so that if the user wants to get
intermediate digests they can do so. */
/* Determine the number of bytes that must be added to the message
to ensure its length is congruent to 896 mod 1024. In other words,
the data to be digested must be a multiple of 1024 bits (or 128 bytes).
This data includes the message, some padding, and the length of the
message. Since the length of the message will be encoded as 16 bytes (128
bits), that means that the last segment of the data must have 112 bytes
(896 bits) of message and padding. Therefore, the length of the message
plus the padding must be congruent to 896 mod 1024 because
1024 - 128 = 896.
In order to fill up the message length it must be filled with
padding that begins with 1 bit followed by all 0 bits. Padding
must *always* be present, so if the message length is already
congruent to 896 mod 1024, then 1024 padding bits must be added. */
var finalBlock = forge.util.createBuffer();
finalBlock.putBytes(_input.bytes());
// compute remaining size to be digested (include message length size)
var remaining = (
md.fullMessageLength[md.fullMessageLength.length - 1] +
md.messageLengthSize);
// add padding for overflow blockSize - overflow
// _padding starts with 1 byte with first bit is set (byte value 128), then
// there may be up to (blockSize - 1) other pad bytes
var overflow = remaining & (md.blockLength - 1);
finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));
// serialize message length in bits in big-endian order; since length
// is stored in bytes we multiply by 8 and add carry from next int
var messageLength = forge.util.createBuffer();
var next, carry;
var bits = md.fullMessageLength[0] * 8;
for(var i = 0; i < md.fullMessageLength.length; ++i) {
next = md.fullMessageLength[i + 1] * 8;
carry = (next / 0x100000000) >>> 0;
bits += carry;
finalBlock.putInt32(bits >>> 0);
bits = next;
}
var h = new Array(_h.length);
for(var i = 0; i < _h.length; ++i) {
h[i] = _h[i].slice(0);
}
_update(h, _w, finalBlock);
var rval = forge.util.createBuffer();
var hlen;
if(algorithm === 'SHA-512') {
hlen = h.length;
} else if(algorithm === 'SHA-384') {
hlen = h.length - 2;
} else {
hlen = h.length - 4;
}
for(var i = 0; i < hlen; ++i) {
rval.putInt32(h[i][0]);
if(i !== hlen - 1 || algorithm !== 'SHA-512/224') {
rval.putInt32(h[i][1]);
}
}
return rval;
};
return md;
};
// sha-512 padding bytes not initialized yet
var _padding = null;
var _initialized = false;
// table of constants
var _k = null;
// initial hash states
var _states = null;
/**
* Initializes the constant tables.
*/
function _init() {
// create padding
_padding = String.fromCharCode(128);
_padding += forge.util.fillString(String.fromCharCode(0x00), 128);
// create K table for SHA-512
_k = [
[0x428a2f98, 0xd728ae22], [0x71374491, 0x23ef65cd],
[0xb5c0fbcf, 0xec4d3b2f], [0xe9b5dba5, 0x8189dbbc],
[0x3956c25b, 0xf348b538], [0x59f111f1, 0xb605d019],
[0x923f82a4, 0xaf194f9b], [0xab1c5ed5, 0xda6d8118],
[0xd807aa98, 0xa3030242], [0x12835b01, 0x45706fbe],
[0x243185be, 0x4ee4b28c], [0x550c7dc3, 0xd5ffb4e2],
[0x72be5d74, 0xf27b896f], [0x80deb1fe, 0x3b1696b1],
[0x9bdc06a7, 0x25c71235], [0xc19bf174, 0xcf692694],
[0xe49b69c1, 0x9ef14ad2], [0xefbe4786, 0x384f25e3],
[0x0fc19dc6, 0x8b8cd5b5], [0x240ca1cc, 0x77ac9c65],
[0x2de92c6f, 0x592b0275], [0x4a7484aa, 0x6ea6e483],
[0x5cb0a9dc, 0xbd41fbd4], [0x76f988da, 0x831153b5],
[0x983e5152, 0xee66dfab], [0xa831c66d, 0x2db43210],
[0xb00327c8, 0x98fb213f], [0xbf597fc7, 0xbeef0ee4],
[0xc6e00bf3, 0x3da88fc2], [0xd5a79147, 0x930aa725],
[0x06ca6351, 0xe003826f], [0x14292967, 0x0a0e6e70],
[0x27b70a85, 0x46d22ffc], [0x2e1b2138, 0x5c26c926],
[0x4d2c6dfc, 0x5ac42aed], [0x53380d13, 0x9d95b3df],
[0x650a7354, 0x8baf63de], [0x766a0abb, 0x3c77b2a8],
[0x81c2c92e, 0x47edaee6], [0x92722c85, 0x1482353b],
[0xa2bfe8a1, 0x4cf10364], [0xa81a664b, 0xbc423001],
[0xc24b8b70, 0xd0f89791], [0xc76c51a3, 0x0654be30],
[0xd192e819, 0xd6ef5218], [0xd6990624, 0x5565a910],
[0xf40e3585, 0x5771202a], [0x106aa070, 0x32bbd1b8],
[0x19a4c116, 0xb8d2d0c8], [0x1e376c08, 0x5141ab53],
[0x2748774c, 0xdf8eeb99], [0x34b0bcb5, 0xe19b48a8],
[0x391c0cb3, 0xc5c95a63], [0x4ed8aa4a, 0xe3418acb],
[0x5b9cca4f, 0x7763e373], [0x682e6ff3, 0xd6b2b8a3],
[0x748f82ee, 0x5defb2fc], [0x78a5636f, 0x43172f60],
[0x84c87814, 0xa1f0ab72], [0x8cc70208, 0x1a6439ec],
[0x90befffa, 0x23631e28], [0xa4506ceb, 0xde82bde9],
[0xbef9a3f7, 0xb2c67915], [0xc67178f2, 0xe372532b],
[0xca273ece, 0xea26619c], [0xd186b8c7, 0x21c0c207],
[0xeada7dd6, 0xcde0eb1e], [0xf57d4f7f, 0xee6ed178],
[0x06f067aa, 0x72176fba], [0x0a637dc5, 0xa2c898a6],
[0x113f9804, 0xbef90dae], [0x1b710b35, 0x131c471b],
[0x28db77f5, 0x23047d84], [0x32caab7b, 0x40c72493],
[0x3c9ebe0a, 0x15c9bebc], [0x431d67c4, 0x9c100d4c],
[0x4cc5d4be, 0xcb3e42b6], [0x597f299c, 0xfc657e2a],
[0x5fcb6fab, 0x3ad6faec], [0x6c44198c, 0x4a475817]
];
// initial hash states
_states = {};
_states['SHA-512'] = [
[0x6a09e667, 0xf3bcc908],
[0xbb67ae85, 0x84caa73b],
[0x3c6ef372, 0xfe94f82b],
[0xa54ff53a, 0x5f1d36f1],
[0x510e527f, 0xade682d1],
[0x9b05688c, 0x2b3e6c1f],
[0x1f83d9ab, 0xfb41bd6b],
[0x5be0cd19, 0x137e2179]
];
_states['SHA-384'] = [
[0xcbbb9d5d, 0xc1059ed8],
[0x629a292a, 0x367cd507],
[0x9159015a, 0x3070dd17],
[0x152fecd8, 0xf70e5939],
[0x67332667, 0xffc00b31],
[0x8eb44a87, 0x68581511],
[0xdb0c2e0d, 0x64f98fa7],
[0x47b5481d, 0xbefa4fa4]
];
_states['SHA-512/256'] = [
[0x22312194, 0xFC2BF72C],
[0x9F555FA3, 0xC84C64C2],
[0x2393B86B, 0x6F53B151],
[0x96387719, 0x5940EABD],
[0x96283EE2, 0xA88EFFE3],
[0xBE5E1E25, 0x53863992],
[0x2B0199FC, 0x2C85B8AA],
[0x0EB72DDC, 0x81C52CA2]
];
_states['SHA-512/224'] = [
[0x8C3D37C8, 0x19544DA2],
[0x73E19966, 0x89DCD4D6],
[0x1DFAB7AE, 0x32FF9C82],
[0x679DD514, 0x582F9FCF],
[0x0F6D2B69, 0x7BD44DA8],
[0x77E36F73, 0x04C48942],
[0x3F9D85A8, 0x6A1D36C8],
[0x1112E6AD, 0x91D692A1]
];
// now initialized
_initialized = true;
}
/**
* Updates a SHA-512 state with the given byte buffer.
*
* @param s the SHA-512 state to update.
* @param w the array to use to store words.
* @param bytes the byte buffer to update with.
*/
function _update(s, w, bytes) {
// consume 512 bit (128 byte) chunks
var t1_hi, t1_lo;
var t2_hi, t2_lo;
var s0_hi, s0_lo;
var s1_hi, s1_lo;
var ch_hi, ch_lo;
var maj_hi, maj_lo;
var a_hi, a_lo;
var b_hi, b_lo;
var c_hi, c_lo;
var d_hi, d_lo;
var e_hi, e_lo;
var f_hi, f_lo;
var g_hi, g_lo;
var h_hi, h_lo;
var i, hi, lo, w2, w7, w15, w16;
var len = bytes.length();
while(len >= 128) {
// the w array will be populated with sixteen 64-bit big-endian words
// and then extended into 64 64-bit words according to SHA-512
for(i = 0; i < 16; ++i) {
w[i][0] = bytes.getInt32() >>> 0;
w[i][1] = bytes.getInt32() >>> 0;
}
for(; i < 80; ++i) {
// for word 2 words ago: ROTR 19(x) ^ ROTR 61(x) ^ SHR 6(x)
w2 = w[i - 2];
hi = w2[0];
lo = w2[1];
// high bits
t1_hi = (
((hi >>> 19) | (lo << 13)) ^ // ROTR 19
((lo >>> 29) | (hi << 3)) ^ // ROTR 61/(swap + ROTR 29)
(hi >>> 6)) >>> 0; // SHR 6
// low bits
t1_lo = (
((hi << 13) | (lo >>> 19)) ^ // ROTR 19
((lo << 3) | (hi >>> 29)) ^ // ROTR 61/(swap + ROTR 29)
((hi << 26) | (lo >>> 6))) >>> 0; // SHR 6
// for word 15 words ago: ROTR 1(x) ^ ROTR 8(x) ^ SHR 7(x)
w15 = w[i - 15];
hi = w15[0];
lo = w15[1];
// high bits
t2_hi = (
((hi >>> 1) | (lo << 31)) ^ // ROTR 1
((hi >>> 8) | (lo << 24)) ^ // ROTR 8
(hi >>> 7)) >>> 0; // SHR 7
// low bits
t2_lo = (
((hi << 31) | (lo >>> 1)) ^ // ROTR 1
((hi << 24) | (lo >>> 8)) ^ // ROTR 8
((hi << 25) | (lo >>> 7))) >>> 0; // SHR 7
// sum(t1, word 7 ago, t2, word 16 ago) modulo 2^64 (carry lo overflow)
w7 = w[i - 7];
w16 = w[i - 16];
lo = (t1_lo + w7[1] + t2_lo + w16[1]);
w[i][0] = (t1_hi + w7[0] + t2_hi + w16[0] +
((lo / 0x100000000) >>> 0)) >>> 0;
w[i][1] = lo >>> 0;
}
// initialize hash value for this chunk
a_hi = s[0][0];
a_lo = s[0][1];
b_hi = s[1][0];
b_lo = s[1][1];
c_hi = s[2][0];
c_lo = s[2][1];
d_hi = s[3][0];
d_lo = s[3][1];
e_hi = s[4][0];
e_lo = s[4][1];
f_hi = s[5][0];
f_lo = s[5][1];
g_hi = s[6][0];
g_lo = s[6][1];
h_hi = s[7][0];
h_lo = s[7][1];
// round function
for(i = 0; i < 80; ++i) {
// Sum1(e) = ROTR 14(e) ^ ROTR 18(e) ^ ROTR 41(e)
s1_hi = (
((e_hi >>> 14) | (e_lo << 18)) ^ // ROTR 14
((e_hi >>> 18) | (e_lo << 14)) ^ // ROTR 18
((e_lo >>> 9) | (e_hi << 23))) >>> 0; // ROTR 41/(swap + ROTR 9)
s1_lo = (
((e_hi << 18) | (e_lo >>> 14)) ^ // ROTR 14
((e_hi << 14) | (e_lo >>> 18)) ^ // ROTR 18
((e_lo << 23) | (e_hi >>> 9))) >>> 0; // ROTR 41/(swap + ROTR 9)
// Ch(e, f, g) (optimized the same way as SHA-1)
ch_hi = (g_hi ^ (e_hi & (f_hi ^ g_hi))) >>> 0;
ch_lo = (g_lo ^ (e_lo & (f_lo ^ g_lo))) >>> 0;
// Sum0(a) = ROTR 28(a) ^ ROTR 34(a) ^ ROTR 39(a)
s0_hi = (
((a_hi >>> 28) | (a_lo << 4)) ^ // ROTR 28
((a_lo >>> 2) | (a_hi << 30)) ^ // ROTR 34/(swap + ROTR 2)
((a_lo >>> 7) | (a_hi << 25))) >>> 0; // ROTR 39/(swap + ROTR 7)
s0_lo = (
((a_hi << 4) | (a_lo >>> 28)) ^ // ROTR 28
((a_lo << 30) | (a_hi >>> 2)) ^ // ROTR 34/(swap + ROTR 2)
((a_lo << 25) | (a_hi >>> 7))) >>> 0; // ROTR 39/(swap + ROTR 7)
// Maj(a, b, c) (optimized the same way as SHA-1)
maj_hi = ((a_hi & b_hi) | (c_hi & (a_hi ^ b_hi))) >>> 0;
maj_lo = ((a_lo & b_lo) | (c_lo & (a_lo ^ b_lo))) >>> 0;
// main algorithm
// t1 = (h + s1 + ch + _k[i] + _w[i]) modulo 2^64 (carry lo overflow)
lo = (h_lo + s1_lo + ch_lo + _k[i][1] + w[i][1]);
t1_hi = (h_hi + s1_hi + ch_hi + _k[i][0] + w[i][0] +
((lo / 0x100000000) >>> 0)) >>> 0;
t1_lo = lo >>> 0;
// t2 = s0 + maj modulo 2^64 (carry lo overflow)
lo = s0_lo + maj_lo;
t2_hi = (s0_hi + maj_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
t2_lo = lo >>> 0;
h_hi = g_hi;
h_lo = g_lo;
g_hi = f_hi;
g_lo = f_lo;
f_hi = e_hi;
f_lo = e_lo;
// e = (d + t1) modulo 2^64 (carry lo overflow)
lo = d_lo + t1_lo;
e_hi = (d_hi + t1_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
e_lo = lo >>> 0;
d_hi = c_hi;
d_lo = c_lo;
c_hi = b_hi;
c_lo = b_lo;
b_hi = a_hi;
b_lo = a_lo;
// a = (t1 + t2) modulo 2^64 (carry lo overflow)
lo = t1_lo + t2_lo;
a_hi = (t1_hi + t2_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
a_lo = lo >>> 0;
}
// update hash state (additional modulo 2^64)
lo = s[0][1] + a_lo;
s[0][0] = (s[0][0] + a_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[0][1] = lo >>> 0;
lo = s[1][1] + b_lo;
s[1][0] = (s[1][0] + b_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[1][1] = lo >>> 0;
lo = s[2][1] + c_lo;
s[2][0] = (s[2][0] + c_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[2][1] = lo >>> 0;
lo = s[3][1] + d_lo;
s[3][0] = (s[3][0] + d_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[3][1] = lo >>> 0;
lo = s[4][1] + e_lo;
s[4][0] = (s[4][0] + e_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[4][1] = lo >>> 0;
lo = s[5][1] + f_lo;
s[5][0] = (s[5][0] + f_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[5][1] = lo >>> 0;
lo = s[6][1] + g_lo;
s[6][0] = (s[6][0] + g_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[6][1] = lo >>> 0;
lo = s[7][1] + h_lo;
s[7][0] = (s[7][0] + h_hi + ((lo / 0x100000000) >>> 0)) >>> 0;
s[7][1] = lo >>> 0;
len -= 128;
}
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'sha512';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

342
forge.js/socket.js Normal file
View File

@@ -0,0 +1,342 @@
/**
* Socket implementation that uses flash SocketPool class as a backend.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// define net namespace
var net = forge.net = forge.net || {};
// map of flash ID to socket pool
net.socketPools = {};
/**
* Creates a flash socket pool.
*
* @param options:
* flashId: the dom ID for the flash object element.
* policyPort: the default policy port for sockets, 0 to use the
* flash default.
* policyUrl: the default policy file URL for sockets (if provided
* used instead of a policy port).
* msie: true if the browser is msie, false if not.
*
* @return the created socket pool.
*/
net.createSocketPool = function(options) {
// set default
options.msie = options.msie || false;
// initialize the flash interface
var spId = options.flashId;
var api = document.getElementById(spId);
api.init({marshallExceptions: !options.msie});
// create socket pool entry
var sp = {
// ID of the socket pool
id: spId,
// flash interface
flashApi: api,
// map of socket ID to sockets
sockets: {},
// default policy port
policyPort: options.policyPort || 0,
// default policy URL
policyUrl: options.policyUrl || null
};
net.socketPools[spId] = sp;
// create event handler, subscribe to flash events
if(options.msie === true) {
sp.handler = function(e) {
if(e.id in sp.sockets) {
// get handler function
var f;
switch(e.type) {
case 'connect':
f = 'connected';
break;
case 'close':
f = 'closed';
break;
case 'socketData':
f = 'data';
break;
default:
f = 'error';
break;
}
/* IE calls javascript on the thread of the external object
that triggered the event (in this case flash) ... which will
either run concurrently with other javascript or pre-empt any
running javascript in the middle of its execution (BAD!) ...
calling setTimeout() will schedule the javascript to run on
the javascript thread and solve this EVIL problem. */
setTimeout(function(){sp.sockets[e.id][f](e);}, 0);
}
};
} else {
sp.handler = function(e) {
if(e.id in sp.sockets) {
// get handler function
var f;
switch(e.type) {
case 'connect':
f = 'connected';
break;
case 'close':
f = 'closed';
break;
case 'socketData':
f = 'data';
break;
default:
f = 'error';
break;
}
sp.sockets[e.id][f](e);
}
};
}
var handler = 'forge.net.socketPools[\'' + spId + '\'].handler';
api.subscribe('connect', handler);
api.subscribe('close', handler);
api.subscribe('socketData', handler);
api.subscribe('ioError', handler);
api.subscribe('securityError', handler);
/**
* Destroys a socket pool. The socket pool still needs to be cleaned
* up via net.cleanup().
*/
sp.destroy = function() {
delete net.socketPools[options.flashId];
for(var id in sp.sockets) {
sp.sockets[id].destroy();
}
sp.sockets = {};
api.cleanup();
};
/**
* Creates a new socket.
*
* @param options:
* connected: function(event) called when the socket connects.
* closed: function(event) called when the socket closes.
* data: function(event) called when socket data has arrived,
* it can be read from the socket using receive().
* error: function(event) called when a socket error occurs.
*/
sp.createSocket = function(options) {
// default to empty options
options = options || {};
// create flash socket
var id = api.create();
// create javascript socket wrapper
var socket = {
id: id,
// set handlers
connected: options.connected || function(e){},
closed: options.closed || function(e){},
data: options.data || function(e){},
error: options.error || function(e){}
};
/**
* Destroys this socket.
*/
socket.destroy = function() {
api.destroy(id);
delete sp.sockets[id];
};
/**
* Connects this socket.
*
* @param options:
* host: the host to connect to.
* port: the port to connect to.
* policyPort: the policy port to use (if non-default), 0 to
* use the flash default.
* policyUrl: the policy file URL to use (instead of port).
*/
socket.connect = function(options) {
// give precedence to policy URL over policy port
// if no policy URL and passed port isn't 0, use default port,
// otherwise use 0 for the port
var policyUrl = options.policyUrl || null;
var policyPort = 0;
if(policyUrl === null && options.policyPort !== 0) {
policyPort = options.policyPort || sp.policyPort;
}
api.connect(id, options.host, options.port, policyPort, policyUrl);
};
/**
* Closes this socket.
*/
socket.close = function() {
api.close(id);
socket.closed({
id: socket.id,
type: 'close',
bytesAvailable: 0
});
};
/**
* Determines if the socket is connected or not.
*
* @return true if connected, false if not.
*/
socket.isConnected = function() {
return api.isConnected(id);
};
/**
* Writes bytes to this socket.
*
* @param bytes the bytes (as a string) to write.
*
* @return true on success, false on failure.
*/
socket.send = function(bytes) {
return api.send(id, forge.util.encode64(bytes));
};
/**
* Reads bytes from this socket (non-blocking). Fewer than the number
* of bytes requested may be read if enough bytes are not available.
*
* This method should be called from the data handler if there are
* enough bytes available. To see how many bytes are available, check
* the 'bytesAvailable' property on the event in the data handler or
* call the bytesAvailable() function on the socket. If the browser is
* msie, then the bytesAvailable() function should be used to avoid
* race conditions. Otherwise, using the property on the data handler's
* event may be quicker.
*
* @param count the maximum number of bytes to read.
*
* @return the bytes read (as a string) or null on error.
*/
socket.receive = function(count) {
var rval = api.receive(id, count).rval;
return (rval === null) ? null : forge.util.decode64(rval);
};
/**
* Gets the number of bytes available for receiving on the socket.
*
* @return the number of bytes available for receiving.
*/
socket.bytesAvailable = function() {
return api.getBytesAvailable(id);
};
// store and return socket
sp.sockets[id] = socket;
return socket;
};
return sp;
};
/**
* Destroys a flash socket pool.
*
* @param options:
* flashId: the dom ID for the flash object element.
*/
net.destroySocketPool = function(options) {
if(options.flashId in net.socketPools) {
var sp = net.socketPools[options.flashId];
sp.destroy();
}
};
/**
* Creates a new socket.
*
* @param options:
* flashId: the dom ID for the flash object element.
* connected: function(event) called when the socket connects.
* closed: function(event) called when the socket closes.
* data: function(event) called when socket data has arrived, it
* can be read from the socket using receive().
* error: function(event) called when a socket error occurs.
*
* @return the created socket.
*/
net.createSocket = function(options) {
var socket = null;
if(options.flashId in net.socketPools) {
// get related socket pool
var sp = net.socketPools[options.flashId];
socket = sp.createSocket(options);
}
return socket;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'net';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

295
forge.js/ssh.js Normal file
View File

@@ -0,0 +1,295 @@
/**
* Functions to output keys in SSH-friendly formats.
*
* This is part of the Forge project which may be used under the terms of
* either the BSD License or the GNU General Public License (GPL) Version 2.
*
* See: https://github.com/digitalbazaar/forge/blob/cbebca3780658703d925b61b2caffb1d263a6c1d/LICENSE
*
* @author https://github.com/shellac
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var ssh = forge.ssh = forge.ssh || {};
/**
* Encodes (and optionally encrypts) a private RSA key as a Putty PPK file.
*
* @param privateKey the key.
* @param passphrase a passphrase to protect the key (falsy for no encryption).
* @param comment a comment to include in the key file.
*
* @return the PPK file as a string.
*/
ssh.privateKeyToPutty = function(privateKey, passphrase, comment) {
comment = comment || '';
passphrase = passphrase || '';
var algorithm = 'ssh-rsa';
var encryptionAlgorithm = (passphrase === '') ? 'none' : 'aes256-cbc';
var ppk = 'PuTTY-User-Key-File-2: ' + algorithm + '\r\n';
ppk += 'Encryption: ' + encryptionAlgorithm + '\r\n';
ppk += 'Comment: ' + comment + '\r\n';
// public key into buffer for ppk
var pubbuffer = forge.util.createBuffer();
_addStringToBuffer(pubbuffer, algorithm);
_addBigIntegerToBuffer(pubbuffer, privateKey.e);
_addBigIntegerToBuffer(pubbuffer, privateKey.n);
// write public key
var pub = forge.util.encode64(pubbuffer.bytes(), 64);
var length = Math.floor(pub.length / 66) + 1; // 66 = 64 + \r\n
ppk += 'Public-Lines: ' + length + '\r\n';
ppk += pub;
// private key into a buffer
var privbuffer = forge.util.createBuffer();
_addBigIntegerToBuffer(privbuffer, privateKey.d);
_addBigIntegerToBuffer(privbuffer, privateKey.p);
_addBigIntegerToBuffer(privbuffer, privateKey.q);
_addBigIntegerToBuffer(privbuffer, privateKey.qInv);
// optionally encrypt the private key
var priv;
if(!passphrase) {
// use the unencrypted buffer
priv = forge.util.encode64(privbuffer.bytes(), 64);
} else {
// encrypt RSA key using passphrase
var encLen = privbuffer.length() + 16 - 1;
encLen -= encLen % 16;
// pad private key with sha1-d data -- needs to be a multiple of 16
var padding = _sha1(privbuffer.bytes());
padding.truncate(padding.length() - encLen + privbuffer.length());
privbuffer.putBuffer(padding);
var aeskey = forge.util.createBuffer();
aeskey.putBuffer(_sha1('\x00\x00\x00\x00', passphrase));
aeskey.putBuffer(_sha1('\x00\x00\x00\x01', passphrase));
// encrypt some bytes using CBC mode
// key is 40 bytes, so truncate *by* 8 bytes
var cipher = forge.aes.createEncryptionCipher(aeskey.truncate(8), 'CBC');
cipher.start(forge.util.createBuffer().fillWithByte(0, 16));
cipher.update(privbuffer.copy());
cipher.finish();
var encrypted = cipher.output;
// Note: this appears to differ from Putty -- is forge wrong, or putty?
// due to padding we finish as an exact multiple of 16
encrypted.truncate(16); // all padding
priv = forge.util.encode64(encrypted.bytes(), 64);
}
// output private key
length = Math.floor(priv.length / 66) + 1; // 64 + \r\n
ppk += '\r\nPrivate-Lines: ' + length + '\r\n';
ppk += priv;
// MAC
var mackey = _sha1('putty-private-key-file-mac-key', passphrase);
var macbuffer = forge.util.createBuffer();
_addStringToBuffer(macbuffer, algorithm);
_addStringToBuffer(macbuffer, encryptionAlgorithm);
_addStringToBuffer(macbuffer, comment);
macbuffer.putInt32(pubbuffer.length());
macbuffer.putBuffer(pubbuffer);
macbuffer.putInt32(privbuffer.length());
macbuffer.putBuffer(privbuffer);
var hmac = forge.hmac.create();
hmac.start('sha1', mackey);
hmac.update(macbuffer.bytes());
ppk += '\r\nPrivate-MAC: ' + hmac.digest().toHex() + '\r\n';
return ppk;
};
/**
* Encodes a public RSA key as an OpenSSH file.
*
* @param key the key.
* @param comment a comment.
*
* @return the public key in OpenSSH format.
*/
ssh.publicKeyToOpenSSH = function(key, comment) {
var type = 'ssh-rsa';
comment = comment || '';
var buffer = forge.util.createBuffer();
_addStringToBuffer(buffer, type);
_addBigIntegerToBuffer(buffer, key.e);
_addBigIntegerToBuffer(buffer, key.n);
return type + ' ' + forge.util.encode64(buffer.bytes()) + ' ' + comment;
};
/**
* Encodes a private RSA key as an OpenSSH file.
*
* @param key the key.
* @param passphrase a passphrase to protect the key (falsy for no encryption).
*
* @return the public key in OpenSSH format.
*/
ssh.privateKeyToOpenSSH = function(privateKey, passphrase) {
if(!passphrase) {
return forge.pki.privateKeyToPem(privateKey);
}
// OpenSSH private key is just a legacy format, it seems
return forge.pki.encryptRsaPrivateKey(privateKey, passphrase,
{legacy: true, algorithm: 'aes128'});
};
/**
* Gets the SSH fingerprint for the given public key.
*
* @param options the options to use.
* [md] the message digest object to use (defaults to forge.md.md5).
* [encoding] an alternative output encoding, such as 'hex'
* (defaults to none, outputs a byte buffer).
* [delimiter] the delimiter to use between bytes for 'hex' encoded
* output, eg: ':' (defaults to none).
*
* @return the fingerprint as a byte buffer or other encoding based on options.
*/
ssh.getPublicKeyFingerprint = function(key, options) {
options = options || {};
var md = options.md || forge.md.md5.create();
var type = 'ssh-rsa';
var buffer = forge.util.createBuffer();
_addStringToBuffer(buffer, type);
_addBigIntegerToBuffer(buffer, key.e);
_addBigIntegerToBuffer(buffer, key.n);
// hash public key bytes
md.start();
md.update(buffer.getBytes());
var digest = md.digest();
if(options.encoding === 'hex') {
var hex = digest.toHex();
if(options.delimiter) {
return hex.match(/.{2}/g).join(options.delimiter);
}
return hex;
} else if(options.encoding === 'binary') {
return digest.getBytes();
} else if(options.encoding) {
throw new Error('Unknown encoding "' + options.encoding + '".');
}
return digest;
};
/**
* Adds len(val) then val to a buffer.
*
* @param buffer the buffer to add to.
* @param val a big integer.
*/
function _addBigIntegerToBuffer(buffer, val) {
var hexVal = val.toString(16);
// ensure 2s complement +ve
if(hexVal[0] >= '8') {
hexVal = '00' + hexVal;
}
var bytes = forge.util.hexToBytes(hexVal);
buffer.putInt32(bytes.length);
buffer.putBytes(bytes);
}
/**
* Adds len(val) then val to a buffer.
*
* @param buffer the buffer to add to.
* @param val a string.
*/
function _addStringToBuffer(buffer, val) {
buffer.putInt32(val.length);
buffer.putString(val);
}
/**
* Hashes the arguments into one value using SHA-1.
*
* @return the sha1 hash of the provided arguments.
*/
function _sha1() {
var sha = forge.md.sha1.create();
var num = arguments.length;
for (var i = 0; i < num; ++i) {
sha.update(arguments[i]);
}
return sha.digest();
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'ssh';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./aes',
'./hmac',
'./md5',
'./sha1',
'./util'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

778
forge.js/task.js Normal file
View File

@@ -0,0 +1,778 @@
/**
* Support for concurrent task management and synchronization in web
* applications.
*
* @author Dave Longley
* @author David I. Lehn <dlehn@digitalbazaar.com>
*
* Copyright (c) 2009-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// logging category
var cat = 'forge.task';
// verbose level
// 0: off, 1: a little, 2: a whole lot
// Verbose debug logging is surrounded by a level check to avoid the
// performance issues with even calling the logging code regardless if it
// is actually logged. For performance reasons this should not be set to 2
// for production use.
// ex: if(sVL >= 2) forge.log.verbose(....)
var sVL = 0;
// track tasks for debugging
var sTasks = {};
var sNextTaskId = 0;
// debug access
forge.debug.set(cat, 'tasks', sTasks);
// a map of task type to task queue
var sTaskQueues = {};
// debug access
forge.debug.set(cat, 'queues', sTaskQueues);
// name for unnamed tasks
var sNoTaskName = '?';
// maximum number of doNext() recursions before a context swap occurs
// FIXME: might need to tweak this based on the browser
var sMaxRecursions = 30;
// time slice for doing tasks before a context swap occurs
// FIXME: might need to tweak this based on the browser
var sTimeSlice = 20;
/**
* Task states.
*
* READY: ready to start processing
* RUNNING: task or a subtask is running
* BLOCKED: task is waiting to acquire N permits to continue
* SLEEPING: task is sleeping for a period of time
* DONE: task is done
* ERROR: task has an error
*/
var READY = 'ready';
var RUNNING = 'running';
var BLOCKED = 'blocked';
var SLEEPING = 'sleeping';
var DONE = 'done';
var ERROR = 'error';
/**
* Task actions. Used to control state transitions.
*
* STOP: stop processing
* START: start processing tasks
* BLOCK: block task from continuing until 1 or more permits are released
* UNBLOCK: release one or more permits
* SLEEP: sleep for a period of time
* WAKEUP: wakeup early from SLEEPING state
* CANCEL: cancel further tasks
* FAIL: a failure occured
*/
var STOP = 'stop';
var START = 'start';
var BLOCK = 'block';
var UNBLOCK = 'unblock';
var SLEEP = 'sleep';
var WAKEUP = 'wakeup';
var CANCEL = 'cancel';
var FAIL = 'fail';
/**
* State transition table.
*
* nextState = sStateTable[currentState][action]
*/
var sStateTable = {};
sStateTable[READY] = {};
sStateTable[READY][STOP] = READY;
sStateTable[READY][START] = RUNNING;
sStateTable[READY][CANCEL] = DONE;
sStateTable[READY][FAIL] = ERROR;
sStateTable[RUNNING] = {};
sStateTable[RUNNING][STOP] = READY;
sStateTable[RUNNING][START] = RUNNING;
sStateTable[RUNNING][BLOCK] = BLOCKED;
sStateTable[RUNNING][UNBLOCK] = RUNNING;
sStateTable[RUNNING][SLEEP] = SLEEPING;
sStateTable[RUNNING][WAKEUP] = RUNNING;
sStateTable[RUNNING][CANCEL] = DONE;
sStateTable[RUNNING][FAIL] = ERROR;
sStateTable[BLOCKED] = {};
sStateTable[BLOCKED][STOP] = BLOCKED;
sStateTable[BLOCKED][START] = BLOCKED;
sStateTable[BLOCKED][BLOCK] = BLOCKED;
sStateTable[BLOCKED][UNBLOCK] = BLOCKED;
sStateTable[BLOCKED][SLEEP] = BLOCKED;
sStateTable[BLOCKED][WAKEUP] = BLOCKED;
sStateTable[BLOCKED][CANCEL] = DONE;
sStateTable[BLOCKED][FAIL] = ERROR;
sStateTable[SLEEPING] = {};
sStateTable[SLEEPING][STOP] = SLEEPING;
sStateTable[SLEEPING][START] = SLEEPING;
sStateTable[SLEEPING][BLOCK] = SLEEPING;
sStateTable[SLEEPING][UNBLOCK] = SLEEPING;
sStateTable[SLEEPING][SLEEP] = SLEEPING;
sStateTable[SLEEPING][WAKEUP] = SLEEPING;
sStateTable[SLEEPING][CANCEL] = DONE;
sStateTable[SLEEPING][FAIL] = ERROR;
sStateTable[DONE] = {};
sStateTable[DONE][STOP] = DONE;
sStateTable[DONE][START] = DONE;
sStateTable[DONE][BLOCK] = DONE;
sStateTable[DONE][UNBLOCK] = DONE;
sStateTable[DONE][SLEEP] = DONE;
sStateTable[DONE][WAKEUP] = DONE;
sStateTable[DONE][CANCEL] = DONE;
sStateTable[DONE][FAIL] = ERROR;
sStateTable[ERROR] = {};
sStateTable[ERROR][STOP] = ERROR;
sStateTable[ERROR][START] = ERROR;
sStateTable[ERROR][BLOCK] = ERROR;
sStateTable[ERROR][UNBLOCK] = ERROR;
sStateTable[ERROR][SLEEP] = ERROR;
sStateTable[ERROR][WAKEUP] = ERROR;
sStateTable[ERROR][CANCEL] = ERROR;
sStateTable[ERROR][FAIL] = ERROR;
/**
* Creates a new task.
*
* @param options options for this task
* run: the run function for the task (required)
* name: the run function for the task (optional)
* parent: parent of this task (optional)
*
* @return the empty task.
*/
var Task = function(options) {
// task id
this.id = -1;
// task name
this.name = options.name || sNoTaskName;
// task has no parent
this.parent = options.parent || null;
// save run function
this.run = options.run;
// create a queue of subtasks to run
this.subtasks = [];
// error flag
this.error = false;
// state of the task
this.state = READY;
// number of times the task has been blocked (also the number
// of permits needed to be released to continue running)
this.blocks = 0;
// timeout id when sleeping
this.timeoutId = null;
// no swap time yet
this.swapTime = null;
// no user data
this.userData = null;
// initialize task
// FIXME: deal with overflow
this.id = sNextTaskId++;
sTasks[this.id] = this;
if(sVL >= 1) {
forge.log.verbose(cat, '[%s][%s] init', this.id, this.name, this);
}
};
/**
* Logs debug information on this task and the system state.
*/
Task.prototype.debug = function(msg) {
msg = msg || '';
forge.log.debug(cat, msg,
'[%s][%s] task:', this.id, this.name, this,
'subtasks:', this.subtasks.length,
'queue:', sTaskQueues);
};
/**
* Adds a subtask to run after task.doNext() or task.fail() is called.
*
* @param name human readable name for this task (optional).
* @param subrun a function to run that takes the current task as
* its first parameter.
*
* @return the current task (useful for chaining next() calls).
*/
Task.prototype.next = function(name, subrun) {
// juggle parameters if it looks like no name is given
if(typeof(name) === 'function') {
subrun = name;
// inherit parent's name
name = this.name;
}
// create subtask, set parent to this task, propagate callbacks
var subtask = new Task({
run: subrun,
name: name,
parent: this
});
// start subtasks running
subtask.state = RUNNING;
subtask.type = this.type;
subtask.successCallback = this.successCallback || null;
subtask.failureCallback = this.failureCallback || null;
// queue a new subtask
this.subtasks.push(subtask);
return this;
};
/**
* Adds subtasks to run in parallel after task.doNext() or task.fail()
* is called.
*
* @param name human readable name for this task (optional).
* @param subrun functions to run that take the current task as
* their first parameter.
*
* @return the current task (useful for chaining next() calls).
*/
Task.prototype.parallel = function(name, subrun) {
// juggle parameters if it looks like no name is given
if(forge.util.isArray(name)) {
subrun = name;
// inherit parent's name
name = this.name;
}
// Wrap parallel tasks in a regular task so they are started at the
// proper time.
return this.next(name, function(task) {
// block waiting for subtasks
var ptask = task;
ptask.block(subrun.length);
// we pass the iterator from the loop below as a parameter
// to a function because it is otherwise included in the
// closure and changes as the loop changes -- causing i
// to always be set to its highest value
var startParallelTask = function(pname, pi) {
forge.task.start({
type: pname,
run: function(task) {
subrun[pi](task);
},
success: function(task) {
ptask.unblock();
},
failure: function(task) {
ptask.unblock();
}
});
};
for(var i = 0; i < subrun.length; i++) {
// Type must be unique so task starts in parallel:
// name + private string + task id + sub-task index
// start tasks in parallel and unblock when the finish
var pname = name + '__parallel-' + task.id + '-' + i;
var pi = i;
startParallelTask(pname, pi);
}
});
};
/**
* Stops a running task.
*/
Task.prototype.stop = function() {
this.state = sStateTable[this.state][STOP];
};
/**
* Starts running a task.
*/
Task.prototype.start = function() {
this.error = false;
this.state = sStateTable[this.state][START];
// try to restart
if(this.state === RUNNING) {
this.start = new Date();
this.run(this);
runNext(this, 0);
}
};
/**
* Blocks a task until it one or more permits have been released. The
* task will not resume until the requested number of permits have
* been released with call(s) to unblock().
*
* @param n number of permits to wait for(default: 1).
*/
Task.prototype.block = function(n) {
n = typeof(n) === 'undefined' ? 1 : n;
this.blocks += n;
if(this.blocks > 0) {
this.state = sStateTable[this.state][BLOCK];
}
};
/**
* Releases a permit to unblock a task. If a task was blocked by
* requesting N permits via block(), then it will only continue
* running once enough permits have been released via unblock() calls.
*
* If multiple processes need to synchronize with a single task then
* use a condition variable (see forge.task.createCondition). It is
* an error to unblock a task more times than it has been blocked.
*
* @param n number of permits to release (default: 1).
*
* @return the current block count (task is unblocked when count is 0)
*/
Task.prototype.unblock = function(n) {
n = typeof(n) === 'undefined' ? 1 : n;
this.blocks -= n;
if(this.blocks === 0 && this.state !== DONE) {
this.state = RUNNING;
runNext(this, 0);
}
return this.blocks;
};
/**
* Sleep for a period of time before resuming tasks.
*
* @param n number of milliseconds to sleep (default: 0).
*/
Task.prototype.sleep = function(n) {
n = typeof(n) === 'undefined' ? 0 : n;
this.state = sStateTable[this.state][SLEEP];
var self = this;
this.timeoutId = setTimeout(function() {
self.timeoutId = null;
self.state = RUNNING;
runNext(self, 0);
}, n);
};
/**
* Waits on a condition variable until notified. The next task will
* not be scheduled until notification. A condition variable can be
* created with forge.task.createCondition().
*
* Once cond.notify() is called, the task will continue.
*
* @param cond the condition variable to wait on.
*/
Task.prototype.wait = function(cond) {
cond.wait(this);
};
/**
* If sleeping, wakeup and continue running tasks.
*/
Task.prototype.wakeup = function() {
if(this.state === SLEEPING) {
cancelTimeout(this.timeoutId);
this.timeoutId = null;
this.state = RUNNING;
runNext(this, 0);
}
};
/**
* Cancel all remaining subtasks of this task.
*/
Task.prototype.cancel = function() {
this.state = sStateTable[this.state][CANCEL];
// remove permits needed
this.permitsNeeded = 0;
// cancel timeouts
if(this.timeoutId !== null) {
cancelTimeout(this.timeoutId);
this.timeoutId = null;
}
// remove subtasks
this.subtasks = [];
};
/**
* Finishes this task with failure and sets error flag. The entire
* task will be aborted unless the next task that should execute
* is passed as a parameter. This allows levels of subtasks to be
* skipped. For instance, to abort only this tasks's subtasks, then
* call fail(task.parent). To abort this task's subtasks and its
* parent's subtasks, call fail(task.parent.parent). To abort
* all tasks and simply call the task callback, call fail() or
* fail(null).
*
* The task callback (success or failure) will always, eventually, be
* called.
*
* @param next the task to continue at, or null to abort entirely.
*/
Task.prototype.fail = function(next) {
// set error flag
this.error = true;
// finish task
finish(this, true);
if(next) {
// propagate task info
next.error = this.error;
next.swapTime = this.swapTime;
next.userData = this.userData;
// do next task as specified
runNext(next, 0);
} else {
if(this.parent !== null) {
// finish root task (ensures it is removed from task queue)
var parent = this.parent;
while(parent.parent !== null) {
// propagate task info
parent.error = this.error;
parent.swapTime = this.swapTime;
parent.userData = this.userData;
parent = parent.parent;
}
finish(parent, true);
}
// call failure callback if one exists
if(this.failureCallback) {
this.failureCallback(this);
}
}
};
/**
* Asynchronously start a task.
*
* @param task the task to start.
*/
var start = function(task) {
task.error = false;
task.state = sStateTable[task.state][START];
setTimeout(function() {
if(task.state === RUNNING) {
task.swapTime = +new Date();
task.run(task);
runNext(task, 0);
}
}, 0);
};
/**
* Run the next subtask or finish this task.
*
* @param task the task to process.
* @param recurse the recursion count.
*/
var runNext = function(task, recurse) {
// get time since last context swap (ms), if enough time has passed set
// swap to true to indicate that doNext was performed asynchronously
// also, if recurse is too high do asynchronously
var swap =
(recurse > sMaxRecursions) ||
(+new Date() - task.swapTime) > sTimeSlice;
var doNext = function(recurse) {
recurse++;
if(task.state === RUNNING) {
if(swap) {
// update swap time
task.swapTime = +new Date();
}
if(task.subtasks.length > 0) {
// run next subtask
var subtask = task.subtasks.shift();
subtask.error = task.error;
subtask.swapTime = task.swapTime;
subtask.userData = task.userData;
subtask.run(subtask);
if(!subtask.error) {
runNext(subtask, recurse);
}
} else {
finish(task);
if(!task.error) {
// chain back up and run parent
if(task.parent !== null) {
// propagate task info
task.parent.error = task.error;
task.parent.swapTime = task.swapTime;
task.parent.userData = task.userData;
// no subtasks left, call run next subtask on parent
runNext(task.parent, recurse);
}
}
}
}
};
if(swap) {
// we're swapping, so run asynchronously
setTimeout(doNext, 0);
} else {
// not swapping, so run synchronously
doNext(recurse);
}
};
/**
* Finishes a task and looks for the next task in the queue to start.
*
* @param task the task to finish.
* @param suppressCallbacks true to suppress callbacks.
*/
var finish = function(task, suppressCallbacks) {
// subtask is now done
task.state = DONE;
delete sTasks[task.id];
if(sVL >= 1) {
forge.log.verbose(cat, '[%s][%s] finish',
task.id, task.name, task);
}
// only do queue processing for root tasks
if(task.parent === null) {
// report error if queue is missing
if(!(task.type in sTaskQueues)) {
forge.log.error(cat,
'[%s][%s] task queue missing [%s]',
task.id, task.name, task.type);
} else if(sTaskQueues[task.type].length === 0) {
// report error if queue is empty
forge.log.error(cat,
'[%s][%s] task queue empty [%s]',
task.id, task.name, task.type);
} else if(sTaskQueues[task.type][0] !== task) {
// report error if this task isn't the first in the queue
forge.log.error(cat,
'[%s][%s] task not first in queue [%s]',
task.id, task.name, task.type);
} else {
// remove ourselves from the queue
sTaskQueues[task.type].shift();
// clean up queue if it is empty
if(sTaskQueues[task.type].length === 0) {
if(sVL >= 1) {
forge.log.verbose(cat, '[%s][%s] delete queue [%s]',
task.id, task.name, task.type);
}
/* Note: Only a task can delete a queue of its own type. This
is used as a way to synchronize tasks. If a queue for a certain
task type exists, then a task of that type is running.
*/
delete sTaskQueues[task.type];
} else {
// dequeue the next task and start it
if(sVL >= 1) {
forge.log.verbose(cat,
'[%s][%s] queue start next [%s] remain:%s',
task.id, task.name, task.type,
sTaskQueues[task.type].length);
}
sTaskQueues[task.type][0].start();
}
}
if(!suppressCallbacks) {
// call final callback if one exists
if(task.error && task.failureCallback) {
task.failureCallback(task);
} else if(!task.error && task.successCallback) {
task.successCallback(task);
}
}
}
};
/* Tasks API */
forge.task = forge.task || {};
/**
* Starts a new task that will run the passed function asynchronously.
*
* In order to finish the task, either task.doNext() or task.fail()
* *must* be called.
*
* The task must have a type (a string identifier) that can be used to
* synchronize it with other tasks of the same type. That type can also
* be used to cancel tasks that haven't started yet.
*
* To start a task, the following object must be provided as a parameter
* (each function takes a task object as its first parameter):
*
* {
* type: the type of task.
* run: the function to run to execute the task.
* success: a callback to call when the task succeeds (optional).
* failure: a callback to call when the task fails (optional).
* }
*
* @param options the object as described above.
*/
forge.task.start = function(options) {
// create a new task
var task = new Task({
run: options.run,
name: options.name || sNoTaskName
});
task.type = options.type;
task.successCallback = options.success || null;
task.failureCallback = options.failure || null;
// append the task onto the appropriate queue
if(!(task.type in sTaskQueues)) {
if(sVL >= 1) {
forge.log.verbose(cat, '[%s][%s] create queue [%s]',
task.id, task.name, task.type);
}
// create the queue with the new task
sTaskQueues[task.type] = [task];
start(task);
} else {
// push the task onto the queue, it will be run after a task
// with the same type completes
sTaskQueues[options.type].push(task);
}
};
/**
* Cancels all tasks of the given type that haven't started yet.
*
* @param type the type of task to cancel.
*/
forge.task.cancel = function(type) {
// find the task queue
if(type in sTaskQueues) {
// empty all but the current task from the queue
sTaskQueues[type] = [sTaskQueues[type][0]];
}
};
/**
* Creates a condition variable to synchronize tasks. To make a task wait
* on the condition variable, call task.wait(condition). To notify all
* tasks that are waiting, call condition.notify().
*
* @return the condition variable.
*/
forge.task.createCondition = function() {
var cond = {
// all tasks that are blocked
tasks: {}
};
/**
* Causes the given task to block until notify is called. If the task
* is already waiting on this condition then this is a no-op.
*
* @param task the task to cause to wait.
*/
cond.wait = function(task) {
// only block once
if(!(task.id in cond.tasks)) {
task.block();
cond.tasks[task.id] = task;
}
};
/**
* Notifies all waiting tasks to wake up.
*/
cond.notify = function() {
// since unblock() will run the next task from here, make sure to
// clear the condition's blocked task list before unblocking
var tmp = cond.tasks;
cond.tasks = {};
for(var id in tmp) {
tmp[id].unblock();
}
};
return cond;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'task';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './debug', './log', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

4316
forge.js/tls.js Normal file

File diff suppressed because it is too large Load Diff

304
forge.js/tlssocket.js Normal file
View File

@@ -0,0 +1,304 @@
/**
* Socket wrapping functions for TLS.
*
* @author Dave Longley
*
* Copyright (c) 2009-2012 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/**
* Wraps a forge.net socket with a TLS layer.
*
* @param options:
* sessionId: a session ID to reuse, null for a new connection if no session
* cache is provided or it is empty.
* caStore: an array of certificates to trust.
* sessionCache: a session cache to use.
* cipherSuites: an optional array of cipher suites to use, see
* tls.CipherSuites.
* socket: the socket to wrap.
* virtualHost: the virtual server name to use in a TLS SNI extension.
* verify: a handler used to custom verify certificates in the chain.
* getCertificate: an optional callback used to get a certificate.
* getPrivateKey: an optional callback used to get a private key.
* getSignature: an optional callback used to get a signature.
* deflate: function(inBytes) if provided, will deflate TLS records using
* the deflate algorithm if the server supports it.
* inflate: function(inBytes) if provided, will inflate TLS records using
* the deflate algorithm if the server supports it.
*
* @return the TLS-wrapped socket.
*/
forge.tls.wrapSocket = function(options) {
// get raw socket
var socket = options.socket;
// create TLS socket
var tlsSocket = {
id: socket.id,
// set handlers
connected: socket.connected || function(e){},
closed: socket.closed || function(e){},
data: socket.data || function(e){},
error: socket.error || function(e){}
};
// create TLS connection
var c = forge.tls.createConnection({
server: false,
sessionId: options.sessionId || null,
caStore: options.caStore || [],
sessionCache: options.sessionCache || null,
cipherSuites: options.cipherSuites || null,
virtualHost: options.virtualHost,
verify: options.verify,
getCertificate: options.getCertificate,
getPrivateKey: options.getPrivateKey,
getSignature: options.getSignature,
deflate: options.deflate,
inflate: options.inflate,
connected: function(c) {
// first handshake complete, call handler
if(c.handshakes === 1) {
tlsSocket.connected({
id: socket.id,
type: 'connect',
bytesAvailable: c.data.length()
});
}
},
tlsDataReady: function(c) {
// send TLS data over socket
return socket.send(c.tlsData.getBytes());
},
dataReady: function(c) {
// indicate application data is ready
tlsSocket.data({
id: socket.id,
type: 'socketData',
bytesAvailable: c.data.length()
});
},
closed: function(c) {
// close socket
socket.close();
},
error: function(c, e) {
// send error, close socket
tlsSocket.error({
id: socket.id,
type: 'tlsError',
message: e.message,
bytesAvailable: 0,
error: e
});
socket.close();
}
});
// handle doing handshake after connecting
socket.connected = function(e) {
c.handshake(options.sessionId);
};
// handle closing TLS connection
socket.closed = function(e) {
if(c.open && c.handshaking) {
// error
tlsSocket.error({
id: socket.id,
type: 'ioError',
message: 'Connection closed during handshake.',
bytesAvailable: 0
});
}
c.close();
// call socket handler
tlsSocket.closed({
id: socket.id,
type: 'close',
bytesAvailable: 0
});
};
// handle error on socket
socket.error = function(e) {
// error
tlsSocket.error({
id: socket.id,
type: e.type,
message: e.message,
bytesAvailable: 0
});
c.close();
};
// handle receiving raw TLS data from socket
var _requiredBytes = 0;
socket.data = function(e) {
// drop data if connection not open
if(!c.open) {
socket.receive(e.bytesAvailable);
} else {
// only receive if there are enough bytes available to
// process a record
if(e.bytesAvailable >= _requiredBytes) {
var count = Math.max(e.bytesAvailable, _requiredBytes);
var data = socket.receive(count);
if(data !== null) {
_requiredBytes = c.process(data);
}
}
}
};
/**
* Destroys this socket.
*/
tlsSocket.destroy = function() {
socket.destroy();
};
/**
* Sets this socket's TLS session cache. This should be called before
* the socket is connected or after it is closed.
*
* The cache is an object mapping session IDs to internal opaque state.
* An application might need to change the cache used by a particular
* tlsSocket between connections if it accesses multiple TLS hosts.
*
* @param cache the session cache to use.
*/
tlsSocket.setSessionCache = function(cache) {
c.sessionCache = tls.createSessionCache(cache);
};
/**
* Connects this socket.
*
* @param options:
* host: the host to connect to.
* port: the port to connect to.
* policyPort: the policy port to use (if non-default), 0 to
* use the flash default.
* policyUrl: the policy file URL to use (instead of port).
*/
tlsSocket.connect = function(options) {
socket.connect(options);
};
/**
* Closes this socket.
*/
tlsSocket.close = function() {
c.close();
};
/**
* Determines if the socket is connected or not.
*
* @return true if connected, false if not.
*/
tlsSocket.isConnected = function() {
return c.isConnected && socket.isConnected();
};
/**
* Writes bytes to this socket.
*
* @param bytes the bytes (as a string) to write.
*
* @return true on success, false on failure.
*/
tlsSocket.send = function(bytes) {
return c.prepare(bytes);
};
/**
* Reads bytes from this socket (non-blocking). Fewer than the number of
* bytes requested may be read if enough bytes are not available.
*
* This method should be called from the data handler if there are enough
* bytes available. To see how many bytes are available, check the
* 'bytesAvailable' property on the event in the data handler or call the
* bytesAvailable() function on the socket. If the browser is msie, then the
* bytesAvailable() function should be used to avoid race conditions.
* Otherwise, using the property on the data handler's event may be quicker.
*
* @param count the maximum number of bytes to read.
*
* @return the bytes read (as a string) or null on error.
*/
tlsSocket.receive = function(count) {
return c.data.getBytes(count);
};
/**
* Gets the number of bytes available for receiving on the socket.
*
* @return the number of bytes available for receiving.
*/
tlsSocket.bytesAvailable = function() {
return c.data.length();
};
return tlsSocket;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'tlssocket';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './tls'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

2988
forge.js/util.js Normal file

File diff suppressed because it is too large Load Diff

3191
forge.js/x509.js Normal file

File diff suppressed because it is too large Load Diff

739
forge.js/xhr.js Normal file
View File

@@ -0,0 +1,739 @@
/**
* XmlHttpRequest implementation that uses TLS and flash SocketPool.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function($) {
// logging category
var cat = 'forge.xhr';
/*
XMLHttpRequest interface definition from:
http://www.w3.org/TR/XMLHttpRequest
interface XMLHttpRequest {
// event handler
attribute EventListener onreadystatechange;
// state
const unsigned short UNSENT = 0;
const unsigned short OPENED = 1;
const unsigned short HEADERS_RECEIVED = 2;
const unsigned short LOADING = 3;
const unsigned short DONE = 4;
readonly attribute unsigned short readyState;
// request
void open(in DOMString method, in DOMString url);
void open(in DOMString method, in DOMString url, in boolean async);
void open(in DOMString method, in DOMString url,
in boolean async, in DOMString user);
void open(in DOMString method, in DOMString url,
in boolean async, in DOMString user, in DOMString password);
void setRequestHeader(in DOMString header, in DOMString value);
void send();
void send(in DOMString data);
void send(in Document data);
void abort();
// response
DOMString getAllResponseHeaders();
DOMString getResponseHeader(in DOMString header);
readonly attribute DOMString responseText;
readonly attribute Document responseXML;
readonly attribute unsigned short status;
readonly attribute DOMString statusText;
};
*/
// readyStates
var UNSENT = 0;
var OPENED = 1;
var HEADERS_RECEIVED = 2;
var LOADING = 3;
var DONE = 4;
// exceptions
var INVALID_STATE_ERR = 11;
var SYNTAX_ERR = 12;
var SECURITY_ERR = 18;
var NETWORK_ERR = 19;
var ABORT_ERR = 20;
// private flash socket pool vars
var _sp = null;
var _policyPort = 0;
var _policyUrl = null;
// default client (used if no special URL provided when creating an XHR)
var _client = null;
// all clients including the default, key'd by full base url
// (multiple cross-domain http clients are permitted so there may be more
// than one client in this map)
// TODO: provide optional clean up API for non-default clients
var _clients = {};
// the default maximum number of concurrents connections per client
var _maxConnections = 10;
// local aliases
if(typeof forge === 'undefined') {
forge = {};
}
var net = forge.net;
var http = forge.http;
// define the xhr interface
var xhrApi = {};
/**
* Initializes flash XHR support.
*
* @param options:
* url: the default base URL to connect to if xhr URLs are relative,
* ie: https://myserver.com.
* flashId: the dom ID of the flash SocketPool.
* policyPort: the port that provides the server's flash policy, 0 to use
* the flash default.
* policyUrl: the policy file URL to use instead of a policy port.
* msie: true if browser is internet explorer, false if not.
* connections: the maximum number of concurrent connections.
* caCerts: a list of PEM-formatted certificates to trust.
* cipherSuites: an optional array of cipher suites to use,
* see forge.tls.CipherSuites.
* verify: optional TLS certificate verify callback to use (see forge.tls
* for details).
* getCertificate: an optional callback used to get a client-side
* certificate (see forge.tls for details).
* getPrivateKey: an optional callback used to get a client-side private
* key (see forge.tls for details).
* getSignature: an optional callback used to get a client-side signature
* (see forge.tls for details).
* persistCookies: true to use persistent cookies via flash local storage,
* false to only keep cookies in javascript.
* primeTlsSockets: true to immediately connect TLS sockets on their
* creation so that they will cache TLS sessions for reuse.
*/
xhrApi.init = function(options) {
forge.log.debug(cat, 'initializing', options);
// update default policy port and max connections
_policyPort = options.policyPort || _policyPort;
_policyUrl = options.policyUrl || _policyUrl;
_maxConnections = options.connections || _maxConnections;
// create the flash socket pool
_sp = net.createSocketPool({
flashId: options.flashId,
policyPort: _policyPort,
policyUrl: _policyUrl,
msie: options.msie || false
});
// create default http client
_client = http.createClient({
url: options.url || (
window.location.protocol + '//' + window.location.host),
socketPool: _sp,
policyPort: _policyPort,
policyUrl: _policyUrl,
connections: options.connections || _maxConnections,
caCerts: options.caCerts,
cipherSuites: options.cipherSuites,
persistCookies: options.persistCookies || true,
primeTlsSockets: options.primeTlsSockets || false,
verify: options.verify,
getCertificate: options.getCertificate,
getPrivateKey: options.getPrivateKey,
getSignature: options.getSignature
});
_clients[_client.url.full] = _client;
forge.log.debug(cat, 'ready');
};
/**
* Called to clean up the clients and socket pool.
*/
xhrApi.cleanup = function() {
// destroy all clients
for(var key in _clients) {
_clients[key].destroy();
}
_clients = {};
_client = null;
// destroy socket pool
_sp.destroy();
_sp = null;
};
/**
* Sets a cookie.
*
* @param cookie the cookie with parameters:
* name: the name of the cookie.
* value: the value of the cookie.
* comment: an optional comment string.
* maxAge: the age of the cookie in seconds relative to created time.
* secure: true if the cookie must be sent over a secure protocol.
* httpOnly: true to restrict access to the cookie from javascript
* (inaffective since the cookies are stored in javascript).
* path: the path for the cookie.
* domain: optional domain the cookie belongs to (must start with dot).
* version: optional version of the cookie.
* created: creation time, in UTC seconds, of the cookie.
*/
xhrApi.setCookie = function(cookie) {
// default cookie expiration to never
cookie.maxAge = cookie.maxAge || -1;
// if the cookie's domain is set, use the appropriate client
if(cookie.domain) {
// add the cookies to the applicable domains
for(var key in _clients) {
var client = _clients[key];
if(http.withinCookieDomain(client.url, cookie) &&
client.secure === cookie.secure) {
client.setCookie(cookie);
}
}
} else {
// use the default domain
// FIXME: should a null domain cookie be added to all clients? should
// this be an option?
_client.setCookie(cookie);
}
};
/**
* Gets a cookie.
*
* @param name the name of the cookie.
* @param path an optional path for the cookie (if there are multiple cookies
* with the same name but different paths).
* @param domain an optional domain for the cookie (if not using the default
* domain).
*
* @return the cookie, cookies (if multiple matches), or null if not found.
*/
xhrApi.getCookie = function(name, path, domain) {
var rval = null;
if(domain) {
// get the cookies from the applicable domains
for(var key in _clients) {
var client = _clients[key];
if(http.withinCookieDomain(client.url, domain)) {
var cookie = client.getCookie(name, path);
if(cookie !== null) {
if(rval === null) {
rval = cookie;
} else if(!forge.util.isArray(rval)) {
rval = [rval, cookie];
} else {
rval.push(cookie);
}
}
}
}
} else {
// get cookie from default domain
rval = _client.getCookie(name, path);
}
return rval;
};
/**
* Removes a cookie.
*
* @param name the name of the cookie.
* @param path an optional path for the cookie (if there are multiple cookies
* with the same name but different paths).
* @param domain an optional domain for the cookie (if not using the default
* domain).
*
* @return true if a cookie was removed, false if not.
*/
xhrApi.removeCookie = function(name, path, domain) {
var rval = false;
if(domain) {
// remove the cookies from the applicable domains
for(var key in _clients) {
var client = _clients[key];
if(http.withinCookieDomain(client.url, domain)) {
if(client.removeCookie(name, path)) {
rval = true;
}
}
}
} else {
// remove cookie from default domain
rval = _client.removeCookie(name, path);
}
return rval;
};
/**
* Creates a new XmlHttpRequest. By default the base URL, flash policy port,
* etc, will be used. However, an XHR can be created to point at another
* cross-domain URL.
*
* @param options:
* logWarningOnError: If true and an HTTP error status code is received then
* log a warning, otherwise log a verbose message.
* verbose: If true be very verbose in the output including the response
* event and response body, otherwise only include status, timing, and
* data size.
* logError: a multi-var log function for warnings that takes the log
* category as the first var.
* logWarning: a multi-var log function for warnings that takes the log
* category as the first var.
* logDebug: a multi-var log function for warnings that takes the log
* category as the first var.
* logVerbose: a multi-var log function for warnings that takes the log
* category as the first var.
* url: the default base URL to connect to if xhr URLs are relative,
* eg: https://myserver.com, and note that the following options will be
* ignored if the URL is absent or the same as the default base URL.
* policyPort: the port that provides the server's flash policy, 0 to use
* the flash default.
* policyUrl: the policy file URL to use instead of a policy port.
* connections: the maximum number of concurrent connections.
* caCerts: a list of PEM-formatted certificates to trust.
* cipherSuites: an optional array of cipher suites to use, see
* forge.tls.CipherSuites.
* verify: optional TLS certificate verify callback to use (see forge.tls
* for details).
* getCertificate: an optional callback used to get a client-side
* certificate.
* getPrivateKey: an optional callback used to get a client-side private key.
* getSignature: an optional callback used to get a client-side signature.
* persistCookies: true to use persistent cookies via flash local storage,
* false to only keep cookies in javascript.
* primeTlsSockets: true to immediately connect TLS sockets on their
* creation so that they will cache TLS sessions for reuse.
*
* @return the XmlHttpRequest.
*/
xhrApi.create = function(options) {
// set option defaults
options = $.extend({
logWarningOnError: true,
verbose: false,
logError: function(){},
logWarning: function(){},
logDebug: function(){},
logVerbose: function(){},
url: null
}, options || {});
// private xhr state
var _state = {
// the http client to use
client: null,
// request storage
request: null,
// response storage
response: null,
// asynchronous, true if doing asynchronous communication
asynchronous: true,
// sendFlag, true if send has been called
sendFlag: false,
// errorFlag, true if a network error occurred
errorFlag: false
};
// private log functions
var _log = {
error: options.logError || forge.log.error,
warning: options.logWarning || forge.log.warning,
debug: options.logDebug || forge.log.debug,
verbose: options.logVerbose || forge.log.verbose
};
// create public xhr interface
var xhr = {
// an EventListener
onreadystatechange: null,
// readonly, the current readyState
readyState: UNSENT,
// a string with the response entity-body
responseText: '',
// a Document for response entity-bodies that are XML
responseXML: null,
// readonly, returns the HTTP status code (i.e. 404)
status: 0,
// readonly, returns the HTTP status message (i.e. 'Not Found')
statusText: ''
};
// determine which http client to use
if(options.url === null) {
// use default
_state.client = _client;
} else {
var url = http.parseUrl(options.url);
if(!url) {
var error = new Error('Invalid url.');
error.details = {
url: options.url
};
}
// find client
if(url.full in _clients) {
// client found
_state.client = _clients[url.full];
} else {
// create client
_state.client = http.createClient({
url: options.url,
socketPool: _sp,
policyPort: options.policyPort || _policyPort,
policyUrl: options.policyUrl || _policyUrl,
connections: options.connections || _maxConnections,
caCerts: options.caCerts,
cipherSuites: options.cipherSuites,
persistCookies: options.persistCookies || true,
primeTlsSockets: options.primeTlsSockets || false,
verify: options.verify,
getCertificate: options.getCertificate,
getPrivateKey: options.getPrivateKey,
getSignature: options.getSignature
});
_clients[url.full] = _state.client;
}
}
/**
* Opens the request. This method will create the HTTP request to send.
*
* @param method the HTTP method (i.e. 'GET').
* @param url the relative url (the HTTP request path).
* @param async always true, ignored.
* @param user always null, ignored.
* @param password always null, ignored.
*/
xhr.open = function(method, url, async, user, password) {
// 1. validate Document if one is associated
// TODO: not implemented (not used yet)
// 2. validate method token
// 3. change method to uppercase if it matches a known
// method (here we just require it to be uppercase, and
// we do not allow the standard methods)
// 4. disallow CONNECT, TRACE, or TRACK with a security error
switch(method) {
case 'DELETE':
case 'GET':
case 'HEAD':
case 'OPTIONS':
case 'PATCH':
case 'POST':
case 'PUT':
// valid method
break;
case 'CONNECT':
case 'TRACE':
case 'TRACK':
throw new Error('CONNECT, TRACE and TRACK methods are disallowed');
default:
throw new Error('Invalid method: ' + method);;
}
// TODO: other validation steps in algorithm are not implemented
// 19. set send flag to false
// set response body to null
// empty list of request headers
// set request method to given method
// set request URL
// set username, password
// set asychronous flag
_state.sendFlag = false;
xhr.responseText = '';
xhr.responseXML = null;
// custom: reset status and statusText
xhr.status = 0;
xhr.statusText = '';
// create the HTTP request
_state.request = http.createRequest({
method: method,
path: url
});
// 20. set state to OPENED
xhr.readyState = OPENED;
// 21. dispatch onreadystatechange
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
};
/**
* Adds an HTTP header field to the request.
*
* @param header the name of the header field.
* @param value the value of the header field.
*/
xhr.setRequestHeader = function(header, value) {
// 1. if state is not OPENED or send flag is true, raise exception
if(xhr.readyState != OPENED || _state.sendFlag) {
throw new Error('XHR not open or sending');
}
// TODO: other validation steps in spec aren't implemented
// set header
_state.request.setField(header, value);
};
/**
* Sends the request and any associated data.
*
* @param data a string or Document object to send, null to send no data.
*/
xhr.send = function(data) {
// 1. if state is not OPENED or 2. send flag is true, raise
// an invalid state exception
if(xhr.readyState != OPENED || _state.sendFlag) {
throw new Error('XHR not open or sending');
}
// 3. ignore data if method is GET or HEAD
if(data &&
_state.request.method !== 'GET' &&
_state.request.method !== 'HEAD') {
// handle non-IE case
if(typeof(XMLSerializer) !== 'undefined') {
if(data instanceof Document) {
var xs = new XMLSerializer();
_state.request.body = xs.serializeToString(data);
} else {
_state.request.body = data;
}
} else {
// poorly implemented IE case
if(typeof(data.xml) !== 'undefined') {
_state.request.body = data.xml;
} else {
_state.request.body = data;
}
}
}
// 4. release storage mutex (not used)
// 5. set error flag to false
_state.errorFlag = false;
// 6. if asynchronous is true (must be in this implementation)
// 6.1 set send flag to true
_state.sendFlag = true;
// 6.2 dispatch onreadystatechange
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
// create send options
var options = {};
options.request = _state.request;
options.headerReady = function(e) {
// make cookies available for ease of use/iteration
xhr.cookies = _state.client.cookies;
// TODO: update document.cookie with any cookies where the
// script's domain matches
// headers received
xhr.readyState = HEADERS_RECEIVED;
xhr.status = e.response.code;
xhr.statusText = e.response.message;
_state.response = e.response;
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
if(!_state.response.aborted) {
// now loading body
xhr.readyState = LOADING;
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
}
};
options.bodyReady = function(e) {
xhr.readyState = DONE;
var ct = e.response.getField('Content-Type');
// Note: this null/undefined check is done outside because IE
// dies otherwise on a "'null' is null" error
if(ct) {
if(ct.indexOf('text/xml') === 0 ||
ct.indexOf('application/xml') === 0 ||
ct.indexOf('+xml') !== -1) {
try {
var doc = new ActiveXObject('MicrosoftXMLDOM');
doc.async = false;
doc.loadXML(e.response.body);
xhr.responseXML = doc;
} catch(ex) {
var parser = new DOMParser();
xhr.responseXML = parser.parseFromString(ex.body, 'text/xml');
}
}
}
var length = 0;
if(e.response.body !== null) {
xhr.responseText = e.response.body;
length = e.response.body.length;
}
// build logging output
var req = _state.request;
var output =
req.method + ' ' + req.path + ' ' +
xhr.status + ' ' + xhr.statusText + ' ' +
length + 'B ' +
(e.request.connectTime + e.request.time + e.response.time) +
'ms';
var lFunc;
if(options.verbose) {
lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
_log.warning : _log.verbose;
lFunc(cat, output,
e, e.response.body ? '\n' + e.response.body : '\nNo content');
} else {
lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
_log.warning : _log.debug;
lFunc(cat, output);
}
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
};
options.error = function(e) {
var req = _state.request;
_log.error(cat, req.method + ' ' + req.path, e);
// 1. set response body to null
xhr.responseText = '';
xhr.responseXML = null;
// 2. set error flag to true (and reset status)
_state.errorFlag = true;
xhr.status = 0;
xhr.statusText = '';
// 3. set state to done
xhr.readyState = DONE;
// 4. asyc flag is always true, so dispatch onreadystatechange
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
};
// 7. send request
_state.client.send(options);
};
/**
* Aborts the request.
*/
xhr.abort = function() {
// 1. abort send
// 2. stop network activity
_state.request.abort();
// 3. set response to null
xhr.responseText = '';
xhr.responseXML = null;
// 4. set error flag to true (and reset status)
_state.errorFlag = true;
xhr.status = 0;
xhr.statusText = '';
// 5. clear user headers
_state.request = null;
_state.response = null;
// 6. if state is DONE or UNSENT, or if OPENED and send flag is false
if(xhr.readyState === DONE || xhr.readyState === UNSENT ||
(xhr.readyState === OPENED && !_state.sendFlag)) {
// 7. set ready state to unsent
xhr.readyState = UNSENT;
} else {
// 6.1 set state to DONE
xhr.readyState = DONE;
// 6.2 set send flag to false
_state.sendFlag = false;
// 6.3 dispatch onreadystatechange
if(xhr.onreadystatechange) {
xhr.onreadystatechange();
}
// 7. set state to UNSENT
xhr.readyState = UNSENT;
}
};
/**
* Gets all response headers as a string.
*
* @return the HTTP-encoded response header fields.
*/
xhr.getAllResponseHeaders = function() {
var rval = '';
if(_state.response !== null) {
var fields = _state.response.fields;
$.each(fields, function(name, array) {
$.each(array, function(i, value) {
rval += name + ': ' + value + '\r\n';
});
});
}
return rval;
};
/**
* Gets a single header field value or, if there are multiple
* fields with the same name, a comma-separated list of header
* values.
*
* @return the header field value(s) or null.
*/
xhr.getResponseHeader = function(header) {
var rval = null;
if(_state.response !== null) {
if(header in _state.response.fields) {
rval = _state.response.fields[header];
if(forge.util.isArray(rval)) {
rval = rval.join();
}
}
}
return rval;
};
return xhr;
};
// expose public api
forge.xhr = xhrApi;
})(jQuery);

BIN
images-commander/Thumbs.db Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images-commander/error.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

BIN
images-commander/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
images-commander/info.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

BIN
images-commander/info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
images-commander/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
images-commander/logox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Some files were not shown because too many files have changed in this diff Show More