forked from mirrors/gecko-dev
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
commit
dd9cb929e6
100 changed files with 4076 additions and 1886 deletions
|
|
@ -778,7 +778,7 @@ window._gBrowser = {
|
|||
|
||||
try {
|
||||
// Use the passed in resolvedURI if we have one
|
||||
const resolvedURI = aResolvedURI || Services.io.newChannelFromURI2(
|
||||
const resolvedURI = aResolvedURI || Services.io.newChannelFromURI(
|
||||
aURI,
|
||||
null, // loadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(), // loadingPrincipal
|
||||
|
|
|
|||
|
|
@ -1591,7 +1591,7 @@ var SessionStoreInternal = {
|
|||
onQuitApplicationGranted: function ssi_onQuitApplicationGranted(syncShutdown = false) {
|
||||
// Collect an initial snapshot of window data before we do the flush.
|
||||
let index = 0;
|
||||
for (let window of this._browserWindows) {
|
||||
for (let window of this._orderedBrowserWindows) {
|
||||
this._collectWindowData(window);
|
||||
this._windows[window.__SSi].zIndex = ++index;
|
||||
}
|
||||
|
|
@ -3416,7 +3416,7 @@ var SessionStoreInternal = {
|
|||
if (RunState.isRunning) {
|
||||
// update the data for all windows with activities since the last save operation.
|
||||
let index = 0;
|
||||
for (let window of this._browserWindows) {
|
||||
for (let window of this._orderedBrowserWindows) {
|
||||
if (!this._isWindowLoaded(window)) // window data is still in _statesToRestore
|
||||
continue;
|
||||
if (aUpdateAll || DirtyWindows.has(window) || window == activeWindow) {
|
||||
|
|
@ -4527,8 +4527,10 @@ var SessionStoreInternal = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Iterator that yields all currently opened browser windows, in order.
|
||||
* Iterator that yields all currently opened browser windows.
|
||||
* (Might miss the most recent one.)
|
||||
* This list is in focus order, but may include minimized windows
|
||||
* before non-minimized windows.
|
||||
*/
|
||||
_browserWindows: {
|
||||
* [Symbol.iterator]() {
|
||||
|
|
@ -4539,6 +4541,30 @@ var SessionStoreInternal = {
|
|||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterator that yields all currently opened browser windows,
|
||||
* with minimized windows last.
|
||||
* (Might miss the most recent window.)
|
||||
*/
|
||||
_orderedBrowserWindows: {
|
||||
* [Symbol.iterator]() {
|
||||
let windows = BrowserWindowTracker.orderedWindows;
|
||||
windows.sort((a, b) => {
|
||||
if (a.windowState == a.STATE_MINIMIZED && b.windowState != b.STATE_MINIMIZED) {
|
||||
return 1;
|
||||
}
|
||||
if (a.windowState != a.STATE_MINIMIZED && b.windowState == b.STATE_MINIMIZED) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
for (let window of windows) {
|
||||
if (window.__SSi && !window.closed)
|
||||
yield window;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns most recent window
|
||||
* @returns Window reference
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
|
||||
// Constants
|
||||
const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
|
||||
const WINDOW_EVENTS = ["activate", "sizemodechange", "unload"];
|
||||
const WINDOW_EVENTS = ["activate", "unload"];
|
||||
const DEBUG = false;
|
||||
|
||||
// Variables
|
||||
|
|
@ -64,9 +64,6 @@ function _handleEvent(event) {
|
|||
case "activate":
|
||||
WindowHelper.onActivate(event.target);
|
||||
break;
|
||||
case "sizemodechange":
|
||||
WindowHelper.onSizemodeChange(event.target);
|
||||
break;
|
||||
case "unload":
|
||||
WindowHelper.removeWindow(event.currentTarget);
|
||||
break;
|
||||
|
|
@ -144,14 +141,6 @@ var WindowHelper = {
|
|||
|
||||
_updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser);
|
||||
},
|
||||
|
||||
onSizemodeChange(window) {
|
||||
if (window.windowState == window.STATE_MINIMIZED) {
|
||||
// Make sure to have the minimized window behind unminimized windows.
|
||||
_untrackWindowOrder(window);
|
||||
_trackWindowOrder(window);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.BrowserWindowTracker = {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class FaviconLoad {
|
|||
constructor(iconInfo) {
|
||||
this.icon = iconInfo;
|
||||
|
||||
this.channel = Services.io.newChannelFromURI2(
|
||||
this.channel = Services.io.newChannelFromURI(
|
||||
iconInfo.iconUri,
|
||||
iconInfo.node,
|
||||
iconInfo.node.nodePrincipal,
|
||||
|
|
|
|||
|
|
@ -110,16 +110,5 @@ add_task(async function test_orderedWindows() {
|
|||
let expected = [1, 6, 4, 9, 8, 7, 5, 3, 2, 0];
|
||||
Assert.deepEqual(expected, ordered2.map(w => windows.indexOf(w)),
|
||||
"After shuffle of focused windows, the order should've changed.");
|
||||
|
||||
// Minimizing a window should put it at the end of the ordered list of windows.
|
||||
let promise = BrowserTestUtils.waitForEvent(windows[9], "sizemodechange");
|
||||
windows[9].minimize();
|
||||
await promise;
|
||||
|
||||
let ordered3 = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW);
|
||||
// Test the end of the array of window indices, because Windows Debug builds
|
||||
// mysteriously swap the order of the first two windows.
|
||||
Assert.deepEqual([8, 7, 5, 3, 2, 0, 9], ordered3.map(w => windows.indexOf(w)).slice(3),
|
||||
"When a window is minimized, the order should've changed.");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,12 +28,7 @@ loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/ins
|
|||
loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true);
|
||||
loader.lazyRequireGetter(this, "MarkupView", "devtools/client/inspector/markup/markup");
|
||||
loader.lazyRequireGetter(this, "HighlightersOverlay", "devtools/client/inspector/shared/highlighters-overlay");
|
||||
loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
|
||||
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
|
||||
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
|
||||
loader.lazyRequireGetter(this, "ExtensionSidebar", "devtools/client/inspector/extensions/extension-sidebar");
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
|
||||
loader.lazyRequireGetter(this, "saveScreenshot", "devtools/shared/screenshot/save");
|
||||
|
||||
loader.lazyImporter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
|
||||
|
|
@ -41,9 +36,6 @@ loader.lazyImporter(this, "DeferredTask", "resource://gre/modules/DeferredTask.j
|
|||
const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
loader.lazyGetter(this, "TOOLBOX_L10N", function() {
|
||||
return new LocalizationHelper("devtools/client/locales/toolbox.properties");
|
||||
});
|
||||
|
||||
// Sidebar dimensions
|
||||
const INITIAL_SIDEBAR_SIZE = 350;
|
||||
|
|
@ -125,12 +117,9 @@ function Inspector(toolbox) {
|
|||
// telemetry counts in the Grid Inspector are not double counted on reload.
|
||||
this.previousURL = this.target.url;
|
||||
|
||||
this.nodeMenuTriggerInfo = null;
|
||||
|
||||
this._clearSearchResultsLabel = this._clearSearchResultsLabel.bind(this);
|
||||
this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
|
||||
this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
|
||||
this._onContextMenu = this._onContextMenu.bind(this);
|
||||
this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
|
||||
this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
|
||||
|
||||
|
|
@ -1402,8 +1391,6 @@ Inspector.prototype = {
|
|||
|
||||
if (this._markupFrame) {
|
||||
this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
|
||||
this._markupFrame.contentWindow.removeEventListener("contextmenu",
|
||||
this._onContextMenu);
|
||||
}
|
||||
|
||||
if (this._search) {
|
||||
|
|
@ -1449,449 +1436,6 @@ Inspector.prototype = {
|
|||
return this._panelDestroyer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the clipboard content if it is appropriate for pasting
|
||||
* into the current node's outer HTML, otherwise returns null.
|
||||
*/
|
||||
_getClipboardContentForPaste: function() {
|
||||
const content = clipboardHelper.getText();
|
||||
if (content && content.trim().length > 0) {
|
||||
return content;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_onContextMenu: function(e) {
|
||||
if (!(e.originalTarget instanceof Element) ||
|
||||
e.originalTarget.closest("input[type=text]") ||
|
||||
e.originalTarget.closest("input:not([type])") ||
|
||||
e.originalTarget.closest("textarea")) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
this._openMenu({
|
||||
screenX: e.screenX,
|
||||
screenY: e.screenY,
|
||||
target: e.target,
|
||||
});
|
||||
},
|
||||
|
||||
_openMenu: function({ target, screenX = 0, screenY = 0 } = { }) {
|
||||
if (this.selection.isSlotted()) {
|
||||
// Slotted elements should not show any context menu.
|
||||
return null;
|
||||
}
|
||||
|
||||
const markupContainer = this.markup.getContainer(this.selection.nodeFront);
|
||||
|
||||
this.contextMenuTarget = target;
|
||||
this.nodeMenuTriggerInfo = markupContainer &&
|
||||
markupContainer.editor.getInfoAtNode(target);
|
||||
|
||||
const isSelectionElement = this.selection.isElementNode() &&
|
||||
!this.selection.isPseudoElementNode();
|
||||
const isEditableElement = isSelectionElement &&
|
||||
!this.selection.isAnonymousNode();
|
||||
const isDuplicatableElement = isSelectionElement &&
|
||||
!this.selection.isAnonymousNode() &&
|
||||
!this.selection.isRoot();
|
||||
const isScreenshotable = isSelectionElement &&
|
||||
this.selection.nodeFront.isTreeDisplayed;
|
||||
|
||||
const menu = new Menu();
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-edithtml",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLEdit.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorHTMLEdit.accesskey"),
|
||||
disabled: !isEditableElement,
|
||||
click: () => this.editHTML(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-add",
|
||||
label: INSPECTOR_L10N.getStr("inspectorAddNode.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorAddNode.accesskey"),
|
||||
disabled: !this.canAddHTMLChild(),
|
||||
click: () => this.addNode(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-duplicatenode",
|
||||
label: INSPECTOR_L10N.getStr("inspectorDuplicateNode.label"),
|
||||
disabled: !isDuplicatableElement,
|
||||
click: () => this.duplicateNode(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-delete",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLDelete.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorHTMLDelete.accesskey"),
|
||||
disabled: !this.isDeletable(this.selection.nodeFront),
|
||||
click: () => this.deleteNode(),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorAttributesSubmenu.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorAttributesSubmenu.accesskey"),
|
||||
submenu: this._getAttributesSubmenu(isEditableElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
// Set the pseudo classes
|
||||
for (const name of ["hover", "active", "focus", "focus-within"]) {
|
||||
const menuitem = new MenuItem({
|
||||
id: "node-menu-pseudo-" + name,
|
||||
label: name,
|
||||
type: "checkbox",
|
||||
click: this.togglePseudoClass.bind(this, ":" + name),
|
||||
});
|
||||
|
||||
if (isSelectionElement) {
|
||||
const checked = this.selection.nodeFront.hasPseudoClassLock(":" + name);
|
||||
menuitem.checked = checked;
|
||||
} else {
|
||||
menuitem.disabled = true;
|
||||
}
|
||||
|
||||
menu.append(menuitem);
|
||||
}
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyHTMLSubmenu.label"),
|
||||
submenu: this._getCopySubmenu(markupContainer, isSelectionElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorPasteHTMLSubmenu.label"),
|
||||
submenu: this._getPasteSubmenu(isEditableElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
const isNodeWithChildren = this.selection.isNode() &&
|
||||
markupContainer.hasChildren;
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-expand",
|
||||
label: INSPECTOR_L10N.getStr("inspectorExpandNode.label"),
|
||||
disabled: !isNodeWithChildren,
|
||||
click: () => this.expandNode(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-collapse",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCollapseAll.label"),
|
||||
disabled: !isNodeWithChildren || !markupContainer.expanded,
|
||||
click: () => this.collapseAll(),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-scrollnodeintoview",
|
||||
label: INSPECTOR_L10N.getStr("inspectorScrollNodeIntoView.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorScrollNodeIntoView.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.scrollNodeIntoView(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-screenshotnode",
|
||||
label: INSPECTOR_L10N.getStr("inspectorScreenshotNode.label"),
|
||||
disabled: !isScreenshotable,
|
||||
click: () => this.screenshotNode().catch(console.error),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-useinconsole",
|
||||
label: INSPECTOR_L10N.getStr("inspectorUseInConsole.label"),
|
||||
click: () => this.useInConsole(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-showdomproperties",
|
||||
label: INSPECTOR_L10N.getStr("inspectorShowDOMProperties.label"),
|
||||
click: () => this.showDOMProperties(),
|
||||
}));
|
||||
|
||||
if (this.selection.nodeFront.customElementLocation) {
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-jumptodefinition",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCustomElementDefinition.label"),
|
||||
click: () => this.jumpToCustomElementDefinition(),
|
||||
}));
|
||||
}
|
||||
|
||||
this.buildA11YMenuItem(menu);
|
||||
|
||||
const nodeLinkMenuItems = this._getNodeLinkMenuItems();
|
||||
if (nodeLinkMenuItems.filter(item => item.visible).length > 0) {
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-link-separator",
|
||||
type: "separator",
|
||||
}));
|
||||
}
|
||||
|
||||
for (const menuitem of nodeLinkMenuItems) {
|
||||
menu.append(menuitem);
|
||||
}
|
||||
|
||||
menu.popup(screenX, screenY, this._toolbox);
|
||||
return menu;
|
||||
},
|
||||
|
||||
buildA11YMenuItem: function(menu) {
|
||||
if (!(this.selection.isElementNode() || this.selection.isTextNode()) ||
|
||||
!Services.prefs.getBoolPref("devtools.accessibility.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const showA11YPropsItem = new MenuItem({
|
||||
id: "node-menu-showaccessibilityproperties",
|
||||
label: INSPECTOR_L10N.getStr("inspectorShowAccessibilityProperties.label"),
|
||||
click: () => this.showAccessibilityProperties(),
|
||||
disabled: true,
|
||||
});
|
||||
// Only attempt to determine if a11y props menu item needs to be enabled if
|
||||
// AccessibilityFront is enabled.
|
||||
if (this.accessibilityFront.enabled) {
|
||||
this._updateA11YMenuItem(showA11YPropsItem);
|
||||
}
|
||||
|
||||
menu.append(showA11YPropsItem);
|
||||
},
|
||||
|
||||
_updateA11YMenuItem: async function(menuItem) {
|
||||
const hasMethod = await this.target.actorHasMethod("domwalker",
|
||||
"hasAccessibilityProperties");
|
||||
if (!hasMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasA11YProps = await this.walker.hasAccessibilityProperties(
|
||||
this.selection.nodeFront);
|
||||
if (hasA11YProps) {
|
||||
this._toolbox.doc.getElementById(menuItem.id).disabled = menuItem.disabled = false;
|
||||
}
|
||||
|
||||
this.emit("node-menu-updated");
|
||||
},
|
||||
|
||||
_getCopySubmenu: function(markupContainer, isSelectionElement) {
|
||||
const copySubmenu = new Menu();
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyinner",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyInnerHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyouter",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyOuterHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyuniqueselector",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyUniqueSelector(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copycsspath",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyCssPath(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyxpath",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyXPath.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyXPath.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyXPath(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyimagedatauri",
|
||||
label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
|
||||
disabled: !isSelectionElement || !markupContainer ||
|
||||
!markupContainer.isPreviewable(),
|
||||
click: () => this.copyImageDataUri(),
|
||||
}));
|
||||
|
||||
return copySubmenu;
|
||||
},
|
||||
|
||||
_getPasteSubmenu: function(isEditableElement) {
|
||||
const isPasteable = isEditableElement && this._getClipboardContentForPaste();
|
||||
const disableAdjacentPaste = !isPasteable || this.selection.isRoot() ||
|
||||
this.selection.isBodyNode() || this.selection.isHeadNode();
|
||||
const disableFirstLastPaste = !isPasteable ||
|
||||
(this.selection.isHTMLNode() && this.selection.isRoot());
|
||||
|
||||
const pasteSubmenu = new Menu();
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pasteinnerhtml",
|
||||
label: INSPECTOR_L10N.getStr("inspectorPasteInnerHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorPasteInnerHTML.accesskey"),
|
||||
disabled: !isPasteable,
|
||||
click: () => this.pasteInnerHTML(),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pasteouterhtml",
|
||||
label: INSPECTOR_L10N.getStr("inspectorPasteOuterHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorPasteOuterHTML.accesskey"),
|
||||
disabled: !isPasteable,
|
||||
click: () => this.pasteOuterHTML(),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pastebefore",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteBefore.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteBefore.accesskey"),
|
||||
disabled: disableAdjacentPaste,
|
||||
click: () => this.pasteAdjacentHTML("beforeBegin"),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pasteafter",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteAfter.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteAfter.accesskey"),
|
||||
disabled: disableAdjacentPaste,
|
||||
click: () => this.pasteAdjacentHTML("afterEnd"),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pastefirstchild",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteFirstChild.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteFirstChild.accesskey"),
|
||||
disabled: disableFirstLastPaste,
|
||||
click: () => this.pasteAdjacentHTML("afterBegin"),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pastelastchild",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteLastChild.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteLastChild.accesskey"),
|
||||
disabled: disableFirstLastPaste,
|
||||
click: () => this.pasteAdjacentHTML("beforeEnd"),
|
||||
}));
|
||||
|
||||
return pasteSubmenu;
|
||||
},
|
||||
|
||||
_getAttributesSubmenu: function(isEditableElement) {
|
||||
const attributesSubmenu = new Menu();
|
||||
const nodeInfo = this.nodeMenuTriggerInfo;
|
||||
const isAttributeClicked = isEditableElement && nodeInfo &&
|
||||
nodeInfo.type === "attribute";
|
||||
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-add-attribute",
|
||||
label: INSPECTOR_L10N.getStr("inspectorAddAttribute.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorAddAttribute.accesskey"),
|
||||
disabled: !isEditableElement,
|
||||
click: () => this.onAddAttribute(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-copy-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
|
||||
isAttributeClicked ? `${nodeInfo.value}` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this.onCopyAttributeValue(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-edit-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
|
||||
isAttributeClicked ? `${nodeInfo.name}` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this.onEditAttribute(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-remove-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
|
||||
isAttributeClicked ? `${nodeInfo.name}` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this.onRemoveAttribute(),
|
||||
}));
|
||||
|
||||
return attributesSubmenu;
|
||||
},
|
||||
|
||||
/**
|
||||
* Link menu items can be shown or hidden depending on the context and
|
||||
* selected node, and their labels can vary.
|
||||
*
|
||||
* @return {Array} list of visible menu items related to links.
|
||||
*/
|
||||
_getNodeLinkMenuItems: function() {
|
||||
const linkFollow = new MenuItem({
|
||||
id: "node-menu-link-follow",
|
||||
visible: false,
|
||||
click: () => this.onFollowLink(),
|
||||
});
|
||||
const linkCopy = new MenuItem({
|
||||
id: "node-menu-link-copy",
|
||||
visible: false,
|
||||
click: () => this.onCopyLink(),
|
||||
});
|
||||
|
||||
// Get information about the right-clicked node.
|
||||
const popupNode = this.contextMenuTarget;
|
||||
if (!popupNode || !popupNode.classList.contains("link")) {
|
||||
return [linkFollow, linkCopy];
|
||||
}
|
||||
|
||||
const type = popupNode.dataset.type;
|
||||
if ((type === "uri" || type === "cssresource" || type === "jsresource")) {
|
||||
// Links can't be opened in new tabs in the browser toolbox.
|
||||
if (type === "uri" && !this.target.chrome) {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = INSPECTOR_L10N.getStr(
|
||||
"inspector.menu.openUrlInNewTab.label");
|
||||
} else if (type === "cssresource") {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = TOOLBOX_L10N.getStr(
|
||||
"toolbox.viewCssSourceInStyleEditor.label");
|
||||
} else if (type === "jsresource") {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = TOOLBOX_L10N.getStr(
|
||||
"toolbox.viewJsSourceInDebugger.label");
|
||||
}
|
||||
|
||||
linkCopy.visible = true;
|
||||
linkCopy.label = INSPECTOR_L10N.getStr(
|
||||
"inspector.menu.copyUrlToClipboard.label");
|
||||
} else if (type === "idref") {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = INSPECTOR_L10N.getFormatStr(
|
||||
"inspector.menu.selectElement.label", popupNode.dataset.link);
|
||||
}
|
||||
|
||||
return [linkFollow, linkCopy];
|
||||
},
|
||||
|
||||
_initMarkup: function() {
|
||||
if (!this._markupFrame) {
|
||||
this._markupFrame = this.panelDoc.createElement("iframe");
|
||||
|
|
@ -1913,7 +1457,6 @@ Inspector.prototype = {
|
|||
|
||||
_onMarkupFrameLoad: function() {
|
||||
this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
|
||||
this._markupFrame.contentWindow.addEventListener("contextmenu", this._onContextMenu);
|
||||
this._markupFrame.contentWindow.focus();
|
||||
this._markupBox.style.visibility = "visible";
|
||||
this.markup = new MarkupView(this, this._markupFrame, this._toolbox.win);
|
||||
|
|
@ -2028,241 +1571,6 @@ Inspector.prototype = {
|
|||
return promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show DOM properties
|
||||
*/
|
||||
showDOMProperties: function() {
|
||||
this._toolbox.openSplitConsole().then(() => {
|
||||
const panel = this._toolbox.getPanel("webconsole");
|
||||
const jsterm = panel.hud.jsterm;
|
||||
|
||||
jsterm.execute("inspect($0)");
|
||||
jsterm.focus();
|
||||
});
|
||||
},
|
||||
|
||||
jumpToCustomElementDefinition: function() {
|
||||
const node = this.selection.nodeFront;
|
||||
const { url, line } = node.customElementLocation;
|
||||
this._toolbox.viewSourceInDebugger(url, line, "show_custom_element");
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Accessibility properties for currently selected node
|
||||
*/
|
||||
async showAccessibilityProperties() {
|
||||
const a11yPanel = await this._toolbox.selectTool("accessibility");
|
||||
// Select the accessible object in the panel and wait for the event that
|
||||
// tells us it has been done.
|
||||
const onSelected = a11yPanel.once("new-accessible-front-selected");
|
||||
a11yPanel.selectAccessibleForNode(this.selection.nodeFront,
|
||||
"inspector-context-menu");
|
||||
await onSelected;
|
||||
},
|
||||
|
||||
/**
|
||||
* Use in Console.
|
||||
*
|
||||
* Takes the currently selected node in the inspector and assigns it to a
|
||||
* temp variable on the content window. Also opens the split console and
|
||||
* autofills it with the temp variable.
|
||||
*/
|
||||
useInConsole: function() {
|
||||
this._toolbox.openSplitConsole().then(() => {
|
||||
const panel = this._toolbox.getPanel("webconsole");
|
||||
const jsterm = panel.hud.jsterm;
|
||||
|
||||
const evalString = `{ let i = 0;
|
||||
while (window.hasOwnProperty("temp" + i) && i < 1000) {
|
||||
i++;
|
||||
}
|
||||
window["temp" + i] = $0;
|
||||
"temp" + i;
|
||||
}`;
|
||||
|
||||
const options = {
|
||||
selectedNodeActor: this.selection.nodeFront.actorID,
|
||||
};
|
||||
jsterm.requestEvaluation(evalString, options).then((res) => {
|
||||
jsterm.setInputValue(res.result);
|
||||
this.emit("console-var-ready");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit the outerHTML of the selected Node.
|
||||
*/
|
||||
editHTML: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
if (this.markup) {
|
||||
this.markup.beginEditingOuterHTML(this.selection.nodeFront);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Paste the contents of the clipboard into the selected Node's outer HTML.
|
||||
*/
|
||||
pasteOuterHTML: function() {
|
||||
const content = this._getClipboardContentForPaste();
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
const node = this.selection.nodeFront;
|
||||
return this.markup.getNodeOuterHTML(node).then(oldContent => {
|
||||
this.markup.updateNodeOuterHTML(node, content, oldContent);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Paste the contents of the clipboard into the selected Node's inner HTML.
|
||||
*/
|
||||
pasteInnerHTML: function() {
|
||||
const content = this._getClipboardContentForPaste();
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
const node = this.selection.nodeFront;
|
||||
return this.markup.getNodeInnerHTML(node).then(oldContent => {
|
||||
this.markup.updateNodeInnerHTML(node, content, oldContent);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Paste the contents of the clipboard as adjacent HTML to the selected Node.
|
||||
* @param position
|
||||
* The position as specified for Element.insertAdjacentHTML
|
||||
* (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
|
||||
*/
|
||||
pasteAdjacentHTML: function(position) {
|
||||
const content = this._getClipboardContentForPaste();
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
const node = this.selection.nodeFront;
|
||||
return this.markup.insertAdjacentHTMLToNode(node, position, content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the innerHTML of the selected Node to the clipboard.
|
||||
*/
|
||||
copyInnerHTML: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
this._copyLongString(this.walker.innerHTML(this.selection.nodeFront));
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the outerHTML of the selected Node to the clipboard.
|
||||
*/
|
||||
copyOuterHTML: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
const node = this.selection.nodeFront;
|
||||
|
||||
switch (node.nodeType) {
|
||||
case nodeConstants.ELEMENT_NODE :
|
||||
this._copyLongString(this.walker.outerHTML(node));
|
||||
break;
|
||||
case nodeConstants.COMMENT_NODE :
|
||||
this._getLongString(node.getNodeValue()).then(comment => {
|
||||
clipboardHelper.copyString("<!--" + comment + "-->");
|
||||
});
|
||||
break;
|
||||
case nodeConstants.DOCUMENT_TYPE_NODE :
|
||||
clipboardHelper.copyString(node.doctypeString);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the data-uri for the currently selected image in the clipboard.
|
||||
*/
|
||||
copyImageDataUri: function() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
if (container && container.isPreviewable()) {
|
||||
container.copyImageDataUri();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the content of a longString (via a promise resolving a
|
||||
* LongStringActor) to the clipboard
|
||||
* @param {Promise} longStringActorPromise
|
||||
* promise expected to resolve a LongStringActor instance
|
||||
* @return {Promise} promise resolving (with no argument) when the
|
||||
* string is sent to the clipboard
|
||||
*/
|
||||
_copyLongString: function(longStringActorPromise) {
|
||||
return this._getLongString(longStringActorPromise).then(string => {
|
||||
clipboardHelper.copyString(string);
|
||||
}).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the content of a longString (via a promise resolving a LongStringActor)
|
||||
* @param {Promise} longStringActorPromise
|
||||
* promise expected to resolve a LongStringActor instance
|
||||
* @return {Promise} promise resolving with the retrieved string as argument
|
||||
*/
|
||||
_getLongString: function(longStringActorPromise) {
|
||||
return longStringActorPromise.then(longStringActor => {
|
||||
return longStringActor.string().then(string => {
|
||||
longStringActor.release().catch(console.error);
|
||||
return string;
|
||||
});
|
||||
}).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a unique selector of the selected Node to the clipboard.
|
||||
*/
|
||||
copyUniqueSelector: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetry.scalarSet("devtools.copy.unique.css.selector.opened", 1);
|
||||
this.selection.nodeFront.getUniqueSelector().then(selector => {
|
||||
clipboardHelper.copyString(selector);
|
||||
}).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the full CSS Path of the selected Node to the clipboard.
|
||||
*/
|
||||
copyCssPath: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetry.scalarSet("devtools.copy.full.css.selector.opened", 1);
|
||||
this.selection.nodeFront.getCssPath().then(path => {
|
||||
clipboardHelper.copyString(path);
|
||||
}).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the XPath of the selected Node to the clipboard.
|
||||
*/
|
||||
copyXPath: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetry.scalarSet("devtools.copy.xpath.opened", 1);
|
||||
this.selection.nodeFront.getXPath().then(path => {
|
||||
clipboardHelper.copyString(path);
|
||||
}).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initiate screenshot command on selected node.
|
||||
*/
|
||||
|
|
@ -2285,160 +1593,6 @@ Inspector.prototype = {
|
|||
await saveScreenshot(this.panelWin, args, screenshot);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll the node into view.
|
||||
*/
|
||||
scrollNodeIntoView: function() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.nodeFront.scrollIntoView();
|
||||
},
|
||||
|
||||
/**
|
||||
* Duplicate the selected node
|
||||
*/
|
||||
duplicateNode: function() {
|
||||
const selection = this.selection;
|
||||
if (!selection.isElementNode() ||
|
||||
selection.isRoot() ||
|
||||
selection.isAnonymousNode() ||
|
||||
selection.isPseudoElementNode()) {
|
||||
return;
|
||||
}
|
||||
this.walker.duplicateNode(selection.nodeFront).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the selected node.
|
||||
*/
|
||||
deleteNode: function() {
|
||||
if (!this.selection.isNode() ||
|
||||
this.selection.isRoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the markup panel is active, use the markup panel to delete
|
||||
// the node, making this an undoable action.
|
||||
if (this.markup) {
|
||||
this.markup.deleteNode(this.selection.nodeFront);
|
||||
} else {
|
||||
// remove the node from content
|
||||
this.walker.removeNode(this.selection.nodeFront);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add attribute to node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
onAddAttribute: function() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
container.addAttribute();
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy attribute value for node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
onCopyAttributeValue: function() {
|
||||
clipboardHelper.copyString(this.nodeMenuTriggerInfo.value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit attribute for node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
onEditAttribute: function() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
container.editAttribute(this.nodeMenuTriggerInfo.name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove attribute from node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
onRemoveAttribute: function() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
container.removeAttribute(this.nodeMenuTriggerInfo.name);
|
||||
},
|
||||
|
||||
expandNode: function() {
|
||||
this.markup.expandAll(this.selection.nodeFront);
|
||||
},
|
||||
|
||||
collapseAll: function() {
|
||||
this.markup.collapseAll(this.selection.nodeFront);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is here for the benefit of the node-menu-link-follow menu item
|
||||
* in the inspector contextual-menu.
|
||||
*/
|
||||
onFollowLink: function() {
|
||||
const type = this.contextMenuTarget.dataset.type;
|
||||
const link = this.contextMenuTarget.dataset.link;
|
||||
|
||||
this.followAttributeLink(type, link);
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a type and link found in a node's attribute in the markup-view,
|
||||
* attempt to follow that link (which may result in opening a new tab, the
|
||||
* style editor or debugger).
|
||||
*/
|
||||
followAttributeLink: function(type, link) {
|
||||
if (!type || !link) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === "uri" || type === "cssresource" || type === "jsresource") {
|
||||
// Open link in a new tab.
|
||||
this.inspector.resolveRelativeURL(
|
||||
link, this.selection.nodeFront).then(url => {
|
||||
if (type === "uri") {
|
||||
openContentLink(url);
|
||||
} else if (type === "cssresource") {
|
||||
return this.toolbox.viewSourceInStyleEditor(url);
|
||||
} else if (type === "jsresource") {
|
||||
return this.toolbox.viewSourceInDebugger(url);
|
||||
}
|
||||
return null;
|
||||
}).catch(console.error);
|
||||
} else if (type == "idref") {
|
||||
// Select the node in the same document.
|
||||
this.walker.document(this.selection.nodeFront).then(doc => {
|
||||
return this.walker.querySelector(doc, "#" + CSS.escape(link)).then(node => {
|
||||
if (!node) {
|
||||
this.emit("idref-attribute-link-failed");
|
||||
return;
|
||||
}
|
||||
this.selection.setNodeFront(node);
|
||||
});
|
||||
}).catch(console.error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is here for the benefit of the node-menu-link-copy menu item
|
||||
* in the inspector contextual-menu.
|
||||
*/
|
||||
onCopyLink: function() {
|
||||
const link = this.contextMenuTarget.dataset.link;
|
||||
|
||||
this.copyAttributeLink(link);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is here for the benefit of copying links.
|
||||
*/
|
||||
copyAttributeLink: function(link) {
|
||||
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
|
||||
clipboardHelper.copyString(url);
|
||||
}, console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an object containing the shared handler functions used in the box
|
||||
* model and grid React components.
|
||||
|
|
@ -2464,18 +1618,6 @@ Inspector.prototype = {
|
|||
toolbox.highlighter.highlight(nodeFront, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether a node can be deleted.
|
||||
*
|
||||
* @param {NodeFront} nodeFront
|
||||
* The node to test for deletion
|
||||
*/
|
||||
isDeletable(nodeFront) {
|
||||
return !(nodeFront.isDocumentElement ||
|
||||
nodeFront.nodeType == nodeConstants.DOCUMENT_TYPE_NODE ||
|
||||
nodeFront.isAnonymous);
|
||||
},
|
||||
|
||||
async inspectNodeActor(nodeActor, inspectFromAnnotation) {
|
||||
const nodeFront = await this.walker.gripToNodeFront({ actor: nodeActor });
|
||||
if (!nodeFront) {
|
||||
|
|
|
|||
776
devtools/client/inspector/markup/markup-context-menu.js
Normal file
776
devtools/client/inspector/markup/markup-context-menu.js
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const promise = require("promise");
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
|
||||
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
|
||||
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
|
||||
loader.lazyRequireGetter(this, "copyLongString", "devtools/client/inspector/shared/utils", true);
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
|
||||
loader.lazyGetter(this, "TOOLBOX_L10N", function() {
|
||||
return new LocalizationHelper("devtools/client/locales/toolbox.properties");
|
||||
});
|
||||
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
||||
/**
|
||||
* Context menu for the Markup view.
|
||||
*/
|
||||
class MarkupContextMenu {
|
||||
constructor(markup) {
|
||||
this.markup = markup;
|
||||
this.inspector = markup.inspector;
|
||||
this.selection = this.inspector.selection;
|
||||
this.target = this.inspector.target;
|
||||
this.telemetry = this.inspector.telemetry;
|
||||
this.toolbox = this.inspector.toolbox;
|
||||
this.walker = this.inspector.walker;
|
||||
}
|
||||
|
||||
show(event) {
|
||||
if (!(event.originalTarget instanceof Element) ||
|
||||
event.originalTarget.closest("input[type=text]") ||
|
||||
event.originalTarget.closest("input:not([type])") ||
|
||||
event.originalTarget.closest("textarea")) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this._openMenu({
|
||||
screenX: event.screenX,
|
||||
screenY: event.screenY,
|
||||
target: event.target,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is here for the benefit of copying links.
|
||||
*/
|
||||
_copyAttributeLink(link) {
|
||||
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
|
||||
clipboardHelper.copyString(url);
|
||||
}, console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the full CSS Path of the selected Node to the clipboard.
|
||||
*/
|
||||
_copyCssPath() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetry.scalarSet("devtools.copy.full.css.selector.opened", 1);
|
||||
this.selection.nodeFront.getCssPath().then(path => {
|
||||
clipboardHelper.copyString(path);
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the data-uri for the currently selected image in the clipboard.
|
||||
*/
|
||||
_copyImageDataUri() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
if (container && container.isPreviewable()) {
|
||||
container.copyImageDataUri();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the innerHTML of the selected Node to the clipboard.
|
||||
*/
|
||||
_copyInnerHTML() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyLongString(this.walker.innerHTML(this.selection.nodeFront));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the outerHTML of the selected Node to the clipboard.
|
||||
*/
|
||||
_copyOuterHTML() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.markup.copyOuterHTML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a unique selector of the selected Node to the clipboard.
|
||||
*/
|
||||
_copyUniqueSelector() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetry.scalarSet("devtools.copy.unique.css.selector.opened", 1);
|
||||
this.selection.nodeFront.getUniqueSelector().then(selector => {
|
||||
clipboardHelper.copyString(selector);
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the XPath of the selected Node to the clipboard.
|
||||
*/
|
||||
_copyXPath() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetry.scalarSet("devtools.copy.xpath.opened", 1);
|
||||
this.selection.nodeFront.getXPath().then(path => {
|
||||
clipboardHelper.copyString(path);
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the selected node.
|
||||
*/
|
||||
_deleteNode() {
|
||||
if (!this.selection.isNode() ||
|
||||
this.selection.isRoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the markup panel is active, use the markup panel to delete
|
||||
// the node, making this an undoable action.
|
||||
if (this.markup) {
|
||||
this.markup.deleteNode(this.selection.nodeFront);
|
||||
} else {
|
||||
// remove the node from content
|
||||
this.walker.removeNode(this.selection.nodeFront);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate the selected node
|
||||
*/
|
||||
_duplicateNode() {
|
||||
if (!this.selection.isElementNode() ||
|
||||
this.selection.isRoot() ||
|
||||
this.selection.isAnonymousNode() ||
|
||||
this.selection.isPseudoElementNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.walker.duplicateNode(this.selection.nodeFront).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the outerHTML of the selected Node.
|
||||
*/
|
||||
_editHTML() {
|
||||
if (!this.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.markup.beginEditingOuterHTML(this.selection.nodeFront);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jumps to the custom element definition in the debugger.
|
||||
*/
|
||||
_jumpToCustomElementDefinition() {
|
||||
const { url, line } = this.selection.nodeFront.customElementLocation;
|
||||
this.toolbox.viewSourceInDebugger(url, line, "show_custom_element");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add attribute to node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
_onAddAttribute() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
container.addAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy attribute value for node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
_onCopyAttributeValue() {
|
||||
clipboardHelper.copyString(this.nodeMenuTriggerInfo.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is here for the benefit of the node-menu-link-copy menu item
|
||||
* in the inspector contextual-menu.
|
||||
*/
|
||||
_onCopyLink() {
|
||||
this.copyAttributeLink(this.contextMenuTarget.dataset.link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit attribute for node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
_onEditAttribute() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
container.editAttribute(this.nodeMenuTriggerInfo.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is here for the benefit of the node-menu-link-follow menu item
|
||||
* in the inspector contextual-menu.
|
||||
*/
|
||||
_onFollowLink() {
|
||||
const type = this.contextMenuTarget.dataset.type;
|
||||
const link = this.contextMenuTarget.dataset.link;
|
||||
this.markup.followAttributeLink(type, link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove attribute from node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
_onRemoveAttribute() {
|
||||
const container = this.markup.getContainer(this.selection.nodeFront);
|
||||
container.removeAttribute(this.nodeMenuTriggerInfo.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste the contents of the clipboard as adjacent HTML to the selected Node.
|
||||
*
|
||||
* @param {String} position
|
||||
* The position as specified for Element.insertAdjacentHTML
|
||||
* (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
|
||||
*/
|
||||
_pasteAdjacentHTML(position) {
|
||||
const content = this._getClipboardContentForPaste();
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
const node = this.selection.nodeFront;
|
||||
return this.markup.insertAdjacentHTMLToNode(node, position, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste the contents of the clipboard into the selected Node's inner HTML.
|
||||
*/
|
||||
_pasteInnerHTML() {
|
||||
const content = this._getClipboardContentForPaste();
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
const node = this.selection.nodeFront;
|
||||
return this.markup.getNodeInnerHTML(node).then(oldContent => {
|
||||
this.markup.updateNodeInnerHTML(node, content, oldContent);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste the contents of the clipboard into the selected Node's outer HTML.
|
||||
*/
|
||||
_pasteOuterHTML() {
|
||||
const content = this._getClipboardContentForPaste();
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
const node = this.selection.nodeFront;
|
||||
return this.markup.getNodeOuterHTML(node).then(oldContent => {
|
||||
this.markup.updateNodeOuterHTML(node, content, oldContent);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Accessibility properties for currently selected node
|
||||
*/
|
||||
async _showAccessibilityProperties() {
|
||||
const a11yPanel = await this.toolbox.selectTool("accessibility");
|
||||
// Select the accessible object in the panel and wait for the event that
|
||||
// tells us it has been done.
|
||||
const onSelected = a11yPanel.once("new-accessible-front-selected");
|
||||
a11yPanel.selectAccessibleForNode(this.selection.nodeFront, "inspector-context-menu");
|
||||
await onSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show DOM properties
|
||||
*/
|
||||
_showDOMProperties() {
|
||||
this.toolbox.openSplitConsole().then(() => {
|
||||
const panel = this.toolbox.getPanel("webconsole");
|
||||
const jsterm = panel.hud.jsterm;
|
||||
|
||||
jsterm.execute("inspect($0)");
|
||||
jsterm.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use in Console.
|
||||
*
|
||||
* Takes the currently selected node in the inspector and assigns it to a
|
||||
* temp variable on the content window. Also opens the split console and
|
||||
* autofills it with the temp variable.
|
||||
*/
|
||||
_useInConsole() {
|
||||
this.toolbox.openSplitConsole().then(() => {
|
||||
const panel = this.toolbox.getPanel("webconsole");
|
||||
const jsterm = panel.hud.jsterm;
|
||||
|
||||
const evalString = `{ let i = 0;
|
||||
while (window.hasOwnProperty("temp" + i) && i < 1000) {
|
||||
i++;
|
||||
}
|
||||
window["temp" + i] = $0;
|
||||
"temp" + i;
|
||||
}`;
|
||||
|
||||
const options = {
|
||||
selectedNodeActor: this.selection.nodeFront.actorID,
|
||||
};
|
||||
jsterm.requestEvaluation(evalString, options).then((res) => {
|
||||
jsterm.setInputValue(res.result);
|
||||
this.inspector.emit("console-var-ready");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_buildA11YMenuItem(menu) {
|
||||
if (!(this.selection.isElementNode() || this.selection.isTextNode()) ||
|
||||
!Services.prefs.getBoolPref("devtools.accessibility.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const showA11YPropsItem = new MenuItem({
|
||||
id: "node-menu-showaccessibilityproperties",
|
||||
label: INSPECTOR_L10N.getStr("inspectorShowAccessibilityProperties.label"),
|
||||
click: () => this._showAccessibilityProperties(),
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
// Only attempt to determine if a11y props menu item needs to be enabled if
|
||||
// AccessibilityFront is enabled.
|
||||
if (this.inspector.accessibilityFront.enabled) {
|
||||
this._updateA11YMenuItem(showA11YPropsItem);
|
||||
}
|
||||
|
||||
menu.append(showA11YPropsItem);
|
||||
}
|
||||
|
||||
_getAttributesSubmenu(isEditableElement) {
|
||||
const attributesSubmenu = new Menu();
|
||||
const nodeInfo = this.nodeMenuTriggerInfo;
|
||||
const isAttributeClicked = isEditableElement && nodeInfo &&
|
||||
nodeInfo.type === "attribute";
|
||||
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-add-attribute",
|
||||
label: INSPECTOR_L10N.getStr("inspectorAddAttribute.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorAddAttribute.accesskey"),
|
||||
disabled: !isEditableElement,
|
||||
click: () => this._onAddAttribute(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-copy-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
|
||||
isAttributeClicked ? `${nodeInfo.value}` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this._onCopyAttributeValue(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-edit-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
|
||||
isAttributeClicked ? `${nodeInfo.name}` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this._onEditAttribute(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-remove-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
|
||||
isAttributeClicked ? `${nodeInfo.name}` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this._onRemoveAttribute(),
|
||||
}));
|
||||
|
||||
return attributesSubmenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the clipboard content if it is appropriate for pasting
|
||||
* into the current node's outer HTML, otherwise returns null.
|
||||
*/
|
||||
_getClipboardContentForPaste() {
|
||||
const content = clipboardHelper.getText();
|
||||
if (content && content.trim().length > 0) {
|
||||
return content;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_getCopySubmenu(markupContainer, isSelectionElement) {
|
||||
const copySubmenu = new Menu();
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyinner",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this._copyInnerHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyouter",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this._copyOuterHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyuniqueselector",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this._copyUniqueSelector(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copycsspath",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this._copyCssPath(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyxpath",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyXPath.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyXPath.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this._copyXPath(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyimagedatauri",
|
||||
label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
|
||||
disabled: !isSelectionElement || !markupContainer ||
|
||||
!markupContainer.isPreviewable(),
|
||||
click: () => this._copyImageDataUri(),
|
||||
}));
|
||||
|
||||
return copySubmenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link menu items can be shown or hidden depending on the context and
|
||||
* selected node, and their labels can vary.
|
||||
*
|
||||
* @return {Array} list of visible menu items related to links.
|
||||
*/
|
||||
_getNodeLinkMenuItems() {
|
||||
const linkFollow = new MenuItem({
|
||||
id: "node-menu-link-follow",
|
||||
visible: false,
|
||||
click: () => this._onFollowLink(),
|
||||
});
|
||||
const linkCopy = new MenuItem({
|
||||
id: "node-menu-link-copy",
|
||||
visible: false,
|
||||
click: () => this._onCopyLink(),
|
||||
});
|
||||
|
||||
// Get information about the right-clicked node.
|
||||
const popupNode = this.contextMenuTarget;
|
||||
if (!popupNode || !popupNode.classList.contains("link")) {
|
||||
return [linkFollow, linkCopy];
|
||||
}
|
||||
|
||||
const type = popupNode.dataset.type;
|
||||
if ((type === "uri" || type === "cssresource" || type === "jsresource")) {
|
||||
// Links can't be opened in new tabs in the browser toolbox.
|
||||
if (type === "uri" && !this.target.chrome) {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = INSPECTOR_L10N.getStr(
|
||||
"inspector.menu.openUrlInNewTab.label");
|
||||
} else if (type === "cssresource") {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = TOOLBOX_L10N.getStr(
|
||||
"toolbox.viewCssSourceInStyleEditor.label");
|
||||
} else if (type === "jsresource") {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = TOOLBOX_L10N.getStr(
|
||||
"toolbox.viewJsSourceInDebugger.label");
|
||||
}
|
||||
|
||||
linkCopy.visible = true;
|
||||
linkCopy.label = INSPECTOR_L10N.getStr(
|
||||
"inspector.menu.copyUrlToClipboard.label");
|
||||
} else if (type === "idref") {
|
||||
linkFollow.visible = true;
|
||||
linkFollow.label = INSPECTOR_L10N.getFormatStr(
|
||||
"inspector.menu.selectElement.label", popupNode.dataset.link);
|
||||
}
|
||||
|
||||
return [linkFollow, linkCopy];
|
||||
}
|
||||
|
||||
_getPasteSubmenu(isEditableElement) {
|
||||
const isPasteable = isEditableElement && this._getClipboardContentForPaste();
|
||||
const disableAdjacentPaste = !isPasteable ||
|
||||
this.selection.isRoot() ||
|
||||
this.selection.isBodyNode() ||
|
||||
this.selection.isHeadNode();
|
||||
const disableFirstLastPaste = !isPasteable ||
|
||||
(this.selection.isHTMLNode() && this.selection.isRoot());
|
||||
|
||||
const pasteSubmenu = new Menu();
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pasteinnerhtml",
|
||||
label: INSPECTOR_L10N.getStr("inspectorPasteInnerHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorPasteInnerHTML.accesskey"),
|
||||
disabled: !isPasteable,
|
||||
click: () => this._pasteInnerHTML(),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pasteouterhtml",
|
||||
label: INSPECTOR_L10N.getStr("inspectorPasteOuterHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorPasteOuterHTML.accesskey"),
|
||||
disabled: !isPasteable,
|
||||
click: () => this._pasteOuterHTML(),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pastebefore",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteBefore.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteBefore.accesskey"),
|
||||
disabled: disableAdjacentPaste,
|
||||
click: () => this._pasteAdjacentHTML("beforeBegin"),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pasteafter",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteAfter.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteAfter.accesskey"),
|
||||
disabled: disableAdjacentPaste,
|
||||
click: () => this._pasteAdjacentHTML("afterEnd"),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pastefirstchild",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteFirstChild.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteFirstChild.accesskey"),
|
||||
disabled: disableFirstLastPaste,
|
||||
click: () => this._pasteAdjacentHTML("afterBegin"),
|
||||
}));
|
||||
pasteSubmenu.append(new MenuItem({
|
||||
id: "node-menu-pastelastchild",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLPasteLastChild.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorHTMLPasteLastChild.accesskey"),
|
||||
disabled: disableFirstLastPaste,
|
||||
click: () => this._pasteAdjacentHTML("beforeEnd"),
|
||||
}));
|
||||
|
||||
return pasteSubmenu;
|
||||
}
|
||||
|
||||
_openMenu({ target, screenX = 0, screenY = 0 } = {}) {
|
||||
if (this.selection.isSlotted()) {
|
||||
// Slotted elements should not show any context menu.
|
||||
return null;
|
||||
}
|
||||
|
||||
const markupContainer = this.markup.getContainer(this.selection.nodeFront);
|
||||
|
||||
this.contextMenuTarget = target;
|
||||
this.nodeMenuTriggerInfo = markupContainer &&
|
||||
markupContainer.editor.getInfoAtNode(target);
|
||||
|
||||
const isSelectionElement = this.selection.isElementNode() &&
|
||||
!this.selection.isPseudoElementNode();
|
||||
const isEditableElement = isSelectionElement &&
|
||||
!this.selection.isAnonymousNode();
|
||||
const isDuplicatableElement = isSelectionElement &&
|
||||
!this.selection.isAnonymousNode() &&
|
||||
!this.selection.isRoot();
|
||||
const isScreenshotable = isSelectionElement &&
|
||||
this.selection.nodeFront.isTreeDisplayed;
|
||||
|
||||
const menu = new Menu();
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-edithtml",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLEdit.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorHTMLEdit.accesskey"),
|
||||
disabled: !isEditableElement,
|
||||
click: () => this._editHTML(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-add",
|
||||
label: INSPECTOR_L10N.getStr("inspectorAddNode.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorAddNode.accesskey"),
|
||||
disabled: !this.inspector.canAddHTMLChild(),
|
||||
click: () => this.inspector.addNode(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-duplicatenode",
|
||||
label: INSPECTOR_L10N.getStr("inspectorDuplicateNode.label"),
|
||||
disabled: !isDuplicatableElement,
|
||||
click: () => this._duplicateNode(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-delete",
|
||||
label: INSPECTOR_L10N.getStr("inspectorHTMLDelete.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorHTMLDelete.accesskey"),
|
||||
disabled: !this.markup.isDeletable(this.selection.nodeFront),
|
||||
click: () => this._deleteNode(),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorAttributesSubmenu.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorAttributesSubmenu.accesskey"),
|
||||
submenu: this._getAttributesSubmenu(isEditableElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
// Set the pseudo classes
|
||||
for (const name of ["hover", "active", "focus", "focus-within"]) {
|
||||
const menuitem = new MenuItem({
|
||||
id: "node-menu-pseudo-" + name,
|
||||
label: name,
|
||||
type: "checkbox",
|
||||
click: () => this.inspector.togglePseudoClass(":" + name),
|
||||
});
|
||||
|
||||
if (isSelectionElement) {
|
||||
const checked = this.selection.nodeFront.hasPseudoClassLock(":" + name);
|
||||
menuitem.checked = checked;
|
||||
} else {
|
||||
menuitem.disabled = true;
|
||||
}
|
||||
|
||||
menu.append(menuitem);
|
||||
}
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyHTMLSubmenu.label"),
|
||||
submenu: this._getCopySubmenu(markupContainer, isSelectionElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorPasteHTMLSubmenu.label"),
|
||||
submenu: this._getPasteSubmenu(isEditableElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
const isNodeWithChildren = this.selection.isNode() &&
|
||||
markupContainer.hasChildren;
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-expand",
|
||||
label: INSPECTOR_L10N.getStr("inspectorExpandNode.label"),
|
||||
disabled: !isNodeWithChildren,
|
||||
click: () => this.markup.expandAll(this.selection.nodeFront),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-collapse",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCollapseAll.label"),
|
||||
disabled: !isNodeWithChildren || !markupContainer.expanded,
|
||||
click: () => this.markup.collapseAll(this.selection.nodeFront),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-scrollnodeintoview",
|
||||
label: INSPECTOR_L10N.getStr("inspectorScrollNodeIntoView.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorScrollNodeIntoView.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.markup.scrollNodeIntoView(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-screenshotnode",
|
||||
label: INSPECTOR_L10N.getStr("inspectorScreenshotNode.label"),
|
||||
disabled: !isScreenshotable,
|
||||
click: () => this.inspector.screenshotNode().catch(console.error),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-useinconsole",
|
||||
label: INSPECTOR_L10N.getStr("inspectorUseInConsole.label"),
|
||||
click: () => this._useInConsole(),
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-showdomproperties",
|
||||
label: INSPECTOR_L10N.getStr("inspectorShowDOMProperties.label"),
|
||||
click: () => this._showDOMProperties(),
|
||||
}));
|
||||
|
||||
if (this.selection.nodeFront.customElementLocation) {
|
||||
menu.append(new MenuItem({
|
||||
type: "separator",
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-jumptodefinition",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCustomElementDefinition.label"),
|
||||
click: () => this._jumpToCustomElementDefinition(),
|
||||
}));
|
||||
}
|
||||
|
||||
this._buildA11YMenuItem(menu);
|
||||
|
||||
const nodeLinkMenuItems = this._getNodeLinkMenuItems();
|
||||
if (nodeLinkMenuItems.filter(item => item.visible).length > 0) {
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-link-separator",
|
||||
type: "separator",
|
||||
}));
|
||||
}
|
||||
|
||||
for (const menuitem of nodeLinkMenuItems) {
|
||||
menu.append(menuitem);
|
||||
}
|
||||
|
||||
menu.popup(screenX, screenY, this.toolbox);
|
||||
return menu;
|
||||
}
|
||||
|
||||
async _updateA11YMenuItem(menuItem) {
|
||||
const hasMethod = await this.target.actorHasMethod("domwalker",
|
||||
"hasAccessibilityProperties");
|
||||
if (!hasMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasA11YProps = await this.walker.hasAccessibilityProperties(
|
||||
this.selection.nodeFront);
|
||||
if (hasA11YProps) {
|
||||
this.toolbox.doc.getElementById(menuItem.id).disabled = menuItem.disabled = false;
|
||||
}
|
||||
|
||||
this.inspector.emit("node-menu-updated");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MarkupContextMenu;
|
||||
|
|
@ -21,9 +21,14 @@ const MarkupReadOnlyContainer = require("devtools/client/inspector/markup/views/
|
|||
const MarkupTextContainer = require("devtools/client/inspector/markup/views/text-container");
|
||||
const RootContainer = require("devtools/client/inspector/markup/views/root-container");
|
||||
|
||||
loader.lazyRequireGetter(this, "MarkupContextMenu", "devtools/client/inspector/markup/markup-context-menu");
|
||||
loader.lazyRequireGetter(this, "SlottedNodeContainer", "devtools/client/inspector/markup/views/slotted-node-container");
|
||||
loader.lazyRequireGetter(this, "copyLongString", "devtools/client/inspector/shared/utils", true);
|
||||
loader.lazyRequireGetter(this, "getLongString", "devtools/client/inspector/shared/utils", true);
|
||||
loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
|
||||
loader.lazyRequireGetter(this, "HTMLTooltip", "devtools/client/shared/widgets/tooltip/HTMLTooltip", true);
|
||||
loader.lazyRequireGetter(this, "UndoStack", "devtools/client/shared/undo", true);
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
|
@ -98,6 +103,7 @@ function MarkupView(inspector, frame, controllerWindow) {
|
|||
this._isImagePreviewTarget = this._isImagePreviewTarget.bind(this);
|
||||
this._mutationObserver = this._mutationObserver.bind(this);
|
||||
this._onBlur = this._onBlur.bind(this);
|
||||
this._onContextMenu = this._onContextMenu.bind(this);
|
||||
this._onCopy = this._onCopy.bind(this);
|
||||
this._onCollapseAttributesPrefChange = this._onCollapseAttributesPrefChange.bind(this);
|
||||
this._onWalkerNodeStatesChanged = this._onWalkerNodeStatesChanged.bind(this);
|
||||
|
|
@ -113,6 +119,7 @@ function MarkupView(inspector, frame, controllerWindow) {
|
|||
// Listening to various events.
|
||||
this._elt.addEventListener("blur", this._onBlur, true);
|
||||
this._elt.addEventListener("click", this._onMouseClick);
|
||||
this._elt.addEventListener("contextmenu", this._onContextMenu);
|
||||
this._elt.addEventListener("mousemove", this._onMouseMove);
|
||||
this._elt.addEventListener("mouseout", this._onMouseOut);
|
||||
this._frame.addEventListener("focus", this._onFocus);
|
||||
|
|
@ -156,6 +163,14 @@ MarkupView.prototype = {
|
|||
|
||||
_selectedContainer: null,
|
||||
|
||||
get contextMenu() {
|
||||
if (!this._contextMenu) {
|
||||
this._contextMenu = new MarkupContextMenu(this);
|
||||
}
|
||||
|
||||
return this._contextMenu;
|
||||
},
|
||||
|
||||
get eventDetailsTooltip() {
|
||||
if (!this._eventDetailsTooltip) {
|
||||
// This tooltip will be attached to the toolbox document.
|
||||
|
|
@ -279,6 +294,10 @@ MarkupView.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_onContextMenu: function(event) {
|
||||
this.contextMenu.show(event);
|
||||
},
|
||||
|
||||
/**
|
||||
* Executed on each mouse-move while a node is being dragged in the view.
|
||||
* Auto-scrolls the view to reveal nodes below the fold to drop the dragged
|
||||
|
|
@ -766,12 +785,73 @@ MarkupView.prototype = {
|
|||
|
||||
const selection = this.inspector.selection;
|
||||
if (selection.isNode()) {
|
||||
this.inspector.copyOuterHTML();
|
||||
this.copyOuterHTML();
|
||||
}
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the outerHTML of the selected Node to the clipboard.
|
||||
*/
|
||||
copyOuterHTML: function() {
|
||||
if (!this.inspector.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
const node = this.inspector.selection.nodeFront;
|
||||
|
||||
switch (node.nodeType) {
|
||||
case nodeConstants.ELEMENT_NODE :
|
||||
copyLongString(this.walker.outerHTML(node));
|
||||
break;
|
||||
case nodeConstants.COMMENT_NODE :
|
||||
getLongString(node.getNodeValue()).then(comment => {
|
||||
clipboardHelper.copyString("<!--" + comment + "-->");
|
||||
});
|
||||
break;
|
||||
case nodeConstants.DOCUMENT_TYPE_NODE :
|
||||
clipboardHelper.copyString(node.doctypeString);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a type and link found in a node's attribute in the markup-view,
|
||||
* attempt to follow that link (which may result in opening a new tab, the
|
||||
* style editor or debugger).
|
||||
*/
|
||||
followAttributeLink: function(type, link) {
|
||||
if (!type || !link) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === "uri" || type === "cssresource" || type === "jsresource") {
|
||||
// Open link in a new tab.
|
||||
this.inspector.inspector.resolveRelativeURL(
|
||||
link, this.inspector.selection.nodeFront).then(url => {
|
||||
if (type === "uri") {
|
||||
openContentLink(url);
|
||||
} else if (type === "cssresource") {
|
||||
return this.toolbox.viewSourceInStyleEditor(url);
|
||||
} else if (type === "jsresource") {
|
||||
return this.toolbox.viewSourceInDebugger(url);
|
||||
}
|
||||
return null;
|
||||
}).catch(console.error);
|
||||
} else if (type == "idref") {
|
||||
// Select the node in the same document.
|
||||
this.walker.document(this.inspector.selection.nodeFront).then(doc => {
|
||||
return this.walker.querySelector(doc, "#" + CSS.escape(link)).then(node => {
|
||||
if (!node) {
|
||||
this.emit("idref-attribute-link-failed");
|
||||
return;
|
||||
}
|
||||
this.inspector.selection.setNodeFront(node);
|
||||
});
|
||||
}).catch(console.error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Register all key shortcuts.
|
||||
*/
|
||||
|
|
@ -820,8 +900,7 @@ MarkupView.prototype = {
|
|||
break;
|
||||
}
|
||||
case "markupView.scrollInto.key": {
|
||||
const selection = this._selectedContainer.node;
|
||||
this.inspector.scrollNodeIntoView(selection);
|
||||
this.scrollNodeIntoView();
|
||||
break;
|
||||
}
|
||||
// Generic keys
|
||||
|
|
@ -960,6 +1039,18 @@ MarkupView.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether a node can be deleted.
|
||||
*
|
||||
* @param {NodeFront} nodeFront
|
||||
* The node to test for deletion
|
||||
*/
|
||||
isDeletable(nodeFront) {
|
||||
return !(nodeFront.isDocumentElement ||
|
||||
nodeFront.nodeType == nodeConstants.DOCUMENT_TYPE_NODE ||
|
||||
nodeFront.isAnonymous);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a node from the DOM.
|
||||
* This is an undoable action.
|
||||
|
|
@ -970,7 +1061,7 @@ MarkupView.prototype = {
|
|||
* If set to true, focus the previous sibling, otherwise the next one.
|
||||
*/
|
||||
deleteNode: function(node, moveBackward) {
|
||||
if (!this.inspector.isDeletable(node)) {
|
||||
if (!this.isDeletable(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1017,6 +1108,17 @@ MarkupView.prototype = {
|
|||
}).catch(console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll the node into view.
|
||||
*/
|
||||
scrollNodeIntoView() {
|
||||
if (!this.inspector.selection.isNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inspector.selection.nodeFront.scrollIntoView();
|
||||
},
|
||||
|
||||
/**
|
||||
* If an editable item is focused, select its container.
|
||||
*/
|
||||
|
|
@ -1952,6 +2054,7 @@ MarkupView.prototype = {
|
|||
|
||||
this._elt.removeEventListener("blur", this._onBlur, true);
|
||||
this._elt.removeEventListener("click", this._onMouseClick);
|
||||
this._elt.removeEventListener("contextmenu", this._onContextMenu);
|
||||
this._elt.removeEventListener("mousemove", this._onMouseMove);
|
||||
this._elt.removeEventListener("mouseout", this._onMouseOut);
|
||||
this._frame.removeEventListener("focus", this._onFocus);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ DIRS += [
|
|||
]
|
||||
|
||||
DevToolsModules(
|
||||
'markup-context-menu.js',
|
||||
'markup.js',
|
||||
'utils.js',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ add_task(async function() {
|
|||
|
||||
info("Follow the link and wait for the new tab to open");
|
||||
const onTabOpened = once(gBrowser.tabContainer, "TabOpen");
|
||||
inspector.onFollowLink();
|
||||
inspector.markup.contextMenu._onFollowLink();
|
||||
const {target: tab} = await onTabOpened;
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ add_task(async function() {
|
|||
|
||||
info("Follow the link and wait for the new node to be selected");
|
||||
const onSelection = inspector.selection.once("new-node-front");
|
||||
inspector.onFollowLink();
|
||||
inspector.markup.contextMenu._onFollowLink();
|
||||
await onSelection;
|
||||
|
||||
ok(true, "A new node was selected");
|
||||
|
|
@ -59,8 +59,8 @@ add_task(async function() {
|
|||
});
|
||||
|
||||
info("Try to follow the link and check that no new node were selected");
|
||||
const onFailed = inspector.once("idref-attribute-link-failed");
|
||||
inspector.onFollowLink();
|
||||
const onFailed = inspector.markup.once("idref-attribute-link-failed");
|
||||
inspector.markup.contextMenu._onFollowLink();
|
||||
await onFailed;
|
||||
|
||||
ok(true, "The node selection failed");
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ add_task(async function() {
|
|||
|
||||
info("Follow the link and wait for the style-editor to open");
|
||||
const onStyleEditorReady = toolbox.once("styleeditor-ready");
|
||||
inspector.onFollowLink();
|
||||
inspector.markup.contextMenu._onFollowLink();
|
||||
await onStyleEditorReady;
|
||||
|
||||
// No real need to test that the editor opened on the right file here as this
|
||||
|
|
@ -44,7 +44,7 @@ add_task(async function() {
|
|||
|
||||
info("Follow the link and wait for the debugger to open");
|
||||
const onDebuggerReady = toolbox.once("jsdebugger-ready");
|
||||
inspector.onFollowLink();
|
||||
inspector.markup.contextMenu._onFollowLink();
|
||||
await onDebuggerReady;
|
||||
|
||||
// No real need to test that the debugger opened on the right file here as
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ async function followLinkWaitForNewNode(linkEl, isMetaClick, inspector) {
|
|||
}
|
||||
|
||||
async function followLinkNoNewNode(linkEl, isMetaClick, inspector) {
|
||||
const onFailed = inspector.once("idref-attribute-link-failed");
|
||||
const onFailed = inspector.markup.once("idref-attribute-link-failed");
|
||||
performMouseDown(linkEl, isMetaClick);
|
||||
await onFailed;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,12 @@ add_task(async function() {
|
|||
await selectNode(divContainer.node, inspector);
|
||||
|
||||
info("Check the copied values for the various copy*Path helpers");
|
||||
await waitForClipboardPromise(() => inspector.copyXPath(), '//*[@id="el1"]');
|
||||
await waitForClipboardPromise(() => inspector.copyCssPath(), "div#el1");
|
||||
await waitForClipboardPromise(() => inspector.copyUniqueSelector(), "#el1");
|
||||
await waitForClipboardPromise(() => inspector.markup.contextMenu._copyXPath(),
|
||||
'//*[@id="el1"]');
|
||||
await waitForClipboardPromise(() => inspector.markup.contextMenu._copyCssPath(),
|
||||
"div#el1");
|
||||
await waitForClipboardPromise(() => inspector.markup.contextMenu._copyUniqueSelector(),
|
||||
"#el1");
|
||||
|
||||
info("Expand the div");
|
||||
await expandContainer(inspector, divContainer);
|
||||
|
|
@ -57,8 +60,10 @@ add_task(async function() {
|
|||
await selectNode(spanContainer.node, inspector);
|
||||
|
||||
info("Check the copied values for the various copy*Path helpers");
|
||||
await waitForClipboardPromise(() => inspector.copyXPath(), "/div/span[3]");
|
||||
await waitForClipboardPromise(() => inspector.copyCssPath(), "div#el1 span");
|
||||
await waitForClipboardPromise(() => inspector.copyUniqueSelector(),
|
||||
await waitForClipboardPromise(() => inspector.markup.contextMenu._copyXPath(),
|
||||
"/div/span[3]");
|
||||
await waitForClipboardPromise(() => inspector.markup.contextMenu._copyCssPath(),
|
||||
"div#el1 span");
|
||||
await waitForClipboardPromise(() => inspector.markup.contextMenu._copyUniqueSelector(),
|
||||
"#el1 > span:nth-child(3)");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ MarkupContainer.prototype = {
|
|||
const type = target.dataset.type;
|
||||
// Make container tabbable descendants not tabbable (by default).
|
||||
this.canFocus = false;
|
||||
this.markup.inspector.followAttributeLink(type, link);
|
||||
this.markup.followAttributeLink(type, link);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const promise = require("promise");
|
|||
loader.lazyRequireGetter(this, "KeyCodes", "devtools/client/shared/keycodes", true);
|
||||
loader.lazyRequireGetter(this, "getCSSLexer", "devtools/shared/css/lexer", true);
|
||||
loader.lazyRequireGetter(this, "parseDeclarations", "devtools/shared/css/parsing-utils", true);
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
|
|
@ -82,6 +83,21 @@ function blurOnMultipleProperties(cssProperties) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the content of a longString (via a promise resolving a
|
||||
* LongStringActor) to the clipboard.
|
||||
*
|
||||
* @param {Promise} longStringActorPromise
|
||||
* promise expected to resolve a LongStringActor instance
|
||||
* @return {Promise} promise resolving (with no argument) when the
|
||||
* string is sent to the clipboard
|
||||
*/
|
||||
function copyLongString(longStringActorPromise) {
|
||||
return getLongString(longStringActorPromise).then(string => {
|
||||
clipboardHelper.copyString(string);
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a child element with a set of attributes.
|
||||
*
|
||||
|
|
@ -109,6 +125,22 @@ function createChild(parent, tagName, attributes = {}) {
|
|||
return elt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content of a longString (via a promise resolving a LongStringActor).
|
||||
*
|
||||
* @param {Promise} longStringActorPromise
|
||||
* promise expected to resolve a LongStringActor instance
|
||||
* @return {Promise} promise resolving with the retrieved string as argument
|
||||
*/
|
||||
function getLongString(longStringActorPromise) {
|
||||
return longStringActorPromise.then(longStringActor => {
|
||||
return longStringActor.string().then(string => {
|
||||
longStringActor.release().catch(console.error);
|
||||
return string;
|
||||
});
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a selector of the Element Rep from the grip. This is based on the
|
||||
* getElements() function in our devtools-reps component for a ElementNode.
|
||||
|
|
@ -196,7 +228,9 @@ function translateNodeFrontToGrip(nodeFront) {
|
|||
exports.advanceValidate = advanceValidate;
|
||||
exports.appendText = appendText;
|
||||
exports.blurOnMultipleProperties = blurOnMultipleProperties;
|
||||
exports.copyLongString = copyLongString;
|
||||
exports.createChild = createChild;
|
||||
exports.getLongString = getLongString;
|
||||
exports.getSelectorFromGrip = getSelectorFromGrip;
|
||||
exports.promiseWarn = promiseWarn;
|
||||
exports.translateNodeFrontToGrip = translateNodeFrontToGrip;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ add_task(async function() {
|
|||
const copyAttributeValue = getMenuItem("node-menu-copy-attribute");
|
||||
|
||||
info("Triggering 'Copy Attribute Value' and waiting for clipboard to copy the value");
|
||||
inspector.nodeMenuTriggerInfo = {
|
||||
inspector.markup.contextMenu.nodeMenuTriggerInfo = {
|
||||
type: "attribute",
|
||||
name: "data-edit",
|
||||
value: "the",
|
||||
|
|
@ -53,7 +53,7 @@ add_task(async function() {
|
|||
"23456789012345678901234567890123456789012345678901234567890123456789012" +
|
||||
"34567890123456789012345678901234567890123456789012345678901234567890123";
|
||||
|
||||
inspector.nodeMenuTriggerInfo = {
|
||||
inspector.markup.contextMenu.nodeMenuTriggerInfo = {
|
||||
type: "attribute",
|
||||
name: "data-edit",
|
||||
value: longAttribute,
|
||||
|
|
@ -67,7 +67,7 @@ add_task(async function() {
|
|||
const editAttribute = getMenuItem("node-menu-edit-attribute");
|
||||
|
||||
info("Triggering 'Edit Attribute' and waiting for mutation to occur");
|
||||
inspector.nodeMenuTriggerInfo = {
|
||||
inspector.markup.contextMenu.nodeMenuTriggerInfo = {
|
||||
type: "attribute",
|
||||
name: "data-edit",
|
||||
};
|
||||
|
|
@ -87,7 +87,7 @@ add_task(async function() {
|
|||
const removeAttribute = getMenuItem("node-menu-remove-attribute");
|
||||
|
||||
info("Triggering 'Remove Attribute' and waiting for mutation to occur");
|
||||
inspector.nodeMenuTriggerInfo = {
|
||||
inspector.markup.contextMenu.nodeMenuTriggerInfo = {
|
||||
type: "attribute",
|
||||
name: "data-remove",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ add_task(async function() {
|
|||
|
||||
async function checkTextBox(textBox, toolbox) {
|
||||
let textboxContextMenu = toolbox.doc.getElementById("toolbox-menu");
|
||||
ok(!textboxContextMenu, "The menu is closed");
|
||||
ok(!textboxContextMenu, "The menu is closed");
|
||||
|
||||
info("Simulating context click on the textbox and expecting the menu to open");
|
||||
const onContextMenu = toolbox.once("menu-open");
|
||||
|
|
|
|||
|
|
@ -660,6 +660,6 @@ function openStyleContextMenuAndGetAllItems(view, target) {
|
|||
* @return An array of MenuItems
|
||||
*/
|
||||
function openContextMenuAndGetAllItems(inspector, options) {
|
||||
const menu = inspector._openMenu(options);
|
||||
const menu = inspector.markup.contextMenu._openMenu(options);
|
||||
return buildContextMenuItems(menu);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ function loadFileContent(aFile, aCharset) {
|
|||
|
||||
var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var chann = ios.newChannel2(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
var chann = ios.newChannel(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
var cis = SpecialPowers.Ci.nsIConverterInputStream;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ function loadFileContent(aFile, aCharset) {
|
|||
|
||||
var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var chann = ios.newChannel2(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
var chann = ios.newChannel(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
var cis = SpecialPowers.Ci.nsIConverterInputStream;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ function loadFileContent(aFile, aCharset) {
|
|||
|
||||
var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var chann = ios.newChannel2(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
var chann = ios.newChannel(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
var cis = SpecialPowers.Ci.nsIConverterInputStream;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ function loadFileContent(aFile, aCharset) {
|
|||
|
||||
var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var chann = ios.newChannel2(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
var chann = ios.newChannel(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
var cis = SpecialPowers.Ci.nsIConverterInputStream;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ function loadFileContent(aFile, aCharset) {
|
|||
|
||||
var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var chann = ios.newChannel2(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
var chann = ios.newChannel(aFile,
|
||||
aCharset,
|
||||
baseUri,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
var cis = SpecialPowers.Ci.nsIConverterInputStream;
|
||||
|
||||
|
|
|
|||
|
|
@ -3422,6 +3422,14 @@ static MathSpace ExtractMathSpace(MDefinition* ins) {
|
|||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown TruncateKind");
|
||||
}
|
||||
|
||||
static bool MonotoneAdd(int32_t lhs, int32_t rhs) {
|
||||
return (lhs >= 0 && rhs >= 0) || (lhs <= 0 && rhs <= 0);
|
||||
}
|
||||
|
||||
static bool MonotoneSub(int32_t lhs, int32_t rhs) {
|
||||
return (lhs >= 0 && rhs <= 0) || (lhs <= 0 && rhs >= 0);
|
||||
}
|
||||
|
||||
// Extract a linear sum from ins, if possible (otherwise giving the
|
||||
// sum 'ins + 0').
|
||||
SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space) {
|
||||
|
|
@ -3471,7 +3479,8 @@ SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space) {
|
|||
int32_t constant;
|
||||
if (space == MathSpace::Modulo) {
|
||||
constant = uint32_t(lsum.constant) + uint32_t(rsum.constant);
|
||||
} else if (!SafeAdd(lsum.constant, rsum.constant, &constant)) {
|
||||
} else if (!SafeAdd(lsum.constant, rsum.constant, &constant) ||
|
||||
!MonotoneAdd(lsum.constant, rsum.constant)) {
|
||||
return SimpleLinearSum(ins, 0);
|
||||
}
|
||||
return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant);
|
||||
|
|
@ -3483,7 +3492,8 @@ SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space) {
|
|||
int32_t constant;
|
||||
if (space == MathSpace::Modulo) {
|
||||
constant = uint32_t(lsum.constant) - uint32_t(rsum.constant);
|
||||
} else if (!SafeSub(lsum.constant, rsum.constant, &constant)) {
|
||||
} else if (!SafeSub(lsum.constant, rsum.constant, &constant) ||
|
||||
!MonotoneSub(lsum.constant, rsum.constant)) {
|
||||
return SimpleLinearSum(ins, 0);
|
||||
}
|
||||
return SimpleLinearSum(lsum.term, constant);
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ nsresult WebrtcProxyChannel::Open(const nsCString& aHost, const int& aPort,
|
|||
// -the previous proxy tunnel didn't support redirects e.g. 307. don't need to
|
||||
// introduce new behavior. can't follow redirects on connect anyway.
|
||||
nsCOMPtr<nsIChannel> localChannel;
|
||||
rv = ioService->NewChannelFromURIWithProxyFlags2(
|
||||
rv = ioService->NewChannelFromURIWithProxyFlags(
|
||||
uri, nullptr,
|
||||
// Proxy flags are overridden by SetConnectOnly()
|
||||
0, aLoadInfo->LoadingNode(), aLoadInfo->LoadingPrincipal(),
|
||||
|
|
|
|||
|
|
@ -301,12 +301,12 @@ var NetUtil = {
|
|||
contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
}
|
||||
|
||||
return Services.io.newChannelFromURI2(uri,
|
||||
loadingNode || null,
|
||||
loadingPrincipal || null,
|
||||
triggeringPrincipal || null,
|
||||
securityFlags,
|
||||
contentPolicyType);
|
||||
return Services.io.newChannelFromURI(uri,
|
||||
loadingNode || null,
|
||||
loadingPrincipal || null,
|
||||
triggeringPrincipal || null,
|
||||
securityFlags,
|
||||
contentPolicyType);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -101,12 +101,12 @@ interface nsIIOService : nsISupports
|
|||
* Keep in mind that URIs coming from a webpage should *never* use the
|
||||
* systemPrincipal as the loadingPrincipal.
|
||||
*/
|
||||
nsIChannel newChannelFromURI2(in nsIURI aURI,
|
||||
in Node aLoadingNode,
|
||||
in nsIPrincipal aLoadingPrincipal,
|
||||
in nsIPrincipal aTriggeringPrincipal,
|
||||
in unsigned long aSecurityFlags,
|
||||
in unsigned long aContentPolicyType);
|
||||
nsIChannel newChannelFromURI(in nsIURI aURI,
|
||||
in Node aLoadingNode,
|
||||
in nsIPrincipal aLoadingPrincipal,
|
||||
in nsIPrincipal aTriggeringPrincipal,
|
||||
in unsigned long aSecurityFlags,
|
||||
in unsigned long aContentPolicyType);
|
||||
|
||||
[noscript, nostdcall, notxpcom]
|
||||
nsresult NewChannelFromURIWithClientAndController(in nsIURI aURI,
|
||||
|
|
@ -120,22 +120,22 @@ interface nsIIOService : nsISupports
|
|||
out nsIChannel aResult);
|
||||
|
||||
/**
|
||||
* Equivalent to newChannelFromURI2(aURI, aLoadingNode, ...)
|
||||
* Equivalent to newChannelFromURI(aURI, aLoadingNode, ...)
|
||||
*/
|
||||
nsIChannel newChannelFromURIWithLoadInfo(in nsIURI aURI,
|
||||
in nsILoadInfo aLoadInfo);
|
||||
|
||||
/**
|
||||
* Equivalent to newChannelFromURI2(newURI(...))
|
||||
* Equivalent to newChannelFromURI(newURI(...))
|
||||
*/
|
||||
nsIChannel newChannel2(in AUTF8String aSpec,
|
||||
in string aOriginCharset,
|
||||
in nsIURI aBaseURI,
|
||||
in Node aLoadingNode,
|
||||
in nsIPrincipal aLoadingPrincipal,
|
||||
in nsIPrincipal aTriggeringPrincipal,
|
||||
in unsigned long aSecurityFlags,
|
||||
in unsigned long aContentPolicyType);
|
||||
nsIChannel newChannel(in AUTF8String aSpec,
|
||||
in string aOriginCharset,
|
||||
in nsIURI aBaseURI,
|
||||
in Node aLoadingNode,
|
||||
in nsIPrincipal aLoadingPrincipal,
|
||||
in nsIPrincipal aTriggeringPrincipal,
|
||||
in unsigned long aSecurityFlags,
|
||||
in unsigned long aContentPolicyType);
|
||||
|
||||
/**
|
||||
* Returns true if networking is in "offline" mode. When in offline mode,
|
||||
|
|
@ -226,14 +226,14 @@ interface nsIIOService : nsISupports
|
|||
* then loadingPrincipal must be equal to loadingNode->NodePrincipal().
|
||||
* But less error prone is to just supply a loadingNode.
|
||||
*/
|
||||
nsIChannel newChannelFromURIWithProxyFlags2(in nsIURI aURI,
|
||||
in nsIURI aProxyURI,
|
||||
in unsigned long aProxyFlags,
|
||||
in Node aLoadingNode,
|
||||
in nsIPrincipal aLoadingPrincipal,
|
||||
in nsIPrincipal aTriggeringPrincipal,
|
||||
in unsigned long aSecurityFlags,
|
||||
in unsigned long aContentPolicyType);
|
||||
nsIChannel newChannelFromURIWithProxyFlags(in nsIURI aURI,
|
||||
in nsIURI aProxyURI,
|
||||
in unsigned long aProxyFlags,
|
||||
in Node aLoadingNode,
|
||||
in nsIPrincipal aLoadingPrincipal,
|
||||
in nsIPrincipal aTriggeringPrincipal,
|
||||
in unsigned long aSecurityFlags,
|
||||
in unsigned long aContentPolicyType);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
|||
|
|
@ -836,18 +836,18 @@ nsIOService::NewFileURI(nsIFile *file, nsIURI **result) {
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOService::NewChannelFromURI2(nsIURI *aURI, nsINode *aLoadingNode,
|
||||
nsIPrincipal *aLoadingPrincipal,
|
||||
nsIPrincipal *aTriggeringPrincipal,
|
||||
uint32_t aSecurityFlags,
|
||||
uint32_t aContentPolicyType,
|
||||
nsIChannel **result) {
|
||||
return NewChannelFromURIWithProxyFlags2(aURI,
|
||||
nullptr, // aProxyURI
|
||||
0, // aProxyFlags
|
||||
aLoadingNode, aLoadingPrincipal,
|
||||
aTriggeringPrincipal, aSecurityFlags,
|
||||
aContentPolicyType, result);
|
||||
nsIOService::NewChannelFromURI(nsIURI *aURI, nsINode *aLoadingNode,
|
||||
nsIPrincipal *aLoadingPrincipal,
|
||||
nsIPrincipal *aTriggeringPrincipal,
|
||||
uint32_t aSecurityFlags,
|
||||
uint32_t aContentPolicyType,
|
||||
nsIChannel **result) {
|
||||
return NewChannelFromURIWithProxyFlags(aURI,
|
||||
nullptr, // aProxyURI
|
||||
0, // aProxyFlags
|
||||
aLoadingNode, aLoadingPrincipal,
|
||||
aTriggeringPrincipal, aSecurityFlags,
|
||||
aContentPolicyType, result);
|
||||
}
|
||||
nsresult nsIOService::NewChannelFromURIWithClientAndController(
|
||||
nsIURI *aURI, nsINode *aLoadingNode, nsIPrincipal *aLoadingPrincipal,
|
||||
|
|
@ -924,8 +924,8 @@ nsresult nsIOService::NewChannelFromURIWithProxyFlagsInternal(
|
|||
nsCOMPtr<nsIChannel> channel;
|
||||
nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
|
||||
if (pph) {
|
||||
rv = pph->NewProxiedChannel2(aURI, nullptr, aProxyFlags, aProxyURI,
|
||||
aLoadInfo, getter_AddRefs(channel));
|
||||
rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI,
|
||||
aLoadInfo, getter_AddRefs(channel));
|
||||
} else {
|
||||
rv = handler->NewChannel(aURI, aLoadInfo, getter_AddRefs(channel));
|
||||
}
|
||||
|
|
@ -976,7 +976,7 @@ nsresult nsIOService::NewChannelFromURIWithProxyFlagsInternal(
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOService::NewChannelFromURIWithProxyFlags2(
|
||||
nsIOService::NewChannelFromURIWithProxyFlags(
|
||||
nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags,
|
||||
nsINode *aLoadingNode, nsIPrincipal *aLoadingPrincipal,
|
||||
nsIPrincipal *aTriggeringPrincipal, uint32_t aSecurityFlags,
|
||||
|
|
@ -989,20 +989,20 @@ nsIOService::NewChannelFromURIWithProxyFlags2(
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOService::NewChannel2(const nsACString &aSpec, const char *aCharset,
|
||||
nsIURI *aBaseURI, nsINode *aLoadingNode,
|
||||
nsIPrincipal *aLoadingPrincipal,
|
||||
nsIPrincipal *aTriggeringPrincipal,
|
||||
uint32_t aSecurityFlags, uint32_t aContentPolicyType,
|
||||
nsIChannel **result) {
|
||||
nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset,
|
||||
nsIURI *aBaseURI, nsINode *aLoadingNode,
|
||||
nsIPrincipal *aLoadingPrincipal,
|
||||
nsIPrincipal *aTriggeringPrincipal,
|
||||
uint32_t aSecurityFlags, uint32_t aContentPolicyType,
|
||||
nsIChannel **result) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NewChannelFromURI2(uri, aLoadingNode, aLoadingPrincipal,
|
||||
aTriggeringPrincipal, aSecurityFlags,
|
||||
aContentPolicyType, result);
|
||||
return NewChannelFromURI(uri, aLoadingNode, aLoadingPrincipal,
|
||||
aTriggeringPrincipal, aSecurityFlags,
|
||||
aContentPolicyType, result);
|
||||
}
|
||||
|
||||
bool nsIOService::IsLinkUp() {
|
||||
|
|
@ -1763,13 +1763,13 @@ nsresult nsIOService::SpeculativeConnectInternal(
|
|||
// channel we create underneath - hence it's safe to use
|
||||
// the systemPrincipal as the loadingPrincipal for this channel.
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NewChannelFromURI2(aURI,
|
||||
nullptr, // aLoadingNode,
|
||||
loadingPrincipal,
|
||||
nullptr, // aTriggeringPrincipal,
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_SPECULATIVE,
|
||||
getter_AddRefs(channel));
|
||||
rv = NewChannelFromURI(aURI,
|
||||
nullptr, // aLoadingNode,
|
||||
loadingPrincipal,
|
||||
nullptr, // aTriggeringPrincipal,
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_SPECULATIVE,
|
||||
getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aAnonymous) {
|
||||
|
|
|
|||
|
|
@ -29,27 +29,8 @@ interface nsIProxiedProtocolHandler : nsIProtocolHandler
|
|||
* a ws:// uri.
|
||||
* @param aLoadInfo used to evaluate who initated the resource request.
|
||||
*/
|
||||
nsIChannel newProxiedChannel2(in nsIURI uri, in nsIProxyInfo proxyInfo,
|
||||
in unsigned long proxyResolveFlags,
|
||||
in nsIURI proxyURI,
|
||||
in nsILoadInfo aLoadInfo);
|
||||
|
||||
/** Create a new channel with the given proxyInfo
|
||||
*
|
||||
* @param uri the channel uri
|
||||
* @param proxyInfo any proxy information that has already been determined,
|
||||
* or null if channel should later determine the proxy on its own using
|
||||
* proxyResolveFlags/proxyURI
|
||||
* @param proxyResolveFlags used if the proxy is later determined
|
||||
* from nsIProtocolProxyService::asyncResolve
|
||||
* @param proxyURI used if the proxy is later determined from
|
||||
* nsIProtocolProxyService::asyncResolve with this as the proxyURI name.
|
||||
* Generally this is the same as uri (or null which has the same
|
||||
* effect), except in the case of websockets which wants to bootstrap
|
||||
* to an http:// channel but make its proxy determination based on
|
||||
* a ws:// uri.
|
||||
*/
|
||||
nsIChannel newProxiedChannel(in nsIURI uri, in nsIProxyInfo proxyInfo,
|
||||
in unsigned long proxyResolveFlags,
|
||||
in nsIURI proxyURI);
|
||||
in nsIURI proxyURI,
|
||||
in nsILoadInfo aLoadInfo);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1889,7 +1889,7 @@ static nsresult CreateHTTPProxiedChannel(nsIChannel *channel, nsIProxyInfo *pi,
|
|||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
channel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
|
||||
return pph->NewProxiedChannel2(uri, pi, 0, nullptr, loadInfo, newChannel);
|
||||
return pph->NewProxiedChannel(uri, pi, 0, nullptr, loadInfo, newChannel);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
|||
|
|
@ -173,15 +173,15 @@ nsFtpProtocolHandler::NewURI(const nsACString &aSpec, const char *aCharset,
|
|||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewChannel(nsIURI *url, nsILoadInfo *aLoadInfo,
|
||||
nsIChannel **result) {
|
||||
return NewProxiedChannel2(url, nullptr, 0, nullptr, aLoadInfo, result);
|
||||
return NewProxiedChannel(url, nullptr, 0, nullptr, aLoadInfo, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewProxiedChannel2(nsIURI *uri, nsIProxyInfo *proxyInfo,
|
||||
uint32_t proxyResolveFlags,
|
||||
nsIURI *proxyURI,
|
||||
nsILoadInfo *aLoadInfo,
|
||||
nsIChannel **result) {
|
||||
nsFtpProtocolHandler::NewProxiedChannel(nsIURI *uri, nsIProxyInfo *proxyInfo,
|
||||
uint32_t proxyResolveFlags,
|
||||
nsIURI *proxyURI,
|
||||
nsILoadInfo *aLoadInfo,
|
||||
nsIChannel **result) {
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
RefPtr<nsBaseChannel> channel;
|
||||
if (IsNeckoChild())
|
||||
|
|
@ -204,14 +204,6 @@ nsFtpProtocolHandler::NewProxiedChannel2(nsIURI *uri, nsIProxyInfo *proxyInfo,
|
|||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewProxiedChannel(nsIURI *uri, nsIProxyInfo *proxyInfo,
|
||||
uint32_t proxyResolveFlags,
|
||||
nsIURI *proxyURI, nsIChannel **result) {
|
||||
return NewProxiedChannel2(uri, proxyInfo, proxyResolveFlags, proxyURI,
|
||||
nullptr /*loadinfo*/, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::AllowPort(int32_t port, const char *scheme,
|
||||
bool *_retval) {
|
||||
|
|
|
|||
|
|
@ -3119,8 +3119,8 @@ nsresult nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo *pi) {
|
|||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
rv = gHttpHandler->NewProxiedChannel2(mURI, pi, mProxyResolveFlags, mProxyURI,
|
||||
mLoadInfo, getter_AddRefs(newChannel));
|
||||
rv = gHttpHandler->NewProxiedChannel(mURI, pi, mProxyResolveFlags, mProxyURI,
|
||||
mLoadInfo, getter_AddRefs(newChannel));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
|
||||
|
|
|
|||
|
|
@ -2047,7 +2047,7 @@ nsHttpHandler::NewChannel(nsIURI *uri, nsILoadInfo *aLoadInfo,
|
|||
}
|
||||
}
|
||||
|
||||
return NewProxiedChannel2(uri, nullptr, 0, nullptr, aLoadInfo, result);
|
||||
return NewProxiedChannel(uri, nullptr, 0, nullptr, aLoadInfo, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
@ -2062,9 +2062,9 @@ nsHttpHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) {
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpHandler::NewProxiedChannel2(nsIURI *uri, nsIProxyInfo *givenProxyInfo,
|
||||
uint32_t proxyResolveFlags, nsIURI *proxyURI,
|
||||
nsILoadInfo *aLoadInfo, nsIChannel **result) {
|
||||
nsHttpHandler::NewProxiedChannel(nsIURI *uri, nsIProxyInfo *givenProxyInfo,
|
||||
uint32_t proxyResolveFlags, nsIURI *proxyURI,
|
||||
nsILoadInfo *aLoadInfo, nsIChannel **result) {
|
||||
RefPtr<HttpBaseChannel> httpChannel;
|
||||
|
||||
LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n", givenProxyInfo));
|
||||
|
|
@ -2127,14 +2127,6 @@ nsHttpHandler::NewProxiedChannel2(nsIURI *uri, nsIProxyInfo *givenProxyInfo,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpHandler::NewProxiedChannel(nsIURI *uri, nsIProxyInfo *givenProxyInfo,
|
||||
uint32_t proxyResolveFlags, nsIURI *proxyURI,
|
||||
nsIChannel **result) {
|
||||
return NewProxiedChannel2(uri, givenProxyInfo, proxyResolveFlags, proxyURI,
|
||||
nullptr, result);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpHandler::nsIHttpProtocolHandler
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ nsresult nsViewSourceChannel::Init(nsIURI *uri) {
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// This function is called from within nsViewSourceHandler::NewChannel2
|
||||
// This function is called from within nsViewSourceHandler::NewChannel
|
||||
// and sets the right loadInfo right after returning from this function.
|
||||
// Until then we follow the principal of least privilege and use
|
||||
// nullPrincipal as the loadingPrincipal and the least permissive
|
||||
|
|
@ -68,7 +68,7 @@ nsresult nsViewSourceChannel::Init(nsIURI *uri) {
|
|||
nsCOMPtr<nsIPrincipal> nullPrincipal =
|
||||
mozilla::NullPrincipal::CreateWithoutOriginAttributes();
|
||||
|
||||
rv = pService->NewChannel2(
|
||||
rv = pService->NewChannel(
|
||||
path,
|
||||
nullptr, // aOriginCharset
|
||||
nullptr, // aCharSet
|
||||
|
|
|
|||
|
|
@ -3387,7 +3387,7 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI, const nsACString &aOrigin,
|
|||
|
||||
// Ideally we'd call newChannelFromURIWithLoadInfo here, but that doesn't
|
||||
// allow setting proxy uri/flags
|
||||
rv = ioService->NewChannelFromURIWithProxyFlags2(
|
||||
rv = ioService->NewChannelFromURIWithProxyFlags(
|
||||
localURI, mURI,
|
||||
nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
|
||||
nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
|
||||
|
|
|
|||
|
|
@ -513,14 +513,14 @@ function test_newChannel_with_string()
|
|||
// Check that we get the same URI back from channel the IO service creates and
|
||||
// the channel the utility method creates.
|
||||
let ios = Services.io
|
||||
let iosChannel = ios.newChannel2(TEST_SPEC,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
let iosChannel = ios.newChannel(TEST_SPEC,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
let NetUtilChannel = NetUtil.newChannel({
|
||||
uri: TEST_SPEC,
|
||||
loadUsingSystemPrincipal: true
|
||||
|
|
@ -537,12 +537,12 @@ function test_newChannel_with_nsIURI()
|
|||
// Check that we get the same URI back from channel the IO service creates and
|
||||
// the channel the utility method creates.
|
||||
let uri = NetUtil.newURI(TEST_SPEC);
|
||||
let iosChannel = Services.io.newChannelFromURI2(uri,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
let iosChannel = Services.io.newChannelFromURI(uri,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
let NetUtilChannel = NetUtil.newChannel({
|
||||
uri: uri,
|
||||
loadUsingSystemPrincipal: true
|
||||
|
|
@ -556,12 +556,12 @@ function test_newChannel_with_options()
|
|||
{
|
||||
let uri = "data:text/plain,";
|
||||
|
||||
let iosChannel = Services.io.newChannelFromURI2(NetUtil.newURI(uri),
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
let iosChannel = Services.io.newChannelFromURI(NetUtil.newURI(uri),
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
function checkEqualToIOSChannel(channel) {
|
||||
Assert.ok(iosChannel.URI.equals(channel.URI));
|
||||
|
|
|
|||
|
|
@ -108,14 +108,14 @@ add_task(async function testDirectProxy() {
|
|||
let ioService = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
let chan = ioService.
|
||||
newChannelFromURIWithProxyFlags2(uri,
|
||||
proxyURI,
|
||||
0,
|
||||
null,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
newChannelFromURIWithProxyFlags(uri,
|
||||
proxyURI,
|
||||
0,
|
||||
null,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
let pi = await TestProxyType(chan, 0);
|
||||
equal(pi, null, "Expected proxy host to be null");
|
||||
|
|
@ -137,14 +137,14 @@ add_task(async function testWebSocketProxy() {
|
|||
let ioService = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
let chan = ioService.
|
||||
newChannelFromURIWithProxyFlags2(uri,
|
||||
proxyURI,
|
||||
proxyFlags,
|
||||
null,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
newChannelFromURIWithProxyFlags(uri,
|
||||
proxyURI,
|
||||
proxyFlags,
|
||||
null,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
let pi = await TestProxyType(chan, proxyFlags);
|
||||
equal(pi.host, "localhost", "Expected proxy host to be localhost");
|
||||
|
|
|
|||
|
|
@ -239,12 +239,12 @@ OCSPRequest::Run() {
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = ios->NewChannel2(mAIALocation, nullptr, nullptr,
|
||||
nullptr, // aLoadingNode
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nullptr, // aTriggeringPrincipal
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER, getter_AddRefs(channel));
|
||||
rv = ios->NewChannel(mAIALocation, nullptr, nullptr,
|
||||
nullptr, // aLoadingNode
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nullptr, // aTriggeringPrincipal
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER, getter_AddRefs(channel));
|
||||
if (NS_FAILED(rv)) {
|
||||
return NotifyDone(rv, lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
b7713856ebf2
|
||||
1f04eea8834a
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
2 functions with some indirect sub-type change:
|
||||
|
||||
[C]'function SECStatus SSL_GetCipherSuiteInfo(PRUint16, SSLCipherSuiteInfo*, PRUintn)' at sslinfo.c:326:1 has some indirect sub-type changes:
|
||||
parameter 2 of type 'SSLCipherSuiteInfo*' has sub-type changes:
|
||||
in pointed to type 'typedef SSLCipherSuiteInfo' at sslt.h:433:1:
|
||||
underlying type 'struct SSLCipherSuiteInfoStr' at sslt.h:366:1 changed:
|
||||
type size changed from 768 to 832 (in bits)
|
||||
1 data member insertion:
|
||||
'SSLHashType SSLCipherSuiteInfoStr::kdfHash', at offset 768 (in bits) at sslt.h:429:1
|
||||
|
||||
[C]'function SECStatus SSL_GetPreliminaryChannelInfo(PRFileDesc*, SSLPreliminaryChannelInfo*, PRUintn)' at sslinfo.c:111:1 has some indirect sub-type changes:
|
||||
parameter 2 of type 'SSLPreliminaryChannelInfo*' has sub-type changes:
|
||||
in pointed to type 'typedef SSLPreliminaryChannelInfo' at sslt.h:379:1:
|
||||
underlying type 'struct SSLPreliminaryChannelInfoStr' at sslt.h:333:1 changed:
|
||||
type size changed from 160 to 192 (in bits)
|
||||
1 data member insertion:
|
||||
'PRUint16 SSLPreliminaryChannelInfoStr::zeroRttCipherSuite', at offset 160 (in bits) at sslt.h:375:1
|
||||
|
||||
|
||||
|
|
@ -56,6 +56,7 @@ NSS_SRCDIRS = \
|
|||
p7sign \
|
||||
p7verify \
|
||||
pk12util \
|
||||
pk11importtest \
|
||||
pk11ectest \
|
||||
pk11gcmtest \
|
||||
pk11mode \
|
||||
|
|
|
|||
43
security/nss/cmd/pk11importtest/Makefile
Normal file
43
security/nss/cmd/pk11importtest/Makefile
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#! gmake
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#######################################################################
|
||||
# (1) Include initial platform-independent assignments (MANDATORY). #
|
||||
#######################################################################
|
||||
|
||||
include manifest.mn
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "global" configuration information. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(CORE_DEPTH)/coreconf/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "component" configuration information. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
#######################################################################
|
||||
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
include ../platlibs.mk
|
||||
|
||||
#######################################################################
|
||||
# (5) Execute "global" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(CORE_DEPTH)/coreconf/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (6) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
include ../platrules.mk
|
||||
15
security/nss/cmd/pk11importtest/manifest.mn
Normal file
15
security/nss/cmd/pk11importtest/manifest.mn
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
CORE_DEPTH = ../..
|
||||
|
||||
MODULE = nss
|
||||
|
||||
CSRCS = pk11importtest.c \
|
||||
$(NULL)
|
||||
|
||||
REQUIRES = seccmd
|
||||
|
||||
PROGRAM = pk11importtest
|
||||
|
||||
406
security/nss/cmd/pk11importtest/pk11importtest.c
Normal file
406
security/nss/cmd/pk11importtest/pk11importtest.c
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "secutil.h"
|
||||
#include "secmod.h"
|
||||
#include "cert.h"
|
||||
#include "secoid.h"
|
||||
#include "nss.h"
|
||||
#include "pk11pub.h"
|
||||
#include "pk11pqg.h"
|
||||
|
||||
/* NSPR 2.0 header files */
|
||||
#include "prinit.h"
|
||||
#include "prprf.h"
|
||||
#include "prsystem.h"
|
||||
#include "prmem.h"
|
||||
/* Portable layer header files */
|
||||
#include "plstr.h"
|
||||
|
||||
SECOidData *
|
||||
getCurveFromString(char *curve_name)
|
||||
{
|
||||
SECOidTag tag = SEC_OID_SECG_EC_SECP256R1;
|
||||
|
||||
if (PORT_Strcasecmp(curve_name, "NISTP256") == 0) {
|
||||
} else if (PORT_Strcasecmp(curve_name, "NISTP384") == 0) {
|
||||
tag = SEC_OID_SECG_EC_SECP384R1;
|
||||
} else if (PORT_Strcasecmp(curve_name, "NISTP521") == 0) {
|
||||
tag = SEC_OID_SECG_EC_SECP521R1;
|
||||
} else if (PORT_Strcasecmp(curve_name, "Curve25519") == 0) {
|
||||
tag = SEC_OID_CURVE25519;
|
||||
}
|
||||
return SECOID_FindOIDByTag(tag);
|
||||
}
|
||||
|
||||
void
|
||||
dumpItem(const char *label, const SECItem *item)
|
||||
{
|
||||
int i;
|
||||
printf("%s = [%d bytes] {", label, item->len);
|
||||
for (i = 0; i < item->len; i++) {
|
||||
if ((i & 0xf) == 0)
|
||||
printf("\n ");
|
||||
else
|
||||
printf(", ");
|
||||
printf("%02x", item->data[i]);
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
SECStatus
|
||||
handleEncryptedPrivateImportTest(char *progName, PK11SlotInfo *slot,
|
||||
char *testname, CK_MECHANISM_TYPE genMech, void *params, void *pwArgs)
|
||||
{
|
||||
SECStatus rv = SECSuccess;
|
||||
SECItem privID = { 0 };
|
||||
SECItem pubID = { 0 };
|
||||
SECItem pubValue = { 0 };
|
||||
SECItem pbePwItem = { 0 };
|
||||
SECItem nickname = { 0 };
|
||||
SECItem token = { 0 };
|
||||
SECKEYPublicKey *pubKey = NULL;
|
||||
SECKEYPrivateKey *privKey = NULL;
|
||||
PK11GenericObject *objs = NULL;
|
||||
PK11GenericObject *obj = NULL;
|
||||
SECKEYEncryptedPrivateKeyInfo *epki = NULL;
|
||||
PRBool keyFound = 0;
|
||||
KeyType keyType;
|
||||
|
||||
fprintf(stderr, "Testing %s PrivateKeyImport ***********************\n",
|
||||
testname);
|
||||
|
||||
/* generate a temp key */
|
||||
privKey = PK11_GenerateKeyPair(slot, genMech, params, &pubKey,
|
||||
PR_FALSE, PR_TRUE, pwArgs);
|
||||
if (privKey == NULL) {
|
||||
SECU_PrintError(progName, "PK11_GenerateKeyPair Failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* wrap the temp key */
|
||||
pbePwItem.data = (unsigned char *)"pw";
|
||||
pbePwItem.len = 2;
|
||||
epki = PK11_ExportEncryptedPrivKeyInfo(slot, SEC_OID_AES_256_CBC,
|
||||
&pbePwItem, privKey, 1, NULL);
|
||||
if (epki == NULL) {
|
||||
SECU_PrintError(progName, "PK11_ExportEncryptedPrivKeyInfo Failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Save the public value, which we will need on import */
|
||||
keyType = pubKey->keyType;
|
||||
switch (keyType) {
|
||||
case rsaKey:
|
||||
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.rsa.modulus);
|
||||
break;
|
||||
case dhKey:
|
||||
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.dh.publicValue);
|
||||
break;
|
||||
case dsaKey:
|
||||
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.dsa.publicValue);
|
||||
break;
|
||||
case ecKey:
|
||||
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.ec.publicValue);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown keytype = %d\n", keyType);
|
||||
goto cleanup;
|
||||
}
|
||||
if (pubValue.data == NULL) {
|
||||
SECU_PrintError(progName, "Unable to allocate memory");
|
||||
goto cleanup;
|
||||
}
|
||||
dumpItem("pubValue", &pubValue);
|
||||
|
||||
/* when Asymetric keys represent session keys, those session keys are
|
||||
* destroyed when we destroy the Asymetric key representations */
|
||||
SECKEY_DestroyPublicKey(pubKey);
|
||||
pubKey = NULL;
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
privKey = NULL;
|
||||
|
||||
/* unwrap the temp key as a perm */
|
||||
nickname.data = (unsigned char *)"testKey";
|
||||
nickname.len = sizeof("testKey");
|
||||
rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
|
||||
slot, epki, &pbePwItem, &nickname, &pubValue,
|
||||
PR_TRUE, PR_TRUE, keyType, 0, &privKey, NULL);
|
||||
if (rv != SECSuccess) {
|
||||
SECU_PrintError(progName, "PK11_ImportEncryptedPrivateKeyInfo Failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* verify the public key exists */
|
||||
rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_ID, &privID);
|
||||
if (rv != SECSuccess) {
|
||||
SECU_PrintError(progName,
|
||||
"Couldn't read CKA_ID from pub key, checking next key");
|
||||
goto cleanup;
|
||||
}
|
||||
dumpItem("privKey CKA_ID", &privID);
|
||||
objs = PK11_FindGenericObjects(slot, CKO_PUBLIC_KEY);
|
||||
for (obj = objs; obj; obj = PK11_GetNextGenericObject(obj)) {
|
||||
rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pubID);
|
||||
if (rv != SECSuccess) {
|
||||
SECU_PrintError(progName,
|
||||
"Couldn't read CKA_ID from object, checking next key");
|
||||
continue;
|
||||
}
|
||||
dumpItem("pubKey CKA_ID", &pubID);
|
||||
if (!SECITEM_ItemsAreEqual(&privID, &pubID)) {
|
||||
fprintf(stderr,
|
||||
"CKA_ID does not match priv key, checking next key\n");
|
||||
SECITEM_FreeItem(&pubID, PR_FALSE);
|
||||
continue;
|
||||
}
|
||||
SECITEM_FreeItem(&pubID, PR_FALSE);
|
||||
rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token);
|
||||
if (rv == SECSuccess) {
|
||||
if (token.len == 1) {
|
||||
keyFound = token.data[0];
|
||||
}
|
||||
SECITEM_FreeItem(&token, PR_FALSE);
|
||||
}
|
||||
if (keyFound) {
|
||||
printf("matching public key found\n");
|
||||
break;
|
||||
}
|
||||
printf("Matching key was not a token key, checking next key\n");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (objs) {
|
||||
PK11_DestroyGenericObjects(objs);
|
||||
}
|
||||
SECITEM_FreeItem(&pubValue, PR_FALSE);
|
||||
SECITEM_FreeItem(&privID, PR_FALSE);
|
||||
PORT_FreeArena(epki->arena, PR_TRUE);
|
||||
SECKEY_DestroyPublicKey(pubKey);
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
fprintf(stderr, "%s PrivateKeyImport %s ***********************\n",
|
||||
testname, keyFound ? "PASSED" : "FAILED");
|
||||
return keyFound ? SECSuccess : SECFailure;
|
||||
}
|
||||
|
||||
static const char *const usageInfo[] = {
|
||||
"pk11import - test PK11_PrivateKeyImport()"
|
||||
"Options:",
|
||||
" -d certdir directory containing cert database",
|
||||
" -k keysize size of the rsa, dh, and dsa key to test (default 1024)",
|
||||
" -C ecc_curve ecc curve (default )",
|
||||
" -f pwFile file to fetch the password from",
|
||||
" -p pwString password",
|
||||
" -r skip rsa test",
|
||||
" -D skip dsa test",
|
||||
" -h skip dh test",
|
||||
" -e skip ec test",
|
||||
};
|
||||
static int nUsageInfo = sizeof(usageInfo) / sizeof(char *);
|
||||
|
||||
static void
|
||||
Usage(char *progName, FILE *outFile)
|
||||
{
|
||||
int i;
|
||||
fprintf(outFile, "Usage: %s [ commands ] options\n", progName);
|
||||
for (i = 0; i < nUsageInfo; i++)
|
||||
fprintf(outFile, "%s\n", usageInfo[i]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
enum {
|
||||
opt_CertDir,
|
||||
opt_KeySize,
|
||||
opt_ECCurve,
|
||||
opt_PWFile,
|
||||
opt_PWString,
|
||||
opt_NoRSA,
|
||||
opt_NoDSA,
|
||||
opt_NoEC,
|
||||
opt_NoDH
|
||||
};
|
||||
|
||||
static secuCommandFlag options[] =
|
||||
{
|
||||
{ /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_KeySize */ 'k', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_ECCurve */ 'C', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_PWFile */ 'f', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_PWString */ 'p', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_NORSA */ 'r', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_NoDSA */ 'D', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_NoDH */ 'h', PR_TRUE, 0, PR_FALSE },
|
||||
{ /* opt_NoEC */ 'e', PR_TRUE, 0, PR_FALSE },
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *progName;
|
||||
SECStatus rv;
|
||||
secuCommand args;
|
||||
PK11SlotInfo *slot = NULL;
|
||||
PRBool failed = PR_FALSE;
|
||||
secuPWData pwArgs = { PW_NONE, 0 };
|
||||
PRBool doRSA = PR_TRUE;
|
||||
PRBool doDSA = PR_TRUE;
|
||||
PRBool doDH = PR_FALSE; /* NSS currently can't export wrapped DH keys */
|
||||
PRBool doEC = PR_TRUE;
|
||||
PQGParams *pqgParams = NULL;
|
||||
int keySize;
|
||||
|
||||
args.numCommands = 0;
|
||||
args.numOptions = sizeof(options) / sizeof(secuCommandFlag);
|
||||
args.commands = NULL;
|
||||
args.options = options;
|
||||
|
||||
#ifdef XP_PC
|
||||
progName = strrchr(argv[0], '\\');
|
||||
#else
|
||||
progName = strrchr(argv[0], '/');
|
||||
#endif
|
||||
progName = progName ? progName + 1 : argv[0];
|
||||
|
||||
rv = SECU_ParseCommandLine(argc, argv, progName, &args);
|
||||
if (SECSuccess != rv) {
|
||||
Usage(progName, stderr);
|
||||
}
|
||||
|
||||
/* Set the certdb directory (default is ~/.netscape) */
|
||||
rv = NSS_InitReadWrite(SECU_ConfigDirectory(args.options[opt_CertDir].arg));
|
||||
if (rv != SECSuccess) {
|
||||
SECU_PrintPRandOSError(progName);
|
||||
return 255;
|
||||
}
|
||||
PK11_SetPasswordFunc(SECU_GetModulePassword);
|
||||
|
||||
/* below here, goto cleanup */
|
||||
SECU_RegisterDynamicOids();
|
||||
|
||||
/* handle the arguments */
|
||||
if (args.options[opt_PWFile].arg) {
|
||||
pwArgs.source = PW_FROMFILE;
|
||||
pwArgs.data = args.options[opt_PWFile].arg;
|
||||
}
|
||||
if (args.options[opt_PWString].arg) {
|
||||
pwArgs.source = PW_PLAINTEXT;
|
||||
pwArgs.data = args.options[opt_PWString].arg;
|
||||
}
|
||||
if (args.options[opt_NoRSA].activated) {
|
||||
doRSA = PR_FALSE;
|
||||
}
|
||||
if (args.options[opt_NoDSA].activated) {
|
||||
doDSA = PR_FALSE;
|
||||
}
|
||||
if (args.options[opt_NoDH].activated) {
|
||||
doDH = PR_FALSE;
|
||||
}
|
||||
if (args.options[opt_NoEC].activated) {
|
||||
doEC = PR_FALSE;
|
||||
}
|
||||
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
if (slot == NULL) {
|
||||
SECU_PrintError(progName, "Couldn't find the internal key slot\n");
|
||||
return 255;
|
||||
}
|
||||
rv = PK11_Authenticate(slot, PR_TRUE, &pwArgs);
|
||||
if (rv != SECSuccess) {
|
||||
SECU_PrintError(progName, "Failed to log into slot");
|
||||
PK11_FreeSlot(slot);
|
||||
return 255;
|
||||
}
|
||||
|
||||
keySize = 1024;
|
||||
if (args.options[opt_KeySize].activated &&
|
||||
args.options[opt_KeySize].arg) {
|
||||
keySize = atoi(args.options[opt_KeySize].arg);
|
||||
}
|
||||
|
||||
if (doDSA || doDH) {
|
||||
PQGVerify *pqgVfy;
|
||||
rv = PK11_PQG_ParamGenV2(keySize, 0, keySize / 16, &pqgParams, &pqgVfy);
|
||||
if (rv == SECSuccess) {
|
||||
PK11_PQG_DestroyVerify(pqgVfy);
|
||||
} else {
|
||||
SECU_PrintError(progName,
|
||||
"PK11_PQG_ParamGenV2 failed, can't test DH or DSA");
|
||||
doDSA = doDH = PR_FALSE;
|
||||
failed = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (doRSA) {
|
||||
PK11RSAGenParams rsaParams;
|
||||
rsaParams.keySizeInBits = keySize;
|
||||
rsaParams.pe = 0x010001;
|
||||
rv = handleEncryptedPrivateImportTest(progName, slot, "RSA",
|
||||
CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams, &pwArgs);
|
||||
if (rv != SECSuccess) {
|
||||
fprintf(stderr, "RSA Import Failed!\n");
|
||||
failed = PR_TRUE;
|
||||
}
|
||||
}
|
||||
if (doDSA) {
|
||||
rv = handleEncryptedPrivateImportTest(progName, slot, "DSA",
|
||||
CKM_DSA_KEY_PAIR_GEN, pqgParams, &pwArgs);
|
||||
if (rv != SECSuccess) {
|
||||
fprintf(stderr, "DSA Import Failed!\n");
|
||||
failed = PR_TRUE;
|
||||
}
|
||||
}
|
||||
if (doDH) {
|
||||
SECKEYDHParams dhParams;
|
||||
dhParams.prime = pqgParams->prime;
|
||||
dhParams.base = pqgParams->base;
|
||||
rv = handleEncryptedPrivateImportTest(progName, slot, "DH",
|
||||
CKM_DH_PKCS_KEY_PAIR_GEN, &dhParams, &pwArgs);
|
||||
if (rv != SECSuccess) {
|
||||
fprintf(stderr, "DH Import Failed!\n");
|
||||
failed = PR_TRUE;
|
||||
}
|
||||
}
|
||||
if (doEC) {
|
||||
SECKEYECParams ecParams;
|
||||
SECOidData *curve = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
|
||||
if (args.options[opt_ECCurve].activated &&
|
||||
args.options[opt_ECCurve].arg) {
|
||||
curve = getCurveFromString(args.options[opt_ECCurve].arg);
|
||||
}
|
||||
ecParams.data = PORT_Alloc(curve->oid.len + 2);
|
||||
if (ecParams.data == NULL) {
|
||||
rv = SECFailure;
|
||||
goto ec_failed;
|
||||
}
|
||||
ecParams.data[0] = SEC_ASN1_OBJECT_ID;
|
||||
ecParams.data[1] = (unsigned char)curve->oid.len;
|
||||
PORT_Memcpy(&ecParams.data[2], curve->oid.data, curve->oid.len);
|
||||
ecParams.len = curve->oid.len + 2;
|
||||
rv = handleEncryptedPrivateImportTest(progName, slot, "ECC",
|
||||
CKM_EC_KEY_PAIR_GEN, &ecParams, &pwArgs);
|
||||
PORT_Free(ecParams.data);
|
||||
ec_failed:
|
||||
if (rv != SECSuccess) {
|
||||
fprintf(stderr, "ECC Import Failed!\n");
|
||||
failed = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (pqgParams) {
|
||||
PK11_PQG_DestroyParams(pqgParams);
|
||||
}
|
||||
|
||||
if (slot) {
|
||||
PK11_FreeSlot(slot);
|
||||
}
|
||||
|
||||
rv = NSS_Shutdown();
|
||||
if (rv != SECSuccess) {
|
||||
fprintf(stderr, "Shutdown failed\n");
|
||||
SECU_PrintPRandOSError(progName);
|
||||
return 255;
|
||||
}
|
||||
|
||||
return failed ? 1 : 0;
|
||||
}
|
||||
25
security/nss/cmd/pk11importtest/pk11importtest.gyp
Normal file
25
security/nss/cmd/pk11importtest/pk11importtest.gyp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
{
|
||||
'includes': [
|
||||
'../../coreconf/config.gypi',
|
||||
'../../cmd/platlibs.gypi'
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'pk11importtest',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'pk11importtest.c'
|
||||
],
|
||||
'dependencies': [
|
||||
'<(DEPTH)/exports.gyp:dbm_exports',
|
||||
'<(DEPTH)/exports.gyp:nss_exports'
|
||||
]
|
||||
}
|
||||
],
|
||||
'variables': {
|
||||
'module': 'nss'
|
||||
}
|
||||
}
|
||||
|
|
@ -10,4 +10,3 @@
|
|||
*/
|
||||
|
||||
#error "Do not include this header file."
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "cert.h"
|
||||
#include "keyhi.h"
|
||||
#include "p12.h"
|
||||
#include "pk11pqg.h"
|
||||
#include "pk11pub.h"
|
||||
#include "pkcs11uri.h"
|
||||
|
||||
|
|
@ -41,6 +42,7 @@ struct ScopedDelete {
|
|||
void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); }
|
||||
void operator()(PK11Context* context) { PK11_DestroyContext(context, true); }
|
||||
void operator()(PK11GenericObject* obj) { PK11_DestroyGenericObject(obj); }
|
||||
void operator()(PQGParams* pqg) { PK11_PQG_DestroyParams(pqg); }
|
||||
void operator()(SEC_PKCS12DecoderContext* dcx) {
|
||||
SEC_PKCS12DecoderFinish(dcx);
|
||||
}
|
||||
|
|
@ -66,6 +68,7 @@ SCOPED(CERTName);
|
|||
SCOPED(CERTSubjectPublicKeyInfo);
|
||||
SCOPED(PK11SlotInfo);
|
||||
SCOPED(PK11SymKey);
|
||||
SCOPED(PQGParams);
|
||||
SCOPED(PRFileDesc);
|
||||
SCOPED(SECAlgorithmID);
|
||||
SCOPED(SECKEYEncryptedPrivateKeyInfo);
|
||||
|
|
@ -82,4 +85,9 @@ SCOPED(CERTDistNames);
|
|||
|
||||
#undef SCOPED
|
||||
|
||||
struct StackSECItem : public SECItem {
|
||||
StackSECItem() : SECItem({siBuffer, nullptr, 0}) {}
|
||||
~StackSECItem() { SECITEM_FreeItem(this, PR_FALSE); }
|
||||
};
|
||||
|
||||
#endif // nss_scoped_ptrs_h__
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ struct ScopedMaybeDelete {
|
|||
SCOPED(SECAlgorithmID);
|
||||
SCOPED(SECItem);
|
||||
SCOPED(PK11URI);
|
||||
SCOPED(PLArenaPool);
|
||||
|
||||
#undef SCOPED
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,32 @@ inline std::ostream& operator<<(std::ostream& os, SSLProtocolVariant v) {
|
|||
return os << ((v == ssl_variant_stream) ? "TLS" : "DTLS");
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, SSLContentType v) {
|
||||
switch (v) {
|
||||
case ssl_ct_change_cipher_spec:
|
||||
return os << "CCS";
|
||||
case ssl_ct_alert:
|
||||
return os << "alert";
|
||||
case ssl_ct_handshake:
|
||||
return os << "handshake";
|
||||
case ssl_ct_application_data:
|
||||
return os << "application data";
|
||||
case ssl_ct_ack:
|
||||
return os << "ack";
|
||||
}
|
||||
return os << "UNKNOWN content type " << static_cast<int>(v);
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, SSLSecretDirection v) {
|
||||
switch (v) {
|
||||
case ssl_secret_read:
|
||||
return os << "read";
|
||||
case ssl_secret_write:
|
||||
return os << "write";
|
||||
}
|
||||
return os << "UNKNOWN secret direction " << static_cast<int>(v);
|
||||
}
|
||||
|
||||
inline bool IsDtls(uint16_t version) { return (version & 0x8000) == 0x8000; }
|
||||
|
||||
inline uint16_t NormalizeTlsVersion(uint16_t version) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,23 @@
|
|||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
|
||||
const char *workdir = "";
|
||||
uint32_t flags = NSS_INIT_READONLY;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-d")) {
|
||||
if (i + 1 >= argc) {
|
||||
PR_fprintf(PR_STDERR, "Usage: %s [-d <dir> [-w]]\n", argv[0]);
|
||||
exit(2);
|
||||
}
|
||||
workdir = argv[i + 1];
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "-w")) {
|
||||
flags &= ~NSS_INIT_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
if (NSS_Initialize(workdir, "", "", SECMOD_DB, flags) != SECSuccess) {
|
||||
return 1;
|
||||
}
|
||||
if (NSS_SetDomesticPolicy() != SECSuccess) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ CPPSRCS = \
|
|||
pk11_ecdsa_unittest.cc \
|
||||
pk11_encrypt_derive_unittest.cc \
|
||||
pk11_export_unittest.cc \
|
||||
pk11_import_unittest.cc \
|
||||
pk11_pbkdf2_unittest.cc \
|
||||
pk11_prf_unittest.cc \
|
||||
pk11_prng_unittest.cc \
|
||||
|
|
@ -33,4 +34,3 @@ EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \
|
|||
$(DIST)/lib/$(LIB_PREFIX)cpputil.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
'pk11_curve25519_unittest.cc',
|
||||
'pk11_ecdsa_unittest.cc',
|
||||
'pk11_encrypt_derive_unittest.cc',
|
||||
'pk11_import_unittest.cc',
|
||||
'pk11_pbkdf2_unittest.cc',
|
||||
'pk11_prf_unittest.cc',
|
||||
'pk11_prng_unittest.cc',
|
||||
|
|
|
|||
|
|
@ -297,38 +297,6 @@ SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
|
|||
return groupDef->keaType;
|
||||
}
|
||||
|
||||
SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
|
||||
sslCipherSpecChangedFunc func,
|
||||
void *arg) {
|
||||
sslSocket *ss;
|
||||
|
||||
ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ss->ssl3.changedCipherSpecFunc = func;
|
||||
ss->ssl3.changedCipherSpecArg = arg;
|
||||
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec) {
|
||||
return spec->keyMaterial.key;
|
||||
}
|
||||
|
||||
SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec) {
|
||||
return spec->cipherDef->calg;
|
||||
}
|
||||
|
||||
const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec) {
|
||||
return spec->keyMaterial.iv;
|
||||
}
|
||||
|
||||
PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec) {
|
||||
return spec->epoch;
|
||||
}
|
||||
|
||||
void SSLInt_SetTicketLifetime(uint32_t lifetime) {
|
||||
ssl_ticket_lifetime = lifetime;
|
||||
}
|
||||
|
|
@ -360,16 +328,14 @@ void SSLInt_RolloverAntiReplay(void) {
|
|||
tls13_AntiReplayRollover(ssl_TimeUsec());
|
||||
}
|
||||
|
||||
SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
|
||||
PRUint16 *writeEpoch) {
|
||||
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending) {
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
if (!ss || !readEpoch || !writeEpoch) {
|
||||
if (!ss) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_GetSpecReadLock(ss);
|
||||
*readEpoch = ss->ssl3.crSpec->epoch;
|
||||
*writeEpoch = ss->ssl3.cwSpec->epoch;
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
*pending = ss->ssl3.hs.msg_body.len > 0;
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
return SECSuccess;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,16 +39,7 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
|
|||
SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
|
||||
SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
|
||||
SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
|
||||
SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
|
||||
PRUint16 *writeEpoch);
|
||||
|
||||
SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
|
||||
sslCipherSpecChangedFunc func,
|
||||
void *arg);
|
||||
PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec);
|
||||
PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec);
|
||||
SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec);
|
||||
const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec);
|
||||
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending);
|
||||
void SSLInt_SetTicketLifetime(uint32_t lifetime);
|
||||
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
|
||||
void SSLInt_RolloverAntiReplay(void);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ CPPSRCS = \
|
|||
ssl_loopback_unittest.cc \
|
||||
ssl_misc_unittest.cc \
|
||||
ssl_record_unittest.cc \
|
||||
ssl_recordsep_unittest.cc \
|
||||
ssl_recordsize_unittest.cc \
|
||||
ssl_resumption_unittest.cc \
|
||||
ssl_renegotiation_unittest.cc \
|
||||
|
|
|
|||
|
|
@ -176,6 +176,321 @@ TEST_P(TlsConnectGeneric, ClientAuth) {
|
|||
CheckKeys();
|
||||
}
|
||||
|
||||
class TlsCertificateRequestContextRecorder : public TlsHandshakeFilter {
|
||||
public:
|
||||
TlsCertificateRequestContextRecorder(const std::shared_ptr<TlsAgent>& a,
|
||||
uint8_t handshake_type)
|
||||
: TlsHandshakeFilter(a, {handshake_type}), buffer_(), filtered_(false) {
|
||||
EnableDecryption();
|
||||
}
|
||||
|
||||
bool filtered() const { return filtered_; }
|
||||
const DataBuffer& buffer() const { return buffer_; }
|
||||
|
||||
protected:
|
||||
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
|
||||
const DataBuffer& input,
|
||||
DataBuffer* output) {
|
||||
assert(1 < input.len());
|
||||
size_t len = input.data()[0];
|
||||
assert(len + 1 < input.len());
|
||||
buffer_.Assign(input.data() + 1, len);
|
||||
filtered_ = true;
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
private:
|
||||
DataBuffer buffer_;
|
||||
bool filtered_;
|
||||
};
|
||||
|
||||
// All stream only tests; DTLS isn't supported yet.
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) {
|
||||
EnsureTlsSetup();
|
||||
auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>(
|
||||
server_, kTlsHandshakeCertificateRequest);
|
||||
auto capture_certificate =
|
||||
MakeTlsFilter<TlsCertificateRequestContextRecorder>(
|
||||
client_, kTlsHandshakeCertificate);
|
||||
client_->SetupClientAuth();
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
size_t called = 0;
|
||||
server_->SetAuthCertificateCallback(
|
||||
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
|
||||
called++;
|
||||
return SECSuccess;
|
||||
});
|
||||
Connect();
|
||||
EXPECT_EQ(0U, called);
|
||||
EXPECT_FALSE(capture_cert_req->filtered());
|
||||
EXPECT_FALSE(capture_certificate->filtered());
|
||||
// Send CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
// Need to do a round-trip so that the post-handshake message is
|
||||
// handled on both client and server.
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ReadBytes(50);
|
||||
EXPECT_EQ(1U, called);
|
||||
EXPECT_TRUE(capture_cert_req->filtered());
|
||||
EXPECT_TRUE(capture_certificate->filtered());
|
||||
// Check if a non-empty request context is generated and it is
|
||||
// properly sent back.
|
||||
EXPECT_LT(0U, capture_cert_req->buffer().len());
|
||||
EXPECT_EQ(capture_cert_req->buffer().len(),
|
||||
capture_certificate->buffer().len());
|
||||
EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(),
|
||||
capture_certificate->buffer().data(),
|
||||
capture_cert_req->buffer().len()));
|
||||
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
|
||||
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
|
||||
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
|
||||
}
|
||||
|
||||
static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd,
|
||||
CERTDistNames* caNames,
|
||||
CERTCertificate** clientCert,
|
||||
SECKEYPrivateKey** clientKey) {
|
||||
ScopedCERTCertificate cert;
|
||||
ScopedSECKEYPrivateKey priv;
|
||||
// use a different certificate than TlsAgent::kClient
|
||||
if (!TlsAgent::LoadCertificate(TlsAgent::kRsa2048, &cert, &priv)) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
*clientCert = cert.release();
|
||||
*clientKey = priv.release();
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMultiple) {
|
||||
client_->SetupClientAuth();
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
size_t called = 0;
|
||||
server_->SetAuthCertificateCallback(
|
||||
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
|
||||
called++;
|
||||
return SECSuccess;
|
||||
});
|
||||
Connect();
|
||||
EXPECT_EQ(0U, called);
|
||||
EXPECT_EQ(nullptr, SSL_PeerCertificate(server_->ssl_fd()));
|
||||
// Send 1st CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ReadBytes(50);
|
||||
EXPECT_EQ(1U, called);
|
||||
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
|
||||
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
|
||||
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
|
||||
// Send 2nd CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(
|
||||
client_->ssl_fd(), GetClientAuthDataHook, nullptr));
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ReadBytes(50);
|
||||
EXPECT_EQ(2U, called);
|
||||
ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd()));
|
||||
ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
|
||||
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert));
|
||||
EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert));
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthConcurrent) {
|
||||
client_->SetupClientAuth();
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
Connect();
|
||||
// Send 1st CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
// Send 2nd CertificateRequest.
|
||||
EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
|
||||
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMissingExtension) {
|
||||
client_->SetupClientAuth();
|
||||
Connect();
|
||||
// Send CertificateRequest, should fail due to missing
|
||||
// post_handshake_auth extension.
|
||||
EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
|
||||
EXPECT_EQ(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterClientAuth) {
|
||||
client_->SetupClientAuth();
|
||||
server_->RequestClientAuth(true);
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
size_t called = 0;
|
||||
server_->SetAuthCertificateCallback(
|
||||
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
|
||||
called++;
|
||||
return SECSuccess;
|
||||
});
|
||||
Connect();
|
||||
EXPECT_EQ(1U, called);
|
||||
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
|
||||
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
|
||||
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
|
||||
// Send CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(
|
||||
client_->ssl_fd(), GetClientAuthDataHook, nullptr));
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ReadBytes(50);
|
||||
EXPECT_EQ(2U, called);
|
||||
ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd()));
|
||||
ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
|
||||
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert));
|
||||
EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert));
|
||||
}
|
||||
|
||||
// Damages the request context in a CertificateRequest message.
|
||||
// We don't modify a Certificate message instead, so that the client
|
||||
// can compute CertificateVerify correctly.
|
||||
class TlsDamageCertificateRequestContextFilter : public TlsHandshakeFilter {
|
||||
public:
|
||||
TlsDamageCertificateRequestContextFilter(const std::shared_ptr<TlsAgent>& a)
|
||||
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}) {
|
||||
EnableDecryption();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
|
||||
const DataBuffer& input,
|
||||
DataBuffer* output) {
|
||||
*output = input;
|
||||
assert(1 < output->len());
|
||||
// The request context has a 1 octet length.
|
||||
output->data()[1] ^= 73;
|
||||
return CHANGE;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthContextMismatch) {
|
||||
EnsureTlsSetup();
|
||||
MakeTlsFilter<TlsDamageCertificateRequestContextFilter>(server_);
|
||||
client_->SetupClientAuth();
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
Connect();
|
||||
// Send CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ExpectSendAlert(kTlsAlertIllegalParameter);
|
||||
server_->ReadBytes(50);
|
||||
EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERTIFICATE, PORT_GetError());
|
||||
server_->ExpectReadWriteError();
|
||||
server_->SendData(50);
|
||||
client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
|
||||
client_->ReadBytes(50);
|
||||
EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, PORT_GetError());
|
||||
}
|
||||
|
||||
// Replaces signature in a CertificateVerify message.
|
||||
class TlsDamageSignatureFilter : public TlsHandshakeFilter {
|
||||
public:
|
||||
TlsDamageSignatureFilter(const std::shared_ptr<TlsAgent>& a)
|
||||
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}) {
|
||||
EnableDecryption();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
|
||||
const DataBuffer& input,
|
||||
DataBuffer* output) {
|
||||
*output = input;
|
||||
assert(2 < output->len());
|
||||
// The signature follows a 2-octet signature scheme.
|
||||
output->data()[2] ^= 73;
|
||||
return CHANGE;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBadSignature) {
|
||||
EnsureTlsSetup();
|
||||
MakeTlsFilter<TlsDamageSignatureFilter>(client_);
|
||||
client_->SetupClientAuth();
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
Connect();
|
||||
// Send CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ExpectSendAlert(kTlsAlertDecodeError);
|
||||
server_->ReadBytes(50);
|
||||
EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERT_VERIFY, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDecline) {
|
||||
EnsureTlsSetup();
|
||||
auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>(
|
||||
server_, kTlsHandshakeCertificateRequest);
|
||||
auto capture_certificate =
|
||||
MakeTlsFilter<TlsCertificateRequestContextRecorder>(
|
||||
client_, kTlsHandshakeCertificate);
|
||||
client_->SetupClientAuth();
|
||||
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
|
||||
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
|
||||
// Client to decline the certificate request.
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_GetClientAuthDataHook(
|
||||
client_->ssl_fd(),
|
||||
[](void*, PRFileDesc*, CERTDistNames*, CERTCertificate**,
|
||||
SECKEYPrivateKey**) -> SECStatus { return SECFailure; },
|
||||
nullptr));
|
||||
size_t called = 0;
|
||||
server_->SetAuthCertificateCallback(
|
||||
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
|
||||
called++;
|
||||
return SECSuccess;
|
||||
});
|
||||
Connect();
|
||||
EXPECT_EQ(0U, called);
|
||||
// Send CertificateRequest.
|
||||
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
|
||||
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
|
||||
server_->SendData(50);
|
||||
client_->ReadBytes(50);
|
||||
client_->SendData(50);
|
||||
server_->ReadBytes(50);
|
||||
// AuthCertificateCallback is not called, because the client sends
|
||||
// an empty certificate_list.
|
||||
EXPECT_EQ(0U, called);
|
||||
EXPECT_TRUE(capture_cert_req->filtered());
|
||||
EXPECT_TRUE(capture_certificate->filtered());
|
||||
// Check if a non-empty request context is generated and it is
|
||||
// properly sent back.
|
||||
EXPECT_LT(0U, capture_cert_req->buffer().len());
|
||||
EXPECT_EQ(capture_cert_req->buffer().len(),
|
||||
capture_certificate->buffer().len());
|
||||
EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(),
|
||||
capture_certificate->buffer().data(),
|
||||
capture_cert_req->buffer().len()));
|
||||
}
|
||||
|
||||
// In TLS 1.3, the client sends its cert rejection on the
|
||||
// second flight, and since it has already received the
|
||||
// server's Finished, it transitions to complete and
|
||||
|
|
@ -273,9 +588,7 @@ class TlsReplaceSignatureSchemeFilter : public TlsHandshakeFilter {
|
|||
TlsReplaceSignatureSchemeFilter(const std::shared_ptr<TlsAgent>& a,
|
||||
SSLSignatureScheme scheme)
|
||||
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}),
|
||||
scheme_(scheme) {
|
||||
EnableDecryption();
|
||||
}
|
||||
scheme_(scheme) {}
|
||||
|
||||
protected:
|
||||
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
|
||||
|
|
@ -552,7 +865,9 @@ TEST_P(TlsConnectTls12, SignatureAlgorithmDrop) {
|
|||
|
||||
TEST_P(TlsConnectTls13, UnsupportedSignatureSchemeAlert) {
|
||||
EnsureTlsSetup();
|
||||
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none);
|
||||
auto filter =
|
||||
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none);
|
||||
filter->EnableDecryption();
|
||||
|
||||
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
|
||||
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
|
||||
|
|
@ -563,8 +878,9 @@ TEST_P(TlsConnectTls13, InconsistentSignatureSchemeAlert) {
|
|||
EnsureTlsSetup();
|
||||
|
||||
// This won't work because we use an RSA cert by default.
|
||||
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(
|
||||
auto filter = MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(
|
||||
server_, ssl_sig_ecdsa_secp256r1_sha256);
|
||||
filter->EnableDecryption();
|
||||
|
||||
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
|
||||
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ TEST_P(TlsConnectGenericPre13, DamageServerSignature) {
|
|||
EnsureTlsSetup();
|
||||
auto filter = MakeTlsFilter<TlsLastByteDamager>(
|
||||
server_, kTlsHandshakeServerKeyExchange);
|
||||
filter->EnableDecryption();
|
||||
ExpectAlert(client_, kTlsAlertDecryptError);
|
||||
ConnectExpectFail();
|
||||
client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
|
||||
|
|
@ -84,7 +83,9 @@ TEST_P(TlsConnectGeneric, DamageClientSignature) {
|
|||
server_->RequestClientAuth(true);
|
||||
auto filter = MakeTlsFilter<TlsLastByteDamager>(
|
||||
client_, kTlsHandshakeCertificateVerify);
|
||||
filter->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
filter->EnableDecryption();
|
||||
}
|
||||
server_->ExpectSendAlert(kTlsAlertDecryptError);
|
||||
// Do these handshakes by hand to avoid race condition on
|
||||
// the client processing the server's alert.
|
||||
|
|
|
|||
|
|
@ -66,6 +66,38 @@ TEST_P(TlsConnectDatagramPre13, DropServerSecondFlightThrice) {
|
|||
Connect();
|
||||
}
|
||||
|
||||
static void CheckAcks(const std::shared_ptr<TlsRecordRecorder>& acks,
|
||||
size_t index, std::vector<uint64_t> expected) {
|
||||
ASSERT_LT(index, acks->count());
|
||||
const DataBuffer& buf = acks->record(index).buffer;
|
||||
size_t offset = 2;
|
||||
uint64_t len;
|
||||
|
||||
EXPECT_EQ(2 + expected.size() * 8, buf.len());
|
||||
ASSERT_TRUE(buf.Read(0, 2, &len));
|
||||
ASSERT_EQ(static_cast<size_t>(len + 2), buf.len());
|
||||
if ((2 + expected.size() * 8) != buf.len()) {
|
||||
while (offset < buf.len()) {
|
||||
uint64_t ack;
|
||||
ASSERT_TRUE(buf.Read(offset, 8, &ack));
|
||||
offset += 8;
|
||||
std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < expected.size(); ++i) {
|
||||
uint64_t a = expected[i];
|
||||
uint64_t ack;
|
||||
ASSERT_TRUE(buf.Read(offset, 8, &ack));
|
||||
offset += 8;
|
||||
if (a != ack) {
|
||||
ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
|
||||
<< " got=0x" << ack << std::dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TlsDropDatagram13 : public TlsConnectDatagram13,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
|
|
@ -139,37 +171,6 @@ class TlsDropDatagram13 : public TlsConnectDatagram13,
|
|||
std::shared_ptr<PacketFilter> chain_;
|
||||
};
|
||||
|
||||
void CheckAcks(const DropAckChain& chain, size_t index,
|
||||
std::vector<uint64_t> acks) {
|
||||
const DataBuffer& buf = chain.ack_->record(index).buffer;
|
||||
size_t offset = 2;
|
||||
uint64_t len;
|
||||
|
||||
EXPECT_EQ(2 + acks.size() * 8, buf.len());
|
||||
ASSERT_TRUE(buf.Read(0, 2, &len));
|
||||
ASSERT_EQ(static_cast<size_t>(len + 2), buf.len());
|
||||
if ((2 + acks.size() * 8) != buf.len()) {
|
||||
while (offset < buf.len()) {
|
||||
uint64_t ack;
|
||||
ASSERT_TRUE(buf.Read(offset, 8, &ack));
|
||||
offset += 8;
|
||||
std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < acks.size(); ++i) {
|
||||
uint64_t a = acks[i];
|
||||
uint64_t ack;
|
||||
ASSERT_TRUE(buf.Read(offset, 8, &ack));
|
||||
offset += 8;
|
||||
if (a != ack) {
|
||||
ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
|
||||
<< " got=0x" << ack << std::dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckedHandshakeSendReceive() {
|
||||
Handshake();
|
||||
CheckPostHandshake();
|
||||
|
|
@ -199,7 +200,7 @@ TEST_P(TlsDropDatagram13, DropClientFirstFlightOnce) {
|
|||
client_->Handshake();
|
||||
server_->Handshake();
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
|
||||
|
|
@ -210,7 +211,7 @@ TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
|
|||
server_->Handshake();
|
||||
server_filters_.drop_->Disable();
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// Dropping the server's first record also does not produce
|
||||
|
|
@ -223,7 +224,7 @@ TEST_P(TlsDropDatagram13, DropServerFirstRecordOnce) {
|
|||
server_->Handshake();
|
||||
Handshake();
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// Dropping the second packet of the server's flight should
|
||||
|
|
@ -236,8 +237,8 @@ TEST_P(TlsDropDatagram13, DropServerSecondRecordOnce) {
|
|||
HandshakeAndAck(client_);
|
||||
expected_client_acks_ = 1;
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(client_filters_, 0, {0}); // ServerHello
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(client_filters_.ack_, 0, {0}); // ServerHello
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// Drop the server ACK and verify that the client retransmits
|
||||
|
|
@ -265,8 +266,8 @@ TEST_P(TlsDropDatagram13, DropServerAckOnce) {
|
|||
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
|
||||
CheckPostHandshake();
|
||||
// There should be two copies of the finished ACK
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 1, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// Drop the client certificate verify.
|
||||
|
|
@ -281,10 +282,10 @@ TEST_P(TlsDropDatagram13, DropClientCertVerify) {
|
|||
expected_server_acks_ = 2;
|
||||
CheckedHandshakeSendReceive();
|
||||
// Ack of the Cert.
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
// Ack of the whole client handshake.
|
||||
CheckAcks(
|
||||
server_filters_, 1,
|
||||
server_filters_.ack_, 1,
|
||||
{0x0002000000000000ULL, // CH (we drop everything after this on client)
|
||||
0x0002000000000003ULL, // CT (2)
|
||||
0x0002000000000004ULL}); // FIN (2)
|
||||
|
|
@ -310,11 +311,11 @@ TEST_P(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
|
|||
// as the previous CT1).
|
||||
EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(client_filters_, 0,
|
||||
CheckAcks(client_filters_.ack_, 0,
|
||||
{0, // SH
|
||||
0x0002000000000000ULL, // EE
|
||||
0x0002000000000002ULL}); // CT2
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// Shrink the MTU down so that certs get split and drop the second piece.
|
||||
|
|
@ -336,13 +337,13 @@ TEST_P(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
|
|||
// Check that the first record is CT1
|
||||
EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(client_filters_, 0,
|
||||
CheckAcks(client_filters_.ack_, 0,
|
||||
{
|
||||
0, // SH
|
||||
0x0002000000000000ULL, // EE
|
||||
0x0002000000000001ULL, // CT1
|
||||
});
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// In this test, the Certificate message is sent four times, we drop all or part
|
||||
|
|
@ -392,18 +393,18 @@ class TlsFragmentationAndRecoveryTest : public TlsDropDatagram13 {
|
|||
0, // SH
|
||||
0x0002000000000000ULL // EE
|
||||
};
|
||||
CheckAcks(client_filters_, 0, client_acks);
|
||||
CheckAcks(client_filters_.ack_, 0, client_acks);
|
||||
// And from the second attempt for the half was kept (we delayed this ACK).
|
||||
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
|
||||
~dropped_half % 2);
|
||||
CheckAcks(client_filters_, 1, client_acks);
|
||||
CheckAcks(client_filters_.ack_, 1, client_acks);
|
||||
// And the third attempt where the first and last thirds got through.
|
||||
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
|
||||
third_flight_count - 1);
|
||||
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
|
||||
third_flight_count + 1);
|
||||
CheckAcks(client_filters_, 2, client_acks);
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(client_filters_.ack_, 2, client_acks);
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -548,7 +549,7 @@ TEST_P(TlsDropDatagram13, NoDropsDuringZeroRtt) {
|
|||
CheckConnected();
|
||||
SendReceive();
|
||||
EXPECT_EQ(0U, client_filters_.ack_->count());
|
||||
CheckAcks(server_filters_, 0,
|
||||
CheckAcks(server_filters_.ack_, 0,
|
||||
{0x0001000000000001ULL, // EOED
|
||||
0x0002000000000000ULL}); // Finished
|
||||
}
|
||||
|
|
@ -567,8 +568,8 @@ TEST_P(TlsDropDatagram13, DropEEDuringZeroRtt) {
|
|||
ExpectEarlyDataAccepted(true);
|
||||
CheckConnected();
|
||||
SendReceive();
|
||||
CheckAcks(client_filters_, 0, {0});
|
||||
CheckAcks(server_filters_, 0,
|
||||
CheckAcks(client_filters_.ack_, 0, {0});
|
||||
CheckAcks(server_filters_.ack_, 0,
|
||||
{0x0001000000000002ULL, // EOED
|
||||
0x0002000000000000ULL}); // Finished
|
||||
}
|
||||
|
|
@ -608,22 +609,22 @@ TEST_P(TlsDropDatagram13, ReorderServerEE) {
|
|||
expected_client_acks_ = 1;
|
||||
HandshakeAndAck(client_);
|
||||
CheckedHandshakeSendReceive();
|
||||
CheckAcks(client_filters_, 0,
|
||||
CheckAcks(client_filters_.ack_, 0,
|
||||
{
|
||||
0, // SH
|
||||
0x0002000000000000, // EE
|
||||
});
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// The client sends an out of order non-handshake message
|
||||
// but with the handshake key.
|
||||
class TlsSendCipherSpecCapturer {
|
||||
public:
|
||||
TlsSendCipherSpecCapturer(std::shared_ptr<TlsAgent>& agent)
|
||||
: send_cipher_specs_() {
|
||||
SSLInt_SetCipherSpecChangeFunc(agent->ssl_fd(), CipherSpecChanged,
|
||||
(void*)this);
|
||||
TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent)
|
||||
: agent_(agent), send_cipher_specs_() {
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this));
|
||||
}
|
||||
|
||||
std::shared_ptr<TlsCipherSpec> spec(size_t i) {
|
||||
|
|
@ -634,28 +635,42 @@ class TlsSendCipherSpecCapturer {
|
|||
}
|
||||
|
||||
private:
|
||||
static void CipherSpecChanged(void* arg, PRBool sending,
|
||||
ssl3CipherSpec* newSpec) {
|
||||
if (!sending) {
|
||||
static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
|
||||
SSLSecretDirection dir, PK11SymKey* secret,
|
||||
void* arg) {
|
||||
auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
|
||||
std::cerr << self->agent_->role_str() << ": capture " << dir
|
||||
<< " secret for epoch " << epoch << std::endl;
|
||||
|
||||
if (dir == ssl_secret_read) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
|
||||
SSLPreliminaryChannelInfo preinfo;
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo,
|
||||
sizeof(preinfo)));
|
||||
EXPECT_EQ(sizeof(preinfo), preinfo.length);
|
||||
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
|
||||
|
||||
auto spec = std::make_shared<TlsCipherSpec>();
|
||||
bool ret = spec->Init(SSLInt_CipherSpecToEpoch(newSpec),
|
||||
SSLInt_CipherSpecToAlgorithm(newSpec),
|
||||
SSLInt_CipherSpecToKey(newSpec),
|
||||
SSLInt_CipherSpecToIv(newSpec));
|
||||
EXPECT_EQ(true, ret);
|
||||
SSLCipherSuiteInfo cipherinfo;
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo,
|
||||
sizeof(cipherinfo)));
|
||||
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
|
||||
|
||||
auto spec = std::make_shared<TlsCipherSpec>(true, epoch);
|
||||
EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret));
|
||||
self->send_cipher_specs_.push_back(spec);
|
||||
}
|
||||
|
||||
std::shared_ptr<TlsAgent> agent_;
|
||||
std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
|
||||
};
|
||||
|
||||
TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
||||
TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
||||
StartConnect();
|
||||
// Capturing secrets means that we can't use decrypting filters on the client.
|
||||
TlsSendCipherSpecCapturer capturer(client_);
|
||||
client_->Handshake();
|
||||
server_->Handshake();
|
||||
|
|
@ -680,9 +695,12 @@ TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
|
|||
EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
||||
TEST_F(TlsConnectDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
||||
StartConnect();
|
||||
TlsSendCipherSpecCapturer capturer(client_);
|
||||
auto acks = MakeTlsFilter<TlsRecordRecorder>(server_, ssl_ct_ack);
|
||||
acks->EnableDecryption();
|
||||
|
||||
client_->Handshake();
|
||||
server_->Handshake();
|
||||
client_->Handshake();
|
||||
|
|
@ -699,10 +717,10 @@ TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
|
|||
ssl_ct_handshake,
|
||||
DataBuffer(buf, sizeof(buf))));
|
||||
server_->Handshake();
|
||||
EXPECT_EQ(2UL, server_filters_.ack_->count());
|
||||
EXPECT_EQ(2UL, acks->count());
|
||||
// The server acknowledges client Finished twice.
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
|
||||
CheckAcks(acks, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(acks, 1, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
// Shrink the MTU down so that certs get split and then swap the first and
|
||||
|
|
@ -726,7 +744,7 @@ TEST_P(TlsReorderDatagram13, ReorderServerCertificate) {
|
|||
ShiftDtlsTimers();
|
||||
CheckedHandshakeSendReceive();
|
||||
EXPECT_EQ(2UL, server_filters_.records_->count()); // ACK + Data
|
||||
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
|
||||
}
|
||||
|
||||
TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
|
||||
|
|
@ -761,7 +779,8 @@ TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
|
|||
CheckConnected();
|
||||
EXPECT_EQ(0U, client_filters_.ack_->count());
|
||||
// Acknowledgements for EOED and Finished.
|
||||
CheckAcks(server_filters_, 0, {0x0001000000000002ULL, 0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0,
|
||||
{0x0001000000000002ULL, 0x0002000000000000ULL});
|
||||
uint8_t buf[8];
|
||||
rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
|
||||
EXPECT_EQ(-1, rv);
|
||||
|
|
@ -800,7 +819,8 @@ TEST_P(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
|
|||
CheckConnected();
|
||||
EXPECT_EQ(0U, client_filters_.ack_->count());
|
||||
// Acknowledgements for EOED and Finished.
|
||||
CheckAcks(server_filters_, 0, {0x0001000000000002ULL, 0x0002000000000000ULL});
|
||||
CheckAcks(server_filters_.ack_, 0,
|
||||
{0x0001000000000002ULL, 0x0002000000000000ULL});
|
||||
uint8_t buf[8];
|
||||
rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
|
||||
EXPECT_EQ(-1, rv);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
'ssl_loopback_unittest.cc',
|
||||
'ssl_misc_unittest.cc',
|
||||
'ssl_record_unittest.cc',
|
||||
'ssl_recordsep_unittest.cc',
|
||||
'ssl_recordsize_unittest.cc',
|
||||
'ssl_resumption_unittest.cc',
|
||||
'ssl_renegotiation_unittest.cc',
|
||||
|
|
|
|||
518
security/nss/gtests/ssl_gtest/ssl_recordsep_unittest.cc
Normal file
518
security/nss/gtests/ssl_gtest/ssl_recordsep_unittest.cc
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "secerr.h"
|
||||
#include "ssl.h"
|
||||
#include "sslerr.h"
|
||||
#include "sslproto.h"
|
||||
|
||||
extern "C" {
|
||||
// This is not something that should make you happy.
|
||||
#include "libssl_internals.h"
|
||||
}
|
||||
|
||||
#include <queue>
|
||||
#include "gtest_utils.h"
|
||||
#include "nss_scoped_ptrs.h"
|
||||
#include "tls_connect.h"
|
||||
#include "tls_filter.h"
|
||||
#include "tls_parser.h"
|
||||
|
||||
namespace nss_test {
|
||||
|
||||
class HandshakeSecretTracker {
|
||||
public:
|
||||
HandshakeSecretTracker(const std::shared_ptr<TlsAgent>& agent,
|
||||
uint16_t first_read_epoch, uint16_t first_write_epoch)
|
||||
: agent_(agent),
|
||||
next_read_epoch_(first_read_epoch),
|
||||
next_write_epoch_(first_write_epoch) {
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_SecretCallback(agent_->ssl_fd(),
|
||||
HandshakeSecretTracker::SecretCb, this));
|
||||
}
|
||||
|
||||
void CheckComplete() const {
|
||||
EXPECT_EQ(0, next_read_epoch_);
|
||||
EXPECT_EQ(0, next_write_epoch_);
|
||||
}
|
||||
|
||||
private:
|
||||
static void SecretCb(PRFileDesc* fd, PRUint16 epoch, SSLSecretDirection dir,
|
||||
PK11SymKey* secret, void* arg) {
|
||||
HandshakeSecretTracker* t = reinterpret_cast<HandshakeSecretTracker*>(arg);
|
||||
t->SecretUpdated(epoch, dir, secret);
|
||||
}
|
||||
|
||||
void SecretUpdated(PRUint16 epoch, SSLSecretDirection dir,
|
||||
PK11SymKey* secret) {
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << agent_->role_str() << ": secret callback for " << dir
|
||||
<< " epoch " << epoch << std::endl;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(secret);
|
||||
uint16_t* p;
|
||||
if (dir == ssl_secret_read) {
|
||||
p = &next_read_epoch_;
|
||||
} else {
|
||||
ASSERT_EQ(ssl_secret_write, dir);
|
||||
p = &next_write_epoch_;
|
||||
}
|
||||
EXPECT_EQ(*p, epoch);
|
||||
switch (*p) {
|
||||
case 1: // 1 == 0-RTT, next should be handshake.
|
||||
case 2: // 2 == handshake, next should be application data.
|
||||
(*p)++;
|
||||
break;
|
||||
|
||||
case 3: // 3 == application data, there should be no more.
|
||||
// Use 0 as a sentinel value.
|
||||
*p = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
ADD_FAILURE() << "Unexpected next epoch: " << *p;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<TlsAgent> agent_;
|
||||
uint16_t next_read_epoch_;
|
||||
uint16_t next_write_epoch_;
|
||||
};
|
||||
|
||||
TEST_F(TlsConnectTest, HandshakeSecrets) {
|
||||
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
|
||||
EnsureTlsSetup();
|
||||
|
||||
HandshakeSecretTracker c(client_, 2, 2);
|
||||
HandshakeSecretTracker s(server_, 2, 2);
|
||||
|
||||
Connect();
|
||||
SendReceive();
|
||||
|
||||
c.CheckComplete();
|
||||
s.CheckComplete();
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectTest, ZeroRttSecrets) {
|
||||
SetupForZeroRtt();
|
||||
|
||||
HandshakeSecretTracker c(client_, 2, 1);
|
||||
HandshakeSecretTracker s(server_, 1, 2);
|
||||
|
||||
client_->Set0RttEnabled(true);
|
||||
server_->Set0RttEnabled(true);
|
||||
ExpectResumption(RESUME_TICKET);
|
||||
ZeroRttSendReceive(true, true);
|
||||
Handshake();
|
||||
ExpectEarlyDataAccepted(true);
|
||||
CheckConnected();
|
||||
SendReceive();
|
||||
|
||||
c.CheckComplete();
|
||||
s.CheckComplete();
|
||||
}
|
||||
|
||||
class KeyUpdateTracker {
|
||||
public:
|
||||
KeyUpdateTracker(const std::shared_ptr<TlsAgent>& agent,
|
||||
bool expect_read_secret)
|
||||
: agent_(agent), expect_read_secret_(expect_read_secret), called_(false) {
|
||||
EXPECT_EQ(SECSuccess, SSL_SecretCallback(agent_->ssl_fd(),
|
||||
KeyUpdateTracker::SecretCb, this));
|
||||
}
|
||||
|
||||
void CheckCalled() const { EXPECT_TRUE(called_); }
|
||||
|
||||
private:
|
||||
static void SecretCb(PRFileDesc* fd, PRUint16 epoch, SSLSecretDirection dir,
|
||||
PK11SymKey* secret, void* arg) {
|
||||
KeyUpdateTracker* t = reinterpret_cast<KeyUpdateTracker*>(arg);
|
||||
t->SecretUpdated(epoch, dir, secret);
|
||||
}
|
||||
|
||||
void SecretUpdated(PRUint16 epoch, SSLSecretDirection dir,
|
||||
PK11SymKey* secret) {
|
||||
EXPECT_EQ(4U, epoch);
|
||||
EXPECT_EQ(expect_read_secret_, dir == ssl_secret_read);
|
||||
EXPECT_TRUE(secret);
|
||||
called_ = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<TlsAgent> agent_;
|
||||
bool expect_read_secret_;
|
||||
bool called_;
|
||||
};
|
||||
|
||||
TEST_F(TlsConnectTest, KeyUpdateSecrets) {
|
||||
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
|
||||
Connect();
|
||||
// The update is to the client write secret; the server read secret.
|
||||
KeyUpdateTracker c(client_, false);
|
||||
KeyUpdateTracker s(server_, true);
|
||||
EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
|
||||
SendReceive(50);
|
||||
SendReceive(60);
|
||||
CheckEpochs(4, 3);
|
||||
c.CheckCalled();
|
||||
s.CheckCalled();
|
||||
}
|
||||
|
||||
// BadPrSocket is an instance of a PR IO layer that crashes the test if it is
|
||||
// ever used for reading or writing. It does that by failing to overwrite any
|
||||
// of the DummyIOLayerMethods, which all crash when invoked.
|
||||
class BadPrSocket : public DummyIOLayerMethods {
|
||||
public:
|
||||
BadPrSocket(std::shared_ptr<TlsAgent>& agent) : DummyIOLayerMethods() {
|
||||
static PRDescIdentity bad_identity = PR_GetUniqueIdentity("bad NSPR id");
|
||||
fd_ = DummyIOLayerMethods::CreateFD(bad_identity, this);
|
||||
|
||||
// This is terrible, but NSPR doesn't provide an easy way to replace the
|
||||
// bottom layer of an IO stack. Take the DummyPrSocket and replace its
|
||||
// NSPR method vtable with the ones from this object.
|
||||
dummy_layer_ =
|
||||
PR_GetIdentitiesLayer(agent->ssl_fd(), DummyPrSocket::LayerId());
|
||||
original_methods_ = dummy_layer_->methods;
|
||||
original_secret_ = dummy_layer_->secret;
|
||||
dummy_layer_->methods = fd_->methods;
|
||||
dummy_layer_->secret = reinterpret_cast<PRFilePrivate*>(this);
|
||||
}
|
||||
|
||||
// This will be destroyed before the agent, so we need to restore the state
|
||||
// before we tampered with it.
|
||||
virtual ~BadPrSocket() {
|
||||
dummy_layer_->methods = original_methods_;
|
||||
dummy_layer_->secret = original_secret_;
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPRFileDesc fd_;
|
||||
PRFileDesc* dummy_layer_;
|
||||
const PRIOMethods* original_methods_;
|
||||
PRFilePrivate* original_secret_;
|
||||
};
|
||||
|
||||
class StagedRecords {
|
||||
public:
|
||||
StagedRecords(std::shared_ptr<TlsAgent>& agent) : agent_(agent), records_() {
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_RecordLayerWriteCallback(
|
||||
agent_->ssl_fd(), StagedRecords::StageRecordData, this));
|
||||
}
|
||||
|
||||
virtual ~StagedRecords() {
|
||||
// Uninstall so that the callback doesn't fire during cleanup.
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_RecordLayerWriteCallback(agent_->ssl_fd(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
bool empty() const { return records_.empty(); }
|
||||
|
||||
void ForwardAll(std::shared_ptr<TlsAgent>& peer) {
|
||||
EXPECT_NE(agent_, peer) << "can't forward to self";
|
||||
for (auto r : records_) {
|
||||
r.Forward(peer);
|
||||
}
|
||||
records_.clear();
|
||||
}
|
||||
|
||||
// This forwards all saved data and checks the resulting state.
|
||||
void ForwardAll(std::shared_ptr<TlsAgent>& peer,
|
||||
TlsAgent::State expected_state) {
|
||||
ForwardAll(peer);
|
||||
peer->Handshake();
|
||||
EXPECT_EQ(expected_state, peer->state());
|
||||
}
|
||||
|
||||
void ForwardPartial(std::shared_ptr<TlsAgent>& peer) {
|
||||
if (records_.empty()) {
|
||||
ADD_FAILURE() << "No records to slice";
|
||||
return;
|
||||
}
|
||||
auto& last = records_.back();
|
||||
auto tail = last.SliceTail();
|
||||
ForwardAll(peer, TlsAgent::STATE_CONNECTING);
|
||||
records_.push_back(tail);
|
||||
EXPECT_EQ(TlsAgent::STATE_CONNECTING, peer->state());
|
||||
}
|
||||
|
||||
private:
|
||||
// A single record.
|
||||
class StagedRecord {
|
||||
public:
|
||||
StagedRecord(const std::string role, uint16_t epoch, SSLContentType ct,
|
||||
const uint8_t* data, size_t len)
|
||||
: role_(role), epoch_(epoch), content_type_(ct), data_(data, len) {
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << role_ << ": staged epoch " << epoch_ << " "
|
||||
<< content_type_ << ": " << data_ << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// This forwards staged data to the identified agent.
|
||||
void Forward(std::shared_ptr<TlsAgent>& peer) {
|
||||
// Now there should be staged data.
|
||||
EXPECT_FALSE(data_.empty());
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << role_ << ": forward " << data_ << std::endl;
|
||||
}
|
||||
SECStatus rv = SSL_RecordLayerData(
|
||||
peer->ssl_fd(), epoch_, content_type_, data_.data(),
|
||||
static_cast<unsigned int>(data_.len()));
|
||||
if (rv != SECSuccess) {
|
||||
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
// Slices the tail off this record and returns it.
|
||||
StagedRecord SliceTail() {
|
||||
size_t slice = 1;
|
||||
if (data_.len() <= slice) {
|
||||
ADD_FAILURE() << "record too small to slice in two";
|
||||
slice = 0;
|
||||
}
|
||||
size_t keep = data_.len() - slice;
|
||||
StagedRecord tail(role_, epoch_, content_type_, data_.data() + keep,
|
||||
slice);
|
||||
data_.Truncate(keep);
|
||||
return tail;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string role_;
|
||||
uint16_t epoch_;
|
||||
SSLContentType content_type_;
|
||||
DataBuffer data_;
|
||||
};
|
||||
|
||||
// This is an SSLRecordWriteCallback that stages data.
|
||||
static SECStatus StageRecordData(PRFileDesc* fd, PRUint16 epoch,
|
||||
SSLContentType content_type,
|
||||
const PRUint8* data, unsigned int len,
|
||||
void* arg) {
|
||||
auto stage = reinterpret_cast<StagedRecords*>(arg);
|
||||
stage->records_.push_back(StagedRecord(stage->agent_->role_str(), epoch,
|
||||
content_type, data,
|
||||
static_cast<size_t>(len)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
std::shared_ptr<TlsAgent>& agent_;
|
||||
std::deque<StagedRecord> records_;
|
||||
};
|
||||
|
||||
// Attempting to feed application data in before the handshake is complete
|
||||
// should be caught.
|
||||
static void RefuseApplicationData(std::shared_ptr<TlsAgent>& peer,
|
||||
uint16_t epoch) {
|
||||
static const uint8_t d[] = {1, 2, 3};
|
||||
EXPECT_EQ(SECFailure,
|
||||
SSL_RecordLayerData(peer->ssl_fd(), epoch, ssl_ct_application_data,
|
||||
d, static_cast<unsigned int>(sizeof(d))));
|
||||
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
||||
}
|
||||
|
||||
static void SendForwardReceive(std::shared_ptr<TlsAgent>& sender,
|
||||
StagedRecords& sender_stage,
|
||||
std::shared_ptr<TlsAgent>& receiver) {
|
||||
const size_t count = 10;
|
||||
sender->SendData(count, count);
|
||||
sender_stage.ForwardAll(receiver);
|
||||
receiver->ReadBytes(count);
|
||||
}
|
||||
|
||||
TEST_P(TlsConnectStream, ReplaceRecordLayer) {
|
||||
StartConnect();
|
||||
client_->SetServerKeyBits(server_->server_key_bits());
|
||||
|
||||
// BadPrSocket installs an IO layer that crashes when the SSL layer attempts
|
||||
// to read or write.
|
||||
BadPrSocket bad_layer_client(client_);
|
||||
BadPrSocket bad_layer_server(server_);
|
||||
|
||||
// StagedRecords installs a handler for unprotected data from the socket, and
|
||||
// captures that data.
|
||||
StagedRecords client_stage(client_);
|
||||
StagedRecords server_stage(server_);
|
||||
|
||||
// Both peers should refuse application data from epoch 0.
|
||||
RefuseApplicationData(client_, 0);
|
||||
RefuseApplicationData(server_, 0);
|
||||
|
||||
// This first call forwards nothing, but it causes the client to handshake,
|
||||
// which starts things off. This stages the ClientHello as a result.
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
|
||||
// This processes the ClientHello and stages the first server flight.
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
|
||||
RefuseApplicationData(server_, 1);
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
// Process the server flight and the client is done.
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
|
||||
} else {
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
|
||||
RefuseApplicationData(client_, 1);
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
|
||||
}
|
||||
CheckKeys();
|
||||
|
||||
// Reading and writing application data should work.
|
||||
SendForwardReceive(client_, client_stage, server_);
|
||||
SendForwardReceive(server_, server_stage, client_);
|
||||
}
|
||||
|
||||
static SECStatus AuthCompleteBlock(TlsAgent*, PRBool, PRBool) {
|
||||
return SECWouldBlock;
|
||||
}
|
||||
|
||||
TEST_P(TlsConnectStream, ReplaceRecordLayerAsyncLateAuth) {
|
||||
StartConnect();
|
||||
client_->SetServerKeyBits(server_->server_key_bits());
|
||||
|
||||
BadPrSocket bad_layer_client(client_);
|
||||
BadPrSocket bad_layer_server(server_);
|
||||
StagedRecords client_stage(client_);
|
||||
StagedRecords server_stage(server_);
|
||||
|
||||
client_->SetAuthCertificateCallback(AuthCompleteBlock);
|
||||
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
|
||||
|
||||
// Prior to TLS 1.3, the client sends its second flight immediately. But in
|
||||
// TLS 1.3, a client won't send a Finished until it is happy with the server
|
||||
// certificate. So blocking certificate validation causes the client to send
|
||||
// nothing.
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
ASSERT_TRUE(client_stage.empty());
|
||||
|
||||
// Client should have stopped reading when it saw the Certificate message,
|
||||
// so it will be reading handshake epoch, and writing cleartext.
|
||||
client_->CheckEpochs(2, 0);
|
||||
// Server should be reading handshake, and writing application data.
|
||||
server_->CheckEpochs(2, 3);
|
||||
|
||||
// Handshake again and the client will read the remainder of the server's
|
||||
// flight, but it will remain blocked.
|
||||
client_->Handshake();
|
||||
ASSERT_TRUE(client_stage.empty());
|
||||
EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
|
||||
} else {
|
||||
// In prior versions, the client's second flight is always sent.
|
||||
ASSERT_FALSE(client_stage.empty());
|
||||
}
|
||||
|
||||
// Now declare the certificate good.
|
||||
EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
|
||||
client_->Handshake();
|
||||
ASSERT_FALSE(client_stage.empty());
|
||||
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
|
||||
} else {
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
|
||||
}
|
||||
CheckKeys();
|
||||
|
||||
// Reading and writing application data should work.
|
||||
SendForwardReceive(client_, client_stage, server_);
|
||||
}
|
||||
|
||||
// This test ensures that data is correctly forwarded when the handshake is
|
||||
// resumed after asynchronous server certificate authentication, when
|
||||
// SSL_AuthCertificateComplete() is called. The logic for resuming the
|
||||
// handshake involves a different code path than the usual one, so this test
|
||||
// exercises that code fully.
|
||||
TEST_F(TlsConnectStreamTls13, ReplaceRecordLayerAsyncEarlyAuth) {
|
||||
StartConnect();
|
||||
client_->SetServerKeyBits(server_->server_key_bits());
|
||||
|
||||
BadPrSocket bad_layer_client(client_);
|
||||
BadPrSocket bad_layer_server(server_);
|
||||
StagedRecords client_stage(client_);
|
||||
StagedRecords server_stage(server_);
|
||||
|
||||
client_->SetAuthCertificateCallback(AuthCompleteBlock);
|
||||
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
|
||||
|
||||
// Send a partial flight on to the client.
|
||||
// This includes enough to trigger the certificate callback.
|
||||
server_stage.ForwardPartial(client_);
|
||||
EXPECT_TRUE(client_stage.empty());
|
||||
|
||||
// Declare the certificate good.
|
||||
EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
|
||||
client_->Handshake();
|
||||
EXPECT_TRUE(client_stage.empty());
|
||||
|
||||
// Send the remainder of the server flight.
|
||||
PRBool pending = PR_FALSE;
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSLInt_HasPendingHandshakeData(client_->ssl_fd(), &pending));
|
||||
EXPECT_EQ(PR_TRUE, pending);
|
||||
EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
|
||||
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
|
||||
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
|
||||
CheckKeys();
|
||||
|
||||
SendForwardReceive(server_, server_stage, client_);
|
||||
}
|
||||
|
||||
TEST_P(TlsConnectStream, ForwardDataFromWrongEpoch) {
|
||||
const uint8_t data[] = {1};
|
||||
Connect();
|
||||
uint16_t next_epoch;
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
EXPECT_EQ(SECFailure,
|
||||
SSL_RecordLayerData(client_->ssl_fd(), 2, ssl_ct_application_data,
|
||||
data, sizeof(data)));
|
||||
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError())
|
||||
<< "Passing data from an old epoch is rejected";
|
||||
next_epoch = 4;
|
||||
} else {
|
||||
// Prior to TLS 1.3, the epoch is only updated once during the handshake.
|
||||
next_epoch = 2;
|
||||
}
|
||||
EXPECT_EQ(SECFailure,
|
||||
SSL_RecordLayerData(client_->ssl_fd(), next_epoch,
|
||||
ssl_ct_application_data, data, sizeof(data)));
|
||||
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError())
|
||||
<< "Passing data from a future epoch blocks";
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectStreamTls13, ForwardInvalidData) {
|
||||
const uint8_t data[1] = {0};
|
||||
|
||||
EnsureTlsSetup();
|
||||
// Zero-length data.
|
||||
EXPECT_EQ(SECFailure, SSL_RecordLayerData(client_->ssl_fd(), 0,
|
||||
ssl_ct_application_data, data, 0));
|
||||
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
||||
|
||||
// NULL data.
|
||||
EXPECT_EQ(SECFailure,
|
||||
SSL_RecordLayerData(client_->ssl_fd(), 0, ssl_ct_application_data,
|
||||
nullptr, 1));
|
||||
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
||||
}
|
||||
|
||||
TEST_F(TlsConnectDatagram13, ForwardDataDtls) {
|
||||
EnsureTlsSetup();
|
||||
const uint8_t data[1] = {0};
|
||||
EXPECT_EQ(SECFailure,
|
||||
SSL_RecordLayerData(client_->ssl_fd(), 0, ssl_ct_application_data,
|
||||
data, sizeof(data)));
|
||||
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
||||
}
|
||||
|
||||
} // namespace nss_test
|
||||
|
|
@ -123,9 +123,11 @@ TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
|
|||
|
||||
EnsureTlsSetup();
|
||||
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
|
||||
client_max->EnableDecryption();
|
||||
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
|
||||
server_max->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
client_max->EnableDecryption();
|
||||
server_max->EnableDecryption();
|
||||
}
|
||||
|
||||
Connect();
|
||||
client_->SendData(send_size, send_size);
|
||||
|
|
@ -140,7 +142,9 @@ TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
|
|||
TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
|
||||
EnsureTlsSetup();
|
||||
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
|
||||
server_max->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
server_max->EnableDecryption();
|
||||
}
|
||||
|
||||
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
|
||||
Connect();
|
||||
|
|
@ -152,7 +156,9 @@ TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
|
|||
TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
|
||||
EnsureTlsSetup();
|
||||
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
|
||||
client_max->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
client_max->EnableDecryption();
|
||||
}
|
||||
|
||||
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
|
||||
Connect();
|
||||
|
|
@ -164,9 +170,11 @@ TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
|
|||
TEST_P(TlsConnectGeneric, RecordSizeAsymmetric) {
|
||||
EnsureTlsSetup();
|
||||
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
|
||||
client_max->EnableDecryption();
|
||||
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
|
||||
server_max->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
client_max->EnableDecryption();
|
||||
server_max->EnableDecryption();
|
||||
}
|
||||
|
||||
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
|
||||
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 100);
|
||||
|
|
@ -256,9 +264,11 @@ class TlsRecordPadder : public TlsRecordFilter {
|
|||
return KEEP;
|
||||
}
|
||||
|
||||
uint16_t protection_epoch;
|
||||
uint8_t inner_content_type;
|
||||
DataBuffer plaintext;
|
||||
if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
|
||||
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
|
||||
&plaintext)) {
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
|
|
@ -267,8 +277,8 @@ class TlsRecordPadder : public TlsRecordFilter {
|
|||
}
|
||||
|
||||
DataBuffer ciphertext;
|
||||
bool ok =
|
||||
Protect(header, inner_content_type, plaintext, &ciphertext, padding_);
|
||||
bool ok = Protect(spec(protection_epoch), header, inner_content_type,
|
||||
plaintext, &ciphertext, padding_);
|
||||
EXPECT_TRUE(ok);
|
||||
if (!ok) {
|
||||
return KEEP;
|
||||
|
|
@ -334,7 +344,9 @@ TEST_P(TlsConnectGeneric, RecordSizeCapExtensionClient) {
|
|||
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
|
||||
auto capture =
|
||||
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_record_size_limit_xtn);
|
||||
capture->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
capture->EnableDecryption();
|
||||
}
|
||||
Connect();
|
||||
|
||||
uint64_t val = 0;
|
||||
|
|
@ -352,7 +364,9 @@ TEST_P(TlsConnectGeneric, RecordSizeCapExtensionServer) {
|
|||
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
|
||||
auto capture =
|
||||
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_record_size_limit_xtn);
|
||||
capture->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
capture->EnableDecryption();
|
||||
}
|
||||
Connect();
|
||||
|
||||
uint64_t val = 0;
|
||||
|
|
@ -393,7 +407,9 @@ TEST_P(TlsConnectGeneric, RecordSizeServerExtensionInvalid) {
|
|||
static const uint8_t v[] = {0xf4, 0x1f};
|
||||
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
|
||||
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
|
||||
replace->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
replace->EnableDecryption();
|
||||
}
|
||||
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +419,9 @@ TEST_P(TlsConnectGeneric, RecordSizeServerExtensionExtra) {
|
|||
static const uint8_t v[] = {0x01, 0x00, 0x00};
|
||||
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
|
||||
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
|
||||
replace->EnableDecryption();
|
||||
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
replace->EnableDecryption();
|
||||
}
|
||||
ConnectExpectAlert(client_, kTlsAlertDecodeError);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,13 @@ namespace nss_test {
|
|||
if (g_ssl_gtest_verbose) LOG(a); \
|
||||
} while (false)
|
||||
|
||||
PRDescIdentity DummyPrSocket::LayerId() {
|
||||
static PRDescIdentity id = PR_GetUniqueIdentity("dummysocket");
|
||||
return id;
|
||||
}
|
||||
|
||||
ScopedPRFileDesc DummyPrSocket::CreateFD() {
|
||||
static PRDescIdentity test_fd_identity =
|
||||
PR_GetUniqueIdentity("testtransportadapter");
|
||||
return DummyIOLayerMethods::CreateFD(test_fd_identity, this);
|
||||
return DummyIOLayerMethods::CreateFD(DummyPrSocket::LayerId(), this);
|
||||
}
|
||||
|
||||
void DummyPrSocket::Reset() {
|
||||
|
|
@ -136,19 +139,18 @@ int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
|
|||
DataBuffer filtered;
|
||||
PacketFilter::Action action = PacketFilter::KEEP;
|
||||
if (filter_) {
|
||||
LOGV("Original packet: " << packet);
|
||||
action = filter_->Process(packet, &filtered);
|
||||
}
|
||||
switch (action) {
|
||||
case PacketFilter::CHANGE:
|
||||
LOG("Original packet: " << packet);
|
||||
LOG("Filtered packet: " << filtered);
|
||||
dst->PacketReceived(filtered);
|
||||
break;
|
||||
case PacketFilter::DROP:
|
||||
LOG("Droppped packet: " << packet);
|
||||
LOG("Drop packet");
|
||||
break;
|
||||
case PacketFilter::KEEP:
|
||||
LOGV("Packet: " << packet);
|
||||
dst->PacketReceived(packet);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ class DummyPrSocket : public DummyIOLayerMethods {
|
|||
write_error_(0) {}
|
||||
virtual ~DummyPrSocket() {}
|
||||
|
||||
static PRDescIdentity LayerId();
|
||||
|
||||
// Create a file descriptor that will reference this object. The fd must not
|
||||
// live longer than this adapter; call PR_Close() before.
|
||||
ScopedPRFileDesc CreateFD();
|
||||
|
|
|
|||
|
|
@ -640,6 +640,16 @@ void TlsAgent::CheckAlpn(SSLNextProtoState expected_state,
|
|||
}
|
||||
}
|
||||
|
||||
void TlsAgent::CheckEpochs(uint16_t expected_read,
|
||||
uint16_t expected_write) const {
|
||||
uint16_t read_epoch = 0;
|
||||
uint16_t write_epoch = 0;
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_GetCurrentEpoch(ssl_fd(), &read_epoch, &write_epoch));
|
||||
EXPECT_EQ(expected_read, read_epoch) << role_str() << " read epoch";
|
||||
EXPECT_EQ(expected_write, write_epoch) << role_str() << " write epoch";
|
||||
}
|
||||
|
||||
void TlsAgent::EnableSrtp() {
|
||||
EXPECT_TRUE(EnsureTlsSetup());
|
||||
const uint16_t ciphers[] = {SRTP_AES128_CM_HMAC_SHA1_80,
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ class TlsAgent : public PollTarget {
|
|||
const std::string& expected = "") const;
|
||||
void EnableSrtp();
|
||||
void CheckSrtp() const;
|
||||
void CheckEpochs(uint16_t expected_read, uint16_t expected_write) const;
|
||||
void CheckErrorCode(int32_t expected) const;
|
||||
void WaitForErrorCode(int32_t expected, uint32_t delay) const;
|
||||
// Send data on the socket, encrypting it.
|
||||
|
|
|
|||
|
|
@ -167,18 +167,8 @@ void TlsConnectTestBase::CheckShares(
|
|||
|
||||
void TlsConnectTestBase::CheckEpochs(uint16_t client_epoch,
|
||||
uint16_t server_epoch) const {
|
||||
uint16_t read_epoch = 0;
|
||||
uint16_t write_epoch = 0;
|
||||
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSLInt_GetEpochs(client_->ssl_fd(), &read_epoch, &write_epoch));
|
||||
EXPECT_EQ(server_epoch, read_epoch) << "client read epoch";
|
||||
EXPECT_EQ(client_epoch, write_epoch) << "client write epoch";
|
||||
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSLInt_GetEpochs(server_->ssl_fd(), &read_epoch, &write_epoch));
|
||||
EXPECT_EQ(client_epoch, read_epoch) << "server read epoch";
|
||||
EXPECT_EQ(server_epoch, write_epoch) << "server write epoch";
|
||||
client_->CheckEpochs(server_epoch, client_epoch);
|
||||
server_->CheckEpochs(client_epoch, server_epoch);
|
||||
}
|
||||
|
||||
void TlsConnectTestBase::ClearStats() {
|
||||
|
|
|
|||
|
|
@ -45,40 +45,65 @@ void TlsVersioned::WriteStream(std::ostream& stream) const {
|
|||
}
|
||||
}
|
||||
|
||||
void TlsRecordFilter::EnableDecryption() {
|
||||
SSLInt_SetCipherSpecChangeFunc(agent()->ssl_fd(), CipherSpecChanged,
|
||||
(void*)this);
|
||||
TlsRecordFilter::TlsRecordFilter(const std::shared_ptr<TlsAgent>& a)
|
||||
: agent_(a) {
|
||||
cipher_specs_.emplace_back(a->variant() == ssl_variant_datagram, 0);
|
||||
}
|
||||
|
||||
void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
|
||||
ssl3CipherSpec* newSpec) {
|
||||
TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
|
||||
PRBool isServer = self->agent()->role() == TlsAgent::SERVER;
|
||||
void TlsRecordFilter::EnableDecryption() {
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_SecretCallback(agent()->ssl_fd(), SecretCallback, this));
|
||||
decrypting_ = true;
|
||||
}
|
||||
|
||||
void TlsRecordFilter::SecretCallback(PRFileDesc* fd, PRUint16 epoch,
|
||||
SSLSecretDirection dir, PK11SymKey* secret,
|
||||
void* arg) {
|
||||
TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << (isServer ? "server" : "client") << ": "
|
||||
<< (sending ? "send" : "receive")
|
||||
<< " cipher spec changed: " << newSpec->epoch << " ("
|
||||
<< newSpec->phase << ")" << std::endl;
|
||||
std::cerr << self->agent()->role_str() << ": " << dir
|
||||
<< " secret changed for epoch " << epoch << std::endl;
|
||||
}
|
||||
if (!sending) {
|
||||
|
||||
if (dir == ssl_secret_read) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t seq_no;
|
||||
if (self->agent()->variant() == ssl_variant_datagram) {
|
||||
seq_no = static_cast<uint64_t>(SSLInt_CipherSpecToEpoch(newSpec)) << 48;
|
||||
} else {
|
||||
seq_no = 0;
|
||||
for (auto& spec : self->cipher_specs_) {
|
||||
ASSERT_NE(spec.epoch(), epoch) << "duplicate spec for epoch " << epoch;
|
||||
}
|
||||
self->in_sequence_number_ = seq_no;
|
||||
self->out_sequence_number_ = seq_no;
|
||||
self->dropped_record_ = false;
|
||||
self->cipher_spec_.reset(new TlsCipherSpec());
|
||||
bool ret = self->cipher_spec_->Init(
|
||||
SSLInt_CipherSpecToEpoch(newSpec), SSLInt_CipherSpecToAlgorithm(newSpec),
|
||||
SSLInt_CipherSpecToKey(newSpec), SSLInt_CipherSpecToIv(newSpec));
|
||||
EXPECT_EQ(true, ret);
|
||||
|
||||
SSLPreliminaryChannelInfo preinfo;
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_GetPreliminaryChannelInfo(self->agent()->ssl_fd(), &preinfo,
|
||||
sizeof(preinfo)));
|
||||
EXPECT_EQ(sizeof(preinfo), preinfo.length);
|
||||
|
||||
// Check the version.
|
||||
if (preinfo.valuesSet & ssl_preinfo_version) {
|
||||
EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_3, preinfo.protocolVersion);
|
||||
} else {
|
||||
EXPECT_EQ(1U, epoch);
|
||||
}
|
||||
|
||||
uint16_t suite;
|
||||
if (epoch == 1) {
|
||||
// 0-RTT
|
||||
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_0rtt_cipher_suite);
|
||||
suite = preinfo.zeroRttCipherSuite;
|
||||
} else {
|
||||
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
|
||||
suite = preinfo.cipherSuite;
|
||||
}
|
||||
|
||||
SSLCipherSuiteInfo cipherinfo;
|
||||
EXPECT_EQ(SECSuccess,
|
||||
SSL_GetCipherSuiteInfo(suite, &cipherinfo, sizeof(cipherinfo)));
|
||||
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
|
||||
|
||||
bool is_dtls = self->agent()->variant() == ssl_variant_datagram;
|
||||
self->cipher_specs_.emplace_back(is_dtls, epoch);
|
||||
EXPECT_TRUE(self->cipher_specs_.back().SetKeys(&cipherinfo, secret));
|
||||
}
|
||||
|
||||
bool TlsRecordFilter::is_dtls13() const {
|
||||
|
|
@ -95,6 +120,23 @@ bool TlsRecordFilter::is_dtls13() const {
|
|||
info.canSendEarlyData;
|
||||
}
|
||||
|
||||
// Gets the cipher spec that matches the specified epoch.
|
||||
TlsCipherSpec& TlsRecordFilter::spec(uint16_t write_epoch) {
|
||||
for (auto& sp : cipher_specs_) {
|
||||
if (sp.epoch() == write_epoch) {
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
|
||||
// If we aren't decrypting, provide a cipher spec that does nothing other than
|
||||
// count sequence numbers.
|
||||
EXPECT_FALSE(decrypting_) << "No spec available for epoch " << write_epoch;
|
||||
;
|
||||
bool is_dtls = agent()->variant() == ssl_variant_datagram;
|
||||
cipher_specs_.emplace_back(is_dtls, write_epoch);
|
||||
return cipher_specs_.back();
|
||||
}
|
||||
|
||||
PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
|
||||
DataBuffer* output) {
|
||||
// Disable during shutdown.
|
||||
|
|
@ -108,34 +150,28 @@ PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
|
|||
output->Allocate(input.len());
|
||||
TlsParser parser(input);
|
||||
|
||||
// This uses the current write spec for the purposes of parsing the epoch and
|
||||
// sequence number from the header. This might be wrong because we can
|
||||
// receive records from older specs, but guessing is good enough:
|
||||
// - In DTLS, parsing the sequence number corrects any errors.
|
||||
// - In TLS, we don't use the sequence number unless decrypting, where we use
|
||||
// trial decryption to get the right epoch.
|
||||
uint16_t write_epoch = 0;
|
||||
SECStatus rv = SSL_GetCurrentEpoch(agent()->ssl_fd(), nullptr, &write_epoch);
|
||||
if (rv != SECSuccess) {
|
||||
ADD_FAILURE() << "unable to read epoch";
|
||||
return KEEP;
|
||||
}
|
||||
uint64_t guess_seqno = static_cast<uint64_t>(write_epoch) << 48;
|
||||
|
||||
while (parser.remaining()) {
|
||||
TlsRecordHeader header;
|
||||
DataBuffer record;
|
||||
|
||||
if (!header.Parse(is_dtls13(), in_sequence_number_, &parser, &record)) {
|
||||
if (!header.Parse(is_dtls13(), guess_seqno, &parser, &record)) {
|
||||
ADD_FAILURE() << "not a valid record";
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
// Track the sequence number, which is necessary for stream mode when
|
||||
// decrypting and for TLS 1.3 datagram to recover the sequence number.
|
||||
//
|
||||
// We reset the counter when the cipher spec changes, but that notification
|
||||
// appears before a record is sent. If multiple records are sent with
|
||||
// different cipher specs, this would fail. This filters out cleartext
|
||||
// records, so we don't get confused by handshake messages that are sent at
|
||||
// the same time as encrypted records. Sequence numbers are therefore
|
||||
// likely to be incorrect for cleartext records.
|
||||
//
|
||||
// This isn't perfectly robust: if there is a change from an active cipher
|
||||
// spec to another active cipher spec (KeyUpdate for instance) AND writes
|
||||
// are consolidated across that change, this code could use the wrong
|
||||
// sequence numbers when re-encrypting records with the old keys.
|
||||
if (header.content_type() == ssl_ct_application_data) {
|
||||
in_sequence_number_ =
|
||||
(std::max)(in_sequence_number_, header.sequence_number() + 1);
|
||||
}
|
||||
|
||||
if (FilterRecord(header, record, &offset, output) != KEEP) {
|
||||
changed = true;
|
||||
} else {
|
||||
|
|
@ -159,14 +195,16 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
DataBuffer filtered;
|
||||
uint8_t inner_content_type;
|
||||
DataBuffer plaintext;
|
||||
uint16_t protection_epoch = 0;
|
||||
|
||||
if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << "unprotect failed: " << header << ":" << record << std::endl;
|
||||
}
|
||||
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
|
||||
&plaintext)) {
|
||||
std::cerr << agent()->role_str() << ": unprotect failed: " << header << ":"
|
||||
<< record << std::endl;
|
||||
return KEEP;
|
||||
}
|
||||
|
||||
auto& protection_spec = spec(protection_epoch);
|
||||
TlsRecordHeader real_header(header.variant(), header.version(),
|
||||
inner_content_type, header.sequence_number());
|
||||
|
||||
|
|
@ -174,7 +212,9 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
// In stream mode, even if something doesn't change we need to re-encrypt if
|
||||
// previous packets were dropped.
|
||||
if (action == KEEP) {
|
||||
if (header.is_dtls() || !dropped_record_) {
|
||||
if (header.is_dtls() || !protection_spec.record_dropped()) {
|
||||
// Count every outgoing packet.
|
||||
protection_spec.RecordProtected();
|
||||
return KEEP;
|
||||
}
|
||||
filtered = plaintext;
|
||||
|
|
@ -182,7 +222,7 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
|
||||
if (action == DROP) {
|
||||
std::cerr << "record drop: " << header << ":" << record << std::endl;
|
||||
dropped_record_ = true;
|
||||
protection_spec.RecordDropped();
|
||||
return DROP;
|
||||
}
|
||||
|
||||
|
|
@ -192,19 +232,18 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
|
|||
std::cerr << "record new: " << filtered << std::endl;
|
||||
}
|
||||
|
||||
uint64_t seq_num;
|
||||
if (header.is_dtls() || !cipher_spec_ ||
|
||||
header.content_type() != ssl_ct_application_data) {
|
||||
seq_num = header.sequence_number();
|
||||
} else {
|
||||
seq_num = out_sequence_number_++;
|
||||
uint64_t seq_num = protection_spec.next_out_seqno();
|
||||
if (!decrypting_ && header.is_dtls()) {
|
||||
// Copy over the epoch, which isn't tracked when not decrypting.
|
||||
seq_num |= header.sequence_number() & (0xffffULL << 48);
|
||||
}
|
||||
|
||||
TlsRecordHeader out_header(header.variant(), header.version(),
|
||||
header.content_type(), seq_num);
|
||||
|
||||
DataBuffer ciphertext;
|
||||
bool rv = Protect(out_header, inner_content_type, filtered, &ciphertext);
|
||||
EXPECT_TRUE(rv);
|
||||
bool rv = Protect(protection_spec, out_header, inner_content_type, filtered,
|
||||
&ciphertext);
|
||||
if (!rv) {
|
||||
return KEEP;
|
||||
}
|
||||
|
|
@ -227,15 +266,20 @@ uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected,
|
|||
uint32_t partial,
|
||||
size_t partial_bits) {
|
||||
EXPECT_GE(32U, partial_bits);
|
||||
uint64_t mask = (1 << partial_bits) - 1;
|
||||
uint64_t mask = (1ULL << partial_bits) - 1;
|
||||
// First we determine the highest possible value. This is half the
|
||||
// expressible range above the expected value.
|
||||
uint64_t cap = expected + (1ULL << (partial_bits - 1));
|
||||
// expressible range above the expected value, less 1.
|
||||
//
|
||||
// We subtract the extra 1 from the cap so that when given a choice between
|
||||
// the equidistant expected+N and expected-N we want to chose the lower. With
|
||||
// 0-RTT, we sometimes have to recover an epoch of 1 when we expect an epoch
|
||||
// of 3 and with 2 partial bits, the alternative result of 5 is wrong.
|
||||
uint64_t cap = expected + (1ULL << (partial_bits - 1)) - 1;
|
||||
// Add the partial piece in. e.g., xxxx789a and 1234 becomes xxxx1234.
|
||||
uint64_t seq_no = (cap & ~mask) | partial;
|
||||
// If the partial value is higher than the same partial piece from the cap,
|
||||
// then the real value has to be lower. e.g., xxxx1234 can't become xxxx5678.
|
||||
if (partial > (cap & mask)) {
|
||||
if (partial > (cap & mask) && (seq_no >= (1ULL << partial_bits))) {
|
||||
seq_no -= 1ULL << partial_bits;
|
||||
}
|
||||
return seq_no;
|
||||
|
|
@ -375,16 +419,41 @@ size_t TlsRecordHeader::Write(DataBuffer* buffer, size_t offset,
|
|||
|
||||
bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
|
||||
const DataBuffer& ciphertext,
|
||||
uint16_t* protection_epoch,
|
||||
uint8_t* inner_content_type,
|
||||
DataBuffer* plaintext) {
|
||||
if (!cipher_spec_ || header.content_type() != ssl_ct_application_data) {
|
||||
if (!decrypting_ || header.content_type() != ssl_ct_application_data) {
|
||||
// Maintain the epoch and sequence number for plaintext records.
|
||||
uint16_t ep = 0;
|
||||
if (agent()->variant() == ssl_variant_datagram) {
|
||||
ep = static_cast<uint16_t>(header.sequence_number() >> 48);
|
||||
}
|
||||
spec(ep).RecordUnprotected(header.sequence_number());
|
||||
*protection_epoch = ep;
|
||||
*inner_content_type = header.content_type();
|
||||
*plaintext = ciphertext;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) {
|
||||
return false;
|
||||
uint16_t ep = 0;
|
||||
if (agent()->variant() == ssl_variant_datagram) {
|
||||
ep = static_cast<uint16_t>(header.sequence_number() >> 48);
|
||||
if (!spec(ep).Unprotect(header, ciphertext, plaintext)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// In TLS, records aren't clearly labelled with their epoch, and we
|
||||
// can't just use the newest keys because the same flight of messages can
|
||||
// contain multiple epochs. So... trial decrypt!
|
||||
for (size_t i = cipher_specs_.size() - 1; i > 0; --i) {
|
||||
if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext)) {
|
||||
ep = cipher_specs_[i].epoch();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ep) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = plaintext->len();
|
||||
|
|
@ -396,33 +465,45 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
|
|||
return false;
|
||||
}
|
||||
|
||||
*protection_epoch = ep;
|
||||
*inner_content_type = plaintext->data()[len - 1];
|
||||
plaintext->Truncate(len - 1);
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << "unprotect: " << std::hex << header.sequence_number()
|
||||
<< std::dec << " type=" << static_cast<int>(*inner_content_type)
|
||||
std::cerr << agent()->role_str() << ": unprotect: epoch=" << ep
|
||||
<< " seq=" << std::hex << header.sequence_number() << std::dec
|
||||
<< " " << *plaintext << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TlsRecordFilter::Protect(const TlsRecordHeader& header,
|
||||
bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec,
|
||||
const TlsRecordHeader& header,
|
||||
uint8_t inner_content_type,
|
||||
const DataBuffer& plaintext,
|
||||
DataBuffer* ciphertext, size_t padding) {
|
||||
if (!cipher_spec_ || header.content_type() != ssl_ct_application_data) {
|
||||
if (!protection_spec.is_protected()) {
|
||||
// Not protected, just keep the sequence numbers updated.
|
||||
protection_spec.RecordProtected();
|
||||
*ciphertext = plaintext;
|
||||
return true;
|
||||
}
|
||||
if (g_ssl_gtest_verbose) {
|
||||
std::cerr << "protect: " << header.sequence_number() << std::endl;
|
||||
}
|
||||
|
||||
DataBuffer padded;
|
||||
padded.Allocate(plaintext.len() + 1 + padding);
|
||||
size_t offset = padded.Write(0, plaintext.data(), plaintext.len());
|
||||
padded.Write(offset, inner_content_type, 1);
|
||||
return cipher_spec_->Protect(header, padded, ciphertext);
|
||||
|
||||
bool ok = protection_spec.Protect(header, padded, ciphertext);
|
||||
if (!ok) {
|
||||
ADD_FAILURE() << "protect fail";
|
||||
} else if (g_ssl_gtest_verbose) {
|
||||
std::cerr << agent()->role_str()
|
||||
<< ": protect: epoch=" << protection_spec.epoch()
|
||||
<< " seq=" << std::hex << header.sequence_number() << std::dec
|
||||
<< " " << *ciphertext << std::endl;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool IsHelloRetry(const DataBuffer& body) {
|
||||
|
|
|
|||
|
|
@ -97,13 +97,7 @@ inline std::shared_ptr<T> MakeTlsFilter(const std::shared_ptr<TlsAgent>& agent,
|
|||
// Abstract filter that operates on entire (D)TLS records.
|
||||
class TlsRecordFilter : public PacketFilter {
|
||||
public:
|
||||
TlsRecordFilter(const std::shared_ptr<TlsAgent>& a)
|
||||
: agent_(a),
|
||||
count_(0),
|
||||
cipher_spec_(),
|
||||
dropped_record_(false),
|
||||
in_sequence_number_(0),
|
||||
out_sequence_number_(0) {}
|
||||
TlsRecordFilter(const std::shared_ptr<TlsAgent>& a);
|
||||
|
||||
std::shared_ptr<TlsAgent> agent() const { return agent_.lock(); }
|
||||
|
||||
|
|
@ -118,10 +112,11 @@ class TlsRecordFilter : public PacketFilter {
|
|||
// behavior.
|
||||
void EnableDecryption();
|
||||
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText,
|
||||
uint8_t* inner_content_type, DataBuffer* plaintext);
|
||||
bool Protect(const TlsRecordHeader& header, uint8_t inner_content_type,
|
||||
const DataBuffer& plaintext, DataBuffer* ciphertext,
|
||||
size_t padding = 0);
|
||||
uint16_t* protection_epoch, uint8_t* inner_content_type,
|
||||
DataBuffer* plaintext);
|
||||
bool Protect(TlsCipherSpec& protection_spec, const TlsRecordHeader& header,
|
||||
uint8_t inner_content_type, const DataBuffer& plaintext,
|
||||
DataBuffer* ciphertext, size_t padding = 0);
|
||||
|
||||
protected:
|
||||
// There are two filter functions which can be overriden. Both are
|
||||
|
|
@ -146,20 +141,17 @@ class TlsRecordFilter : public PacketFilter {
|
|||
}
|
||||
|
||||
bool is_dtls13() const;
|
||||
TlsCipherSpec& spec(uint16_t epoch);
|
||||
|
||||
private:
|
||||
static void CipherSpecChanged(void* arg, PRBool sending,
|
||||
ssl3CipherSpec* newSpec);
|
||||
static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
|
||||
SSLSecretDirection dir, PK11SymKey* secret,
|
||||
void* arg);
|
||||
|
||||
std::weak_ptr<TlsAgent> agent_;
|
||||
size_t count_;
|
||||
std::unique_ptr<TlsCipherSpec> cipher_spec_;
|
||||
// Whether we dropped a record since the cipher spec changed.
|
||||
bool dropped_record_;
|
||||
// The sequence number we use for reading records as they are written.
|
||||
uint64_t in_sequence_number_;
|
||||
// The sequence number we use for writing modified records.
|
||||
uint64_t out_sequence_number_;
|
||||
size_t count_ = 0;
|
||||
std::vector<TlsCipherSpec> cipher_specs_;
|
||||
bool decrypting_ = false;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& stream, const TlsVersioned& v) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#include "tls_protect.h"
|
||||
#include "tls_filter.h"
|
||||
|
||||
// Do this to avoid having to re-implement HKDF.
|
||||
#include "tls13hkdf.h"
|
||||
|
||||
namespace nss_test {
|
||||
|
||||
AeadCipher::~AeadCipher() {
|
||||
|
|
@ -15,32 +18,37 @@ AeadCipher::~AeadCipher() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AeadCipher::Init(PK11SymKey *key, const uint8_t *iv) {
|
||||
key_ = PK11_ReferenceSymKey(key);
|
||||
bool AeadCipher::Init(PK11SymKey* key, const uint8_t* iv) {
|
||||
key_ = key;
|
||||
if (!key_) return false;
|
||||
|
||||
memcpy(iv_, iv, sizeof(iv_));
|
||||
if (g_ssl_gtest_verbose) {
|
||||
EXPECT_EQ(SECSuccess, PK11_ExtractKeyValue(key_));
|
||||
SECItem* raw_key = PK11_GetKeyData(key_);
|
||||
std::cerr << "key: " << DataBuffer(raw_key->data, raw_key->len)
|
||||
<< std::endl;
|
||||
std::cerr << "iv: " << DataBuffer(iv_, 12) << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AeadCipher::FormatNonce(uint64_t seq, uint8_t *nonce) {
|
||||
void AeadCipher::FormatNonce(uint64_t seq, uint8_t* nonce) {
|
||||
memcpy(nonce, iv_, 12);
|
||||
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
nonce[12 - (i + 1)] ^= seq & 0xff;
|
||||
seq >>= 8;
|
||||
}
|
||||
|
||||
DataBuffer d(nonce, 12);
|
||||
}
|
||||
|
||||
bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
|
||||
const uint8_t *in, size_t inlen, uint8_t *out,
|
||||
size_t *outlen, size_t maxlen) {
|
||||
bool AeadCipher::AeadInner(bool decrypt, void* params, size_t param_length,
|
||||
const uint8_t* in, size_t inlen, uint8_t* out,
|
||||
size_t* outlen, size_t maxlen) {
|
||||
SECStatus rv;
|
||||
unsigned int uoutlen = 0;
|
||||
SECItem param = {
|
||||
siBuffer, static_cast<unsigned char *>(params),
|
||||
siBuffer, static_cast<unsigned char*>(params),
|
||||
static_cast<unsigned int>(param_length),
|
||||
};
|
||||
|
||||
|
|
@ -54,28 +62,28 @@ bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
|
|||
return rv == SECSuccess;
|
||||
}
|
||||
|
||||
bool AeadCipherAesGcm::Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
|
||||
uint64_t seq, const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen, size_t maxlen) {
|
||||
bool AeadCipherAesGcm::Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len,
|
||||
uint64_t seq, const uint8_t* in, size_t inlen,
|
||||
uint8_t* out, size_t* outlen, size_t maxlen) {
|
||||
CK_GCM_PARAMS aeadParams;
|
||||
unsigned char nonce[12];
|
||||
|
||||
memset(&aeadParams, 0, sizeof(aeadParams));
|
||||
aeadParams.pIv = nonce;
|
||||
aeadParams.ulIvLen = sizeof(nonce);
|
||||
aeadParams.pAAD = const_cast<uint8_t *>(hdr);
|
||||
aeadParams.pAAD = const_cast<uint8_t*>(hdr);
|
||||
aeadParams.ulAADLen = hdr_len;
|
||||
aeadParams.ulTagBits = 128;
|
||||
|
||||
FormatNonce(seq, nonce);
|
||||
return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
|
||||
in, inlen, out, outlen, maxlen);
|
||||
return AeadInner(decrypt, (unsigned char*)&aeadParams, sizeof(aeadParams), in,
|
||||
inlen, out, outlen, maxlen);
|
||||
}
|
||||
|
||||
bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t *hdr,
|
||||
bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t* hdr,
|
||||
size_t hdr_len, uint64_t seq,
|
||||
const uint8_t *in, size_t inlen,
|
||||
uint8_t *out, size_t *outlen,
|
||||
const uint8_t* in, size_t inlen,
|
||||
uint8_t* out, size_t* outlen,
|
||||
size_t maxlen) {
|
||||
CK_NSS_AEAD_PARAMS aeadParams;
|
||||
unsigned char nonce[12];
|
||||
|
|
@ -83,67 +91,126 @@ bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t *hdr,
|
|||
memset(&aeadParams, 0, sizeof(aeadParams));
|
||||
aeadParams.pNonce = nonce;
|
||||
aeadParams.ulNonceLen = sizeof(nonce);
|
||||
aeadParams.pAAD = const_cast<uint8_t *>(hdr);
|
||||
aeadParams.pAAD = const_cast<uint8_t*>(hdr);
|
||||
aeadParams.ulAADLen = hdr_len;
|
||||
aeadParams.ulTagLen = 16;
|
||||
|
||||
FormatNonce(seq, nonce);
|
||||
return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
|
||||
in, inlen, out, outlen, maxlen);
|
||||
return AeadInner(decrypt, (unsigned char*)&aeadParams, sizeof(aeadParams), in,
|
||||
inlen, out, outlen, maxlen);
|
||||
}
|
||||
|
||||
bool TlsCipherSpec::Init(uint16_t epoc, SSLCipherAlgorithm cipher,
|
||||
PK11SymKey *key, const uint8_t *iv) {
|
||||
epoch_ = epoc;
|
||||
switch (cipher) {
|
||||
static uint64_t FirstSeqno(bool dtls, uint16_t epoc) {
|
||||
if (dtls) {
|
||||
return static_cast<uint64_t>(epoc) << 48;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TlsCipherSpec::TlsCipherSpec(bool dtls, uint16_t epoc)
|
||||
: dtls_(dtls),
|
||||
epoch_(epoc),
|
||||
in_seqno_(FirstSeqno(dtls, epoc)),
|
||||
out_seqno_(FirstSeqno(dtls, epoc)) {}
|
||||
|
||||
bool TlsCipherSpec::SetKeys(SSLCipherSuiteInfo* cipherinfo,
|
||||
PK11SymKey* secret) {
|
||||
CK_MECHANISM_TYPE mech;
|
||||
switch (cipherinfo->symCipher) {
|
||||
case ssl_calg_aes_gcm:
|
||||
aead_.reset(new AeadCipherAesGcm());
|
||||
mech = CKM_AES_GCM;
|
||||
break;
|
||||
case ssl_calg_chacha20:
|
||||
aead_.reset(new AeadCipherChacha20Poly1305());
|
||||
mech = CKM_NSS_CHACHA20_POLY1305;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
PK11SymKey* key;
|
||||
const std::string kPurposeKey = "key";
|
||||
SECStatus rv = tls13_HkdfExpandLabel(
|
||||
secret, cipherinfo->kdfHash, NULL, 0, kPurposeKey.c_str(),
|
||||
kPurposeKey.length(), mech, cipherinfo->symKeyBits / 8, &key);
|
||||
if (rv != SECSuccess) {
|
||||
ADD_FAILURE() << "unable to derive key for epoch " << epoch_;
|
||||
return false;
|
||||
}
|
||||
|
||||
// No constant for IV length, but everything we know of uses 12.
|
||||
uint8_t iv[12];
|
||||
const std::string kPurposeIv = "iv";
|
||||
rv = tls13_HkdfExpandLabelRaw(secret, cipherinfo->kdfHash, NULL, 0,
|
||||
kPurposeIv.c_str(), kPurposeIv.length(), iv,
|
||||
sizeof(iv));
|
||||
if (rv != SECSuccess) {
|
||||
ADD_FAILURE() << "unable to derive IV for epoch " << epoch_;
|
||||
return false;
|
||||
}
|
||||
|
||||
return aead_->Init(key, iv);
|
||||
}
|
||||
|
||||
bool TlsCipherSpec::Unprotect(const TlsRecordHeader &header,
|
||||
const DataBuffer &ciphertext,
|
||||
DataBuffer *plaintext) {
|
||||
bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header,
|
||||
const DataBuffer& ciphertext,
|
||||
DataBuffer* plaintext) {
|
||||
if (aead_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// Make space.
|
||||
plaintext->Allocate(ciphertext.len());
|
||||
|
||||
auto header_bytes = header.header();
|
||||
size_t len;
|
||||
bool ret =
|
||||
aead_->Aead(true, header_bytes.data(), header_bytes.len(),
|
||||
header.sequence_number(), ciphertext.data(), ciphertext.len(),
|
||||
plaintext->data(), &len, plaintext->len());
|
||||
if (!ret) return false;
|
||||
uint64_t seqno;
|
||||
if (dtls_) {
|
||||
seqno = header.sequence_number();
|
||||
} else {
|
||||
seqno = in_seqno_;
|
||||
}
|
||||
bool ret = aead_->Aead(true, header_bytes.data(), header_bytes.len(), seqno,
|
||||
ciphertext.data(), ciphertext.len(), plaintext->data(),
|
||||
&len, plaintext->len());
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecordUnprotected(seqno);
|
||||
plaintext->Truncate(len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TlsCipherSpec::Protect(const TlsRecordHeader &header,
|
||||
const DataBuffer &plaintext,
|
||||
DataBuffer *ciphertext) {
|
||||
bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
|
||||
const DataBuffer& plaintext,
|
||||
DataBuffer* ciphertext) {
|
||||
if (aead_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// Make a padded buffer.
|
||||
|
||||
ciphertext->Allocate(plaintext.len() +
|
||||
32); // Room for any plausible auth tag
|
||||
size_t len;
|
||||
|
||||
DataBuffer header_bytes;
|
||||
(void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16);
|
||||
bool ret =
|
||||
aead_->Aead(false, header_bytes.data(), header_bytes.len(),
|
||||
header.sequence_number(), plaintext.data(), plaintext.len(),
|
||||
ciphertext->data(), &len, ciphertext->len());
|
||||
if (!ret) return false;
|
||||
uint64_t seqno;
|
||||
if (dtls_) {
|
||||
seqno = header.sequence_number();
|
||||
} else {
|
||||
seqno = out_seqno_;
|
||||
}
|
||||
|
||||
bool ret = aead_->Aead(false, header_bytes.data(), header_bytes.len(), seqno,
|
||||
plaintext.data(), plaintext.len(), ciphertext->data(),
|
||||
&len, ciphertext->len());
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecordProtected();
|
||||
ciphertext->Truncate(len);
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -22,19 +22,19 @@ class AeadCipher {
|
|||
AeadCipher(CK_MECHANISM_TYPE mech) : mech_(mech), key_(nullptr) {}
|
||||
virtual ~AeadCipher();
|
||||
|
||||
bool Init(PK11SymKey *key, const uint8_t *iv);
|
||||
virtual bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
|
||||
uint64_t seq, const uint8_t *in, size_t inlen, uint8_t *out,
|
||||
size_t *outlen, size_t maxlen) = 0;
|
||||
bool Init(PK11SymKey* key, const uint8_t* iv);
|
||||
virtual bool Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len,
|
||||
uint64_t seq, const uint8_t* in, size_t inlen, uint8_t* out,
|
||||
size_t* outlen, size_t maxlen) = 0;
|
||||
|
||||
protected:
|
||||
void FormatNonce(uint64_t seq, uint8_t *nonce);
|
||||
bool AeadInner(bool decrypt, void *params, size_t param_length,
|
||||
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
|
||||
void FormatNonce(uint64_t seq, uint8_t* nonce);
|
||||
bool AeadInner(bool decrypt, void* params, size_t param_length,
|
||||
const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen,
|
||||
size_t maxlen);
|
||||
|
||||
CK_MECHANISM_TYPE mech_;
|
||||
PK11SymKey *key_;
|
||||
PK11SymKey* key_;
|
||||
uint8_t iv_[12];
|
||||
};
|
||||
|
||||
|
|
@ -43,8 +43,8 @@ class AeadCipherChacha20Poly1305 : public AeadCipher {
|
|||
AeadCipherChacha20Poly1305() : AeadCipher(CKM_NSS_CHACHA20_POLY1305) {}
|
||||
|
||||
protected:
|
||||
bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
|
||||
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
|
||||
bool Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len, uint64_t seq,
|
||||
const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen,
|
||||
size_t maxlen);
|
||||
};
|
||||
|
||||
|
|
@ -53,27 +53,42 @@ class AeadCipherAesGcm : public AeadCipher {
|
|||
AeadCipherAesGcm() : AeadCipher(CKM_AES_GCM) {}
|
||||
|
||||
protected:
|
||||
bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
|
||||
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
|
||||
bool Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len, uint64_t seq,
|
||||
const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen,
|
||||
size_t maxlen);
|
||||
};
|
||||
|
||||
// Our analog of ssl3CipherSpec
|
||||
class TlsCipherSpec {
|
||||
public:
|
||||
TlsCipherSpec() : epoch_(0), aead_() {}
|
||||
TlsCipherSpec(bool dtls, uint16_t epoc);
|
||||
bool SetKeys(SSLCipherSuiteInfo* cipherinfo, PK11SymKey* secret);
|
||||
|
||||
bool Init(uint16_t epoch, SSLCipherAlgorithm cipher, PK11SymKey *key,
|
||||
const uint8_t *iv);
|
||||
bool Protect(const TlsRecordHeader& header, const DataBuffer& plaintext,
|
||||
DataBuffer* ciphertext);
|
||||
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext,
|
||||
DataBuffer* plaintext);
|
||||
|
||||
bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
|
||||
DataBuffer *ciphertext);
|
||||
bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
|
||||
DataBuffer *plaintext);
|
||||
uint16_t epoch() const { return epoch_; }
|
||||
uint64_t next_in_seqno() const { return in_seqno_; }
|
||||
void RecordUnprotected(uint64_t seqno) {
|
||||
// Reordering happens, so don't let this go backwards.
|
||||
in_seqno_ = (std::max)(in_seqno_, seqno + 1);
|
||||
}
|
||||
uint64_t next_out_seqno() { return out_seqno_; }
|
||||
void RecordProtected() { out_seqno_++; }
|
||||
|
||||
void RecordDropped() { record_dropped_ = true; }
|
||||
bool record_dropped() const { return record_dropped_; }
|
||||
|
||||
bool is_protected() const { return aead_ != nullptr; }
|
||||
|
||||
private:
|
||||
bool dtls_;
|
||||
uint16_t epoch_;
|
||||
uint64_t in_seqno_;
|
||||
uint64_t out_seqno_;
|
||||
bool record_dropped_ = false;
|
||||
std::unique_ptr<AeadCipher> aead_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1677,6 +1677,92 @@ PK11_MakeKEAPubKey(unsigned char *keyData, int length)
|
|||
return pubk;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SECKEY_SetPublicValue(SECKEYPrivateKey *privKey, SECItem *publicValue)
|
||||
{
|
||||
SECStatus rv;
|
||||
SECKEYPublicKey pubKey;
|
||||
PLArenaPool *arena;
|
||||
PK11SlotInfo *slot = privKey->pkcs11Slot;
|
||||
CK_OBJECT_HANDLE privKeyID = privKey->pkcs11ID;
|
||||
|
||||
if (privKey == NULL || publicValue == NULL ||
|
||||
publicValue->data == NULL || publicValue->len == 0) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
pubKey.arena = NULL;
|
||||
pubKey.keyType = privKey->keyType;
|
||||
pubKey.pkcs11Slot = NULL;
|
||||
pubKey.pkcs11ID = CK_INVALID_HANDLE;
|
||||
/* can't use PORT_InitCheapArena here becase SECKEY_DestroyPublic is used
|
||||
* to free it, and it uses PORT_FreeArena which not only frees the
|
||||
* underlying arena, it also frees the allocated arena struct. */
|
||||
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||||
pubKey.arena = arena;
|
||||
if (arena == NULL) {
|
||||
return SECFailure;
|
||||
}
|
||||
rv = SECFailure;
|
||||
switch (privKey->keyType) {
|
||||
default:
|
||||
/* error code already set to SECFailure */
|
||||
break;
|
||||
case rsaKey:
|
||||
pubKey.u.rsa.modulus = *publicValue;
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_PUBLIC_EXPONENT,
|
||||
arena, &pubKey.u.rsa.publicExponent);
|
||||
break;
|
||||
case dsaKey:
|
||||
pubKey.u.dsa.publicValue = *publicValue;
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_PRIME,
|
||||
arena, &pubKey.u.dsa.params.prime);
|
||||
if (rv != SECSuccess) {
|
||||
break;
|
||||
}
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_SUBPRIME,
|
||||
arena, &pubKey.u.dsa.params.subPrime);
|
||||
if (rv != SECSuccess) {
|
||||
break;
|
||||
}
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_BASE,
|
||||
arena, &pubKey.u.dsa.params.base);
|
||||
break;
|
||||
case dhKey:
|
||||
pubKey.u.dh.publicValue = *publicValue;
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_PRIME,
|
||||
arena, &pubKey.u.dh.prime);
|
||||
if (rv != SECSuccess) {
|
||||
break;
|
||||
}
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_BASE,
|
||||
arena, &pubKey.u.dh.base);
|
||||
break;
|
||||
case ecKey:
|
||||
pubKey.u.ec.publicValue = *publicValue;
|
||||
pubKey.u.ec.encoding = ECPoint_Undefined;
|
||||
pubKey.u.ec.size = 0;
|
||||
rv = PK11_ReadAttribute(slot, privKeyID, CKA_EC_PARAMS,
|
||||
arena, &pubKey.u.ec.DEREncodedParams);
|
||||
break;
|
||||
}
|
||||
if (rv == SECSuccess) {
|
||||
rv = PK11_ImportPublicKey(slot, &pubKey, PR_TRUE);
|
||||
}
|
||||
/* Even though pubKey is stored on the stack, we've allocated
|
||||
* some of it's data from the arena. SECKEY_DestroyPublicKey
|
||||
* destroys keys by freeing the arena, so this will clean up all
|
||||
* the data we allocated specifically for the key above. It will
|
||||
* also free any slot references which we may have picked up in
|
||||
* PK11_ImportPublicKey. It won't delete the underlying key if
|
||||
* its a Token/Permanent key (which it will be if
|
||||
* PK11_ImportPublicKey succeeds). */
|
||||
SECKEY_DestroyPublicKey(&pubKey);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: This function doesn't return a SECKEYPrivateKey struct to represent
|
||||
* the new private key object. If it were to create a session object that
|
||||
|
|
@ -1802,12 +1888,6 @@ try_faulty_3des:
|
|||
nickname, publicValue, isPerm, isPrivate,
|
||||
key_type, usage, usageCount, wincx);
|
||||
if (privKey) {
|
||||
if (privk) {
|
||||
*privk = privKey;
|
||||
} else {
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
}
|
||||
privKey = NULL;
|
||||
rv = SECSuccess;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1837,6 +1917,25 @@ try_faulty_3des:
|
|||
rv = SECFailure;
|
||||
|
||||
done:
|
||||
if ((rv == SECSuccess) && isPerm) {
|
||||
/* If we are importing a token object,
|
||||
* create the corresponding public key.
|
||||
* If this fails, just continue as the target
|
||||
* token simply might not support persistant
|
||||
* public keys. Such tokens are usable, but
|
||||
* need to be authenticated before searching
|
||||
* for user certs. */
|
||||
(void)SECKEY_SetPublicValue(privKey, publicValue);
|
||||
}
|
||||
|
||||
if (privKey) {
|
||||
if (privk) {
|
||||
*privk = privKey;
|
||||
} else {
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
}
|
||||
privKey = NULL;
|
||||
}
|
||||
if (crypto_param != NULL) {
|
||||
SECITEM_ZfreeItem(crypto_param, PR_TRUE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1815,8 +1815,6 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type,
|
|||
break; /* key was not DER encoded, no need to unwrap */
|
||||
}
|
||||
|
||||
PORT_Assert(pubKey->u.ec.ecParams.name != ECCurve25519);
|
||||
|
||||
/* handle the encoded case */
|
||||
if ((pubKey->u.ec.publicValue.data[0] == SEC_ASN1_OCTET_STRING) &&
|
||||
pubKey->u.ec.publicValue.len > keyLen) {
|
||||
|
|
@ -1827,7 +1825,13 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type,
|
|||
SEC_ASN1_GET(SEC_OctetStringTemplate),
|
||||
&pubKey->u.ec.publicValue);
|
||||
/* nope, didn't decode correctly */
|
||||
if ((rv != SECSuccess) || (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) || (publicValue.len != keyLen)) {
|
||||
if ((rv != SECSuccess) || (publicValue.len != keyLen)) {
|
||||
crv = CKR_ATTRIBUTE_VALUE_INVALID;
|
||||
break;
|
||||
}
|
||||
/* we don't handle compressed points except in the case of ECCurve25519 */
|
||||
if ((pubKey->u.ec.ecParams.fieldID.type != ec_field_plain) &&
|
||||
(publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED)) {
|
||||
crv = CKR_ATTRIBUTE_VALUE_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@ dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf)
|
|||
* for the holddown period to process retransmitted Finisheds.
|
||||
*/
|
||||
if (!ss->sec.isServer && (ss->ssl3.hs.ws == idle_handshake)) {
|
||||
ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead,
|
||||
ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read,
|
||||
TrafficKeyHandshake);
|
||||
}
|
||||
}
|
||||
|
|
@ -509,6 +509,6 @@ dtls13_HolddownTimerCb(sslSocket *ss)
|
|||
{
|
||||
SSL_TRC(10, ("%d: SSL3[%d]: holddown timer fired",
|
||||
SSL_GETPID(), ss->fd));
|
||||
ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyHandshake);
|
||||
ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyHandshake);
|
||||
ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,7 +542,6 @@ dtls_QueueMessage(sslSocket *ss, SSLContentType ct,
|
|||
|
||||
/* Add DTLS handshake message to the pending queue
|
||||
* Empty the sendBuf buffer.
|
||||
* This function returns SECSuccess or SECFailure, never SECWouldBlock.
|
||||
* Always set sendBuf.len to 0, even when returning SECFailure.
|
||||
*
|
||||
* Called from:
|
||||
|
|
|
|||
|
|
@ -299,6 +299,17 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
|
|||
* This is disabled by default and will be removed in a future version. */
|
||||
#define SSL_ENABLE_V2_COMPATIBLE_HELLO 38
|
||||
|
||||
/* Enables the post-handshake authentication in TLS 1.3. If it is set
|
||||
* to PR_TRUE, the client will send the "post_handshake_auth"
|
||||
* extension to indicate that it will process CertificateRequest
|
||||
* messages after handshake.
|
||||
*
|
||||
* This option applies only to clients. For a server, the
|
||||
* SSL_SendCertificateRequest can be used to request post-handshake
|
||||
* authentication.
|
||||
*/
|
||||
#define SSL_ENABLE_POST_HANDSHAKE_AUTH 39
|
||||
|
||||
#ifdef SSL_DEPRECATED_FUNCTION
|
||||
/* Old deprecated function names */
|
||||
SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on);
|
||||
|
|
|
|||
|
|
@ -1394,14 +1394,14 @@ loser:
|
|||
}
|
||||
|
||||
static SECStatus
|
||||
ssl3_SetupPendingCipherSpec(sslSocket *ss, CipherSpecDirection direction,
|
||||
ssl3_SetupPendingCipherSpec(sslSocket *ss, SSLSecretDirection direction,
|
||||
const ssl3CipherSuiteDef *suiteDef,
|
||||
ssl3CipherSpec **specp)
|
||||
{
|
||||
ssl3CipherSpec *spec;
|
||||
const ssl3CipherSpec *prev;
|
||||
|
||||
prev = (direction == CipherSpecWrite) ? ss->ssl3.cwSpec : ss->ssl3.crSpec;
|
||||
prev = (direction == ssl_secret_write) ? ss->ssl3.cwSpec : ss->ssl3.crSpec;
|
||||
if (prev->epoch == PR_UINT16_MAX) {
|
||||
PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
|
||||
return SECFailure;
|
||||
|
|
@ -1417,7 +1417,7 @@ ssl3_SetupPendingCipherSpec(sslSocket *ss, CipherSpecDirection direction,
|
|||
|
||||
spec->epoch = prev->epoch + 1;
|
||||
spec->nextSeqNum = 0;
|
||||
if (IS_DTLS(ss) && direction == CipherSpecRead) {
|
||||
if (IS_DTLS(ss) && direction == ssl_secret_read) {
|
||||
dtls_InitRecvdRecords(&spec->recvdRecords);
|
||||
}
|
||||
ssl_SetSpecVersions(ss, spec);
|
||||
|
|
@ -1471,12 +1471,12 @@ ssl3_SetupBothPendingCipherSpecs(sslSocket *ss)
|
|||
ss->ssl3.hs.kea_def = &kea_defs[kea];
|
||||
PORT_Assert(ss->ssl3.hs.kea_def->kea == kea);
|
||||
|
||||
rv = ssl3_SetupPendingCipherSpec(ss, CipherSpecRead, suiteDef,
|
||||
rv = ssl3_SetupPendingCipherSpec(ss, ssl_secret_read, suiteDef,
|
||||
&ss->ssl3.prSpec);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
rv = ssl3_SetupPendingCipherSpec(ss, CipherSpecWrite, suiteDef,
|
||||
rv = ssl3_SetupPendingCipherSpec(ss, ssl_secret_write, suiteDef,
|
||||
&ss->ssl3.pwSpec);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
|
|
@ -1727,7 +1727,7 @@ ssl3_InitPendingContexts(sslSocket *ss, ssl3CipherSpec *spec)
|
|||
|
||||
spec->cipher = (SSLCipher)PK11_CipherOp;
|
||||
encMechanism = ssl3_Alg2Mech(calg);
|
||||
encMode = (spec->direction == CipherSpecWrite) ? CKA_ENCRYPT : CKA_DECRYPT;
|
||||
encMode = (spec->direction == ssl_secret_write) ? CKA_ENCRYPT : CKA_DECRYPT;
|
||||
|
||||
/*
|
||||
* build the context
|
||||
|
|
@ -2215,7 +2215,7 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSLContentType ct,
|
|||
unsigned int lenOffset;
|
||||
SECStatus rv;
|
||||
|
||||
PORT_Assert(cwSpec->direction == CipherSpecWrite);
|
||||
PORT_Assert(cwSpec->direction == ssl_secret_write);
|
||||
PORT_Assert(SSL_BUFFER_LEN(wrBuf) == 0);
|
||||
PORT_Assert(cwSpec->cipherDef->max_records <= RECORD_SEQ_MAX);
|
||||
|
||||
|
|
@ -2314,8 +2314,8 @@ ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSLContentType ct,
|
|||
* Returns the number of bytes of plaintext that were successfully sent
|
||||
* plus the number of bytes of plaintext that were copied into the
|
||||
* output (write) buffer.
|
||||
* Returns SECFailure on a hard IO error, memory error, or crypto error.
|
||||
* Does NOT return SECWouldBlock.
|
||||
* Returns -1 on an error. PR_WOULD_BLOCK_ERROR is set if the error is blocking
|
||||
* and not terminal.
|
||||
*
|
||||
* Notes on the use of the private ssl flags:
|
||||
* (no private SSL flags)
|
||||
|
|
@ -2360,13 +2360,26 @@ ssl3_SendRecord(sslSocket *ss,
|
|||
* error, so don't overwrite. */
|
||||
PORT_SetError(SSL_ERROR_HANDSHAKE_FAILED);
|
||||
}
|
||||
return SECFailure;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check for Token Presence */
|
||||
if (!ssl3_ClientAuthTokenPresent(ss->sec.ci.sid)) {
|
||||
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
|
||||
return SECFailure;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ss->recordWriteCallback) {
|
||||
PRUint16 epoch;
|
||||
ssl_GetSpecReadLock(ss);
|
||||
epoch = ss->ssl3.cwSpec->epoch;
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
rv = ss->recordWriteCallback(ss->fd, epoch, ct, pIn, nIn,
|
||||
ss->recordWriteCallbackArg);
|
||||
if (rv != SECSuccess) {
|
||||
return -1;
|
||||
}
|
||||
return nIn;
|
||||
}
|
||||
|
||||
if (cwSpec) {
|
||||
|
|
@ -2470,7 +2483,7 @@ loser:
|
|||
#define SSL3_PENDING_HIGH_WATER 1024
|
||||
|
||||
/* Attempt to send the content of "in" in an SSL application_data record.
|
||||
* Returns "len" or SECFailure, never SECWouldBlock, nor SECSuccess.
|
||||
* Returns "len" or -1 on failure.
|
||||
*/
|
||||
int
|
||||
ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
|
||||
|
|
@ -2485,21 +2498,21 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
|
|||
PORT_Assert(!(flags & ssl_SEND_FLAG_NO_RETRANSMIT));
|
||||
if (len < 0 || !in) {
|
||||
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
|
||||
return SECFailure;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ss->pendingBuf.len > SSL3_PENDING_HIGH_WATER &&
|
||||
!ssl_SocketIsBlocking(ss)) {
|
||||
PORT_Assert(!ssl_SocketIsBlocking(ss));
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ss->appDataBuffered && len) {
|
||||
PORT_Assert(in[0] == (unsigned char)(ss->appDataBuffered));
|
||||
if (in[0] != (unsigned char)(ss->appDataBuffered)) {
|
||||
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
|
||||
return SECFailure;
|
||||
return -1;
|
||||
}
|
||||
in++;
|
||||
len--;
|
||||
|
|
@ -2548,7 +2561,7 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
|
|||
PORT_Assert(ss->lastWriteBlocked);
|
||||
break;
|
||||
}
|
||||
return SECFailure; /* error code set by ssl3_SendRecord */
|
||||
return -1; /* error code set by ssl3_SendRecord */
|
||||
}
|
||||
totalSent += sent;
|
||||
if (ss->pendingBuf.len) {
|
||||
|
|
@ -2577,7 +2590,6 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
|
|||
}
|
||||
|
||||
/* Attempt to send buffered handshake messages.
|
||||
* This function returns SECSuccess or SECFailure, never SECWouldBlock.
|
||||
* Always set sendBuf.len to 0, even when returning SECFailure.
|
||||
*
|
||||
* Depending on whether we are doing DTLS or not, this either calls
|
||||
|
|
@ -2600,7 +2612,6 @@ ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
|
|||
}
|
||||
|
||||
/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
|
||||
* This function returns SECSuccess or SECFailure, never SECWouldBlock.
|
||||
* Always set sendBuf.len to 0, even when returning SECFailure.
|
||||
*
|
||||
* Called from ssl3_FlushHandshake
|
||||
|
|
@ -7382,6 +7393,9 @@ ssl3_CompleteHandleCertificateRequest(sslSocket *ss,
|
|||
if (ss->getClientAuthData != NULL) {
|
||||
PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
|
||||
ssl_preinfo_all);
|
||||
PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
|
||||
PORT_Assert(ss->ssl3.clientCertificate == NULL);
|
||||
PORT_Assert(ss->ssl3.clientCertChain == NULL);
|
||||
/* XXX Should pass cert_types and algorithms in this call!! */
|
||||
rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg,
|
||||
ss->fd, ca_list,
|
||||
|
|
@ -7603,7 +7617,8 @@ ssl3_SendClientSecondRound(sslSocket *ss)
|
|||
" certificate authentication is still pending.",
|
||||
SSL_GETPID(), ss->fd));
|
||||
ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
|
||||
return SECWouldBlock;
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_GetXmitBufLock(ss); /*******************************/
|
||||
|
|
@ -10737,6 +10752,9 @@ ssl3_AuthCertificate(sslSocket *ss)
|
|||
}
|
||||
}
|
||||
|
||||
if (ss->sec.ci.sid->peerCert) {
|
||||
CERT_DestroyCertificate(ss->sec.ci.sid->peerCert);
|
||||
}
|
||||
ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
|
||||
|
||||
if (!ss->sec.isServer) {
|
||||
|
|
@ -10898,13 +10916,6 @@ ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error)
|
|||
}
|
||||
|
||||
rv = target(ss);
|
||||
/* Even if we blocked here, we have accomplished enough to claim
|
||||
* success. Any remaining work will be taken care of by subsequent
|
||||
* calls to SSL_ForceHandshake/PR_Send/PR_Read/etc.
|
||||
*/
|
||||
if (rv == SECWouldBlock) {
|
||||
rv = SECSuccess;
|
||||
}
|
||||
} else {
|
||||
SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with"
|
||||
" peer's finished message",
|
||||
|
|
@ -11445,7 +11456,8 @@ xmit_loser:
|
|||
}
|
||||
|
||||
ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
|
||||
return SECWouldBlock;
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = ssl3_FinishHandshake(ss);
|
||||
|
|
@ -11649,9 +11661,10 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
|
|||
* authenticate the certificate in ssl3_HandleCertificateStatus.
|
||||
*/
|
||||
rv = ssl3_AuthCertificate(ss); /* sets ss->ssl3.hs.ws */
|
||||
PORT_Assert(rv != SECWouldBlock);
|
||||
if (rv != SECSuccess) {
|
||||
return rv;
|
||||
/* This can't block. */
|
||||
PORT_Assert(PORT_GetError() != PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -11809,28 +11822,17 @@ ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
|
|||
static SECStatus
|
||||
ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
|
||||
{
|
||||
/*
|
||||
* There may be a partial handshake message already in the handshake
|
||||
* state. The incoming buffer may contain another portion, or a
|
||||
* complete message or several messages followed by another portion.
|
||||
*
|
||||
* Each message is made contiguous before being passed to the actual
|
||||
* message parser.
|
||||
*/
|
||||
sslBuffer *buf = &ss->ssl3.hs.msgState; /* do not lose the original buffer pointer */
|
||||
sslBuffer buf = *origBuf; /* Work from a copy. */
|
||||
SECStatus rv;
|
||||
|
||||
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
||||
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
||||
|
||||
if (buf->buf == NULL) {
|
||||
*buf = *origBuf;
|
||||
}
|
||||
while (buf->len > 0) {
|
||||
while (buf.len > 0) {
|
||||
if (ss->ssl3.hs.header_bytes < 4) {
|
||||
PRUint8 t;
|
||||
t = *(buf->buf++);
|
||||
buf->len--;
|
||||
t = *(buf.buf++);
|
||||
buf.len--;
|
||||
if (ss->ssl3.hs.header_bytes++ == 0)
|
||||
ss->ssl3.hs.msg_type = (SSLHandshakeType)t;
|
||||
else
|
||||
|
|
@ -11847,7 +11849,7 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
|
|||
#undef MAX_HANDSHAKE_MSG_LEN
|
||||
|
||||
/* If msg_len is zero, be sure we fall through,
|
||||
** even if buf->len is zero.
|
||||
** even if buf.len is zero.
|
||||
*/
|
||||
if (ss->ssl3.hs.msg_len > 0)
|
||||
continue;
|
||||
|
|
@ -11858,22 +11860,15 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
|
|||
* data available for this message. If it can be done right out
|
||||
* of the original buffer, then use it from there.
|
||||
*/
|
||||
if (ss->ssl3.hs.msg_body.len == 0 && buf->len >= ss->ssl3.hs.msg_len) {
|
||||
if (ss->ssl3.hs.msg_body.len == 0 && buf.len >= ss->ssl3.hs.msg_len) {
|
||||
/* handle it from input buffer */
|
||||
rv = ssl3_HandleHandshakeMessage(ss, buf->buf, ss->ssl3.hs.msg_len,
|
||||
buf->len == ss->ssl3.hs.msg_len);
|
||||
if (rv == SECFailure) {
|
||||
/* This test wants to fall through on either
|
||||
* SECSuccess or SECWouldBlock.
|
||||
* ssl3_HandleHandshakeMessage MUST set the error code.
|
||||
*/
|
||||
return rv;
|
||||
}
|
||||
buf->buf += ss->ssl3.hs.msg_len;
|
||||
buf->len -= ss->ssl3.hs.msg_len;
|
||||
rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len,
|
||||
buf.len == ss->ssl3.hs.msg_len);
|
||||
buf.buf += ss->ssl3.hs.msg_len;
|
||||
buf.len -= ss->ssl3.hs.msg_len;
|
||||
ss->ssl3.hs.msg_len = 0;
|
||||
ss->ssl3.hs.header_bytes = 0;
|
||||
if (rv != SECSuccess) { /* return if SECWouldBlock. */
|
||||
if (rv != SECSuccess) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -11881,7 +11876,7 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
|
|||
unsigned int bytes;
|
||||
|
||||
PORT_Assert(ss->ssl3.hs.msg_body.len < ss->ssl3.hs.msg_len);
|
||||
bytes = PR_MIN(buf->len, ss->ssl3.hs.msg_len - ss->ssl3.hs.msg_body.len);
|
||||
bytes = PR_MIN(buf.len, ss->ssl3.hs.msg_len - ss->ssl3.hs.msg_body.len);
|
||||
|
||||
/* Grow the buffer if needed */
|
||||
rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, ss->ssl3.hs.msg_len);
|
||||
|
|
@ -11891,10 +11886,10 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
|
|||
}
|
||||
|
||||
PORT_Memcpy(ss->ssl3.hs.msg_body.buf + ss->ssl3.hs.msg_body.len,
|
||||
buf->buf, bytes);
|
||||
buf.buf, bytes);
|
||||
ss->ssl3.hs.msg_body.len += bytes;
|
||||
buf->buf += bytes;
|
||||
buf->len -= bytes;
|
||||
buf.buf += bytes;
|
||||
buf.len -= bytes;
|
||||
|
||||
PORT_Assert(ss->ssl3.hs.msg_body.len <= ss->ssl3.hs.msg_len);
|
||||
|
||||
|
|
@ -11902,29 +11897,21 @@ ssl3_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
|
|||
if (ss->ssl3.hs.msg_body.len == ss->ssl3.hs.msg_len) {
|
||||
rv = ssl3_HandleHandshakeMessage(
|
||||
ss, ss->ssl3.hs.msg_body.buf, ss->ssl3.hs.msg_len,
|
||||
buf->len == 0);
|
||||
if (rv == SECFailure) {
|
||||
/* This test wants to fall through on either
|
||||
* SECSuccess or SECWouldBlock.
|
||||
* ssl3_HandleHandshakeMessage MUST set error code.
|
||||
*/
|
||||
return rv;
|
||||
}
|
||||
buf.len == 0);
|
||||
ss->ssl3.hs.msg_body.len = 0;
|
||||
ss->ssl3.hs.msg_len = 0;
|
||||
ss->ssl3.hs.header_bytes = 0;
|
||||
if (rv != SECSuccess) { /* return if SECWouldBlock. */
|
||||
if (rv != SECSuccess) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
PORT_Assert(buf->len == 0);
|
||||
PORT_Assert(buf.len == 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} /* end loop */
|
||||
|
||||
origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */
|
||||
buf->buf = NULL; /* not a leak. */
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
|
|
@ -12183,7 +12170,7 @@ ssl3_UnprotectRecord(sslSocket *ss,
|
|||
unsigned int hashBytes = MAX_MAC_LENGTH + 1;
|
||||
SECStatus rv;
|
||||
|
||||
PORT_Assert(spec->direction == CipherSpecRead);
|
||||
PORT_Assert(spec->direction == ssl_secret_read);
|
||||
|
||||
good = ~0U;
|
||||
minLength = spec->macDef->mac_size;
|
||||
|
|
@ -12372,7 +12359,7 @@ ssl3_HandleNonApplicationData(sslSocket *ss, SSLContentType rType,
|
|||
ssl_GetSSL3HandshakeLock(ss);
|
||||
|
||||
/* All the functions called in this switch MUST set error code if
|
||||
** they return SECFailure or SECWouldBlock.
|
||||
** they return SECFailure.
|
||||
*/
|
||||
switch (rType) {
|
||||
case ssl_ct_change_cipher_spec:
|
||||
|
|
@ -12429,7 +12416,7 @@ ssl3_GetCipherSpec(sslSocket *ss, SSL3Ciphertext *cText)
|
|||
}
|
||||
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
/* Try to find the cipher spec. */
|
||||
newSpec = ssl_FindCipherSpecByEpoch(ss, CipherSpecRead,
|
||||
newSpec = ssl_FindCipherSpecByEpoch(ss, ssl_secret_read,
|
||||
epoch);
|
||||
if (newSpec != NULL) {
|
||||
return newSpec;
|
||||
|
|
@ -12694,8 +12681,8 @@ ssl3_InitState(sslSocket *ss)
|
|||
|
||||
ssl_GetSpecWriteLock(ss);
|
||||
PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
|
||||
rv = ssl_SetupNullCipherSpec(ss, CipherSpecRead);
|
||||
rv |= ssl_SetupNullCipherSpec(ss, CipherSpecWrite);
|
||||
rv = ssl_SetupNullCipherSpec(ss, ssl_secret_read);
|
||||
rv |= ssl_SetupNullCipherSpec(ss, ssl_secret_write);
|
||||
ss->ssl3.pwSpec = ss->ssl3.prSpec = NULL;
|
||||
ssl_ReleaseSpecWriteLock(ss);
|
||||
if (rv != SECSuccess) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = {
|
|||
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
|
||||
{ ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
|
||||
{ ssl_tls13_encrypted_sni_xtn, &tls13_ServerHandleEsniXtn },
|
||||
{ ssl_tls13_post_handshake_auth_xtn, &tls13_ServerHandlePostHandshakeAuthXtn },
|
||||
{ ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
|
@ -138,6 +139,7 @@ static const sslExtensionBuilder clientHelloSendersTLS[] =
|
|||
{ ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn },
|
||||
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn },
|
||||
{ ssl_tls13_encrypted_sni_xtn, &tls13_ClientSendEsniXtn },
|
||||
{ ssl_tls13_post_handshake_auth_xtn, &tls13_ClientSendPostHandshakeAuthXtn },
|
||||
{ ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn },
|
||||
/* The pre_shared_key extension MUST be last. */
|
||||
{ ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
|
||||
|
|
|
|||
|
|
@ -389,7 +389,6 @@ dtls_GatherData(sslSocket *ss, sslGather *gs, int flags)
|
|||
* application data is available.
|
||||
* Returns 0 if ssl3_GatherData hits EOF.
|
||||
* Returns -1 on read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
|
||||
* Returns -2 on SECWouldBlock return from ssl3_HandleRecord.
|
||||
*
|
||||
* Called from ssl_GatherRecord1stHandshake in sslcon.c,
|
||||
* and from SSL_ForceHandshake in sslsecur.c
|
||||
|
|
@ -422,13 +421,19 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
||||
|
||||
do {
|
||||
PRBool handleRecordNow = PR_FALSE;
|
||||
PRBool processingEarlyData;
|
||||
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
|
||||
processingEarlyData = ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
|
||||
|
||||
/* If we have a detached record layer, don't ever gather. */
|
||||
if (ss->recordWriteCallback) {
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return (int)SECFailure;
|
||||
}
|
||||
|
||||
/* Without this, we may end up wrongly reporting
|
||||
* SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
|
||||
* peer while we are waiting to be restarted.
|
||||
|
|
@ -439,93 +444,68 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
return (int)SECFailure;
|
||||
}
|
||||
|
||||
/* Treat an empty msgState like a NULL msgState. (Most of the time
|
||||
* when ssl3_HandleHandshake returns SECWouldBlock, it leaves
|
||||
* behind a non-NULL but zero-length msgState).
|
||||
* Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
|
||||
*/
|
||||
if (ss->ssl3.hs.msgState.buf) {
|
||||
if (ss->ssl3.hs.msgState.len == 0) {
|
||||
ss->ssl3.hs.msgState.buf = NULL;
|
||||
} else {
|
||||
handleRecordNow = PR_TRUE;
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
|
||||
/* State for SSLv2 client hello support. */
|
||||
ssl2Gather ssl2gs = { PR_FALSE, 0 };
|
||||
ssl2Gather *ssl2gs_ptr = NULL;
|
||||
|
||||
/* If we're a server and waiting for a client hello, accept v2. */
|
||||
if (ss->sec.isServer && ss->opt.enableV2CompatibleHello &&
|
||||
ss->ssl3.hs.ws == wait_client_hello) {
|
||||
ssl2gs_ptr = &ssl2gs;
|
||||
}
|
||||
|
||||
/* bring in the next sslv3 record. */
|
||||
if (ss->recvdCloseNotify) {
|
||||
/* RFC 5246 Section 7.2.1:
|
||||
* Any data received after a closure alert is ignored.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_DTLS(ss)) {
|
||||
/* If we're a server waiting for a ClientHello then pass
|
||||
* ssl2gs to support SSLv2 ClientHello messages. */
|
||||
rv = ssl3_GatherData(ss, &ss->gs, flags, ssl2gs_ptr);
|
||||
} else {
|
||||
rv = dtls_GatherData(ss, &ss->gs, flags);
|
||||
|
||||
/* If we got a would block error, that means that no data was
|
||||
* available, so we check the timer to see if it's time to
|
||||
* retransmit */
|
||||
if (rv == SECFailure &&
|
||||
(PORT_GetError() == PR_WOULD_BLOCK_ERROR)) {
|
||||
dtls_CheckTimer(ss);
|
||||
/* Restore the error in case something succeeded */
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (handleRecordNow) {
|
||||
/* ssl3_HandleHandshake previously returned SECWouldBlock and the
|
||||
* as-yet-unprocessed plaintext of that previous handshake record.
|
||||
* We need to process it now before we overwrite it with the next
|
||||
* handshake record.
|
||||
*/
|
||||
SSL_DBG(("%d: SSL3[%d]: resuming handshake",
|
||||
SSL_GETPID(), ss->fd));
|
||||
PORT_Assert(!IS_DTLS(ss));
|
||||
rv = ssl3_HandleNonApplicationData(ss, ssl_ct_handshake,
|
||||
0, 0, &ss->gs.buf);
|
||||
} else {
|
||||
/* State for SSLv2 client hello support. */
|
||||
ssl2Gather ssl2gs = { PR_FALSE, 0 };
|
||||
ssl2Gather *ssl2gs_ptr = NULL;
|
||||
|
||||
if (ss->sec.isServer && ss->opt.enableV2CompatibleHello &&
|
||||
ss->ssl3.hs.ws == wait_client_hello) {
|
||||
ssl2gs_ptr = &ssl2gs;
|
||||
}
|
||||
|
||||
/* bring in the next sslv3 record. */
|
||||
if (ss->recvdCloseNotify) {
|
||||
/* RFC 5246 Section 7.2.1:
|
||||
* Any data received after a closure alert is ignored.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_DTLS(ss)) {
|
||||
/* Passing a non-NULL ssl2gs here enables detection of
|
||||
* SSLv2-compatible ClientHello messages. */
|
||||
rv = ssl3_GatherData(ss, &ss->gs, flags, ssl2gs_ptr);
|
||||
} else {
|
||||
rv = dtls_GatherData(ss, &ss->gs, flags);
|
||||
|
||||
/* If we got a would block error, that means that no data was
|
||||
* available, so we check the timer to see if it's time to
|
||||
* retransmit */
|
||||
if (rv == SECFailure &&
|
||||
(PORT_GetError() == PR_WOULD_BLOCK_ERROR)) {
|
||||
dtls_CheckTimer(ss);
|
||||
/* Restore the error in case something succeeded */
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (rv <= 0) {
|
||||
if (ssl2gs.isV2) {
|
||||
rv = ssl3_HandleV2ClientHello(ss, ss->gs.inbuf.buf,
|
||||
ss->gs.inbuf.len,
|
||||
ssl2gs.padding);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (ssl2gs.isV2) {
|
||||
rv = ssl3_HandleV2ClientHello(ss, ss->gs.inbuf.buf,
|
||||
ss->gs.inbuf.len,
|
||||
ssl2gs.padding);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
/* decipher it, and handle it if it's a handshake.
|
||||
* If it's application data, ss->gs.buf will not be empty upon return.
|
||||
* If it's a change cipher spec, alert, or handshake message,
|
||||
* ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
|
||||
*
|
||||
* cText only needs to be valid for this next function call, so
|
||||
* it can borrow gs.hdr.
|
||||
*/
|
||||
cText.hdr = ss->gs.hdr;
|
||||
cText.hdrLen = ss->gs.hdrLen;
|
||||
cText.buf = &ss->gs.inbuf;
|
||||
rv = ssl3_HandleRecord(ss, &cText);
|
||||
}
|
||||
} else {
|
||||
/* decipher it, and handle it if it's a handshake.
|
||||
* If it's application data, ss->gs.buf will not be empty upon return.
|
||||
* If it's a change cipher spec, alert, or handshake message,
|
||||
* ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
|
||||
*
|
||||
* cText only needs to be valid for this next function call, so
|
||||
* it can borrow gs.hdr.
|
||||
*/
|
||||
cText.hdr = ss->gs.hdr;
|
||||
cText.hdrLen = ss->gs.hdrLen;
|
||||
cText.buf = &ss->gs.inbuf;
|
||||
rv = ssl3_HandleRecord(ss, &cText);
|
||||
}
|
||||
if (rv < 0) {
|
||||
return ss->recvdCloseNotify ? 0 : rv;
|
||||
|
|
@ -575,7 +555,7 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
* delivered to the application before the handshake completes. */
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECWouldBlock;
|
||||
return -1;
|
||||
}
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
} while (keepGoing);
|
||||
|
|
@ -596,7 +576,6 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
|||
* Returns 1 when application data is available.
|
||||
* Returns 0 if ssl3_GatherData hits EOF.
|
||||
* Returns -1 on read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
|
||||
* Returns -2 on SECWouldBlock return from ssl3_HandleRecord.
|
||||
*
|
||||
* Called from DoRecv in sslsecur.c
|
||||
* Caller must hold the recv buf lock.
|
||||
|
|
@ -616,3 +595,108 @@ ssl3_GatherAppDataRecord(sslSocket *ss, int flags)
|
|||
|
||||
return rv;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SSLExp_RecordLayerData(PRFileDesc *fd, PRUint16 epoch,
|
||||
SSLContentType contentType,
|
||||
const PRUint8 *data, unsigned int len)
|
||||
{
|
||||
SECStatus rv;
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
return SECFailure;
|
||||
}
|
||||
if (IS_DTLS(ss) || data == NULL || len == 0) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* Run any handshake function. If SSL_RecordLayerData is the only way that
|
||||
* the handshake is driven, then this is necessary to ensure that
|
||||
* ssl_BeginClientHandshake or ssl_BeginServerHandshake is called. Note that
|
||||
* the other function that might be set to ss->handshake,
|
||||
* ssl3_GatherCompleteHandshake, does nothing when this function is used. */
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
rv = ssl_Do1stHandshake(ss);
|
||||
if (rv != SECSuccess && PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
|
||||
goto early_loser; /* Rely on the existing code. */
|
||||
}
|
||||
|
||||
/* Don't allow application data before handshake completion. */
|
||||
if (contentType == ssl_ct_application_data && !ss->firstHsDone) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
goto early_loser;
|
||||
}
|
||||
|
||||
/* Then we can validate the epoch. */
|
||||
PRErrorCode epochError;
|
||||
ssl_GetSpecReadLock(ss);
|
||||
if (epoch < ss->ssl3.crSpec->epoch) {
|
||||
epochError = SEC_ERROR_INVALID_ARGS; /* Too c/old. */
|
||||
} else if (epoch > ss->ssl3.crSpec->epoch) {
|
||||
epochError = PR_WOULD_BLOCK_ERROR; /* Too warm/new. */
|
||||
} else {
|
||||
epochError = 0; /* Just right. */
|
||||
}
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
if (epochError) {
|
||||
PORT_SetError(epochError);
|
||||
goto early_loser;
|
||||
}
|
||||
|
||||
/* If the handshake is still running, we need to run that. */
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
rv = ssl_Do1stHandshake(ss);
|
||||
if (rv != SECSuccess && PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* Finally, save the data... */
|
||||
ssl_GetRecvBufLock(ss);
|
||||
rv = sslBuffer_Append(&ss->gs.buf, data, len);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/* ...and process it. Just saving application data is enough for it to be
|
||||
* available to PR_Read(). */
|
||||
if (contentType != ssl_ct_application_data) {
|
||||
rv = ssl3_HandleNonApplicationData(ss, contentType, 0, 0, &ss->gs.buf);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
}
|
||||
|
||||
ssl_ReleaseRecvBufLock(ss);
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
return SECSuccess;
|
||||
|
||||
loser:
|
||||
/* Make sure that any data is not used again. */
|
||||
ss->gs.buf.len = 0;
|
||||
ssl_ReleaseRecvBufLock(ss);
|
||||
early_loser:
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SSLExp_GetCurrentEpoch(PRFileDesc *fd, PRUint16 *readEpoch,
|
||||
PRUint16 *writeEpoch)
|
||||
{
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_GetSpecReadLock(ss);
|
||||
if (readEpoch) {
|
||||
*readEpoch = ss->ssl3.crSpec->epoch;
|
||||
}
|
||||
if (writeEpoch) {
|
||||
*writeEpoch = ss->ssl3.cwSpec->epoch;
|
||||
}
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
return SECSuccess;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,17 +44,13 @@ const char *ssl_version = "SECURITY_VERSION:"
|
|||
* This function acquires and releases the RecvBufLock.
|
||||
*
|
||||
* returns SECSuccess for success.
|
||||
* returns SECWouldBlock when that value is returned by
|
||||
* ssl3_GatherCompleteHandshake().
|
||||
* returns SECFailure on all other errors.
|
||||
* returns SECFailure on error, setting PR_WOULD_BLOCK_ERROR if only blocked.
|
||||
*
|
||||
* The gather functions called by ssl_GatherRecord1stHandshake are expected
|
||||
* to return values interpreted as follows:
|
||||
* 1 : the function completed without error.
|
||||
* 0 : the function read EOF.
|
||||
* -1 : read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
|
||||
* -2 : the function wants ssl_GatherRecord1stHandshake to be called again
|
||||
* immediately, by ssl_Do1stHandshake.
|
||||
*
|
||||
* This code is similar to, and easily confused with, DoRecv() in sslsecur.c
|
||||
*
|
||||
|
|
@ -82,16 +78,14 @@ ssl_GatherRecord1stHandshake(sslSocket *ss)
|
|||
ssl_ReleaseRecvBufLock(ss);
|
||||
|
||||
if (rv <= 0) {
|
||||
if (rv == SECWouldBlock) {
|
||||
/* Progress is blocked waiting for callback completion. */
|
||||
SSL_TRC(10, ("%d: SSL[%d]: handshake blocked (need %d)",
|
||||
SSL_GETPID(), ss->fd, ss->gs.remainder));
|
||||
return SECWouldBlock;
|
||||
}
|
||||
if (rv == 0) {
|
||||
/* EOF. Loser */
|
||||
PORT_SetError(PR_END_OF_FILE_ERROR);
|
||||
}
|
||||
if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) {
|
||||
SSL_TRC(10, ("%d: SSL[%d]: handshake blocked (need %d)",
|
||||
SSL_GETPID(), ss->fd, ss->gs.remainder));
|
||||
}
|
||||
return SECFailure; /* rv is < 0 here. */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ ssl_DefRecv(sslSocket *ss, unsigned char *buf, int len, int flags)
|
|||
* For blocking sockets, always returns len or SECFailure, no short writes.
|
||||
* For non-blocking sockets:
|
||||
* Returns positive count if any data was written, else returns SECFailure.
|
||||
* Short writes may occur. Does not return SECWouldBlock.
|
||||
* Short writes may occur.
|
||||
*/
|
||||
int
|
||||
ssl_DefSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@ typedef enum {
|
|||
SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION = (SSL_ERROR_BASE + 177),
|
||||
SSL_ERROR_MISSING_ESNI_EXTENSION = (SSL_ERROR_BASE + 178),
|
||||
SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE = (SSL_ERROR_BASE + 179),
|
||||
SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION = (SSL_ERROR_BASE + 180),
|
||||
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
|
||||
} SSLErrorCodes;
|
||||
#endif /* NO_SECURITY_ERROR_ENUM */
|
||||
|
|
|
|||
|
|
@ -350,6 +350,27 @@ typedef SSLHelloRetryRequestAction(PR_CALLBACK *SSLHelloRetryRequestCallback)(
|
|||
(PRFileDesc * _fd, PRBool _requestUpdate), \
|
||||
(fd, requestUpdate))
|
||||
|
||||
/* This function allows a server application to trigger
|
||||
* re-authentication (TLS 1.3 only) after handshake.
|
||||
*
|
||||
* This function will cause a CertificateRequest message to be sent by
|
||||
* a server. This can be called once at a time, and is not allowed
|
||||
* until an answer is received.
|
||||
*
|
||||
* The AuthCertificateCallback is called when the answer is received.
|
||||
* If the answer is accepted by the server, the value returned by
|
||||
* SSL_PeerCertificate() is replaced. If you need to remember all the
|
||||
* certificates, you will need to call SSL_PeerCertificate() and save
|
||||
* what you get before calling this.
|
||||
*
|
||||
* If the AuthCertificateCallback returns SECFailure, the connection
|
||||
* is aborted.
|
||||
*/
|
||||
#define SSL_SendCertificateRequest(fd) \
|
||||
SSL_EXPERIMENTAL_API("SSL_SendCertificateRequest", \
|
||||
(PRFileDesc * _fd), \
|
||||
(fd))
|
||||
|
||||
/*
|
||||
* Session cache API.
|
||||
*/
|
||||
|
|
@ -511,6 +532,100 @@ typedef SECStatus(PR_CALLBACK *SSLResumptionTokenCallback)(
|
|||
group, pubKey, pad, notBefore, notAfter, \
|
||||
out, outlen, maxlen))
|
||||
|
||||
/* SSL_SetSecretCallback installs a callback that TLS calls when it installs new
|
||||
* traffic secrets.
|
||||
*
|
||||
* SSLSecretCallback is called with the current epoch and the corresponding
|
||||
* secret; this matches the epoch used in DTLS 1.3, even if the socket is
|
||||
* operating in stream mode:
|
||||
*
|
||||
* - client_early_traffic_secret corresponds to epoch 1
|
||||
* - {client|server}_handshake_traffic_secret is epoch 2
|
||||
* - {client|server}_application_traffic_secret_{N} is epoch 3+N
|
||||
*
|
||||
* The callback is invoked separately for read secrets (client secrets on the
|
||||
* server; server secrets on the client), and write secrets.
|
||||
*
|
||||
* This callback is only called if (D)TLS 1.3 is negotiated.
|
||||
*/
|
||||
typedef void(PR_CALLBACK *SSLSecretCallback)(
|
||||
PRFileDesc *fd, PRUint16 epoch, SSLSecretDirection dir, PK11SymKey *secret,
|
||||
void *arg);
|
||||
|
||||
#define SSL_SecretCallback(fd, cb, arg) \
|
||||
SSL_EXPERIMENTAL_API("SSL_SecretCallback", \
|
||||
(PRFileDesc * _fd, SSLSecretCallback _cb, void *_arg), \
|
||||
(fd, cb, arg))
|
||||
|
||||
/* SSL_RecordLayerWriteCallback() is used to replace the TLS record layer. This
|
||||
* function installs a callback that TLS calls when it would otherwise encrypt
|
||||
* and write a record to the underlying NSPR IO layer. The application is
|
||||
* responsible for ensuring that these records are encrypted and written.
|
||||
*
|
||||
* Calling this API also disables reads from the underlying NSPR layer. The
|
||||
* application is expected to push data when it is available using
|
||||
* SSL_RecordLayerData().
|
||||
*
|
||||
* When data would be written, the provided SSLRecordWriteCallback with the
|
||||
* epoch, TLS content type, and the data. The data provided to the callback is
|
||||
* not split into record-sized writes. If the callback returns SECFailure, the
|
||||
* write will be considered to have failed; in particular, PR_WOULD_BLOCK_ERROR
|
||||
* is not handled specially.
|
||||
*
|
||||
* If TLS 1.3 is in use, the epoch indicates the expected level of protection
|
||||
* that the record would receive, this matches that used in DTLS 1.3:
|
||||
*
|
||||
* - epoch 0 corresponds to no record protection
|
||||
* - epoch 1 corresponds to 0-RTT
|
||||
* - epoch 2 corresponds to TLS handshake
|
||||
* - epoch 3 and higher are application data
|
||||
*
|
||||
* Prior versions of TLS use epoch 1 and higher for application data.
|
||||
*
|
||||
* This API is not supported for DTLS.
|
||||
*/
|
||||
typedef SECStatus(PR_CALLBACK *SSLRecordWriteCallback)(
|
||||
PRFileDesc *fd, PRUint16 epoch, SSLContentType contentType,
|
||||
const PRUint8 *data, unsigned int len, void *arg);
|
||||
|
||||
#define SSL_RecordLayerWriteCallback(fd, writeCb, arg) \
|
||||
SSL_EXPERIMENTAL_API("SSL_RecordLayerWriteCallback", \
|
||||
(PRFileDesc * _fd, SSLRecordWriteCallback _wCb, \
|
||||
void *_arg), \
|
||||
(fd, writeCb, arg))
|
||||
|
||||
/* SSL_RecordLayerData() is used to provide new data to TLS. The application
|
||||
* indicates the epoch (see the description of SSL_RecordLayerWriteCallback()),
|
||||
* content type, and the data that was received. The application is responsible
|
||||
* for removing any encryption or other protection before passing data to this
|
||||
* function.
|
||||
*
|
||||
* This returns SECSuccess if the data was successfully processed. If this
|
||||
* function is used to drive the handshake and the caller needs to know when the
|
||||
* handshake is complete, a call to SSL_ForceHandshake will return SECSuccess
|
||||
* when the handshake is complete.
|
||||
*
|
||||
* This API is not supported for DTLS sockets.
|
||||
*/
|
||||
#define SSL_RecordLayerData(fd, epoch, ct, data, len) \
|
||||
SSL_EXPERIMENTAL_API("SSL_RecordLayerData", \
|
||||
(PRFileDesc * _fd, PRUint16 _epoch, \
|
||||
SSLContentType _contentType, \
|
||||
const PRUint8 *_data, unsigned int _len), \
|
||||
(fd, epoch, ct, data, len))
|
||||
|
||||
/*
|
||||
* SSL_GetCurrentEpoch() returns the read and write epochs that the socket is
|
||||
* currently using. NULL values for readEpoch or writeEpoch are ignored.
|
||||
*
|
||||
* See SSL_RecordLayerWriteCallback() for details on epochs.
|
||||
*/
|
||||
#define SSL_GetCurrentEpoch(fd, readEpoch, writeEpoch) \
|
||||
SSL_EXPERIMENTAL_API("SSL_GetCurrentEpoch", \
|
||||
(PRFileDesc * _fd, PRUint16 * _readEpoch, \
|
||||
PRUint16 * _writeEpoch), \
|
||||
(fd, readEpoch, writeEpoch))
|
||||
|
||||
/* Deprecated experimental APIs */
|
||||
#define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ typedef struct sslOptionsStr {
|
|||
unsigned int enableDtlsShortHeader : 1;
|
||||
unsigned int enableHelloDowngradeCheck : 1;
|
||||
unsigned int enableV2CompatibleHello : 1;
|
||||
unsigned int enablePostHandshakeAuth : 1;
|
||||
} sslOptions;
|
||||
|
||||
typedef enum { sslHandshakingUndetermined = 0,
|
||||
|
|
@ -622,8 +623,6 @@ typedef struct SSL3HandshakeStateStr {
|
|||
unsigned long msg_len;
|
||||
PRBool isResuming; /* we are resuming (not used in TLS 1.3) */
|
||||
PRBool sendingSCSV; /* instead of empty RI */
|
||||
sslBuffer msgState; /* current state for handshake messages*/
|
||||
/* protected by recvBufLock */
|
||||
|
||||
/* The session ticket received in a NewSessionTicket message is temporarily
|
||||
* stored in newSessionTicket until the handshake is finished; then it is
|
||||
|
|
@ -744,10 +743,10 @@ struct ssl3StateStr {
|
|||
* update is initiated locally. */
|
||||
PRBool peerRequestedKeyUpdate;
|
||||
|
||||
/* Internal callback for when we do a cipher suite change. Used for
|
||||
* debugging in TLS 1.3. This can only be set by non-public functions. */
|
||||
sslCipherSpecChangedFunc changedCipherSpecFunc;
|
||||
void *changedCipherSpecArg;
|
||||
/* This is true after the server requests client certificate;
|
||||
* false after the client certificate is received. Used by the
|
||||
* server. */
|
||||
PRBool clientCertRequested;
|
||||
|
||||
CERTCertificate *clientCertificate; /* used by client */
|
||||
SECKEYPrivateKey *clientPrivateKey; /* used by client */
|
||||
|
|
@ -994,6 +993,10 @@ struct sslSocketStr {
|
|||
PRCList extensionHooks;
|
||||
SSLResumptionTokenCallback resumptionTokenCallback;
|
||||
void *resumptionTokenContext;
|
||||
SSLSecretCallback secretCallback;
|
||||
void *secretCallbackArg;
|
||||
SSLRecordWriteCallback recordWriteCallback;
|
||||
void *recordWriteCallbackArg;
|
||||
|
||||
PRIntervalTime rTimeout; /* timeout for NSPR I/O */
|
||||
PRIntervalTime wTimeout; /* timeout for NSPR I/O */
|
||||
|
|
@ -1174,7 +1177,7 @@ extern SECStatus ssl_SaveWriteData(sslSocket *ss,
|
|||
const void *p, unsigned int l);
|
||||
extern SECStatus ssl_BeginClientHandshake(sslSocket *ss);
|
||||
extern SECStatus ssl_BeginServerHandshake(sslSocket *ss);
|
||||
extern int ssl_Do1stHandshake(sslSocket *ss);
|
||||
extern SECStatus ssl_Do1stHandshake(sslSocket *ss);
|
||||
|
||||
extern SECStatus ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret,
|
||||
PRBool derive);
|
||||
|
|
@ -1742,6 +1745,17 @@ SECStatus SSLExp_GetResumptionTokenInfo(const PRUint8 *tokenData, unsigned int t
|
|||
|
||||
SECStatus SSLExp_DestroyResumptionTokenInfo(SSLResumptionTokenInfo *token);
|
||||
|
||||
SECStatus SSLExp_SecretCallback(PRFileDesc *fd, SSLSecretCallback cb,
|
||||
void *arg);
|
||||
SECStatus SSLExp_RecordLayerWriteCallback(PRFileDesc *fd,
|
||||
SSLRecordWriteCallback write,
|
||||
void *arg);
|
||||
SECStatus SSLExp_RecordLayerData(PRFileDesc *fd, PRUint16 epoch,
|
||||
SSLContentType contentType,
|
||||
const PRUint8 *data, unsigned int len);
|
||||
SECStatus SSLExp_GetCurrentEpoch(PRFileDesc *fd, PRUint16 *readEpoch,
|
||||
PRUint16 *writeEpoch);
|
||||
|
||||
#define SSLResumptionTokenVersion 2
|
||||
|
||||
SEC_END_PROTOS
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
|
|||
} else {
|
||||
inf.maxEarlyDataSize = 0;
|
||||
}
|
||||
inf.zeroRttCipherSuite = ss->ssl3.hs.zeroRttSuite;
|
||||
|
||||
memcpy(info, &inf, inf.length);
|
||||
return SECSuccess;
|
||||
|
|
@ -234,89 +235,89 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
|
|||
|
||||
static const SSLCipherSuiteInfo suiteInfo[] = {
|
||||
/* <------ Cipher suite --------------------> <auth> <KEA> <bulk cipher> <MAC> <FIPS> */
|
||||
{ 0, CS_(TLS_AES_128_GCM_SHA256), S_ANY, K_ANY, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ANY },
|
||||
{ 0, CS_(TLS_CHACHA20_POLY1305_SHA256), S_ANY, K_ANY, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY },
|
||||
{ 0, CS_(TLS_AES_256_GCM_SHA384), S_ANY, K_ANY, C_AESGCM, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY },
|
||||
{ 0, CS_(TLS_AES_128_GCM_SHA256), S_ANY, K_ANY, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ANY, ssl_hash_sha256 },
|
||||
{ 0, CS_(TLS_CHACHA20_POLY1305_SHA256), S_ANY, K_ANY, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY, ssl_hash_sha256 },
|
||||
{ 0, CS_(TLS_AES_256_GCM_SHA384), S_ANY, K_ANY, C_AESGCM, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY, ssl_hash_sha384 },
|
||||
|
||||
{ 0, CS(RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_RSA, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(DHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_DHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_RSA, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAD, ssl_hash_sha256 },
|
||||
{ 0, CS(DHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_DHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
|
||||
{ 0, CS(DHE_RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_DSS_WITH_CAMELLIA_256_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA), S_DSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA256), S_DSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_RSA, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_AES_256_CBC_SHA), S_RSA, K_RSA, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(DHE_RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_CAMELLIA_256_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA), S_DSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA256), S_DSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_DSA, ssl_hash_sha256 },
|
||||
{ 0, CS(RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_RSA, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAD, ssl_hash_sha256 },
|
||||
{ 0, CS(RSA_WITH_AES_256_CBC_SHA), S_RSA, K_RSA, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
|
||||
|
||||
{ 0, CS(DHE_RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_DSS_WITH_CAMELLIA_128_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_DSS_WITH_RC4_128_SHA), S_DSA, K_DHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_128_GCM_SHA256), S_DSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA), S_DSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA256), S_DSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(RSA_WITH_SEED_CBC_SHA), S_RSA, K_RSA, C_SEED, B_128, M_SHA, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_RC4_128_SHA), S_RSA, K_RSA, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_RC4_128_MD5), S_RSA, K_RSA, C_RC4, B_128, M_MD5, F_NFIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_RSA, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_AES_128_CBC_SHA), S_RSA, K_RSA, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(DHE_RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_CAMELLIA_128_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_RC4_128_SHA), S_DSA, K_DHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_128_GCM_SHA256), S_DSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_DSA, ssl_hash_sha256 },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA), S_DSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA256), S_DSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_DSA, ssl_hash_sha256 },
|
||||
{ 0, CS(RSA_WITH_SEED_CBC_SHA), S_RSA, K_RSA, C_SEED, B_128, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_RC4_128_SHA), S_RSA, K_RSA, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_RC4_128_MD5), S_RSA, K_RSA, C_RC4, B_128, M_MD5, F_NFIPS_STD, A_RSAD, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_RSA, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAD, ssl_hash_sha256 },
|
||||
{ 0, CS(RSA_WITH_AES_128_CBC_SHA), S_RSA, K_RSA, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
|
||||
|
||||
{ 0, CS(DHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_DSS_WITH_3DES_EDE_CBC_SHA), S_DSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_RSA, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(DHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_3DES_EDE_CBC_SHA), S_DSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_RSA, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
|
||||
|
||||
{ 0, CS(DHE_RSA_WITH_DES_CBC_SHA), S_RSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(DHE_DSS_WITH_DES_CBC_SHA), S_DSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_DSA },
|
||||
{ 0, CS(RSA_WITH_DES_CBC_SHA), S_RSA, K_RSA, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAD },
|
||||
{ 0, CS(DHE_RSA_WITH_DES_CBC_SHA), S_RSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(DHE_DSS_WITH_DES_CBC_SHA), S_DSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_DES_CBC_SHA), S_RSA, K_RSA, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
|
||||
|
||||
{ 0, CS(RSA_WITH_NULL_SHA256), S_RSA, K_RSA, C_NULL, B_0, M_SHA256, F_EXPORT, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_NULL_SHA), S_RSA, K_RSA, C_NULL, B_0, M_SHA, F_EXPORT, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_NULL_MD5), S_RSA, K_RSA, C_NULL, B_0, M_MD5, F_EXPORT, A_RSAD },
|
||||
{ 0, CS(RSA_WITH_NULL_SHA256), S_RSA, K_RSA, C_NULL, B_0, M_SHA256, F_EXPORT, A_RSAD, ssl_hash_sha256 },
|
||||
{ 0, CS(RSA_WITH_NULL_SHA), S_RSA, K_RSA, C_NULL, B_0, M_SHA, F_EXPORT, A_RSAD, ssl_hash_none },
|
||||
{ 0, CS(RSA_WITH_NULL_MD5), S_RSA, K_RSA, C_NULL, B_0, M_MD5, F_EXPORT, A_RSAD, ssl_hash_none },
|
||||
|
||||
/* ECC cipher suites */
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_E },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_E },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_E },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_E },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_E },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ECDSA, ssl_hash_sha256 },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_E, ssl_hash_none },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_E, ssl_hash_none },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_E, ssl_hash_none },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_E, ssl_hash_none },
|
||||
{ 0, CS(ECDH_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_E, ssl_hash_none },
|
||||
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256), S_ECDSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDSA, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDSA, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDSA, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDSA, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_ECDSA, ssl_hash_sha256 },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDSA, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256), S_ECDSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ECDSA, ssl_hash_sha256 },
|
||||
|
||||
{ 0, CS(ECDH_RSA_WITH_NULL_SHA), S_RSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_R },
|
||||
{ 0, CS(ECDH_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_R },
|
||||
{ 0, CS(ECDH_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_R },
|
||||
{ 0, CS(ECDH_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_R },
|
||||
{ 0, CS(ECDH_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_R },
|
||||
{ 0, CS(ECDH_RSA_WITH_NULL_SHA), S_RSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_R, ssl_hash_none },
|
||||
{ 0, CS(ECDH_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_R, ssl_hash_none },
|
||||
{ 0, CS(ECDH_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_R, ssl_hash_none },
|
||||
{ 0, CS(ECDH_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_R, ssl_hash_none },
|
||||
{ 0, CS(ECDH_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_R, ssl_hash_none },
|
||||
|
||||
{ 0, CS(ECDHE_RSA_WITH_NULL_SHA), S_RSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA384), S_RSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), S_ECDSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_ECDSA },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(ECDHE_RSA_WITH_NULL_SHA), S_RSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
|
||||
{ 0, CS(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS, ssl_hash_sha256 },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA384), S_RSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_RSAS, ssl_hash_sha384 },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_ECDSA, ssl_hash_sha384 },
|
||||
{ 0, CS(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), S_ECDSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_ECDSA, ssl_hash_sha384 },
|
||||
{ 0, CS(ECDHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha384 },
|
||||
|
||||
{ 0, CS(DHE_DSS_WITH_AES_256_GCM_SHA384), S_DSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_DSA },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS },
|
||||
{ 0, CS(RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_RSA, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAD },
|
||||
{ 0, CS(DHE_DSS_WITH_AES_256_GCM_SHA384), S_DSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_DSA, ssl_hash_sha384 },
|
||||
{ 0, CS(DHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha384 },
|
||||
{ 0, CS(RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_RSA, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAD, ssl_hash_sha384 },
|
||||
};
|
||||
|
||||
#define NUM_SUITEINFOS ((sizeof suiteInfo) / (sizeof suiteInfo[0]))
|
||||
|
|
|
|||
|
|
@ -16,32 +16,7 @@
|
|||
#include "nss.h" /* for NSS_RegisterShutdown */
|
||||
#include "prinit.h" /* for PR_CallOnceWithArg */
|
||||
|
||||
/* Returns a SECStatus: SECSuccess or SECFailure, NOT SECWouldBlock.
|
||||
*
|
||||
* Currently, the list of functions called through ss->handshake is:
|
||||
*
|
||||
* In sslsocks.c:
|
||||
* SocksGatherRecord
|
||||
* SocksHandleReply
|
||||
* SocksStartGather
|
||||
*
|
||||
* In sslcon.c:
|
||||
* ssl_GatherRecord1stHandshake
|
||||
* ssl_BeginClientHandshake
|
||||
* ssl_BeginServerHandshake
|
||||
*
|
||||
* The ss->handshake function returns SECWouldBlock if it was returned by
|
||||
* one of the callback functions, via one of these paths:
|
||||
*
|
||||
* - ssl_GatherRecord1stHandshake() -> ssl3_GatherCompleteHandshake() ->
|
||||
* ssl3_HandleRecord() -> ssl3_HandleHandshake() ->
|
||||
* ssl3_HandleHandshakeMessage() -> ssl3_HandleCertificate() ->
|
||||
* ss->handleBadCert()
|
||||
*
|
||||
* - ssl_GatherRecord1stHandshake() -> ssl3_GatherCompleteHandshake() ->
|
||||
* ssl3_HandleRecord() -> ssl3_HandleHandshake() ->
|
||||
* ssl3_HandleHandshakeMessage() -> ssl3_HandleCertificateRequest() ->
|
||||
* ss->getClientAuthData()
|
||||
/* Step through the handshake functions.
|
||||
*
|
||||
* Called from: SSL_ForceHandshake (below),
|
||||
* ssl_SecureRecv (below) and
|
||||
|
|
@ -52,10 +27,10 @@
|
|||
*
|
||||
* Caller must hold the (write) handshakeLock.
|
||||
*/
|
||||
int
|
||||
SECStatus
|
||||
ssl_Do1stHandshake(sslSocket *ss)
|
||||
{
|
||||
int rv = SECSuccess;
|
||||
SECStatus rv = SECSuccess;
|
||||
|
||||
while (ss->handshake && rv == SECSuccess) {
|
||||
PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
|
||||
|
|
@ -70,10 +45,6 @@ ssl_Do1stHandshake(sslSocket *ss)
|
|||
PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss));
|
||||
PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss));
|
||||
|
||||
if (rv == SECWouldBlock) {
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
rv = SECFailure;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +77,8 @@ ssl_FinishHandshake(sslSocket *ss)
|
|||
static SECStatus
|
||||
ssl3_AlwaysBlock(sslSocket *ss)
|
||||
{
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR); /* perhaps redundant. */
|
||||
return SECWouldBlock;
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -400,10 +371,13 @@ SSL_ForceHandshake(PRFileDesc *fd)
|
|||
ssl_ReleaseRecvBufLock(ss);
|
||||
if (gatherResult > 0) {
|
||||
rv = SECSuccess;
|
||||
} else if (gatherResult == 0) {
|
||||
PORT_SetError(PR_END_OF_FILE_ERROR);
|
||||
} else if (gatherResult == SECWouldBlock) {
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
} else {
|
||||
if (gatherResult == 0) {
|
||||
PORT_SetError(PR_END_OF_FILE_ERROR);
|
||||
}
|
||||
/* We can rely on ssl3_GatherCompleteHandshake to set
|
||||
* PR_WOULD_BLOCK_ERROR as needed here. */
|
||||
rv = SECFailure;
|
||||
}
|
||||
} else {
|
||||
PORT_Assert(!ss->firstHsDone);
|
||||
|
|
@ -515,8 +489,7 @@ DoRecv(sslSocket *ss, unsigned char *out, int len, int flags)
|
|||
SSL_GETPID(), ss->fd));
|
||||
goto done;
|
||||
}
|
||||
if ((rv != SECWouldBlock) &&
|
||||
(PR_GetError() != PR_WOULD_BLOCK_ERROR)) {
|
||||
if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
|
||||
/* Some random error */
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -741,7 +714,7 @@ ssl_SecureShutdown(sslSocket *ss, int nsprHow)
|
|||
/************************************************************************/
|
||||
|
||||
static SECStatus
|
||||
tls13_CheckKeyUpdate(sslSocket *ss, CipherSpecDirection dir)
|
||||
tls13_CheckKeyUpdate(sslSocket *ss, SSLSecretDirection dir)
|
||||
{
|
||||
PRBool keyUpdate;
|
||||
ssl3CipherSpec *spec;
|
||||
|
|
@ -765,7 +738,7 @@ tls13_CheckKeyUpdate(sslSocket *ss, CipherSpecDirection dir)
|
|||
* having the write margin larger reduces the number of times that a
|
||||
* KeyUpdate is sent by a reader. */
|
||||
ssl_GetSpecReadLock(ss);
|
||||
if (dir == CipherSpecRead) {
|
||||
if (dir == ssl_secret_read) {
|
||||
spec = ss->ssl3.crSpec;
|
||||
margin = spec->cipherDef->max_records / 8;
|
||||
} else {
|
||||
|
|
@ -781,10 +754,10 @@ tls13_CheckKeyUpdate(sslSocket *ss, CipherSpecDirection dir)
|
|||
|
||||
SSL_TRC(5, ("%d: SSL[%d]: automatic key update at %llx for %s cipher spec",
|
||||
SSL_GETPID(), ss->fd, seqNum,
|
||||
(dir == CipherSpecRead) ? "read" : "write"));
|
||||
(dir == ssl_secret_read) ? "read" : "write"));
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
rv = tls13_SendKeyUpdate(ss, (dir == CipherSpecRead) ? update_requested : update_not_requested,
|
||||
dir == CipherSpecWrite /* buffer */);
|
||||
rv = tls13_SendKeyUpdate(ss, (dir == ssl_secret_read) ? update_requested : update_not_requested,
|
||||
dir == ssl_secret_write /* buffer */);
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -829,7 +802,7 @@ ssl_SecureRecv(sslSocket *ss, unsigned char *buf, int len, int flags)
|
|||
}
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
} else {
|
||||
if (tls13_CheckKeyUpdate(ss, CipherSpecRead) != SECSuccess) {
|
||||
if (tls13_CheckKeyUpdate(ss, ssl_secret_read) != SECSuccess) {
|
||||
rv = PR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
|
@ -955,7 +928,7 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
|
|||
}
|
||||
|
||||
if (ss->firstHsDone) {
|
||||
if (tls13_CheckKeyUpdate(ss, CipherSpecWrite) != SECSuccess) {
|
||||
if (tls13_CheckKeyUpdate(ss, ssl_secret_write) != SECSuccess) {
|
||||
rv = PR_FAILURE;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1010,6 +983,35 @@ ssl_SecureWrite(sslSocket *ss, const unsigned char *buf, int len)
|
|||
return ssl_SecureSend(ss, buf, len, 0);
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SSLExp_RecordLayerWriteCallback(PRFileDesc *fd, SSLRecordWriteCallback cb,
|
||||
void *arg)
|
||||
{
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
SSL_DBG(("%d: SSL[%d]: invalid socket for SSL_RecordLayerWriteCallback",
|
||||
SSL_GETPID(), fd));
|
||||
return SECFailure;
|
||||
}
|
||||
if (IS_DTLS(ss)) {
|
||||
SSL_DBG(("%d: SSL[%d]: DTLS socket for SSL_RecordLayerWriteCallback",
|
||||
SSL_GETPID(), fd));
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* This needs both HS and Xmit locks because this value is checked under
|
||||
* both locks. HS to disable reading from the underlying IO layer; Xmit to
|
||||
* prevent writing. */
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
ssl_GetXmitBufLock(ss);
|
||||
ss->recordWriteCallback = cb;
|
||||
ss->recordWriteCallbackArg = arg;
|
||||
ssl_ReleaseXmitBufLock(ss);
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SSL_AlertReceivedCallback(PRFileDesc *fd, SSLAlertCallback cb, void *arg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@ static sslOptions ssl_defaults = {
|
|||
.enableTls13CompatMode = PR_FALSE,
|
||||
.enableDtlsShortHeader = PR_FALSE,
|
||||
.enableHelloDowngradeCheck = PR_FALSE,
|
||||
.enableV2CompatibleHello = PR_FALSE
|
||||
.enableV2CompatibleHello = PR_FALSE,
|
||||
.enablePostHandshakeAuth = PR_FALSE
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -842,6 +843,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val)
|
|||
ss->opt.enableV2CompatibleHello = val;
|
||||
break;
|
||||
|
||||
case SSL_ENABLE_POST_HANDSHAKE_AUTH:
|
||||
ss->opt.enablePostHandshakeAuth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
rv = SECFailure;
|
||||
|
|
@ -990,6 +995,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRIntn *pVal)
|
|||
case SSL_ENABLE_V2_COMPATIBLE_HELLO:
|
||||
val = ss->opt.enableV2CompatibleHello;
|
||||
break;
|
||||
case SSL_ENABLE_POST_HANDSHAKE_AUTH:
|
||||
val = ss->opt.enablePostHandshakeAuth;
|
||||
break;
|
||||
default:
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
rv = SECFailure;
|
||||
|
|
@ -1122,6 +1130,9 @@ SSL_OptionGetDefault(PRInt32 which, PRIntn *pVal)
|
|||
case SSL_ENABLE_V2_COMPATIBLE_HELLO:
|
||||
val = ssl_defaults.enableV2CompatibleHello;
|
||||
break;
|
||||
case SSL_ENABLE_POST_HANDSHAKE_AUTH:
|
||||
val = ssl_defaults.enablePostHandshakeAuth;
|
||||
break;
|
||||
default:
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
rv = SECFailure;
|
||||
|
|
@ -1325,6 +1336,10 @@ SSL_OptionSetDefault(PRInt32 which, PRIntn val)
|
|||
ssl_defaults.enableV2CompatibleHello = val;
|
||||
break;
|
||||
|
||||
case SSL_ENABLE_POST_HANDSHAKE_AUTH:
|
||||
ssl_defaults.enablePostHandshakeAuth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
|
|
@ -4026,20 +4041,25 @@ struct {
|
|||
void *function;
|
||||
} ssl_experimental_functions[] = {
|
||||
#ifndef SSL_DISABLE_EXPERIMENTAL_API
|
||||
EXP(DestroyResumptionTokenInfo),
|
||||
EXP(EnableESNI),
|
||||
EXP(EncodeESNIKeys),
|
||||
EXP(GetCurrentEpoch),
|
||||
EXP(GetExtensionSupport),
|
||||
EXP(GetResumptionTokenInfo),
|
||||
EXP(HelloRetryRequestCallback),
|
||||
EXP(InstallExtensionHooks),
|
||||
EXP(KeyUpdate),
|
||||
EXP(RecordLayerData),
|
||||
EXP(RecordLayerWriteCallback),
|
||||
EXP(SecretCallback),
|
||||
EXP(SendCertificateRequest),
|
||||
EXP(SendSessionTicket),
|
||||
EXP(SetESNIKeyPair),
|
||||
EXP(SetMaxEarlyDataSize),
|
||||
EXP(SetupAntiReplay),
|
||||
EXP(SetResumptionTokenCallback),
|
||||
EXP(SetResumptionToken),
|
||||
EXP(GetResumptionTokenInfo),
|
||||
EXP(DestroyResumptionTokenInfo),
|
||||
EXP(SetESNIKeyPair),
|
||||
EXP(EncodeESNIKeys),
|
||||
EXP(EnableESNI),
|
||||
EXP(SetupAntiReplay),
|
||||
#endif
|
||||
{ "", NULL }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ ssl_GetMacDef(const sslSocket *ss, const ssl3CipherSuiteDef *suiteDef)
|
|||
}
|
||||
|
||||
ssl3CipherSpec *
|
||||
ssl_FindCipherSpecByEpoch(sslSocket *ss, CipherSpecDirection direction,
|
||||
ssl_FindCipherSpecByEpoch(sslSocket *ss, SSLSecretDirection direction,
|
||||
DTLSEpoch epoch)
|
||||
{
|
||||
PRCList *cur_p;
|
||||
|
|
@ -134,7 +134,7 @@ ssl_FindCipherSpecByEpoch(sslSocket *ss, CipherSpecDirection direction,
|
|||
}
|
||||
|
||||
ssl3CipherSpec *
|
||||
ssl_CreateCipherSpec(sslSocket *ss, CipherSpecDirection direction)
|
||||
ssl_CreateCipherSpec(sslSocket *ss, SSLSecretDirection direction)
|
||||
{
|
||||
ssl3CipherSpec *spec = PORT_ZNew(ssl3CipherSpec);
|
||||
if (!spec) {
|
||||
|
|
@ -159,7 +159,7 @@ ssl_SaveCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
|
|||
/* Called from ssl3_InitState. */
|
||||
/* Caller must hold the SpecWriteLock. */
|
||||
SECStatus
|
||||
ssl_SetupNullCipherSpec(sslSocket *ss, CipherSpecDirection dir)
|
||||
ssl_SetupNullCipherSpec(sslSocket *ss, SSLSecretDirection dir)
|
||||
{
|
||||
ssl3CipherSpec *spec;
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ ssl_SetupNullCipherSpec(sslSocket *ss, CipherSpecDirection dir)
|
|||
dtls_InitRecvdRecords(&spec->recvdRecords);
|
||||
|
||||
ssl_SaveCipherSpec(ss, spec);
|
||||
if (dir == CipherSpecRead) {
|
||||
if (dir == ssl_secret_read) {
|
||||
ss->ssl3.crSpec = spec;
|
||||
} else {
|
||||
ss->ssl3.cwSpec = spec;
|
||||
|
|
@ -259,13 +259,13 @@ ssl_DestroyCipherSpecs(PRCList *list)
|
|||
}
|
||||
|
||||
void
|
||||
ssl_CipherSpecReleaseByEpoch(sslSocket *ss, CipherSpecDirection dir,
|
||||
ssl_CipherSpecReleaseByEpoch(sslSocket *ss, SSLSecretDirection dir,
|
||||
DTLSEpoch epoch)
|
||||
{
|
||||
ssl3CipherSpec *spec;
|
||||
SSL_TRC(10, ("%d: SSL[%d]: releasing %s cipher spec for epoch %d",
|
||||
SSL_GETPID(), ss->fd,
|
||||
(dir == CipherSpecRead) ? "read" : "write", epoch));
|
||||
(dir == ssl_secret_read) ? "read" : "write", epoch));
|
||||
|
||||
spec = ssl_FindCipherSpecByEpoch(ss, dir, epoch);
|
||||
if (spec) {
|
||||
|
|
|
|||
|
|
@ -19,13 +19,8 @@ typedef enum {
|
|||
TrafficKeyApplicationData = 3
|
||||
} TrafficKeyType;
|
||||
|
||||
typedef enum {
|
||||
CipherSpecRead,
|
||||
CipherSpecWrite,
|
||||
} CipherSpecDirection;
|
||||
|
||||
#define SPEC_DIR(spec) \
|
||||
((spec->direction == CipherSpecRead) ? "read" : "write")
|
||||
((spec->direction == ssl_secret_read) ? "read" : "write")
|
||||
|
||||
typedef struct ssl3CipherSpecStr ssl3CipherSpec;
|
||||
typedef struct ssl3BulkCipherDefStr ssl3BulkCipherDef;
|
||||
|
|
@ -146,7 +141,7 @@ struct ssl3CipherSpecStr {
|
|||
PRCList link;
|
||||
PRUint8 refCt;
|
||||
|
||||
CipherSpecDirection direction;
|
||||
SSLSecretDirection direction;
|
||||
SSL3ProtocolVersion version;
|
||||
SSL3ProtocolVersion recordVersion;
|
||||
|
||||
|
|
@ -184,17 +179,17 @@ const ssl3BulkCipherDef *ssl_GetBulkCipherDef(const ssl3CipherSuiteDef *cipher_d
|
|||
const ssl3MACDef *ssl_GetMacDefByAlg(SSL3MACAlgorithm mac);
|
||||
const ssl3MACDef *ssl_GetMacDef(const sslSocket *ss, const ssl3CipherSuiteDef *suiteDef);
|
||||
|
||||
ssl3CipherSpec *ssl_CreateCipherSpec(sslSocket *ss, CipherSpecDirection direction);
|
||||
ssl3CipherSpec *ssl_CreateCipherSpec(sslSocket *ss, SSLSecretDirection direction);
|
||||
void ssl_SaveCipherSpec(sslSocket *ss, ssl3CipherSpec *spec);
|
||||
void ssl_CipherSpecAddRef(ssl3CipherSpec *spec);
|
||||
void ssl_CipherSpecRelease(ssl3CipherSpec *spec);
|
||||
void ssl_DestroyCipherSpecs(PRCList *list);
|
||||
SECStatus ssl_SetupNullCipherSpec(sslSocket *ss, CipherSpecDirection dir);
|
||||
SECStatus ssl_SetupNullCipherSpec(sslSocket *ss, SSLSecretDirection dir);
|
||||
|
||||
ssl3CipherSpec *ssl_FindCipherSpecByEpoch(sslSocket *ss,
|
||||
CipherSpecDirection direction,
|
||||
SSLSecretDirection direction,
|
||||
DTLSEpoch epoch);
|
||||
void ssl_CipherSpecReleaseByEpoch(sslSocket *ss, CipherSpecDirection direction,
|
||||
void ssl_CipherSpecReleaseByEpoch(sslSocket *ss, SSLSecretDirection direction,
|
||||
DTLSEpoch epoch);
|
||||
|
||||
#endif /* __sslspec_h_ */
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@ typedef enum {
|
|||
ssl_ct_ack = 25
|
||||
} SSLContentType;
|
||||
|
||||
typedef enum {
|
||||
ssl_secret_read = 1,
|
||||
ssl_secret_write = 2,
|
||||
} SSLSecretDirection;
|
||||
|
||||
typedef struct SSL3StatisticsStr {
|
||||
/* statistics from ssl3_SendClientHello (sch) */
|
||||
long sch_sid_cache_hits;
|
||||
|
|
@ -328,6 +333,9 @@ typedef struct SSLChannelInfoStr {
|
|||
/* Preliminary channel info */
|
||||
#define ssl_preinfo_version (1U << 0)
|
||||
#define ssl_preinfo_cipher_suite (1U << 1)
|
||||
#define ssl_preinfo_0rtt_cipher_suite (1U << 2)
|
||||
/* ssl_preinfo_all doesn't contain ssl_preinfo_0rtt_cipher_suite because that
|
||||
* field is only set if 0-RTT is sent (client) or accepted (server). */
|
||||
#define ssl_preinfo_all (ssl_preinfo_version | ssl_preinfo_cipher_suite)
|
||||
|
||||
typedef struct SSLPreliminaryChannelInfoStr {
|
||||
|
|
@ -359,6 +367,13 @@ typedef struct SSLPreliminaryChannelInfoStr {
|
|||
* resume this session. */
|
||||
PRUint32 maxEarlyDataSize;
|
||||
|
||||
/* The following fields were added in NSS 3.39. */
|
||||
/* This reports the cipher suite used for 0-RTT if it sent or accepted. For
|
||||
* a client, this is set earlier than |cipherSuite|, and will match that
|
||||
* value if 0-RTT is accepted by the server. The server only sets this
|
||||
* after accepting 0-RTT, so this will contain the same value. */
|
||||
PRUint16 zeroRttCipherSuite;
|
||||
|
||||
/* When adding new fields to this structure, please document the
|
||||
* NSS version in which they were added. */
|
||||
} SSLPreliminaryChannelInfo;
|
||||
|
|
@ -407,6 +422,12 @@ typedef struct SSLCipherSuiteInfoStr {
|
|||
* this instead of |authAlgorithm|. */
|
||||
SSLAuthType authType;
|
||||
|
||||
/* The following fields were added in NSS 3.39. */
|
||||
/* This reports the hash function used in the TLS KDF, or HKDF for TLS 1.3.
|
||||
* For suites defined for versions of TLS earlier than TLS 1.2, this reports
|
||||
* ssl_hash_none. */
|
||||
SSLHashType kdfHash;
|
||||
|
||||
/* When adding new fields to this structure, please document the
|
||||
* NSS version in which they were added. */
|
||||
} SSLCipherSuiteInfo;
|
||||
|
|
@ -450,6 +471,7 @@ typedef enum {
|
|||
ssl_tls13_psk_key_exchange_modes_xtn = 45,
|
||||
ssl_tls13_ticket_early_data_info_xtn = 46, /* Deprecated. */
|
||||
ssl_tls13_certificate_authorities_xtn = 47,
|
||||
ssl_tls13_post_handshake_auth_xtn = 49,
|
||||
ssl_signature_algorithms_cert_xtn = 50,
|
||||
ssl_tls13_key_share_xtn = 51,
|
||||
ssl_next_proto_nego_xtn = 13172, /* Deprecated. */
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include "tls13hashstate.h"
|
||||
|
||||
static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
|
||||
CipherSpecDirection install,
|
||||
SSLSecretDirection install,
|
||||
PRBool deleteSecret);
|
||||
static SECStatus tls13_AESGCM(
|
||||
ssl3KeyMaterial *keys,
|
||||
|
|
@ -56,6 +56,7 @@ static SECStatus tls13_SendCertificate(sslSocket *ss);
|
|||
static SECStatus tls13_HandleCertificate(
|
||||
sslSocket *ss, PRUint8 *b, PRUint32 length);
|
||||
static SECStatus tls13_ReinjectHandshakeTranscript(sslSocket *ss);
|
||||
static SECStatus tls13_SendCertificateRequest(sslSocket *ss);
|
||||
static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b,
|
||||
PRUint32 length);
|
||||
static SECStatus
|
||||
|
|
@ -104,6 +105,9 @@ static SECStatus tls13_ComputeFinished(
|
|||
PRBool sending, PRUint8 *output, unsigned int *outputLen,
|
||||
unsigned int maxOutputLen);
|
||||
static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
|
||||
static SECStatus tls13_SendClientSecondFlight(sslSocket *ss,
|
||||
PRBool sendClientCert,
|
||||
SSL3AlertDescription *sendAlert);
|
||||
static SECStatus tls13_FinishHandshake(sslSocket *ss);
|
||||
|
||||
const char kHkdfLabelClient[] = "c";
|
||||
|
|
@ -588,13 +592,13 @@ loser:
|
|||
}
|
||||
|
||||
static PRBool
|
||||
tls13_UseServerSecret(sslSocket *ss, CipherSpecDirection direction)
|
||||
tls13_UseServerSecret(sslSocket *ss, SSLSecretDirection direction)
|
||||
{
|
||||
return ss->sec.isServer == (direction == CipherSpecWrite);
|
||||
return ss->sec.isServer == (direction == ssl_secret_write);
|
||||
}
|
||||
|
||||
static PK11SymKey **
|
||||
tls13_TrafficSecretRef(sslSocket *ss, CipherSpecDirection direction)
|
||||
tls13_TrafficSecretRef(sslSocket *ss, SSLSecretDirection direction)
|
||||
{
|
||||
if (tls13_UseServerSecret(ss, direction)) {
|
||||
return &ss->ssl3.hs.serverTrafficSecret;
|
||||
|
|
@ -603,7 +607,7 @@ tls13_TrafficSecretRef(sslSocket *ss, CipherSpecDirection direction)
|
|||
}
|
||||
|
||||
SECStatus
|
||||
tls13_UpdateTrafficKeys(sslSocket *ss, CipherSpecDirection direction)
|
||||
tls13_UpdateTrafficKeys(sslSocket *ss, SSLSecretDirection direction)
|
||||
{
|
||||
PK11SymKey **secret;
|
||||
PK11SymKey *updatedSecret;
|
||||
|
|
@ -626,7 +630,7 @@ tls13_UpdateTrafficKeys(sslSocket *ss, CipherSpecDirection direction)
|
|||
*secret = updatedSecret;
|
||||
|
||||
ssl_GetSpecReadLock(ss);
|
||||
if (direction == CipherSpecRead) {
|
||||
if (direction == ssl_secret_read) {
|
||||
epoch = ss->ssl3.crSpec->epoch;
|
||||
} else {
|
||||
epoch = ss->ssl3.cwSpec->epoch;
|
||||
|
|
@ -640,6 +644,11 @@ tls13_UpdateTrafficKeys(sslSocket *ss, CipherSpecDirection direction)
|
|||
}
|
||||
++epoch;
|
||||
|
||||
if (ss->secretCallback) {
|
||||
ss->secretCallback(ss->fd, epoch, direction, updatedSecret,
|
||||
ss->secretCallbackArg);
|
||||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, epoch, direction, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
||||
|
|
@ -698,7 +707,7 @@ tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request, PRBool buffer)
|
|||
}
|
||||
ssl_ReleaseXmitBufLock(ss);
|
||||
|
||||
rv = tls13_UpdateTrafficKeys(ss, CipherSpecWrite);
|
||||
rv = tls13_UpdateTrafficKeys(ss, ssl_secret_write);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* error code set by tls13_UpdateTrafficKeys */
|
||||
}
|
||||
|
|
@ -791,7 +800,7 @@ tls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length)
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = tls13_UpdateTrafficKeys(ss, CipherSpecRead);
|
||||
rv = tls13_UpdateTrafficKeys(ss, ssl_secret_read);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */
|
||||
}
|
||||
|
|
@ -820,6 +829,56 @@ tls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length)
|
|||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SSLExp_SendCertificateRequest(PRFileDesc *fd)
|
||||
{
|
||||
SECStatus rv;
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* Not supported. */
|
||||
if (IS_DTLS(ss)) {
|
||||
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (!ss->firstHsDone || ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (ss->ssl3.clientCertRequested) {
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS,
|
||||
idle_handshake);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_post_handshake_auth_xtn)) {
|
||||
PORT_SetError(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
|
||||
rv = tls13_SendCertificateRequest(ss);
|
||||
if (rv == SECSuccess) {
|
||||
ssl_GetXmitBufLock(ss);
|
||||
rv = ssl3_FlushHandshake(ss, 0);
|
||||
ssl_ReleaseXmitBufLock(ss);
|
||||
ss->ssl3.clientCertRequested = PR_TRUE;
|
||||
}
|
||||
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
return rv;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
||||
{
|
||||
|
|
@ -1030,6 +1089,13 @@ tls13_DeriveEarlySecrets(sslSocket *ss)
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
if (ss->secretCallback) {
|
||||
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyEarlyApplicationData,
|
||||
ss->sec.isServer ? ssl_secret_read : ssl_secret_write,
|
||||
ss->ssl3.hs.clientEarlyTrafficSecret,
|
||||
ss->secretCallbackArg);
|
||||
}
|
||||
|
||||
rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
|
||||
NULL, kHkdfLabelEarlyExporterSecret,
|
||||
keylogLabelEarlyExporterSecret,
|
||||
|
|
@ -1098,6 +1164,18 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (ss->secretCallback) {
|
||||
SSLSecretDirection dir =
|
||||
ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
|
||||
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir,
|
||||
ss->ssl3.hs.clientHsTrafficSecret,
|
||||
ss->secretCallbackArg);
|
||||
dir = ss->sec.isServer ? ssl_secret_write : ssl_secret_read;
|
||||
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir,
|
||||
ss->ssl3.hs.serverHsTrafficSecret,
|
||||
ss->secretCallbackArg);
|
||||
}
|
||||
|
||||
SSL_TRC(5, ("%d: TLS13[%d]: compute master secret (%s)",
|
||||
SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
|
||||
|
||||
|
|
@ -1148,6 +1226,18 @@ tls13_ComputeApplicationSecrets(sslSocket *ss)
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
if (ss->secretCallback) {
|
||||
SSLSecretDirection dir =
|
||||
ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
|
||||
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData,
|
||||
dir, ss->ssl3.hs.clientTrafficSecret,
|
||||
ss->secretCallbackArg);
|
||||
dir = ss->sec.isServer ? ssl_secret_write : ssl_secret_read;
|
||||
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData,
|
||||
dir, ss->ssl3.hs.serverTrafficSecret,
|
||||
ss->secretCallbackArg);
|
||||
}
|
||||
|
||||
rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
|
||||
NULL, kHkdfLabelExporterMasterSecret,
|
||||
keylogLabelExporterSecret,
|
||||
|
|
@ -1294,6 +1384,8 @@ tls13_NegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid)
|
|||
PORT_Assert(ss->statelessResume);
|
||||
ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted;
|
||||
ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
|
||||
ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite;
|
||||
ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_0rtt_cipher_suite;
|
||||
}
|
||||
|
||||
/* Check if the offered group is acceptable. */
|
||||
|
|
@ -2101,8 +2193,27 @@ tls13_SendCertificateRequest(sslSocket *ss)
|
|||
/* We should always have at least one of these. */
|
||||
PORT_Assert(SSL_BUFFER_LEN(&extensionBuf) > 0);
|
||||
|
||||
/* Create a new request context for post-handshake authentication */
|
||||
if (ss->firstHsDone) {
|
||||
PRUint8 context[16];
|
||||
SECItem contextItem = { siBuffer, context, sizeof(context) };
|
||||
|
||||
rv = PK11_GenerateRandom(context, sizeof(context));
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE);
|
||||
rv = SECITEM_CopyItem(NULL, &ss->xtnData.certReqContext, &contextItem);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error);
|
||||
goto loser;
|
||||
}
|
||||
}
|
||||
|
||||
rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_request,
|
||||
1 + 0 + /* empty request context */
|
||||
1 + /* request context length */
|
||||
ss->xtnData.certReqContext.len +
|
||||
2 + /* extension length */
|
||||
SSL_BUFFER_LEN(&extensionBuf));
|
||||
if (rv != SECSuccess) {
|
||||
|
|
@ -2110,7 +2221,8 @@ tls13_SendCertificateRequest(sslSocket *ss)
|
|||
}
|
||||
|
||||
/* Context. */
|
||||
rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
|
||||
rv = ssl3_AppendHandshakeVariable(ss, ss->xtnData.certReqContext.data,
|
||||
ss->xtnData.certReqContext.len, 1);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* err set by AppendHandshake. */
|
||||
}
|
||||
|
|
@ -2198,7 +2310,7 @@ tls13_HandleHelloRetryRequest(sslSocket *ss, const PRUint8 *savedMsg,
|
|||
/* Restore the null cipher spec for writing. */
|
||||
ssl_GetSpecWriteLock(ss);
|
||||
ssl_CipherSpecRelease(ss->ssl3.cwSpec);
|
||||
ss->ssl3.cwSpec = ssl_FindCipherSpecByEpoch(ss, CipherSpecWrite,
|
||||
ss->ssl3.cwSpec = ssl_FindCipherSpecByEpoch(ss, ssl_secret_write,
|
||||
TrafficKeyClearText);
|
||||
PORT_Assert(ss->ssl3.cwSpec);
|
||||
ssl_ReleaseSpecWriteLock(ss);
|
||||
|
|
@ -2274,25 +2386,49 @@ tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
||||
|
||||
/* Client */
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
|
||||
wait_cert_request);
|
||||
if (ss->opt.enablePostHandshakeAuth) {
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
|
||||
wait_cert_request, idle_handshake);
|
||||
} else {
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
|
||||
wait_cert_request);
|
||||
}
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
PORT_Assert(ss->ssl3.clientCertChain == NULL);
|
||||
PORT_Assert(ss->ssl3.clientCertificate == NULL);
|
||||
PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
|
||||
PORT_Assert(!ss->ssl3.hs.clientCertRequested);
|
||||
if (ss->firstHsDone) {
|
||||
/* clean up anything left from previous handshake. */
|
||||
if (ss->ssl3.clientCertChain != NULL) {
|
||||
CERT_DestroyCertificateList(ss->ssl3.clientCertChain);
|
||||
ss->ssl3.clientCertChain = NULL;
|
||||
}
|
||||
if (ss->ssl3.clientCertificate != NULL) {
|
||||
CERT_DestroyCertificate(ss->ssl3.clientCertificate);
|
||||
ss->ssl3.clientCertificate = NULL;
|
||||
}
|
||||
if (ss->ssl3.clientPrivateKey != NULL) {
|
||||
SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
|
||||
ss->ssl3.clientPrivateKey = NULL;
|
||||
}
|
||||
SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE);
|
||||
ss->xtnData.certReqContext.data = NULL;
|
||||
} else {
|
||||
PORT_Assert(ss->ssl3.clientCertChain == NULL);
|
||||
PORT_Assert(ss->ssl3.clientCertificate == NULL);
|
||||
PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
|
||||
PORT_Assert(!ss->ssl3.hs.clientCertRequested);
|
||||
PORT_Assert(ss->xtnData.certReqContext.data == NULL);
|
||||
}
|
||||
|
||||
rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* We don't support post-handshake client auth, the certificate request
|
||||
* context must always be empty. */
|
||||
if (context.len > 0) {
|
||||
/* Unless it is a post-handshake client auth, the certificate
|
||||
* request context must be empty. */
|
||||
if (!ss->firstHsDone && context.len > 0) {
|
||||
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST, illegal_parameter);
|
||||
return SECFailure;
|
||||
}
|
||||
|
|
@ -2326,7 +2462,35 @@ tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
}
|
||||
|
||||
ss->ssl3.hs.clientCertRequested = PR_TRUE;
|
||||
TLS13_SET_HS_STATE(ss, wait_server_cert);
|
||||
|
||||
if (ss->firstHsDone) {
|
||||
SSL3AlertDescription sendAlert = no_alert;
|
||||
|
||||
/* Request a client certificate. */
|
||||
rv = ssl3_CompleteHandleCertificateRequest(
|
||||
ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes,
|
||||
&ss->xtnData.certReqAuthorities);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
||||
return rv;
|
||||
}
|
||||
|
||||
ssl_GetXmitBufLock(ss);
|
||||
rv = tls13_SendClientSecondFlight(ss, !ss->ssl3.sendEmptyCert,
|
||||
&sendAlert);
|
||||
ssl_ReleaseXmitBufLock(ss);
|
||||
if (rv != SECSuccess) {
|
||||
if (sendAlert != no_alert) {
|
||||
FATAL_ERROR(ss, PORT_GetError(), sendAlert);
|
||||
} else {
|
||||
LOG_ERROR(ss, PORT_GetError());
|
||||
}
|
||||
return SECFailure;
|
||||
}
|
||||
PORT_Assert(ss->ssl3.hs.ws == idle_handshake);
|
||||
} else {
|
||||
TLS13_SET_HS_STATE(ss, wait_server_cert);
|
||||
}
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
|
|
@ -2348,7 +2512,7 @@ tls13_SendEncryptedServerSequence(sslSocket *ss)
|
|||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
|
||||
CipherSpecWrite, PR_FALSE);
|
||||
ssl_secret_write, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
@ -2458,7 +2622,7 @@ tls13_SendServerHelloSequence(sslSocket *ss)
|
|||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
|
||||
CipherSpecWrite, PR_FALSE);
|
||||
ssl_secret_write, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
@ -2470,7 +2634,7 @@ tls13_SendServerHelloSequence(sslSocket *ss)
|
|||
}
|
||||
if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData,
|
||||
CipherSpecRead, PR_TRUE);
|
||||
ssl_secret_read, PR_TRUE);
|
||||
if (rv != SECSuccess) {
|
||||
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
@ -2482,7 +2646,7 @@ tls13_SendServerHelloSequence(sslSocket *ss)
|
|||
|
||||
rv = tls13_SetCipherSpec(ss,
|
||||
TrafficKeyHandshake,
|
||||
CipherSpecRead, PR_FALSE);
|
||||
ssl_secret_read, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
@ -2591,11 +2755,11 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
|
|||
/* When we send 0-RTT, we saved the null spec in case we needed it to
|
||||
* send another ClientHello in response to a HelloRetryRequest. Now
|
||||
* that we won't be receiving a HelloRetryRequest, release the spec. */
|
||||
ssl_CipherSpecReleaseByEpoch(ss, CipherSpecWrite, TrafficKeyClearText);
|
||||
ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_write, TrafficKeyClearText);
|
||||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
|
||||
CipherSpecRead, PR_FALSE);
|
||||
ssl_secret_read, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error);
|
||||
return SECFailure;
|
||||
|
|
@ -2862,8 +3026,13 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
||||
|
||||
if (ss->sec.isServer) {
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
|
||||
wait_client_cert);
|
||||
if (ss->ssl3.clientCertRequested) {
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
|
||||
idle_handshake);
|
||||
} else {
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
|
||||
wait_client_cert);
|
||||
}
|
||||
} else {
|
||||
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
|
||||
wait_cert_request, wait_server_cert);
|
||||
|
|
@ -2873,7 +3042,7 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
|
||||
/* We can ignore any other cleartext from the client. */
|
||||
if (ss->sec.isServer && IS_DTLS(ss)) {
|
||||
ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyClearText);
|
||||
ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyClearText);
|
||||
dtls_ReceivedFirstMessageInFlight(ss);
|
||||
}
|
||||
/* Process the context string */
|
||||
|
|
@ -2881,10 +3050,12 @@ tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
if (rv != SECSuccess)
|
||||
return SECFailure;
|
||||
|
||||
if (context.len) {
|
||||
/* The context string MUST be empty */
|
||||
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter);
|
||||
return SECFailure;
|
||||
if (ss->ssl3.clientCertRequested) {
|
||||
PORT_Assert(ss->sec.isServer);
|
||||
if (SECITEM_CompareItem(&context, &ss->xtnData.certReqContext) != 0) {
|
||||
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter);
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
|
||||
rv = ssl3_ConsumeHandshakeVariable(ss, &certList, 3, &b, &length);
|
||||
|
|
@ -3125,6 +3296,25 @@ tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key,
|
|||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
SSLExp_SecretCallback(PRFileDesc *fd, SSLSecretCallback cb, void *arg)
|
||||
{
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SecretCallback",
|
||||
SSL_GETPID(), fd));
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
ss->secretCallback = cb;
|
||||
ss->secretCallbackArg = arg;
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/* Derive traffic keys for the next cipher spec in the queue. */
|
||||
static SECStatus
|
||||
tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *spec,
|
||||
|
|
@ -3251,7 +3441,7 @@ tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
|
|||
/* We want to keep read cipher specs around longer because
|
||||
* there are cases where we might get either epoch N or
|
||||
* epoch N+1. */
|
||||
if (IS_DTLS(ss) && spec->direction == CipherSpecRead) {
|
||||
if (IS_DTLS(ss) && spec->direction == ssl_secret_read) {
|
||||
ssl_CipherSpecAddRef(spec);
|
||||
}
|
||||
|
||||
|
|
@ -3274,7 +3464,7 @@ tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
|
|||
/* The record size limit is reduced by one so that the remainder of the
|
||||
* record handling code can use the same checks for all versions. */
|
||||
if (ssl3_ExtensionNegotiated(ss, ssl_record_size_limit_xtn)) {
|
||||
spec->recordSizeLimit = ((spec->direction == CipherSpecRead)
|
||||
spec->recordSizeLimit = ((spec->direction == ssl_secret_read)
|
||||
? ss->opt.recordSizeLimit
|
||||
: ss->xtnData.recordSizeLimit) -
|
||||
1;
|
||||
|
|
@ -3310,7 +3500,7 @@ tls13_SetAlertCipherSpec(sslSocket *ss)
|
|||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
|
||||
CipherSpecWrite, PR_FALSE);
|
||||
ssl_secret_write, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
@ -3325,7 +3515,7 @@ tls13_SetAlertCipherSpec(sslSocket *ss)
|
|||
*/
|
||||
static SECStatus
|
||||
tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
|
||||
CipherSpecDirection direction, PRBool deleteSecret)
|
||||
SSLSecretDirection direction, PRBool deleteSecret)
|
||||
{
|
||||
TrafficKeyType type;
|
||||
SECStatus rv;
|
||||
|
|
@ -3364,7 +3554,7 @@ tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
|
|||
}
|
||||
|
||||
/* Now that we've set almost everything up, finally cut over. */
|
||||
specp = (direction == CipherSpecRead) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec;
|
||||
specp = (direction == ssl_secret_read) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec;
|
||||
ssl_GetSpecWriteLock(ss);
|
||||
ssl_CipherSpecRelease(*specp); /* May delete old cipher. */
|
||||
*specp = spec; /* Overwrite. */
|
||||
|
|
@ -3373,11 +3563,6 @@ tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
|
|||
SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for epoch=%d (%s) dir=%s",
|
||||
SSL_GETPID(), ss->fd, SSL_ROLE(ss), spec->epoch,
|
||||
spec->phase, SPEC_DIR(spec)));
|
||||
|
||||
if (ss->ssl3.changedCipherSpecFunc) {
|
||||
ss->ssl3.changedCipherSpecFunc(ss->ssl3.changedCipherSpecArg,
|
||||
direction == CipherSpecWrite, spec);
|
||||
}
|
||||
return SECSuccess;
|
||||
|
||||
loser:
|
||||
|
|
@ -3937,6 +4122,10 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
}
|
||||
}
|
||||
|
||||
if (ss->ssl3.clientCertRequested) {
|
||||
PORT_Assert(ss->sec.isServer);
|
||||
ss->ssl3.clientCertRequested = PR_FALSE;
|
||||
}
|
||||
TLS13_SET_HS_STATE(ss, wait_finished);
|
||||
|
||||
return SECSuccess;
|
||||
|
|
@ -4238,26 +4427,32 @@ tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
SSL_TRC(3, ("%d: TLS13[%d]: server handle finished handshake",
|
||||
SSL_GETPID(), ss->fd));
|
||||
|
||||
rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.clientHsTrafficSecret,
|
||||
rv = tls13_CommonHandleFinished(ss,
|
||||
ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret,
|
||||
b, length);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (ss->firstHsDone) {
|
||||
TLS13_SET_HS_STATE(ss, idle_handshake);
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
if (!tls13_ShouldRequestClientAuth(ss) &&
|
||||
(ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) {
|
||||
dtls_ReceivedFirstMessageInFlight(ss);
|
||||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
|
||||
CipherSpecRead, PR_FALSE);
|
||||
ssl_secret_read, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (IS_DTLS(ss)) {
|
||||
ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyClearText);
|
||||
ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyClearText);
|
||||
/* We need to keep the handshake cipher spec so we can
|
||||
* read re-transmitted client Finished. */
|
||||
rv = dtls_StartTimer(ss, ss->ssl3.hs.hdTimer,
|
||||
|
|
@ -4361,7 +4556,7 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert,
|
|||
}
|
||||
}
|
||||
|
||||
rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret);
|
||||
rv = tls13_SendFinished(ss, ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure; /* err code was set. */
|
||||
}
|
||||
|
|
@ -4404,7 +4599,8 @@ tls13_SendClientSecondRound(sslSocket *ss)
|
|||
" certificate authentication is still pending.",
|
||||
SSL_GETPID(), ss->fd));
|
||||
ss->ssl3.hs.restartTarget = tls13_SendClientSecondRound;
|
||||
return SECWouldBlock;
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = tls13_ComputeApplicationSecrets(ss);
|
||||
|
|
@ -4432,14 +4628,14 @@ tls13_SendClientSecondRound(sslSocket *ss)
|
|||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
|
||||
CipherSpecWrite, PR_FALSE);
|
||||
ssl_secret_write, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
|
||||
CipherSpecRead, PR_FALSE);
|
||||
ssl_secret_read, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
||||
return SECFailure;
|
||||
|
|
@ -4457,7 +4653,7 @@ tls13_SendClientSecondRound(sslSocket *ss)
|
|||
return SECFailure;
|
||||
}
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
|
||||
CipherSpecWrite, PR_FALSE);
|
||||
ssl_secret_write, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
@ -4815,7 +5011,8 @@ static const struct {
|
|||
{ ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello,
|
||||
hello_retry_request) },
|
||||
{ ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) },
|
||||
{ ssl_tls13_encrypted_sni_xtn, _M2(client_hello, encrypted_extensions) }
|
||||
{ ssl_tls13_encrypted_sni_xtn, _M2(client_hello, encrypted_extensions) },
|
||||
{ ssl_tls13_post_handshake_auth_xtn, _M1(client_hello) }
|
||||
};
|
||||
|
||||
tls13ExtensionStatus
|
||||
|
|
@ -4924,7 +5121,7 @@ tls13_ProtectRecord(sslSocket *ss,
|
|||
const int tagLen = cipher_def->tag_size;
|
||||
SECStatus rv;
|
||||
|
||||
PORT_Assert(cwSpec->direction == CipherSpecWrite);
|
||||
PORT_Assert(cwSpec->direction == ssl_secret_write);
|
||||
SSL_TRC(3, ("%d: TLS13[%d]: spec=%d epoch=%d (%s) protect 0x%0llx len=%u",
|
||||
SSL_GETPID(), ss->fd, cwSpec, cwSpec->epoch, cwSpec->phase,
|
||||
cwSpec->nextSeqNum, contentLen));
|
||||
|
|
@ -5018,7 +5215,7 @@ tls13_UnprotectRecord(sslSocket *ss,
|
|||
|
||||
*alert = bad_record_mac; /* Default alert for most issues. */
|
||||
|
||||
PORT_Assert(spec->direction == CipherSpecRead);
|
||||
PORT_Assert(spec->direction == ssl_secret_read);
|
||||
SSL_TRC(3, ("%d: TLS13[%d]: spec=%d epoch=%d (%s) unprotect 0x%0llx len=%u",
|
||||
SSL_GETPID(), ss->fd, spec, spec->epoch, spec->phase,
|
||||
cText->seqNum, cText->buf->len));
|
||||
|
|
@ -5175,6 +5372,9 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
|
|||
|
||||
ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
|
||||
ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite;
|
||||
/* Note: Reset the preliminary info here rather than just add 0-RTT. We are
|
||||
* only guessing what might happen at this point.*/
|
||||
ss->ssl3.hs.preliminaryInfo = ssl_preinfo_0rtt_cipher_suite;
|
||||
|
||||
SSL_TRC(3, ("%d: TLS13[%d]: in 0-RTT mode", SSL_GETPID(), ss->fd));
|
||||
|
||||
|
|
@ -5203,9 +5403,6 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
|
|||
}
|
||||
}
|
||||
|
||||
/* Cipher suite already set in tls13_SetupClientHello. */
|
||||
ss->ssl3.hs.preliminaryInfo = 0;
|
||||
|
||||
rv = tls13_DeriveEarlySecrets(ss);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
|
|
@ -5216,7 +5413,7 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
|
|||
ssl_CipherSpecAddRef(ss->ssl3.cwSpec);
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData,
|
||||
CipherSpecWrite, PR_TRUE);
|
||||
ssl_secret_write, PR_TRUE);
|
||||
if (rv != SECSuccess) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
|
@ -5279,7 +5476,7 @@ tls13_HandleEndOfEarlyData(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
/* We shouldn't be getting any more early data, and if we do,
|
||||
* it is because of reordering and we drop it. */
|
||||
if (IS_DTLS(ss)) {
|
||||
ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead,
|
||||
ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read,
|
||||
TrafficKeyEarlyApplicationData);
|
||||
dtls_ReceivedFirstMessageInFlight(ss);
|
||||
}
|
||||
|
|
@ -5292,7 +5489,7 @@ tls13_HandleEndOfEarlyData(sslSocket *ss, PRUint8 *b, PRUint32 length)
|
|||
}
|
||||
|
||||
rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
|
||||
CipherSpecRead, PR_FALSE);
|
||||
ssl_secret_read, PR_FALSE);
|
||||
if (rv != SECSuccess) {
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ SECStatus SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate);
|
|||
PRBool tls13_MaybeTls13(sslSocket *ss);
|
||||
SSLAEADCipher tls13_GetAead(const ssl3BulkCipherDef *cipherDef);
|
||||
void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec);
|
||||
SECStatus SSLExp_SendCertificateRequest(PRFileDesc *fd);
|
||||
|
||||
/* Use this instead of FATAL_ERROR when no alert shall be sent. */
|
||||
#define LOG_ERROR(ss, prError) \
|
||||
|
|
|
|||
|
|
@ -915,6 +915,37 @@ tls13_ServerHandleCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
|
|||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
|
||||
TLSExtensionData *xtnData,
|
||||
sslBuffer *buf, PRBool *added)
|
||||
{
|
||||
SSL_TRC(3, ("%d: TLS13[%d]: send post_handshake_auth extension",
|
||||
SSL_GETPID(), ss->fd));
|
||||
|
||||
*added = ss->opt.enablePostHandshakeAuth;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss,
|
||||
TLSExtensionData *xtnData,
|
||||
SECItem *data)
|
||||
{
|
||||
SSL_TRC(3, ("%d: TLS13[%d]: handle post_handshake_auth extension",
|
||||
SSL_GETPID(), ss->fd));
|
||||
|
||||
if (data->len) {
|
||||
PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* Keep track of negotiated extensions. */
|
||||
xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_post_handshake_auth_xtn;
|
||||
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
|
||||
*
|
||||
|
|
|
|||
|
|
@ -93,5 +93,11 @@ SECStatus tls13_ClientSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData
|
|||
SECStatus tls13_ServerHandleEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
|
||||
SECItem *data);
|
||||
SECStatus tls13_ClientCheckEsniXtn(sslSocket *ss);
|
||||
SECStatus tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
|
||||
TLSExtensionData *xtnData,
|
||||
sslBuffer *buf, PRBool *added);
|
||||
SECStatus tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss,
|
||||
TLSExtensionData *xtnData,
|
||||
SECItem *data);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@
|
|||
'cmd/pk11ectest/pk11ectest.gyp:pk11ectest',
|
||||
'cmd/pk11gcmtest/pk11gcmtest.gyp:pk11gcmtest',
|
||||
'cmd/pk11mode/pk11mode.gyp:pk11mode',
|
||||
'cmd/pk11importtest/pk11importtest.gyp:pk11importtest',
|
||||
'cmd/pk1sign/pk1sign.gyp:pk1sign',
|
||||
'cmd/pp/pp.gyp:pp',
|
||||
'cmd/rsaperf/rsaperf.gyp:rsaperf',
|
||||
|
|
|
|||
|
|
@ -252,7 +252,15 @@ dbtest_main()
|
|||
else
|
||||
html_passed "Nicknane conflict test-setting nickname conflict was correctly rejected"
|
||||
fi
|
||||
|
||||
# import a token private key and make sure the corresponding public key is
|
||||
# created
|
||||
${BINDIR}/pk11importtest -d ${CONFLICT_DIR} -f ${R_PWFILE}
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
html_failed "Importing Token Private Key does not create the corrresponding Public Key"
|
||||
else
|
||||
html_passed "Importing Token Private Key correctly creates the corrresponding Public Key"
|
||||
fi
|
||||
}
|
||||
|
||||
################## main #################################################
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
gtest_init()
|
||||
{
|
||||
cd "$(dirname "$1")"
|
||||
SOURCE_DIR="$PWD"/../..
|
||||
if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
|
||||
cd ../common
|
||||
. ./init.sh
|
||||
|
|
@ -33,6 +34,7 @@ gtest_init()
|
|||
if [ -z "${CLEANUP}" ] ; then # if nobody else is responsible for
|
||||
CLEANUP="${SCRIPTNAME}" # cleaning this script will do it
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
########################## gtest_start #############################
|
||||
|
|
@ -42,7 +44,7 @@ gtest_start()
|
|||
{
|
||||
echo "gtests: ${GTESTS}"
|
||||
for i in ${GTESTS}; do
|
||||
if [ ! -f ${BINDIR}/$i ]; then
|
||||
if [ ! -f "${BINDIR}/$i" ]; then
|
||||
html_unknown "Skipping $i (not built)"
|
||||
continue
|
||||
fi
|
||||
|
|
@ -50,20 +52,22 @@ gtest_start()
|
|||
html_head "$i"
|
||||
if [ ! -d "$GTESTDIR" ]; then
|
||||
mkdir -p "$GTESTDIR"
|
||||
echo "${BINDIR}/certutil" -N -d "$GTESTDIR" --empty-password 2>&1
|
||||
"${BINDIR}/certutil" -N -d "$GTESTDIR" --empty-password 2>&1
|
||||
fi
|
||||
cd "$GTESTDIR"
|
||||
GTESTREPORT="$GTESTDIR/report.xml"
|
||||
PARSED_REPORT="$GTESTDIR/report.parsed"
|
||||
echo "executing $i"
|
||||
${BINDIR}/$i "${SOURCE_DIR}/gtests/freebl_gtest/kat/Hash_DRBG.rsp" \
|
||||
-d "$GTESTDIR" --gtest_output=xml:"${GTESTREPORT}" \
|
||||
--gtest_filter="${GTESTFILTER-*}"
|
||||
"${BINDIR}/$i" "${SOURCE_DIR}/gtests/freebl_gtest/kat/Hash_DRBG.rsp" \
|
||||
-d "$GTESTDIR" -w --gtest_output=xml:"${GTESTREPORT}" \
|
||||
--gtest_filter="${GTESTFILTER:-*}"
|
||||
html_msg $? 0 "$i run successfully"
|
||||
echo "test output dir: ${GTESTREPORT}"
|
||||
echo "executing sed to parse the xml report"
|
||||
sed -f ${COMMON}/parsegtestreport.sed "${GTESTREPORT}" > "${PARSED_REPORT}"
|
||||
sed -f "${COMMON}/parsegtestreport.sed" "$GTESTREPORT" > "$PARSED_REPORT"
|
||||
echo "processing the parsed report"
|
||||
cat "${PARSED_REPORT}" | while read result name; do
|
||||
cat "$PARSED_REPORT" | while read result name; do
|
||||
if [ "$result" = "notrun" ]; then
|
||||
echo "$name" SKIPPED
|
||||
elif [ "$result" = "run" ]; then
|
||||
|
|
@ -78,13 +82,12 @@ gtest_start()
|
|||
gtest_cleanup()
|
||||
{
|
||||
html "</TABLE><BR>"
|
||||
cd ${QADIR}
|
||||
cd "${QADIR}"
|
||||
. common/cleanup.sh
|
||||
}
|
||||
|
||||
################## main #################################################
|
||||
GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest blake2b_gtest"
|
||||
SOURCE_DIR="$PWD"/../..
|
||||
gtest_init $0
|
||||
GTESTS="${GTESTS:-prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest blake2b_gtest}"
|
||||
gtest_init "$0"
|
||||
gtest_start
|
||||
gtest_cleanup
|
||||
|
|
|
|||
|
|
@ -43,25 +43,25 @@ let prompterParent = runInParent(() => {
|
|||
let prompter2 = promptFac.getPrompt(chromeWin, Ci.nsIAuthPrompt2);
|
||||
|
||||
let channels = {};
|
||||
channels.channel1 = Services.io.newChannel2("http://example.com",
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.
|
||||
scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
channels.channel1 = Services.io.newChannel("http://example.com",
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.
|
||||
scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
channels.channel2 = Services.io.newChannel2("http://example2.com",
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.
|
||||
scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
channels.channel2 = Services.io.newChannel("http://example2.com",
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.
|
||||
scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
addMessageListener("proxyPrompter", function onMessage(msg) {
|
||||
let args = [...msg.args];
|
||||
|
|
|
|||
|
|
@ -103,14 +103,14 @@ var resolveCallback = SpecialPowers.wrapCallbackObject({
|
|||
// The proxyChannel needs to move to at least on-modify-request to
|
||||
// have valid ProxyInfo, but we use OnStartRequest during startup()
|
||||
// for simplicity.
|
||||
proxyChannel = SpecialPowers.Services.io.newChannel2(proxiedHost,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
systemPrincipal,
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
proxyChannel = SpecialPowers.Services.io.newChannel(proxiedHost,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
systemPrincipal,
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
proxyChannel.asyncOpen(SpecialPowers.wrapCallbackObject(new proxyChannelListener()));
|
||||
},
|
||||
});
|
||||
|
|
@ -119,14 +119,14 @@ function startup() {
|
|||
// Need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy.
|
||||
var pps = SpecialPowers.Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
|
||||
|
||||
var channel = SpecialPowers.Services.io.newChannel2("http://example.com",
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
systemPrincipal,
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
var channel = SpecialPowers.Services.io.newChannel("http://example.com",
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
systemPrincipal,
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
pps.asyncResolve(channel, 0, resolveCallback);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1584,12 +1584,12 @@ nsresult PendingLookup::SendRemoteQueryInternal(Reason& aReason) {
|
|||
|
||||
// Set up the channel to transmit the request to the service.
|
||||
nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
|
||||
rv = ios->NewChannel2(serviceUrl, nullptr, nullptr,
|
||||
nullptr, // aLoadingNode
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nullptr, // aTriggeringPrincipal
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER, getter_AddRefs(mChannel));
|
||||
rv = ios->NewChannel(serviceUrl, nullptr, nullptr,
|
||||
nullptr, // aLoadingNode
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nullptr, // aTriggeringPrincipal
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER, getter_AddRefs(mChannel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
|
||||
|
|
|
|||
|
|
@ -735,12 +735,12 @@ function makeURI(aURLSpec, aCharset) {
|
|||
function makeChannel(url) {
|
||||
try {
|
||||
let uri = typeof url == "string" ? Services.io.newURI(url) : url;
|
||||
return Services.io.newChannelFromURI2(uri,
|
||||
null, /* loadingNode */
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, /* triggeringPrincipal */
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
return Services.io.newChannelFromURI(uri,
|
||||
null, /* loadingNode */
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, /* triggeringPrincipal */
|
||||
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
} catch (ex) { }
|
||||
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue