Skip to content

Commit

Permalink
Add compatibility with TokenSigning
Browse files Browse the repository at this point in the history
  • Loading branch information
mrts committed Dec 21, 2020
1 parent afb605c commit bce8793
Show file tree
Hide file tree
Showing 18 changed files with 682 additions and 88 deletions.
38 changes: 38 additions & 0 deletions src/background/actions/TokenSigning/errorToResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 The Web eID Project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import ErrorCode from "@web-eid/web-eid-library/errors/ErrorCode";
import { serializeError } from "@web-eid/web-eid-library/utils/errorSerializer";
import { TokenSigningErrorResponse } from "../../../models/TokenSigning/TokenSigningResponse";
import tokenSigningResponse from "../../../shared/tokenSigningResponse";

export default function errorToResponse(nonce: string, error: any): TokenSigningErrorResponse {
if (error.code === ErrorCode.ERR_WEBEID_USER_CANCELLED) {
return tokenSigningResponse<TokenSigningErrorResponse>("user_cancel", nonce);
} else if (error.code === ErrorCode.ERR_WEBEID_NATIVE_FATAL) {
const nativeException = serializeError(error);

return tokenSigningResponse<TokenSigningErrorResponse>("driver_error", nonce, { nativeException });
} else {
return tokenSigningResponse<TokenSigningErrorResponse>("technical_error", nonce, { error });
}
}
84 changes: 84 additions & 0 deletions src/background/actions/TokenSigning/getCertificate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2020 The Web eID Project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import UserTimeoutError from "@web-eid/web-eid-library/errors/UserTimeoutError";

import config from "../../../config";
import ByteArray from "../../../shared/ByteArray";
import NativeAppService from "../../services/NativeAppService";
import tokenSigningResponse from "../../../shared/tokenSigningResponse";
import {
TokenSigningCertResponse,
TokenSigningErrorResponse,
} from "../../../models/TokenSigning/TokenSigningResponse";
import { throwAfterTimeout } from "../../../shared/utils";
import errorToResponse from "./errorToResponse";

export default async function getCertificate(
nonce: string,
sourceUrl: string,
lang?: string,
filter: "AUTH" | "SIGN" = "SIGN",
): Promise<TokenSigningCertResponse | TokenSigningErrorResponse> {
try {
const nativeAppService = new NativeAppService();
const nativeAppStatus = await nativeAppService.connect();

console.log("Get certificate: connected to native", nativeAppStatus);

const certificateResponse = await Promise.race([
nativeAppService.send({
command: "get-certificate",

arguments: {
"type": filter.toLowerCase(),
"origin": (new URL(sourceUrl)).origin,

// TODO: Implement i18n in native application
// "lang": lang
},
}),

throwAfterTimeout(config.TOKEN_SIGNING_USER_INTERACTION_TIMEOUT, new UserTimeoutError()),
]) as {
certificate: string;
error?: string;

"supported-signature-algos": Array<{
"crypto-algo": string;
"hash-algo": string;
"padding-algo": string;
}>;
};

if (!certificateResponse.certificate) {
return tokenSigningResponse<TokenSigningErrorResponse>("no_certificates", nonce);
} else {
return tokenSigningResponse<TokenSigningCertResponse>("ok", nonce, {
cert: new ByteArray().fromBase64(certificateResponse.certificate).toHex(),
});
}
} catch (error) {
console.error(error);
return errorToResponse(nonce, error);
}
}
51 changes: 51 additions & 0 deletions src/background/actions/TokenSigning/getStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 The Web eID Project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import NativeAppService from "../../services/NativeAppService";
import tokenSigningResponse from "../../../shared/tokenSigningResponse";
import {
TokenSigningErrorResponse,
TokenSigningStatusResponse,
} from "../../../models/TokenSigning/TokenSigningResponse";
import errorToResponse from "./errorToResponse";

export default async function getStatus(
nonce: string,
): Promise<TokenSigningStatusResponse | TokenSigningErrorResponse> {

try {
const nativeAppService = new NativeAppService();
const nativeAppStatus = await nativeAppService.connect();

// The token-signing uses x.y.z.build version string pattern
const version = nativeAppStatus.version.replace("+", ".");

if (!version) {
throw new Error("missing native application version");
}

return tokenSigningResponse<TokenSigningStatusResponse>("ok", nonce, { version });
} catch (error) {
console.error(error);
return errorToResponse(nonce, error);
}
}
31 changes: 31 additions & 0 deletions src/background/actions/TokenSigning/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2020 The Web eID Project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import getStatus from "./getStatus";
import getCertificate from "./getCertificate";
import sign from "./sign";

export default {
getStatus,
getCertificate,
sign,
};
78 changes: 78 additions & 0 deletions src/background/actions/TokenSigning/sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2020 The Web eID Project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import UserTimeoutError from "@web-eid/web-eid-library/errors/UserTimeoutError";

import config from "../../../config";
import ByteArray from "../../../shared/ByteArray";
import NativeAppService from "../../services/NativeAppService";
import tokenSigningResponse from "../../../shared/tokenSigningResponse";
import {
TokenSigningSignResponse,
TokenSigningErrorResponse,
} from "../../../models/TokenSigning/TokenSigningResponse";
import { throwAfterTimeout } from "../../../shared/utils";
import errorToResponse from "./errorToResponse";

export default async function sign(
nonce: string,
sourceUrl: string,
certificate: string,
hash: string,
algorithm: string,
lang?: string,
): Promise<TokenSigningSignResponse | TokenSigningErrorResponse> {
try {
const nativeAppService = new NativeAppService();
const nativeAppStatus = await nativeAppService.connect();

console.log("Sign: connected to native", nativeAppStatus);

const signatureResponse = await Promise.race([
nativeAppService.send({
command: "sign",

arguments: {
"doc-hash": new ByteArray().fromHex(hash).toBase64(),
"hash-algo": algorithm,
"origin": (new URL(sourceUrl)).origin,
"user-eid-cert": new ByteArray().fromHex(certificate).toBase64(),

// TODO: Implement i18n in native application
// "lang": lang
},
}),
throwAfterTimeout(config.TOKEN_SIGNING_USER_INTERACTION_TIMEOUT, new UserTimeoutError()),
]) as { signature: string; error: string };

if (!signatureResponse.signature) {
return tokenSigningResponse<TokenSigningErrorResponse>("technical_error", nonce);
} else {
return tokenSigningResponse<TokenSigningSignResponse>("ok", nonce, {
signature: new ByteArray().fromBase64(signatureResponse.signature).toHex(),
});
}
} catch (error) {
console.error(error);
return errorToResponse(nonce, error);
}
}
5 changes: 3 additions & 2 deletions src/background/actions/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import NativeAppService from "../services/NativeAppService";
import WebServerService from "../services/WebServerService";
import TypedMap from "../../models/TypedMap";
import HttpResponse from "../../models/HttpResponse";
import { toBase64, pick, throwAfterTimeout, isSameOrigin } from "../../shared/utils";
import { pick, throwAfterTimeout, isSameOrigin } from "../../shared/utils";
import ByteArray from "../../shared/ByteArray";

export default async function authenticate(
getAuthChallengeUrl: string,
Expand Down Expand Up @@ -89,7 +90,7 @@ export default async function authenticate(

"origin-cert": (
response.certificateInfo?.rawDER
? toBase64(response.certificateInfo?.rawDER)
? new ByteArray(response.certificateInfo?.rawDER).toBase64()
: null
),
},
Expand Down
57 changes: 49 additions & 8 deletions src/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,76 @@ import Action from "@web-eid/web-eid-library/models/Action";
import libraryConfig from "@web-eid/web-eid-library/config";

import { LibraryMessage } from "../models/LibraryMessage";
import { MessageSender } from "../models/Browser/Runtime";
import authenticate from "./actions/authenticate";
import sign from "./actions/sign";
import getStatus from "./actions/getStatus";

browser.runtime.onMessage.addListener((message: LibraryMessage, sender: any, sendResponse: any) => {
import { TokenSigningMessage } from "../models/TokenSigning/TokenSigningMessage";
import TokenSigningAction from "./actions/TokenSigning";

async function onAction(message: LibraryMessage): Promise<void | object> {
switch (message.action) {
case Action.AUTHENTICATE:
authenticate(
return await authenticate(
message.getAuthChallengeUrl,
message.postAuthTokenUrl,
message.headers,
message.userInteractionTimeout || libraryConfig.DEFAULT_USER_INTERACTION_TIMEOUT,
message.serverRequestTimeout || libraryConfig.DEFAULT_SERVER_REQUEST_TIMEOUT,
).then(sendResponse);
break;
);

case Action.SIGN:
sign(
return await sign(
message.postPrepareSigningUrl,
message.postFinalizeSigningUrl,
message.headers,
message.userInteractionTimeout || libraryConfig.DEFAULT_USER_INTERACTION_TIMEOUT,
message.serverRequestTimeout || libraryConfig.DEFAULT_SERVER_REQUEST_TIMEOUT,
).then(sendResponse);
break;
);

case Action.STATUS:
getStatus().then(sendResponse);
return await getStatus();
}
}

async function onTokenSigningAction(message: TokenSigningMessage, sender: MessageSender): Promise<void | object> {
if (!sender.url) return;

switch (message.type) {
case "VERSION": {
return await TokenSigningAction.getStatus(
message.nonce,
);
}

case "CERT": {
return await TokenSigningAction.getCertificate(
message.nonce,
sender.url,
message.lang,
message.filter,
);
}

case "SIGN": {
return await TokenSigningAction.sign(
message.nonce,
sender.url,
message.cert,
message.hash,
message.hashtype,
message.lang,
);
}
}
}

browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
if ((message as LibraryMessage).action) {
onAction(message).then(sendResponse);
} else if ((message as TokenSigningMessage).type) {
onTokenSigningAction(message, sender).then(sendResponse);
}
return true;
});
3 changes: 2 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ export default Object.freeze({

NATIVE_MESSAGE_MAX_BYTES: 8192,

TOKEN_SIGNING_BACKWARDS_COMPATIBILITY: true,
TOKEN_SIGNING_BACKWARDS_COMPATIBILITY: true,
TOKEN_SIGNING_USER_INTERACTION_TIMEOUT: 1000 * 60 * 5, // 5 minutes
});
Loading

0 comments on commit bce8793

Please sign in to comment.