Merge branch '2025.3.1-rc' into moderation

This commit is contained in:
yflory 2025-05-23 16:07:39 +02:00
commit aba6a16cd8
195 changed files with 2345 additions and 25721 deletions

View File

@ -89,13 +89,10 @@ body:
label: Version
description: What version of CryptPad are you running?
options:
- 2025.3.0
- 2024.12.0
- 2024.9.1
- 2024.9.0
- 2024.6.1
- 2024.6.0
- 2024.3.1
- 2024.3.0
- Other
validations:
required: true

View File

@ -4,6 +4,11 @@ SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and cont
SPDX-License-Identifier: AGPL-3.0-or-later
-->
# Release (2025.3.1)
> [!NOTE]
> The new version of OnlyOffice is much bigger than the previous versions. When installing version 8.3 with `install-onlyoffice.sh` ~830MB additional disk space is needed.
# 💐 Spring 2025 release (2025.3.0)
## Goals

View File

@ -1,10 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>

View File

@ -22,4 +22,5 @@ We do also communicate about this topic on:
## Reporting a Vulnerability
Vulnerabilities can be reported using the GitHub Security interface. You can also send us an email at security@cryptpad.org
Brefore reaching out about a potential vulnerability, ensure it falls within the scope of our project. Please read thoroughly our [whitepaper](https://blog.cryptpad.org/2023/02/02/Whitepaper/) describing our threat model and what we consider acceptable or not security-wise. If you are sure you found a real vulnerability, you can report it using the GitHub Security interface. You can also send us an email at security@cryptpad.org

View File

@ -53,8 +53,8 @@ define([
var keys = Nacl.sign.keyPair();
var inviteToken = window.location.hash.slice(1);
localStorage.CP_sso_auth = JSON.stringify({
s: Nacl.util.encodeBase64(keys.secretKey),
p: Nacl.util.encodeBase64(keys.publicKey),
s: Util.encodeBase64(keys.secretKey),
p: Util.encodeBase64(keys.publicKey),
token: inviteToken
});
ServerCommand(keys, {

View File

@ -137,6 +137,13 @@ define(req, function(AppConfig, Default, Language) {
Messages.admin_documentsDeletionHint = "Archive a list of documents using their id (one document id per line)";
Messages.admin_documentsDeletionTitle = "Archive multiple documents";
// XXX
Messages.badges_admin = "Instance administrator";
Messages.badges_moderator = "Instance moderator";
Messages.badges_premium = "Premium user";
Messages.badges_error = "Error while validating this user's data";
Messages.profile_badges = "Badges";
return Messages;
});

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2025 XWiki CryptPad Team <contact@cryptpad.org> and contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
.badges_vars(
@width: 30px
) {
@badges-width: @width;
}
.badges_main(@width: 30px) {
--LessLoader_require: LessLoader_currentFile();
.badges_vars(@width);
--badges-width: @badges-width;
--badges-font: ceil(@badges-width*2/3);
}
& {
.badges_vars();
i.cp-badge {
width: var(--badges-width);
height: var(--badges-width);
box-sizing: border-box;
border: 1px solid @cryptpad_text_col;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
font-size: var(--badges-font);
}
}

View File

@ -263,6 +263,14 @@
.cp-toolbar-userlist-friend {
padding: 0;
}
.cp-userlist-badge {
i {
color: @cryptpad_color_brand;
}
i.cp-badge-error {
color: @cryptpad_color_warn_red;
}
}
}
}
}

View File

@ -104,6 +104,7 @@ server {
# Enable SharedArrayBuffer in Firefox (for .xlsx export)
add_header Cross-Origin-Resource-Policy cross-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
add_header Referrer-Policy same-origin;
# Insert the path to your CryptPad repository root here
root /home/cryptpad/cryptpad;
@ -169,6 +170,7 @@ server {
# they unfortunately still require exceptions to the sandboxing to work correctly.
if ($uri ~ ^/(?:sheet|doc|presentation)/inner.html) { set $unsafe 1; }
if ($uri ~ ^/common/onlyoffice/.*/.*\.html) { set $unsafe 1; }
if ($uri ~ ^/common/onlyoffice/dist/.*/sdkjs/common/spell/spell/spell.js.*$) { set $unsafe 1; }
# everything except the sandbox domain is a privileged scope, as they might be used to handle keys
if ($host != $sandbox_domain) { set $unsafe 0; }

151
funding.json Normal file
View File

@ -0,0 +1,151 @@
{
"version": "v1.0.0",
"drips": {
"ethereum": {
"ownedBy": "0xa117114b3a22e038c1545043d610dbd4029355b1"
}
},
"entity": {
"type": "organisation",
"role": "owner",
"name": "XWiki SAS",
"email": "funding@xwiki.com",
"phone": "",
"description": "XWiki SAS is a European Open Source organization, maintaining Open Source Collaboration projects, XWiki and CryptPad. We believe that Open Source projects are key to getting back control on software, and that companies are important to allow the maintenance of Open Source projects.",
"webpageUrl": {
"url": "https://xwiki.com",
"wellKnown": "https://xwiki.com/.well-known/funding-manifest-urls"
}
},
"projects": [
{
"guid": "cryptpad",
"name": "CryptPad",
"description": "CryptPad is an end-to-end encrypted Collaboration Platform focused on providing real-time editing tools while protecting privacy.",
"webpageUrl": {
"url": "https://cryptpad.org",
"wellKnown": "https://cryptpad.org/.well-known/funding-manifest-urls"
},
"repositoryUrl": {
"url": "https://github.com/cryptpad/cryptpad"
},
"licenses": [
"spdx:AGPL-3.0-or-later"
],
"tags": [
"privacy",
"e2ee",
"collaboration",
"realtimeediting",
"office",
"drive"
]
}
],
"funding": {
"channels": [
{
"guid": "opencollective-cryptpad",
"type": "payment-provider",
"address": "https://opencollective.com/cryptpad",
"description": "OpenCollective CryptPad Account"
},
{
"guid": "bank",
"type": "bank",
"address": "Contact XWiki SAS",
"description": "Donation by Bank Transfer or other means with XWiki SAS"
}
],
"plans": [
{
"guid": "cryptpad-individual-helper",
"status": "active",
"name": "Individual Donator - Starter",
"description": "Individual Donator would like to help us fund the maintenance and further improvement of the software.\n\n35000 people donating this every year would be needed to fund the current team to allow CryptPad to be independent from outside grants.\n\nThis plans corresponds to:\n\n- an occasional user of CryptPad\n- a regular user with limited funds\n- somebody just wanting to give us a nudge",
"amount": 20,
"currency": "EUR",
"frequency": "yearly",
"channels": [
"opencollective-cryptpad"
]
},
{
"guid": "cryptpad-individual-user",
"status": "active",
"name": "Individual Donator - User",
"description": "Individual Donator would like to help us fund the maintenance and further improvement of the software.\n\n14000 people donating this every year would be needed to fund the current team to allow CryptPad to be independent from outside grants.\n\nThis plans corresponds to:\n\n- an regular user of CryptPad\n- a privacy believer who would like to help us\n- a personal instance owner with limited funds",
"amount": 50,
"currency": "EUR",
"frequency": "yearly",
"channels": [
"opencollective-cryptpad"
]
},
{
"guid": "cryptpad-individual-sponsor",
"status": "active",
"name": "Individual Donator - Sponsor",
"description": "Individual Donator would like to help us fund the maintenance and further improvement of the software.\n\n3500 people donating this every year would be needed to fund the current team to allow CryptPad to be independent from outside grants.\n\nThis plans corresponds to:\n\n- an regular user of CryptPad who really likes CryptPad\n- a privacy believer who wants to accelerate privacy online\n- a personal or community small size instance owner",
"amount": 200,
"currency": "EUR",
"frequency": "yearly",
"channels": [
"opencollective-cryptpad"
]
},
{
"guid": "cryptpad-instanceowner-smallorg",
"status": "active",
"name": "Small Organization Using CryptPad",
"description": "Small Organization which would like to help us fund the maintenance and further improvement of the software.\n\n1500 organizations donating this every year would be needed to fund the current team to allow CryptPad to be independent from outside grants.\n\nThis plans corresponds to:\n\n- a non-profit organization having some funds hosting CryptPad for their members\n- a small company using CryptPad for its users\n- any organizations wanting Privacy software to progress",
"amount": 500,
"currency": "EUR",
"frequency": "yearly",
"channels": [
"opencollective-cryptpad"
]
},
{
"guid": "cryptpad-instanceowner-bigorg",
"status": "active",
"name": "Small Organization Using CryptPad",
"description": "Small Organization which would like to help us fund the maintenance and further improvement of the software.\n\n350 organizations donating this every year would be needed to fund the current team to allow CryptPad to be independent from outside grants.\n\nThis plans corresponds to:\n\n- a commercial company using CryptPad regularly internally or externally\n- any organization having sufficient funds wanting to support Open Source Software doing the right thing\n",
"amount": 2000,
"currency": "EUR",
"frequency": "yearly",
"channels": [
"opencollective-cryptpad",
"bank"
]
},
{
"guid": "cryptpad-instanceowner-bigorg",
"status": "active",
"name": "Organization using CryptPad heavily or as part of their offering",
"description": "Organization using CryptPad heavily or as part of it's commercial offering and which would like to significantly contribute to it's development.\n\n70 organizations donating this every year would be needed to fund the current team to allow CryptPad to be independent from outside grants.\n\nThis plans corresponds to:\n\n- a commercial company using CryptPad heavily or as part of a commercial offering\n- foundations who would like to support the development.",
"amount": 10000,
"currency": "EUR",
"frequency": "yearly",
"channels": [
"opencollective-cryptpad",
"bank"
]
},
{
"guid": "cryptpad-org-funder",
"status": "active",
"name": "Funding organization or large company supporting the maintenance and development of CryptPad",
"description": "This is meant for an organization investing in Privacy Preserving technologies and/or Open Source software which is important for the internet and wanting to support CryptPad with a grant. The grant can be for specific development useful for our roadmap or just for maintenance of the software.\n\n12 such grants would be needed to fund the current team.",
"amount": 50000,
"currency": "EUR",
"frequency": "one-time",
"channels": [
"opencollective-cryptpad",
"bank"
]
}
],
"history": []
}
}

View File

@ -15,6 +15,10 @@ PROPS_FILE="$CONF_DIR"/onlyoffice.properties
declare -A PROPS
main() {
# clean build env in case a previous build has failed
rm -rf "$BUILDS_DIR"
mkdir -p "$CONF_DIR"
load_props
@ -23,17 +27,18 @@ main() {
ask_for_license
# Remeber the 1st version that is installed. This will help us install only
# Remember the 1st version that is installed. This will help us install only
# needed OnlyOffice versions in a later version of this script.
set_prop oldest_needed_version v1
mkdir -p "$OO_DIR"
install_version v1 4f370beb
install_version v2b d9da72fd
install_version v4 6ebc6938
install_version v5 88a356f0
install_version v6 abd8a309
install_version v7 e1267803
install_old_version v1 4f370beb
install_old_version v2b d9da72fd
install_old_version v4 6ebc6938
install_old_version v5 88a356f0
install_old_version v6 abd8a309
install_version v7 v7.3.3.60+11 1e65be6dc87d97e82b4972f303956e5397b34d637ca80a4239c48e49ab829ee5afc8f5b1680b2fb14230d63ff872ec5f9b562bb6c3f1811316b68f8b436f7ee6
install_version v8 v8.3.3.23+4 01abfb3e13dae2066c9fcdc9fd3a3a21cd08212feb7ee2f927d8acaa5c3e560f8ce7c78c533c6aad7048aaecc14f7445891f06cb38a1720e1637a971c0a02295
install_x2t v7.3+1 ab0c05b0e4c81071acea83f0c6a8e75f5870c360ec4abc4af09105dd9b52264af9711ec0b7020e87095193ac9b6e20305e446f2321a541f743626a598e5318c1
rm -rf "$BUILDS_DIR"
@ -156,23 +161,22 @@ EOF
}
ensure_oo_is_downloaded() {
ensure_command_available git
ensure_command_available git
if ! [ -d "$BUILDS_DIR" ]; then
echo "Downloading OnlyOffice..."
git clone --bare https://github.com/cryptpad/onlyoffice-builds.git "$BUILDS_DIR"
fi
if [ ${TRUST_REPOSITORY+x} ] || [ "${PROPS[trust_repository]:-no}" == yes ]; then
git config --global --add safe.directory /cryptpad/onlyoffice-conf/onlyoffice-builds.git
fi
if ! [ -d "$BUILDS_DIR" ]; then
echo "Downloading OnlyOffice..."
git clone --bare https://github.com/cryptpad/onlyoffice-builds.git "$BUILDS_DIR"
fi
if [ ${TRUST_REPOSITORY+x} ] || [ "${PROPS[trust_repository]:-no}" == yes ]; then
git config --global --add safe.directory /cryptpad/onlyoffice-conf/onlyoffice-builds.git
fi
}
install_version() {
install_old_version() {
local DIR=$1
local COMMIT=$2
local FULL_DIR=$OO_DIR/$DIR
local LAST_DIR
LAST_DIR=$(pwd)
local LAST_DIR=$(pwd)
local ACTUAL_COMMIT="not installed"
if [ -e "$FULL_DIR"/.commit ]; then
@ -206,6 +210,40 @@ install_version() {
fi
}
install_version() {
ensure_command_available curl
ensure_command_available sha512sum
ensure_command_available unzip
local DIR=$1
local VERSION=$2
local HASH=$3
local FULL_DIR=$OO_DIR/$DIR
local LAST_DIR=$(pwd)
if [ ! -e "$FULL_DIR"/.version ] || [ "$(cat "$FULL_DIR"/.version)" != "$VERSION" ]; then
rm -rf "$FULL_DIR"
mkdir -p "$FULL_DIR"
cd "$FULL_DIR"
curl "https://github.com/cryptpad/onlyoffice-editor/releases/download/$VERSION/onlyoffice-editor.zip" --location --output "onlyoffice-editor.zip"
echo "$HASH onlyoffice-editor.zip" >onlyoffice-editor.zip.sha512
if ! sha512sum --check onlyoffice-editor.zip.sha512; then
echo "onlyoffice-editor.zip does not match expected checksum"
exit 1
fi
unzip onlyoffice-editor.zip
rm onlyoffice-editor.zip*
echo "$VERSION" >"$FULL_DIR"/.version
echo "$DIR updated"
else
echo "$DIR was up to date"
fi
}
install_x2t() {
local VERSION=$1
local HASH=$2
@ -233,7 +271,6 @@ install_x2t() {
ensure_command_available sha512sum
ensure_command_available unzip
curl "https://github.com/cryptpad/onlyoffice-x2t-wasm/releases/download/$VERSION/x2t.zip" --location --output x2t.zip
# curl "https://github.com/cryptpad/onlyoffice-x2t-wasm/releases/download/v7.3%2B1/x2t.zip" --location --output x2t.zip
echo "$HASH x2t.zip" >x2t.zip.sha512
if ! sha512sum --check x2t.zip.sha512; then
echo "x2t.zip does not match expected checksum"

View File

@ -12,6 +12,7 @@ const Fse = require("fs-extra");
const Path = require("path");
const Nacl = require("tweetnacl/nacl-fast");
const Hash = require('./common-hash');
const Util = require('./common-util');
module.exports.create = function (Env) {
var log = Env.Log;
@ -92,7 +93,7 @@ nThen(function (w) {
if (typeof(Env.bearerSecret) === 'string') { return; }
// if one does not exist, then create one and remember it
// 256 bits
var bearerSecret = Nacl.util.encodeBase64(Nacl.randomBytes(32));
var bearerSecret = Util.encodeBase64(Nacl.randomBytes(32));
Env.Log.info("GENERATING_BEARER_SECRET", {});
Decrees.write(Env, [
'SET_BEARER_SECRET',

View File

@ -44,14 +44,14 @@ Block.validateLoginBlock = function (Env, publicKey, signature, block, _cb) {
var u8_public_key;
try {
u8_public_key = Nacl.util.decodeBase64(publicKey);
u8_public_key = Util.decodeBase64(publicKey);
} catch (e) {
return void cb('E_INVALID_KEY');
}
var u8_signature;
try {
u8_signature = Nacl.util.decodeBase64(signature);
u8_signature = Util.decodeBase64(signature);
} catch (e) {
Env.Log.error('INVALID_BLOCK_SIGNATURE', e);
return void cb('E_INVALID_SIGNATURE');
@ -60,7 +60,7 @@ Block.validateLoginBlock = function (Env, publicKey, signature, block, _cb) {
// convert the block to a Uint8Array
var u8_block;
try {
u8_block = Nacl.util.decodeBase64(block);
u8_block = Util.decodeBase64(block);
} catch (e) {
return void cb('E_INVALID_BLOCK');
}
@ -85,9 +85,9 @@ Block.validateAncestorProof = function (Env, proof, _cb) {
try {
var parsed = JSON.parse(proof);
var pub = parsed[0];
var u8_pub = Nacl.util.decodeBase64(pub);
var u8_pub = Util.decodeBase64(pub);
var sig = parsed[1];
var u8_sig = Nacl.util.decodeBase64(sig);
var u8_sig = Util.decodeBase64(sig);
var valid = false;
nThen(function (w) {
valid = Nacl.sign.detached.verify(u8_pub, u8_sig, u8_pub);
@ -159,7 +159,7 @@ Block.writeLoginBlock = function (Env, msg, _cb) {
}).nThen(function () {
var buffer;
try {
buffer = Buffer.from(Nacl.util.decodeBase64(validatedBlock));
buffer = Buffer.from(Util.decodeBase64(validatedBlock));
} catch (err) {
return void cb('E_BLOCK_DESERIALIZATION');
}

View File

@ -225,8 +225,8 @@ Channel.deleteMailboxMessage = function (Env, data, cb) {
const proof = data.proof;
let nonce, proofBytes;
try {
nonce = Nacl.util.decodeBase64(proof.split('|')[0]);
proofBytes = Nacl.util.decodeBase64(proof.split('|')[1]);
nonce = Util.decodeBase64(proof.split('|')[0]);
proofBytes = Util.decodeBase64(proof.split('|')[1]);
} catch (e) {
return void cb('EINVAL');
}
@ -234,10 +234,10 @@ Channel.deleteMailboxMessage = function (Env, data, cb) {
// Check if you're allowed to delete this hash
try {
const mySecret = Env.curvePrivate;
const msgBytes = Nacl.util.decodeBase64(msg).subarray(64); // Remove signature
const msgBytes = Util.decodeBase64(msg).subarray(64); // Remove signature
const theirPublic = msgBytes.subarray(24,56); // 0-24 = nonce; 24-56=publickey (32 bytes)
const hashBytes = Nacl.box.open(proofBytes, nonce, theirPublic, mySecret);
return Nacl.util.encodeUTF8(hashBytes) === hash;
return Util.encodeUTF8(hashBytes) === hash;
} catch (e) {
return false;
}

View File

@ -309,3 +309,9 @@ Pinning.getDeletedPads = function (Env, channels, cb) {
Pinning.isChannelPinned = function (Env, channel, cb) {
return void cb(void 0, true);
};
Pinning.isPremium = function (Env, userKey, cb) {
const limit = Env.limits[userKey];
return void cb(void 0, !!limit?.plan);
//return void cb(void 0, (limit?.plan && limit.plan !== "custom"));
};

View File

@ -57,6 +57,7 @@ Default.httpHeaders = function (Env) {
"X-XSS-Protection": "1; mode=block",
"X-Content-Type-Options": "nosniff",
"Access-Control-Allow-Origin": Env.enableEmbedding? '*': Env.permittedEmbedders,
"Referrer-Policy": "same-origin",
"Permissions-policy":"interest-cohort=()"
};
};

View File

@ -254,7 +254,7 @@ module.exports.create = function (config) {
// initialized as undefined
bearerSecret: void 0,
curvePrivate: curve.secretKey,
curvePublic: Nacl.util.encodeBase64(curve.publicKey),
curvePublic: Util.encodeBase64(curve.publicKey),
selfDestructTo: {},
};

View File

@ -8,9 +8,7 @@ var Util = require("../lib/common-util");
var Pins = require("../lib/pins");
var Keys = require("./keys");
var Path = require('node:path');
var config = require("./load-config");
var Fs = require("node:fs");
var Fse = require("fs-extra");
var getNewestTime = function (stats) {
return stats[['atime', 'ctime', 'mtime'].reduce(function (a, b) {
@ -76,103 +74,6 @@ var evictArchived = function (Env, cb) {
blobs = Env.blobStore;
};
var migrateBlobRoot = function (from, to) {
// only migrate subpaths, leave everything else alone
if (!Path.dirname(from).startsWith(Path.dirname(to))) { return; }
// expects a directory
var recurse = function (relativePath) {
var src = Path.join(from, relativePath);
var children;
try {
children = Fs.readdirSync(src);
} catch (err) {
if (err.code === 'ENOENT') { return; }
// if you can't read a directory's contents
// then nothing else will work, so just abort
Log.verbose("EVICT_ARCHIVED_NOT_DIRECTORY", {
error: err,
});
return;
}
var dest;
if (children.length === 0) {
try {
Fse.removeSync(src);
} catch (err2) {
Log.error('EVICT_ARCHIVED_EMPTY_DIR_REMOVAL', {
error: err2,
});
// removal is non-essential, so we can continue
}
} else {
// make an equivalent path in the target directory
dest = Path.join(to, relativePath);
try {
Fse.mkdirpSync(dest);
} catch (err3) {
Log.error("EVICT_ARCHIVED_BLOB_MIGRATION", {
error: err3,
});
// failure to create the host directory
// will cause problems when we try to move
// so bail out here
return;
}
}
children.forEach(function (child) {
var childSrcPath = Path.join(src, child);
var stat = Fs.statSync(childSrcPath);
if (stat.isDirectory()) {
return void recurse(Path.join(relativePath, child));
}
var childDestPath = Path.join(dest, child);
try {
Log.verbose("EVICT_ARCHIVED_MOVE_FROM_DEPRECATED_PATH", {
from: childSrcPath,
to: childDestPath,
});
Fse.moveSync(childSrcPath, childDestPath, {
overwrite: false,
});
} catch (err4) {
Log.error('EVICT_ARCHIVED_MOVE_FAILURE', {
error: err4,
});
}
});
};
recurse('');
};
/* In CryptPad 5.2.0 we merged a patch which converted
all of CryptPad's root filepaths to their absolute form,
rather than the relative paths we'd been using until then.
Unfortunately, we overlooked a case where two absolute
paths were concatenated together, resulting in blobs being
archived to an incorrect path.
This migration detects evidence of incorrect archivals
and moves such archived files to their intended location
before continuing with the normal eviction procedure.
*/
var migrateIncorrectBlobs = function () {
var incorrectPaths = [
Path.join(Env.paths.archive, config.blobPath),
Path.join(Env.paths.archive, Path.resolve(config.blobPath))
];
var correctPath = Path.join(Env.paths.archive, 'blob');
incorrectPaths.forEach(root => {
migrateBlobRoot(root, correctPath);
});
};
var removeArchivedChannels = function (w) {
// this block will iterate over archived channels and removes them
// if they've been in cold storage for longer than your configured archive time
@ -236,6 +137,30 @@ var evictArchived = function (Env, cb) {
store.listArchivedChannels(handler, w(done));
};
// Blob proofs are no longer supported and can't be restored
// so we can delete them all
var removeArchivedBlobProofs = function (w) {
var archivePath = Path.join(Env.paths.archive, 'blob');
const cb = Util.once(w());
let i = 0;
nThen(w => {
Fs.readdir(archivePath, w((err, list) => {
if (err) { return; }
list.forEach(dir => {
// Look for 3 characters long folders
if (dir.length !== 3) { return; }
let path = Path.join(archivePath, dir);
Fs.rm(path, { recursive: true, force: true }, w(err => {
if (err) { return; }
i++;
}));
});
}));
}).nThen(() => {
Log.info('EVICT_ARCHIVED_BLOB_PROOFS', i);
cb();
});
};
var removeArchivedBlobs = function (w) {
if (typeof(Env.archiveRetentionTime) !== "number") { return; }
// Iterate over archived blobs and remove them
@ -269,8 +194,8 @@ var evictArchived = function (Env, cb) {
if (Env.DRY_RUN) { Env.Log.info('DRY RUN'); }
nThen(loadStorage)
.nThen(migrateIncorrectBlobs)
.nThen(removeArchivedChannels)
.nThen(removeArchivedBlobProofs)
.nThen(removeArchivedBlobs)
.nThen(function () {
cb(void 0, report);
@ -588,7 +513,7 @@ module.exports = function (Env, cb) {
if (newerItem && getNewestTime(newerItem) > retentionTime) {
// it's actually active, so don't archive it.
w.abort();
cb();
next();
}
// else fall through to the archival
}));

View File

@ -116,7 +116,7 @@ HK.closeNetfluxSession = function (Env, netfluxId) {
const isValidValidateKeyString = function (key) {
try {
return typeof(key) === 'string' &&
Nacl.util.decodeBase64(key).length === Nacl.sign.publicKeyLength;
Util.decodeBase64(key).length === Nacl.sign.publicKeyLength;
} catch (e) {
return false;
}

View File

@ -99,7 +99,7 @@ COMMANDS.SSO_UPDATE_BLOCK = SSO.SSO_UPDATE_BLOCK; // Password change
COMMANDS.SSO_VALIDATE = SSO.SSO_VALIDATE;
*/
var randomToken = () => Nacl.util.encodeBase64(Nacl.randomBytes(24)).replace(/\//g, '-');
var randomToken = () => Util.encodeBase64(Nacl.randomBytes(24)).replace(/\//g, '-');
// this function handles the first stage of the protocol
// (the server's validation of the client's request and the generation of its challenge)
@ -278,9 +278,9 @@ var handleResponse = function (Env, req, res) {
u8_publicKey;
try {
u8_toVerify = Nacl.util.decodeUTF8(text);
u8_sig = Nacl.util.decodeBase64(sig);
u8_publicKey = Nacl.util.decodeBase64(publicKey);
u8_toVerify = Util.decodeUTF8(text);
u8_sig = Util.decodeBase64(sig);
u8_publicKey = Util.decodeBase64(publicKey);
} catch (err3) {
Env.Log.error('CHALLENGE_RESPONSE_DECODING_ERROR', {
text: text,

View File

@ -18,6 +18,7 @@ const bodyParser = require('body-parser');
const BlobStore = require("./storage/blob");
const BlockStore = require("./storage/block");
const plugins = require("./plugin-manager");
const gzipStatic = require('connect-gzip-static');
const DEFAULT_QUERY_TIMEOUT = 5000;
const PID = process.pid;
@ -134,6 +135,7 @@ var applyHeaderMap = function (res, map) {
var EXEMPT = [
/^\/common\/onlyoffice\/.*\.html.*/,
/^\/common\/onlyoffice\/dist\/.*\/sdkjs\/common\/spell\/spell\/spell.js.*/, // OnlyOffice loads spell.wasm in a way that needs unsave-eval
/^\/(sheet|presentation|doc)\/inner\.html.*/,
/^\/unsafeiframe\/inner\.html.*$/,
];
@ -336,7 +338,7 @@ Object.keys(plugins || {}).forEach(name => {
// serve custom app content from the customize directory
// useful for testing pages customized with opengraph data
app.use(Express.static(Path.resolve('./customize/www')));
app.use(Express.static(Path.resolve('./www')));
app.use(gzipStatic(Path.resolve('./www')));
var mainPages = Env.mainPages || Default.mainPages();
var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$');

View File

@ -8,6 +8,7 @@ const Fs = require("fs");
const Path = require("path");
const Util = require("./common-util");
const Plan = require("./plan");
const Store = require('./storage/file');
const Semaphore = require('saferphore');
const nThen = require('nthen');
@ -258,8 +259,20 @@ Pins.load = function (cb, config) {
var pinPath = config.pinPath || './pins';
var done = Util.once(cb);
var handler = config.handler;
let store;
nThen((waitFor) => {
Store.create({
filePath: config.pinPath,
volumeId: 'pins'
}, waitFor((err, _) => {
if (err) {
waitFor.abort();
return void done(err);
}
store = _;
}));
}).nThen((waitFor) => {
// recurse over the configured pinPath, or the default
Fs.readdir(pinPath, waitFor((err, list) => {
if (err) {
@ -283,26 +296,29 @@ Pins.load = function (cb, config) {
}
list2.forEach((ff) => {
if (config && config.exclude && config.exclude.indexOf(ff) > -1) { return; }
fileList.push(Path.join(pinPath, f, ff));
fileList.push(ff.replace(/(\.ndjson)$/, ''));
});
})));
});
});
}).nThen((waitFor) => {
fileList.forEach((f) => {
fileList.forEach((id) => {
sema.take((returnAfter) => {
var next = waitFor(returnAfter());
Fs.readFile(f, (err, content) => {
var ref = {};
var h = createLineHandler(ref, id);
store.readMessagesBin(id, 0, (msgObj, next) => {
h(msgObj.buff.toString('utf8'));
next();
}, (err) => {
if (err) {
waitFor.abort();
return void done(err);
}
var id = f.replace(/.*\/([^/]*).ndjson$/, (x, y)=>y);
var contentString = content.toString('utf8');
if (handler) {
return void handler(processPinFile(contentString, f), id, next);
return void handler(ref, id, next);
}
const hashes = Pins.calculateFromLog(contentString, f);
const hashes = Object.keys(ref.pins);
hashes.forEach((x) => {
(pinned[x] = pinned[x] || {})[id] = 1;
});

View File

@ -24,6 +24,7 @@ const UNAUTHENTICATED_CALLS = {
WRITE_PRIVATE_MESSAGE: Channel.writePrivateMessage,
DELETE_MAILBOX_MESSAGE: Channel.deleteMailboxMessage,
GET_METADATA: Metadata.getMetadata,
IS_PREMIUM: Pinning.isPremium,
ADD_FIRST_ADMIN: Admin.addFirstAdmin
};

View File

@ -473,9 +473,29 @@ var archiveBlob = function (Env, blobId, reason, cb) {
};
var removeArchivedBlob = function (Env, blobId, cb) {
var CB = Util.once(cb);
var archivePath = prependArchive(Env, makeBlobPath(Env, blobId));
var metadataPath = prependArchive(Env, mkMetadataPath(Env, blobId));
Fs.unlink(archivePath, cb);
removeArchivedActivity(Env, blobId, () => {});
nThen(function (w) {
Fs.unlink(archivePath, w(function (err) {
if (err) {
if (err.code === "ENOENT") { return; }
w.abort();
CB("E_ARCHIVED_BLOB_REMOVAL_"+ err.code);
}
}));
Fs.unlink(metadataPath, w(function (err) {
if (err) {
if (err.code === "ENOENT") { return; }
w.abort();
CB("E_ARCHIVED_BLOBMD_REMOVAL_"+ err.code);
}
}));
removeArchivedActivity(Env, blobId, () => {});
}).nThen(function () {
CB();
});
};
// restoreBlob

View File

@ -573,6 +573,7 @@ var removeArchivedChannel = function (env, channelName, cb) {
nThen(function (w) {
Fs.unlink(channelPath, w(function (err) {
if (err) {
if (err.code === "ENOENT") { return; }
w.abort();
CB(labelError("E_ARCHIVED_CHANNEL_REMOVAL", err));
}

View File

@ -29,7 +29,7 @@ var pathFromId = function (Env, id, ref) {
return Path.join(Env.paths.base, "sessions", id.slice(0, 2), id, ref);
};
Sessions.randomId = () => Nacl.util.encodeBase64(Nacl.randomBytes(24)).replace(/\//g, '-');
Sessions.randomId = () => Util.encodeBase64(Nacl.randomBytes(24)).replace(/\//g, '-');
Sessions.read = function (Env, id, ref, cb) {
var path = pathFromId(Env, id, ref);

View File

@ -7,6 +7,7 @@ var Fse = require("fs-extra");
var Path = require("path");
var nacl = require("tweetnacl/nacl-fast");
var nThen = require("nthen");
var Util = require('../common-util');
var Tasks = module.exports;
@ -49,7 +50,7 @@ var makeDirectoryId = function (d) {
var write = function (env, task, cb) {
var str = JSON.stringify(task) + '\n';
var id = nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(str))).replace(/\//g, '-');
var id = Util.encodeBase64(nacl.hash(Util.decodeUTF8(str))).replace(/\//g, '-');
var dir = makeDirectoryId(task[0]);
var path = Path.join(env.root, dir);

View File

@ -772,14 +772,14 @@ COMMANDS.INLINE = function (data, cb) {
monitoringIncrement('inlineValidation');
var signedMsg;
try {
signedMsg = Nacl.util.decodeBase64(data.msg);
signedMsg = Util.decodeBase64(data.msg);
} catch (e) {
return void cb('E_BAD_MESSAGE');
}
var validateKey;
try {
validateKey = Nacl.util.decodeBase64(data.key);
validateKey = Util.decodeBase64(data.key);
} catch (e) {
return void cb("E_BADKEY");
}
@ -801,19 +801,19 @@ const checkDetachedSignature = function (signedMsg, signature, publicKey) {
var signatureBuffer;
try {
signedBuffer = Nacl.util.decodeUTF8(signedMsg);
signedBuffer = Util.decodeUTF8(signedMsg);
} catch (e) {
throw new Error("INVALID_SIGNED_BUFFER");
}
try {
pubBuffer = Nacl.util.decodeBase64(publicKey);
pubBuffer = Util.decodeBase64(publicKey);
} catch (e) {
throw new Error("INVALID_PUBLIC_KEY");
}
try {
signatureBuffer = Nacl.util.decodeBase64(signature);
signatureBuffer = Util.decodeBase64(signature);
} catch (e) {
throw new Error("INVALID_SIGNATURE");
}
@ -851,8 +851,7 @@ COMMANDS.HASH_CHANNEL_LIST = function (data, cb) {
});
uniques.sort();
var hash = Nacl.util.encodeBase64(Nacl.hash(Nacl
.util.decodeUTF8(JSON.stringify(uniques))));
var hash = Util.encodeBase64(Nacl.hash(Util.decodeUTF8(JSON.stringify(uniques))));
cb(void 0, hash);
};

View File

@ -53,7 +53,7 @@ Workers.initialize = function (Env, config, _cb) {
//return Object.keys(workers[index].tasks || {}).length;
};
const WORKER_TASK_LIMIT = 100000; // XXX
const WORKER_TASK_LIMIT = 250000; // XXX
var workerOffset = -1;
var queue = [];
@ -260,6 +260,16 @@ Workers.initialize = function (Env, config, _cb) {
pid: worker.pid, // store the child process's id in an easily accessible location
};
let pid = worker.pid;
const onWorkerClosed = () => {
Object.keys(Env.plugins || {}).forEach(name => {
let plugin = Env.plugins[name];
if (!plugin.onWorkerClosed) { return; }
try { plugin.onWorkerClosed("db-worker", pid); }
catch (e) {}
});
};
state.replaceWorker = () => {
let index = workers.indexOf(state);
if (index === -1) { return; }
@ -291,6 +301,7 @@ Workers.initialize = function (Env, config, _cb) {
worker: state.worker.pid,
count: state.count
});
onWorkerClosed();
delete state.worker;
worker.kill();
};
@ -323,14 +334,8 @@ Workers.initialize = function (Env, config, _cb) {
handleResponse(state, res);
});
let pid = worker.pid;
var substituteWorker = Util.once(function () {
Object.keys(Env.plugins || {}).forEach(name => {
let plugin = Env.plugins[name];
if (!plugin.onWorkerClosed) { return; }
try { plugin.onWorkerClosed("db-worker", pid); }
catch (e) {}
});
onWorkerClosed();
Env.Log.info("SUBSTITUTE_DB_WORKER", '');
var idx = workers.indexOf(state);

View File

@ -7,7 +7,7 @@
// merge code more easily.
// Set REVERSE to true to copy from "www" to "src" once the changes have been merged.
const REVERSE = true;
const REVERSE = false;
const Fs = require('node:fs');
const map = {
'./src/worker/components/roster.js': './www/common/outer/roster.js',
@ -20,7 +20,6 @@ const map = {
'./src/common/common-credential.js': './www/common/common-credential.js',
'./src/common/common-feedback.js': './www/common/common-feedback.js',
'./src/common/common-hash.js': './www/common/common-hash.js',
'./src/common/common-messaging.js': './www/common/common-messaging.js',
'./src/common/common-realtime.js': './www/common/common-realtime.js',
'./src/common/common-signing-keys.js': './www/common/common-signing-keys.js',
'./src/common/common-util.js': './www/common/common-util.js',

82
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "cryptpad",
"version": "2025.3.0",
"version": "2025.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cryptpad",
"version": "2025.3.0",
"version": "2025.3.1",
"license": "AGPL-3.0+",
"dependencies": {
"@mcrowe/minibloom": "^0.2.0",
@ -16,13 +16,14 @@
"bootstrap": "^4.0.0",
"bootstrap-tokenfield": "^0.12.0",
"chainpad": "^5.2.6",
"chainpad-crypto": "^0.2.5",
"chainpad-listmap": "^1.1.1",
"chainpad-netflux": "^1.2.3",
"chainpad-crypto": "^0.3.0",
"chainpad-listmap": "^1.2.0",
"chainpad-netflux": "^1.3.0",
"chainpad-server": "^5.2.4",
"ckeditor": "npm:ckeditor4@~4.22.1",
"codemirror": "^5.19.0",
"components-font-awesome": "^4.6.3",
"connect-gzip-static": "^4.2.1",
"cookie-parser": "^1.4.7",
"croppie": "^2.5.0",
"dragula": "3.7.2",
@ -58,7 +59,8 @@
"sortify": "^1.0.4",
"stream-to-pull-stream": "^1.7.2",
"thirty-two": "^1.0.2",
"tweetnacl": "file:./src/tweetnacl",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1",
"ulimit": "0.0.2",
"ws": "^8.17.1",
"x2js": "^3.4.4"
@ -1902,19 +1904,22 @@
}
},
"node_modules/chainpad-crypto": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.8.tgz",
"integrity": "sha512-B0/aW0TUcRQUF8Aaz7bBFfIPs/ybdr92Rl0ezVrSxEQuxaRHBNGJrUvVxdKreLuh+XYdbQeeRKDXbgvEcLDcEg==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.3.0.tgz",
"integrity": "sha512-L6gUiE8m/ZrUE3eVMju6qzG92Mis3M4T1xCuxVF3eqwa9+46fl9OFwt/TiSmi+oVHSIBmgDTcnOEF/JSxnxYxw==",
"license": "AGPL-3.0+",
"dependencies": {
"tweetnacl": "~0.12.2"
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1"
}
},
"node_modules/chainpad-listmap": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/chainpad-listmap/-/chainpad-listmap-1.1.1.tgz",
"integrity": "sha512-SXb3iJqWyp2wOMa9FT4kqOuR1KgjMPcNCzLlYignu1GdxMI67ZDGTLDucmht4CD8yuYzUWzWmJgs1vs/acH+rw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/chainpad-listmap/-/chainpad-listmap-1.2.0.tgz",
"integrity": "sha512-PI7iwk/yKzEb1iaVmLKvMJYXjCjvIgUWzI07LeDGVvZPScnYbEzMso9YMQyxzjqrp8cyQxsL7sA8c/hZAz01cw==",
"license": "AGPL-3.0+",
"dependencies": {
"chainpad-netflux": "^1.2.0",
"chainpad-netflux": "^1.3.0",
"json.sortify": "~2.1.0"
}
},
@ -1927,12 +1932,14 @@
}
},
"node_modules/chainpad-netflux": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/chainpad-netflux/-/chainpad-netflux-1.2.3.tgz",
"integrity": "sha512-bXLUSxAsCu4kRnGorW+7fVofiCWSNGRkB+uFJ4kUV/zDJ3RJYU4J3+DZsL4rCTkLkAjgKt+YQCoYciCKLlTwqg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/chainpad-netflux/-/chainpad-netflux-1.3.0.tgz",
"integrity": "sha512-oc47vLt78a/Fp7acQJvLNuX1JRrH9oOWAbzo+yOO1asCWDuI4oesPY81wHzVySKpo58GGfNpZN7CSTI8EZjYrQ==",
"license": "LGPL-2.1",
"dependencies": {
"netflux-websocket": "^1.2.0"
"netflux-websocket": "^1.2.0",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1"
}
},
"node_modules/chainpad-server": {
@ -2060,6 +2067,22 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/connect-gzip-static": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/connect-gzip-static/-/connect-gzip-static-4.2.1.tgz",
"integrity": "sha512-6qC1NYUEZU6mftg2gPspcoPjgBzADm+fB1fPXu3RxBVe59P/Q9HQGQEHnuDaGRDJUoJwl5NX2OgoaaZp7RdpfQ==",
"license": "MIT",
"dependencies": {
"debug": "~2||~3||~4",
"mime-types": "~2",
"parseurl": "~1",
"send": "~0 || ~1",
"serve-static": "~1 || ~2"
},
"engines": {
"node": ">= 20.1.0"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -3267,9 +3290,10 @@
}
},
"node_modules/http-proxy-middleware": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz",
"integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz",
"integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==",
"license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.15",
"debug": "^4.3.6",
@ -6149,8 +6173,16 @@
"dev": true
},
"node_modules/tweetnacl": {
"resolved": "src/tweetnacl",
"link": true
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
"license": "Unlicense"
},
"node_modules/tweetnacl-util": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
"integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==",
"license": "Unlicense"
},
"node_modules/type-check": {
"version": "0.4.0",
@ -6497,10 +6529,6 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"src/tweetnacl": {
"version": "0.12.2",
"license": "Public domain"
}
}
}

View File

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
"version": "2025.3.0",
"version": "2025.3.1",
"license": "AGPL-3.0+",
"repository": {
"type": "git",
@ -19,13 +19,14 @@
"bootstrap": "^4.0.0",
"bootstrap-tokenfield": "^0.12.0",
"chainpad": "^5.2.6",
"chainpad-crypto": "^0.2.5",
"chainpad-listmap": "^1.1.1",
"chainpad-netflux": "^1.2.3",
"chainpad-crypto": "^0.3.0",
"chainpad-listmap": "^1.2.0",
"chainpad-netflux": "^1.3.0",
"chainpad-server": "^5.2.4",
"ckeditor": "npm:ckeditor4@~4.22.1",
"codemirror": "^5.19.0",
"components-font-awesome": "^4.6.3",
"connect-gzip-static": "^4.2.1",
"cookie-parser": "^1.4.7",
"croppie": "^2.5.0",
"dragula": "3.7.2",
@ -61,7 +62,8 @@
"sortify": "^1.0.4",
"stream-to-pull-stream": "^1.7.2",
"thirty-two": "^1.0.2",
"tweetnacl": "file:./src/tweetnacl",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1",
"ulimit": "0.0.2",
"ws": "^8.17.1",
"x2js": "^3.4.4"

View File

@ -7,6 +7,7 @@ const nThen = require('nthen');
const Nacl = require('tweetnacl/nacl-fast');
const Path = require('path');
const Pins = require('../lib/pins');
const Util = require('../lib/common-util');
const Config = require('../lib/load-config');
var escapeKeyCharacters = function (key) {
@ -24,9 +25,9 @@ if (dataIdx === -1) {
const deleteData = JSON.parse(process.argv[dataIdx+1]);
if (!deleteData.toSign || !deleteData.proof) { return void console.error("Invalid arguments"); }
// Check sig
const ed = Nacl.util.decodeBase64(deleteData.toSign.edPublic);
const signed = Nacl.util.decodeUTF8(JSON.stringify(deleteData.toSign));
const proof = Nacl.util.decodeBase64(deleteData.proof);
const ed = Util.decodeBase64(deleteData.toSign.edPublic);
const signed = Util.decodeUTF8(JSON.stringify(deleteData.toSign));
const proof = Util.decodeBase64(deleteData.proof);
if (!Nacl.sign.detached.verify(signed, proof, ed)) { return void console.error("Invalid signature"); }
edPublic = escapeKeyCharacters(deleteData.toSign.edPublic);
}
@ -43,7 +44,7 @@ nThen((waitFor) => {
pinned = Pins.calculateFromLog(content.toString('utf8'), f);
}));
}).nThen((waitFor) => {
Pins.list(waitFor((err, d) => {
Pins.load(waitFor((err, d) => {
data = Object.keys(d);
}), {
exclude: [edPublic + '.ndjson']

View File

@ -20,7 +20,6 @@ var compare = function () {
Pins.list(w(function (err, p) {
if (err) { throw err; }
list = p;
console.log(p);
console.log(list);
console.log();
}), conf);

View File

@ -34,6 +34,7 @@ Fse.rmSync(oldComponentsPath, { recursive: true, force: true });
"mathjax",
"open-sans-fontface",
"tweetnacl",
"tweetnacl-util",
"require-css",
"requirejs",
"requirejs-plugins",

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
const Nacl = require('tweetnacl/nacl-fast');
const Util = require('../lib/common-util');
const keyPair = Nacl.box.keyPair();
console.log("You've just generated a new key pair for your support mailbox.");
@ -19,9 +20,9 @@ console.log("NOTE: You can change the key pair at any time if you want to revoke
console.log();
console.log();
console.log("Your public key (add it to config.js):");
console.log(Nacl.util.encodeBase64(keyPair.publicKey));
console.log(Util.encodeBase64(keyPair.publicKey));
console.log();
console.log();
console.log("Your private key (store it in a safe place and send it to your instance's admins):");
console.log(Nacl.util.encodeBase64(keyPair.secretKey));
console.log(Util.encodeBase64(keyPair.secretKey));

View File

@ -5,13 +5,14 @@
let SodiumNative = require('sodium-native');
let Nacl = require('tweetnacl/nacl-fast');
let LibSodium = require('libsodium-wrappers');
let Util = require('../lib/common-util');
let msgStr = "This is a test";
let keys = Nacl.sign.keyPair();
let pub = keys.publicKey;
let msg = Nacl.util.decodeUTF8(msgStr);
let msg = Util.decodeUTF8(msgStr);
let signedMsg = Nacl.sign(msg, keys.secretKey);
let sig = signedMsg.subarray(0, 64);

View File

@ -7,6 +7,7 @@ var Nacl = require("tweetnacl/nacl-fast");
var nThen = require("nthen");
var CPNetflux = require("../../www/components/chainpad-netflux/chainpad-netflux");
var Hash = require("../../www/common/common-hash");
var Util = require("../../lib/common-util");
var Rpc = require("../../www/common/rpc");
var HK = require("../../lib/hk-util");
@ -69,7 +70,7 @@ nThen(function (w) {
//console.log(i);
if (i-- <= 0) { return void done(); }
var ciphertext = Nacl.util.encodeBase64(Nacl.randomBytes(256));
var ciphertext = Util.encodeBase64(Nacl.randomBytes(256));
client.anonRpc.send('WRITE_PRIVATE_MESSAGE', [
client.channel,

View File

@ -55,16 +55,16 @@ var state = {};
var makeCurveKeys = function () {
var pair = Nacl.box.keyPair();
return {
curvePrivate: Nacl.util.encodeBase64(pair.secretKey),
curvePublic: Nacl.util.encodeBase64(pair.publicKey),
curvePrivate: Util.encodeBase64(pair.secretKey),
curvePublic: Util.encodeBase64(pair.publicKey),
};
};
var makeEdKeys = function () {
var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength));
return {
edPrivate: Nacl.util.encodeBase64(keys.secretKey),
edPublic: Nacl.util.encodeBase64(keys.publicKey),
edPrivate: Util.encodeBase64(keys.secretKey),
edPublic: Util.encodeBase64(keys.publicKey),
};
};
@ -198,7 +198,7 @@ nThen(function (w) {
alice.anonRpc.send('WRITE_PRIVATE_MESSAGE', [
alice.mailboxChannel,
msg
//Nacl.util.encodeBase64(Nacl.randomBytes(128))
//Util.encodeBase64(Nacl.randomBytes(128))
], w(function (err) {
if (err) { throw new Error(err); }
console.log('message %s written successfully', i);

View File

@ -33,7 +33,7 @@ var handler = function (ref, id /* safeKey */, pinned) {
//console.log(ref, id);
};
Pins.list(function (err) {
Pins.load(function (err) {
if (err) { return void console.error(err); }
/*
for (var id in pinned) {

View File

@ -43,16 +43,16 @@ process.on('unhandledRejection', function (err) {
var makeCurveKeys = function () {
var pair = Nacl.box.keyPair();
return {
curvePrivate: Nacl.util.encodeBase64(pair.secretKey),
curvePublic: Nacl.util.encodeBase64(pair.publicKey),
curvePrivate: Util.encodeBase64(pair.secretKey),
curvePublic: Util.encodeBase64(pair.publicKey),
};
};
var makeEdKeys = function () {
var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength));
return {
edPrivate: Nacl.util.encodeBase64(keys.secretKey),
edPublic: Nacl.util.encodeBase64(keys.publicKey),
edPrivate: Util.encodeBase64(keys.secretKey),
edPublic: Util.encodeBase64(keys.publicKey),
};
};

View File

@ -9,13 +9,13 @@ var factory = function (Util, Crypto, Keys, Nacl) {
var uint8ArrayToHex = Util.uint8ArrayToHex;
var hexToBase64 = Util.hexToBase64;
var base64ToHex = Util.base64ToHex;
Hash.encodeBase64 = Nacl.util.encodeBase64;
Hash.decodeBase64 = Nacl.util.decodeBase64;
Hash.encodeBase64 = Util.encodeBase64;
Hash.decodeBase64 = Util.decodeBase64;
// This implementation must match that on the server
// it's used for a checksum
Hash.hashChannelList = function (list) {
return Nacl.util.encodeBase64(Nacl.hash(Nacl.util
return Util.encodeBase64(Nacl.hash(Util
.decodeUTF8(JSON.stringify(list))));
};
@ -34,15 +34,15 @@ var factory = function (Util, Crypto, Keys, Nacl) {
Hash.getSignPublicFromPrivate = function (edPrivateSafeStr) {
var edPrivateStr = Crypto.b64AddSlashes(edPrivateSafeStr);
var privateKey = Nacl.util.decodeBase64(edPrivateStr);
var privateKey = Util.decodeBase64(edPrivateStr);
var keyPair = Nacl.sign.keyPair.fromSecretKey(privateKey);
return Nacl.util.encodeBase64(keyPair.publicKey);
return Util.encodeBase64(keyPair.publicKey);
};
Hash.getCurvePublicFromPrivate = function (curvePrivateSafeStr) {
var curvePrivateStr = Crypto.b64AddSlashes(curvePrivateSafeStr);
var privateKey = Nacl.util.decodeBase64(curvePrivateStr);
var privateKey = Util.decodeBase64(curvePrivateStr);
var keyPair = Nacl.box.keyPair.fromSecretKey(privateKey);
return Nacl.util.encodeBase64(keyPair.publicKey);
return Util.encodeBase64(keyPair.publicKey);
};
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
@ -585,7 +585,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
secret.channel = base64ToHex(parsed.channel);
secret.keys = {
fileKeyStr: parsed.key,
cryptKey: Nacl.util.decodeBase64(parsed.key)
cryptKey: Util.decodeBase64(parsed.key)
};
} else if (parsed.type === "user") {
throw new Error("User hashes can't be opened (yet)");
@ -653,15 +653,15 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
var keys = secret && secret.keys;
var secondary = keys && keys.secondaryKey;
if (!secondary) { return; }
var curvePair = Nacl.box.keyPair.fromSecretKey(Nacl.util.decodeUTF8(secondary).slice(0,32));
var curvePair = Nacl.box.keyPair.fromSecretKey(Util.decodeUTF8(secondary).slice(0,32));
var ret = {};
ret.form_public = Nacl.util.encodeBase64(curvePair.publicKey);
var privateKey = ret.form_private = Nacl.util.encodeBase64(curvePair.secretKey);
ret.form_public = Util.encodeBase64(curvePair.publicKey);
var privateKey = ret.form_private = Util.encodeBase64(curvePair.secretKey);
var auditorHash = Hash.getViewHashFromKeys({
version: 1,
channel: secret.channel,
keys: { viewKeyStr: Nacl.util.encodeBase64(keys.cryptKey) }
keys: { viewKeyStr: Util.encodeBase64(keys.cryptKey) }
});
var _parsed = Hash.parseTypeHash('pad', auditorHash);
ret.form_auditorHash = _parsed.getHash({auditorKey: privateKey});
@ -720,12 +720,12 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
Hash.decodeDataOptions = function (opts) {
var b64 = decodeURIComponent(opts);
var str = Nacl.util.encodeUTF8(Nacl.util.decodeBase64(b64));
var str = Util.encodeUTF8(Util.decodeBase64(b64));
return Util.tryParse(str) || {};
};
Hash.encodeDataOptions = function (opts) {
var str = JSON.stringify(opts);
var b64 = Nacl.util.encodeBase64(Nacl.util.decodeUTF8(str));
var b64 = Util.encodeBase64(Util.decodeUTF8(str));
return encodeURIComponent(b64);
};
Hash.getNewPadURL = function (href, opts) {

View File

@ -3,12 +3,22 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
(function (window) {
var Util = {};
const factory = (NaclUtil) => {
var Util = window.CryptPad_Util = {};
// polyfill for atob in case you're using this from node...
window.atob = window.atob || function (str) { return Buffer.from(str, 'base64').toString('binary'); };
window.btoa = window.btoa || function (str) { return Buffer.from(str, 'binary').toString('base64'); };
Util.encodeBase64 = NaclUtil.encodeBase64;
Util.decodeBase64 = str => {
let i = str.length % 4;
if (i) { str += '='.repeat(4-i); }
return NaclUtil.decodeBase64(str);
};
Util.encodeUTF8 = NaclUtil.encodeUTF8;
Util.decodeUTF8 = NaclUtil.decodeUTF8;
Util.slice = function (A, start, end) {
return Array.prototype.slice.call(A, start, end);
};
@ -857,14 +867,16 @@
};
/* End of code copied from saferphore */
return Util;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = Util;
module.exports = factory(require('tweetnacl-util'));
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define([], function () {
window.CryptPad_Util = Util;
return Util;
define(['/components/tweetnacl-util/nacl-util.min.js'], function () {
return factory(globalThis?.nacl?.util);
});
} else {
window.CryptPad_Util = Util;
// Unsupported initialization
}
}(typeof(self) !== 'undefined'? self: this));

View File

@ -26,7 +26,7 @@ const factory = (nThen, Util, ApiConfig = {}, Nacl) => {
};
var clone = o => JSON.parse(JSON.stringify(o));
var randomToken = () => Nacl.util.encodeBase64(Nacl.randomBytes(24));
var randomToken = () => Util.encodeBase64(Nacl.randomBytes(24));
var postData = function (url, data, cb) {
var CB = Util.once(Util.mkAsync(cb));
fetch(url, {
@ -53,7 +53,7 @@ const factory = (nThen, Util, ApiConfig = {}, Nacl) => {
var serverCommand = function (keypair, my_data, cb) {
var obj = clone(my_data);
obj.publicKey = Nacl.util.encodeBase64(keypair.publicKey);
obj.publicKey = Util.encodeBase64(keypair.publicKey);
obj.nonce = randomToken();
var href = new URL('/api/auth/', API_ORIGIN);
var txid, date;
@ -82,9 +82,9 @@ const factory = (nThen, Util, ApiConfig = {}, Nacl) => {
var copy = clone(obj);
copy.txid = txid;
copy.date = date;
var toSign = Nacl.util.decodeUTF8(JSON.stringify(copy));
var toSign = Util.decodeUTF8(JSON.stringify(copy));
var sig = Nacl.sign.detached(toSign, keypair.secretKey);
var encoded = Nacl.util.encodeBase64(sig);
var encoded = Util.encodeBase64(sig);
var obj2 = {
sig: encoded,
txid: txid,

View File

@ -23,7 +23,7 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
// [b64_public, b64_sig, b64_block [version, nonce, content]]
Block.seed = function () {
return Nacl.hash(Nacl.util.decodeUTF8('pewpewpew'));
return Nacl.hash(Util.decodeUTF8('pewpewpew'));
};
// should be deterministic from a seed...
@ -49,8 +49,8 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
try {
var sign = keys.sign;
return {
edPrivate: Nacl.util.encodeBase64(sign.secretKey),
edPublic: Nacl.util.encodeBase64(sign.publicKey),
edPrivate: Util.encodeBase64(sign.secretKey),
edPublic: Util.encodeBase64(sign.publicKey),
};
} catch (err) {
console.error(err);
@ -60,7 +60,7 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
// (UTF8 content, keys object) => Uint8Array block
Block.encrypt = function (version, content, keys) {
var u8 = Nacl.util.decodeUTF8(content);
var u8 = Util.decodeUTF8(content);
var nonce = Nacl.randomBytes(Nacl.secretbox.nonceLength);
return Block.join([
[0],
@ -77,7 +77,7 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
var plaintext = Nacl.secretbox.open(box, nonce, keys.symmetric);
try {
return JSON.parse(Nacl.util.encodeUTF8(plaintext));
return JSON.parse(Util.encodeUTF8(plaintext));
} catch (e) {
console.error(e);
return;
@ -98,9 +98,9 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
// serialize {publickey, sig, ciphertext}
return {
publicKey: Nacl.util.encodeBase64(keys.sign.publicKey),
signature: Nacl.util.encodeBase64(sig),
ciphertext: Nacl.util.encodeBase64(ciphertext),
publicKey: Util.encodeBase64(keys.sign.publicKey),
signature: Util.encodeBase64(sig),
ciphertext: Util.encodeBase64(ciphertext),
};
};
@ -111,14 +111,14 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
// sign your old publicKey with your old privateKey
var u8_sig = Nacl.sign.detached(u8_pub, u8_secret);
// return an array with the sig and the pubkey
return JSON.stringify([u8_pub, u8_sig].map(Nacl.util.encodeBase64));
return JSON.stringify([u8_pub, u8_sig].map(Util.encodeBase64));
} catch (err) {
return void console.error(err);
}
};
var urlSafeB64 = function (u8) {
return Nacl.util.encodeBase64(u8).replace(/\//g, '-');
return Util.encodeBase64(u8).replace(/\//g, '-');
};
Block.getBlockUrl = function (keys) {
@ -138,7 +138,7 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
var decodeSafeB64 = function (b64) {
try {
return Nacl.util.decodeBase64(b64.replace(/\-/g, '/'));
return Util.decodeBase64(b64.replace(/\-/g, '/'));
} catch (e) {
console.error(e);
return;

View File

@ -4,8 +4,10 @@
(() => {
const factory = () => {
const version = 8;
return {
currentVersion: 'v7'
currentVersionNumber: version,
currentVersion: 'v' + version
};
};

View File

@ -27,7 +27,7 @@ const factory = (AppConfig = {}, ApiConfig = {},
setCustomize({AppConfig,ApiConfig});
}
const Types = { setCustomize };
const Types = { OO_APPS, setCustomize };
Types.__defineGetter__("availableTypes", function () {
if (ApiConfig.appsToDisable) {

View File

@ -14,8 +14,8 @@ var factory = function (Util, Nacl) {
// we will sign various message with our edPrivate keys
// this handles that in a generic way
var signMsg = function (data, signKey) {
var buffer = Nacl.util.decodeUTF8(JSON.stringify(data));
return Nacl.util.encodeBase64(Nacl.sign.detached(buffer, signKey));
var buffer = Util.decodeUTF8(JSON.stringify(data));
return Util.encodeBase64(Nacl.sign.detached(buffer, signKey));
};
// sendMsg takes a pre-formed message, does a little validation
@ -299,7 +299,7 @@ var factory = function (Util, Nacl) {
var signKey;
try {
signKey = Nacl.util.decodeBase64(edPrivateKey);
signKey = Util.decodeBase64(edPrivateKey);
if (signKey.length !== 64) {
throw new Error('private key did not match expected length of 64');
}
@ -308,7 +308,7 @@ var factory = function (Util, Nacl) {
}
try {
if (Nacl.util.decodeBase64(edPublicKey).length !== 32) {
if (Util.decodeBase64(edPublicKey).length !== 32) {
return void cb('expected public key to be 32 uint');
}
} catch (err) { return void cb(err); }

View File

@ -1 +0,0 @@
node_modules

View File

@ -1,3 +0,0 @@
test
.travis.yml
bower.json

View File

@ -1,7 +0,0 @@
language: node_js
node_js:
- "0.10"
env:
- NACL_SRC=nacl.min.js
- NACL_SRC=nacl-fast.min.js
script: "npm run testall"

View File

@ -1,101 +0,0 @@
TweetNaCl.js Changelog
======================
v0.12.2
-------
* Improved performance of curve operations, making `nacl.scalarMult`, `nacl.box`,
`nacl.sign` and related functions up to 3x faster in `nacl-fast` version.
v0.12.1
-------
* Significantly improved performance of Salsa20 (~1.5x faster) and
Poly1305 (~3.5x faster) in `nacl-fast` version.
v0.12.0
-------
* Instead of using the given secret key directly, TweetNaCl.js now copies it to
a new array in `nacl.box.keyPair.fromSecretKey` and
`nacl.sign.keyPair.fromSecretKey`.
v0.11.2
-------
* Added new constant: `nacl.sign.seedLength`.
v0.11.1
-------
* Even faster hash for both short and long inputs (in `nacl-fast`).
v0.11.0
-------
* Implement `nacl.sign.keyPair.fromSeed` to enable creation of sign key pairs
deterministically from a 32-byte seed. (It behaves like
[libsodium's](http://doc.libsodium.org/public-key_cryptography/public-key_signatures.html)
`crypto_sign_seed_keypair`: the seed becomes a secret part of the secret key.)
* Fast version now has an improved hash implementation that is 2x-5x faster.
* Fixed benchmarks, which may have produced incorrect measurements.
v0.10.1
-------
* Exported undocumented `nacl.lowlevel.crypto_core_hsalsa20`.
v0.10.0
-------
* **Signature API breaking change!** `nacl.sign` and `nacl.sign.open` now deal
with signed messages, and new `nacl.sign.detached` and
`nacl.sign.detached.verify` are available.
Previously, `nacl.sign` returned a signature, and `nacl.sign.open` accepted a
message and "detached" signature. This was unlike NaCl's API, which dealt with
signed messages (concatenation of signature and message).
The new API is:
nacl.sign(message, secretKey) -> signedMessage
nacl.sign.open(signedMessage, publicKey) -> message | null
Since detached signatures are common, two new API functions were introduced:
nacl.sign.detached(message, secretKey) -> signature
nacl.sign.detached.verify(message, signature, publicKey) -> true | false
(Note that it's `verify`, not `open`, and it returns a boolean value, unlike
`open`, which returns an "unsigned" message.)
* NPM package now comes without `test` directory to keep it small.
v0.9.2
------
* Improved documentation.
* Fast version: increased theoretical message size limit from 2^32-1 to 2^52
bytes in Poly1305 (and thus, secretbox and box). However this has no impact
in practice since JavaScript arrays or ArrayBuffers are limited to 32-bit
indexes, and most implementations won't allocate more than a gigabyte or so.
(Obviously, there are no tests for the correctness of implementation.) Also,
it's not recommended to use messages that large without splitting them into
smaller packets anyway.
v0.9.1
------
* Initial release

View File

@ -1,455 +0,0 @@
TweetNaCl.js
============
Port of [TweetNaCl](http://tweetnacl.cr.yp.to) / [NaCl](http://nacl.cr.yp.to/)
to JavaScript for modern browsers and Node.js. Public domain.
[![Build Status](https://travis-ci.org/dchest/tweetnacl-js.svg?branch=master)
](https://travis-ci.org/dchest/tweetnacl-js)
[Demo](https://dchest.github.io/tweetnacl-js/)
**:warning: Beta version. The library is stable and API is frozen, however
it has not been independently reviewed. If you can help reviewing it, please
[contact me](mailto:dmitry@codingrobots.com).**
Documentation
=============
* [Overview](#overview)
* [Installation](#installation)
* [Usage](#usage)
* [Public-key authenticated encryption (box)](#public-key-authenticated-encryption-box)
* [Secret-key authenticated encryption (secretbox)](#secret-key-authenticated-encryption-secretbox)
* [Scalar multiplication](#scalar-multiplication)
* [Signatures](#signatures)
* [Hashing](#hashing)
* [Random bytes generation](#random-bytes-generation)
* [Constant-time comparison](#constant-time-comparison)
* [Utilities](#utilities)
* [Examples](#examples)
* [System requirements](#system-requirements)
* [Development and testing](#development-and-testing)
* [Contributors](#contributors)
* [Who uses it](#who-uses-it)
Overview
--------
The primary goal of this project is to produce a translation of TweetNaCl to
JavaScript which is as close as possible to the original C implementation, plus
a thin layer of idiomatic high-level API on top of it.
There are two versions, you can use either of them:
* `nacl.js` is the port of TweetNaCl with minimum differences from the
original + high-level API.
* `nacl-fast.js` is like `nacl.js`, but with some functions replaced with
faster versions.
Installation
------------
You can install TweetNaCl.is via a package manager:
[Bower](http://bower.io):
$ bower install tweetnacl
[NPM](https://www.npmjs.org/):
$ npm install tweetnacl
or [download source code](https://github.com/dchest/tweetnacl-js/releases).
Usage
------
All API functions accept and return bytes as `Uint8Array`s. If you need to
encode or decode strings, use functions from `nacl.util` namespace.
### Public-key authenticated encryption (box)
Implements *curve25519-xsalsa20-poly1305*.
#### nacl.box.keyPair()
Generates a new random key pair for box and returns it as an object with
`publicKey` and `secretKey` members:
{
publicKey: ..., // Uint8Array with 32-byte public key
secretKey: ... // Uint8Array with 32-byte secret key
}
#### nacl.box.keyPair.fromSecretKey(secretKey)
Returns a key pair for box with public key corresponding to the given secret
key.
#### nacl.box(message, nonce, theirPublicKey, mySecretKey)
Encrypt and authenticates message using peer's public key, our secret key, and
the given nonce, which must be unique for each distinct message for a key pair.
Returns an encrypted and authenticated message, which is
`nacl.box.overheadLength` longer than the original message.
#### nacl.box.open(box, nonce, theirPublicKey, mySecretKey)
Authenticates and decrypts the given box with peer's public key, our secret
key, and the given nonce.
Returns the original message, or `false` if authentication fails.
#### nacl.box.before(theirPublicKey, mySecretKey)
Returns a precomputed shared key which can be used in `nacl.box.after` and
`nacl.box.open.after`.
#### nacl.box.after(message, nonce, sharedKey)
Same as `nacl.box`, but uses a shared key precomputed with `nacl.box.before`.
#### nacl.box.open.after(box, nonce, sharedKey)
Same as `nacl.box.open`, but uses a shared key precomputed with `nacl.box.before`.
#### nacl.box.publicKeyLength = 32
Length of public key in bytes.
#### nacl.box.secretKeyLength = 32
Length of secret key in bytes.
#### nacl.box.sharedKeyLength = 32
Length of precomputed shared key in bytes.
#### nacl.box.nonceLength = 24
Length of nonce in bytes.
#### nacl.box.overheadLength = 16
Length of overhead added to box compared to original message.
### Secret-key authenticated encryption (secretbox)
Implements *xsalsa20-poly1305*.
#### nacl.secretbox(message, nonce, key)
Encrypt and authenticates message using the key and the nonce. The nonce must
be unique for each distinct message for this key.
Returns an encrypted and authenticated message, which is
`nacl.secretbox.overheadLength` longer than the original message.
#### nacl.secretbox.open(box, nonce, key)
Authenticates and decrypts the given secret box using the key and the nonce.
Returns the original message, or `false` if authentication fails.
#### nacl.secretbox.keyLength = 32
Length of key in bytes.
#### nacl.secretbox.nonceLength = 24
Length of nonce in bytes.
#### nacl.secretbox.overheadLength = 16
Length of overhead added to secret box compared to original message.
### Scalar multiplication
Implements *curve25519*.
#### nacl.scalarMult(n, p)
Multiplies an integer `n` by a group element `p` and returns the resulting
group element.
#### nacl.scalarMult.base(n)
Multiplies an integer `n` by a standard group element and returns the resulting
group element.
#### nacl.scalarMult.scalarLength = 32
Length of scalar in bytes.
#### nacl.scalarMult.groupElementLength = 32
Length of group element in bytes.
### Signatures
Implements [ed25519](http://ed25519.cr.yp.to).
#### nacl.sign.keyPair()
Generates new random key pair for signing and returns it as an object with
`publicKey` and `secretKey` members:
{
publicKey: ..., // Uint8Array with 32-byte public key
secretKey: ... // Uint8Array with 64-byte secret key
}
#### nacl.sign.keyPair.fromSecretKey(secretKey)
Returns a signing key pair with public key corresponding to the given
64-byte secret key. The secret key must have been generated by
`nacl.sign.keyPair` or `nacl.sign.keyPair.fromSeed`.
#### nacl.sign.keyPair.fromSeed(seed)
Returns a new signing key pair generated deterministically from a 32-byte seed.
The seed must contain enough entropy to be secure. This method is not
recommended for general use: instead, use `nacl.sign.keyPair` to generate a new
key pair from a random seed.
#### nacl.sign(message, secretKey)
Signs the message using the secret key and returns a signed message.
#### nacl.sign.open(signedMessage, publicKey)
Verifies the signed message and returns the message without signature.
Returns `null` if verification failed.
#### nacl.sign.detached(message, secretKey)
Signs the message using the secret key and returns a signature.
#### nacl.sign.detached.verify(message, signature, publicKey)
Verifies the signature for the message and returns `true` if verification
succeeded or `false` if it failed.
#### nacl.sign.publicKeyLength = 32
Length of signing public key in bytes.
#### nacl.sign.secretKeyLength = 64
Length of signing secret key in bytes.
#### nacl.sign.seedLength = 32
Length of seed for `nacl.sign.keyPair.fromSeed` in bytes.
#### nacl.sign.signatureLength = 64
Length of signature in bytes.
### Hashing
Implements *SHA-512*.
#### nacl.hash(message)
Returns SHA-512 hash of the message.
#### nacl.hash.hashLength = 64
Length of hash in bytes.
### Random bytes generation
#### nacl.randomBytes(length)
Returns a `Uint8Array` of the given length containing random bytes of
cryptographic quality.
**Implementation note**
TweetNaCl.js uses the following methods to generate random bytes,
depending on the platform it runs on:
* `window.crypto.getRandomValues` (WebCrypto standard)
* `window.msCrypto.getRandomValues` (Internet Explorer 11)
* `crypto.randomBytes` (Node.js)
Note that browsers are required to throw `QuotaExceededError` exception if
requested `length` is more than 65536, so do not ask for more than 65536 bytes
in *one call* (multiple calls to get as many bytes as you like are okay:
browsers can generate infinite amount of random bytes without any bad
consequences).
If the platform doesn't provide a suitable PRNG, the following functions,
which require random numbers, will throw exception:
* `nacl.randomBytes`
* `nacl.box.keyPair`
* `nacl.sign.keyPair`
Other functions are deterministic and will continue working.
If a platform you are targeting doesn't implement secure random number
generator, but you somehow have a cryptographically-strong source of entropy
(not `Math.random`!), and you know what you are doing, you can plug it into
TweetNaCl.js like this:
nacl.setPRNG(function(x, n) {
// ... copy n random bytes into x ...
});
Note that `nacl.setPRNG` *completely replaces* internal random byte generator
with the one provided.
### Constant-time comparison
#### nacl.verify(x, y)
Compares `x` and `y` in constant time and returns `true` if their lengths are
non-zero and equal, and their contents are equal.
Returns `false` if either of the arguments has zero length, or arguments have
different lengths, or their contents differ.
### Utilities
Encoding/decoding functions are provided for convenience. They are correct,
however their performance and wide compatibility with uncommon runtimes is not
something that is considered important compared to the simplicity and size of
implementation. You can use third-party libraries if you need to.
#### nacl.util.decodeUTF8(string)
Decodes string and returns `Uint8Array` of bytes.
#### nacl.util.encodeUTF8(array)
Encodes `Uint8Array` or `Array` of bytes into string.
#### nacl.util.decodeBase64(string)
Decodes Base-64 encoded string and returns `Uint8Array` of bytes.
#### nacl.util.encodeBase64(array)
Encodes `Uint8Array` or `Array` of bytes into string using Base-64 encoding.
System requirements
-------------------
TweetNaCl.js supports modern browsers that have a cryptographically secure
pseudorandom number generator and typed arrays, including the latest versions
of:
* Chrome
* Firefox
* Safari (Mac, iOS)
* Internet Explorer 11
Other systems:
* Node.js (we test on 0.10 and later)
Development and testing
------------------------
Install NPM modules needed for development:
$ npm install
To build minified versions:
$ npm run build
Tests use minified version, so make sure to rebuild it every time you change
`nacl.js` or `nacl-fast.js`.
### Testing
To run tests in Node.js:
$ npm test
By default all tests described here work on `nacl.min.js`. To test other
versions, set environment variable `NACL_SRC` to the file name you want to test.
For example, the following command will test fast minified version:
$ NACL_SRC=nacl-fast.min.js npm test
To run full suite of tests in Node.hs, including comparing outputs of
JavaScript port to outputs of the original C version:
$ npm run testall
To prepare tests for browsers:
$ npm run browser
and then open `tests/browser/test.html` (or `tests/browser/test-fast.html`) to
run them.
To run headless browser tests with `testling`:
$ npm run testling
(If you get `Error: spawn ENOENT`, install *xvfb*: `sudo apt-get install xvfb`.)
### Benchmarking
To run benchmarks in Node.js:
$ npm run bench
$ NACL_SRC=nacl-fast.min.js npm run bench
To run benchmarks in a browser, open `test/benchmark/bench.html` (or
`test/benchmark/bench-fast.html`).
Contributors
------------
JavaScript port:
* [Dmitry Chestnykh](http://github.com/dchest) (ported xsalsa20, poly1305, curve25519)
* [Devi Mandiri](https://github.com/devi) (ported curve25519, ed25519, sha512)
Original authors of [NaCl](http://nacl.cr.yp.to), [TweetNaCl](http://tweetnacl.cr.yp.to)
and [Poly1305-donna](https://github.com/floodyberry/poly1305-donna)
(who are *not* responsible for any errors in this implementation):
* [Daniel J. Bernstein](http://cr.yp.to/djb.html)
* Wesley Janssen
* [Tanja Lange](http://hyperelliptic.org/tanja)
* [Peter Schwabe](http://www.cryptojedi.org/users/peter/)
* [Matthew Dempsky](https://github.com/mdempsky)
* [Andrew Moon](https://github.com/floodyberry)
Contributors have dedicated their work to the public domain.
This software is distributed without any warranty.
Who uses it
-----------
Some notable users of TweetNaCl.js:
* [miniLock](http://minilock.io/)
* [Stellar](https://www.stellar.org/)

View File

@ -1,36 +0,0 @@
{
"name": "tweetnacl",
"version": "0.12.2",
"homepage": "https://dchest.github.io/tweetnacl-js",
"authors": [
"TweetNaCl.js Contributors"
],
"description": "Port of TweetNaCl cryptographic library to JavaScript",
"main": "nacl.js",
"moduleType": [
"globals",
"node"
],
"keywords": [
"crypto",
"cryptography",
"curve25519",
"ed25519",
"encrypt",
"hash",
"key",
"nacl",
"poly1305",
"public",
"salsa20",
"signatures"
],
"license": "Public domain",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"package.json"
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,57 +0,0 @@
{
"name": "tweetnacl",
"version": "0.12.2",
"description": "Port of TweetNaCl cryptographic library to JavaScript",
"main": "nacl.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "uglifyjs nacl.js -c -m -o nacl.min.js && uglifyjs nacl-fast.js -c -m -o nacl-fast.min.js",
"test": "tape test/*.js | faucet",
"testall": "make -C test/c && tape test/*.js test/c/*.js | faucet",
"browser": "browserify test/browser/init.js test/*.js | uglifyjs -c -m -o test/browser/_bundle.js 2>/dev/null",
"browser-quick": "browserify test/browser/init.js test/*.quick.js | uglifyjs -c -m -o test/browser/_bundle-quick.js 2>/dev/null",
"testling": "browserify test/browser/testling_init.js test/*.js | testling | faucet",
"firefox": "browserify test/browser/testling_init.js test/*.js | testling -x firefox | faucet",
"chrome": "browserify test/browser/testling_init.js test/*.js | testling -x google-chrome | faucet",
"bench": "node test/benchmark/bench.js"
},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/11..latest",
"chrome/22..latest",
"firefox/16..latest",
"safari/latest",
"opera/11.0..latest",
"iphone/6..latest",
"ipad/6..latest",
"android-browser/latest"
]
},
"repository": {
"type": "git",
"url": "https://github.com/dchest/tweetnacl-js.git"
},
"keywords": [
"crypto",
"cryptography",
"curve25519",
"ed25519",
"encrypt",
"hash",
"key",
"nacl",
"poly1305",
"public",
"salsa20",
"signatures"
],
"author": "TweetNaCl-js contributors",
"license": "Public domain",
"bugs": {
"url": "https://github.com/dchest/tweetnacl-js/issues"
},
"homepage": "https://dchest.github.io/tweetnacl-js"
}

View File

@ -1,15 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var b64Vectors = require('./data/base64.random');
test('nacl.util.encodeBase64 random test vectors', function(t) {
b64Vectors.forEach(function(vec) {
var b = new Uint8Array(vec[0]);
var s = vec[1];
t.equal(nacl.util.encodeBase64(b), s);
t.deepEqual(nacl.util.decodeBase64(s), b);
});
t.end();
});

View File

@ -1,26 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var testBytes = new Uint8Array([208,159,209,128,208,184,208,178,208,181,209,130,44,32,78,97,67,108]);
var utf8String = "Привет, NaCl";
var b64String = "0J/RgNC40LLQtdGCLCBOYUNs";
test('nacl.util.decodeUTF8', function(t) {
t.plan(1);
t.deepEqual(nacl.util.decodeUTF8(utf8String), testBytes);
});
test('nacl.util.encodeUTF8', function(t) {
t.plan(1);
t.equal(nacl.util.encodeUTF8(testBytes), utf8String);
});
test('nacl.util.decodeBase64', function(t) {
t.plan(1);
t.deepEqual(nacl.util.decodeBase64(b64String), testBytes);
});
test('nacl.util.encodeBase64', function(t) {
t.plan(1);
t.equal(nacl.util.encodeBase64(testBytes), b64String);
});

View File

@ -1,16 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
test('nacl.verify', function(t) {
t.ok(nacl.verify(new Uint8Array(1), new Uint8Array(1)), 'equal arrays of length 1 should verify');
t.ok(nacl.verify(new Uint8Array(1000), new Uint8Array(1000)), 'equal arrays of length 1000 should verify');
var a = new Uint8Array(764), b = new Uint8Array(764);
for (i = 0; i < a.length; i++) a[i] = b[i] = i & 0xff;
t.ok(nacl.verify(a, b), 'equal arrays should verify');
t.ok(nacl.verify(a, a), 'same arrays should verify');
b[0] = 255;
t.notOk(nacl.verify(a, b), 'different arrays don\'t verify');
t.notOk(nacl.verify(new Uint8Array(1), new Uint8Array(10)), 'arrays of different lengths should not verify');
t.notOk(nacl.verify(new Uint8Array(0), new Uint8Array(0)), 'zero-length arrays should not verify');
t.end();
});

View File

@ -1,16 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
test('nacl.randomBytes', function(t) {
t.plan(1);
var set = {}, s, i;
for (i = 0; i < 10000; i++) {
s = nacl.util.encodeBase64(nacl.randomBytes(32));
if (set[s]) {
t.fail("duplicate random sequence! ", s);
return;
}
set[s] = true;
}
t.pass('no collisions');
});

View File

@ -1,17 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var specVectors = require('./data/onetimeauth.spec');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.lowlevel.crypto_onetimeauth specified vectors', function(t) {
var out = new Uint8Array(16);
specVectors.forEach(function(v) {
nacl.lowlevel.crypto_onetimeauth(out, 0, v.m, 0, v.m.length, v.k);
t.equal(enc(out), enc(v.out));
});
t.end();
});

View File

@ -1,24 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var randomVectors = require('./data/secretbox.random');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.secretbox random test vectors', function(t) {
randomVectors.forEach(function(vec) {
var key = dec(vec[0]);
var nonce = dec(vec[1]);
var msg = dec(vec[2]);
var goodBox = dec(vec[3]);
var box = nacl.secretbox(msg, nonce, key);
t.ok(box, 'box should be created');
t.equal(enc(box), enc(goodBox));
var openedBox = nacl.secretbox.open(goodBox, nonce, key);
t.ok(openedBox, 'box should open');
t.equal(enc(openedBox), enc(msg));
});
t.end();
});

View File

@ -1,52 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.secretbox and nacl.secretbox.open', function(t) {
var key = new Uint8Array(nacl.secretbox.keyLength);
var nonce = new Uint8Array(nacl.secretbox.nonceLength);
for (var i = 0; i < key.length; i++) key[i] = i & 0xff;
for (i = 0; i < nonce.length; i++) nonce[i] = (32+i) & 0xff;
var msg = nacl.util.decodeUTF8('message to encrypt');
var box = nacl.secretbox(msg, nonce, key);
var openedMsg = nacl.secretbox.open(box, nonce, key);
t.equal(nacl.util.encodeUTF8(openedMsg), nacl.util.encodeUTF8(msg), 'opened messages should be equal');
t.end();
});
test('nacl.secretbox.open with invalid box', function(t) {
var key = new Uint8Array(nacl.secretbox.keyLength);
var nonce = new Uint8Array(nacl.secretbox.nonceLength);
t.equal(nacl.secretbox.open(new Uint8Array(0), nonce, key), false);
t.equal(nacl.secretbox.open(new Uint8Array(10), nonce, key), false);
t.equal(nacl.secretbox.open(new Uint8Array(100), nonce, key), false);
t.end();
});
test('nacl.secretbox.open with invalid nonce', function(t) {
var key = new Uint8Array(nacl.secretbox.keyLength);
var nonce = new Uint8Array(nacl.secretbox.nonceLength);
for (i = 0; i < nonce.length; i++) nonce[i] = i & 0xff;
var msg = nacl.util.decodeUTF8('message to encrypt');
var box = nacl.secretbox(msg, nonce, key);
t.equal(nacl.util.encodeUTF8(nacl.secretbox.open(box, nonce, key)),
nacl.util.encodeUTF8(msg));
nonce[0] = 255;
t.equal(nacl.secretbox.open(box, nonce, key), false);
t.end();
});
test('nacl.secretbox.open with invalid key', function(t) {
var key = new Uint8Array(nacl.secretbox.keyLength);
for (var i = 0; i < key.length; i++) key[i] = i & 0xff;
var nonce = new Uint8Array(nacl.secretbox.nonceLength);
var msg = nacl.util.decodeUTF8('message to encrypt');
var box = nacl.secretbox(msg, nonce, key);
t.equal(nacl.util.encodeUTF8(nacl.secretbox.open(box, nonce, key)),
nacl.util.encodeUTF8(msg));
key[0] = 255;
t.equal(nacl.secretbox.open(box, nonce, key), false);
t.end();
});

View File

@ -1,43 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var randomVectors = require('./data/scalarmult.random');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.scalarMult.base', function(t) {
// This takes takes a bit of time.
// Similar to https://code.google.com/p/go/source/browse/curve25519/curve25519_test.go?repo=crypto
var golden = new Uint8Array([0x89, 0x16, 0x1f, 0xde, 0x88, 0x7b, 0x2b, 0x53, 0xde, 0x54,
0x9a, 0xf4, 0x83, 0x94, 0x01, 0x06, 0xec, 0xc1, 0x14, 0xd6, 0x98, 0x2d,
0xaa, 0x98, 0x25, 0x6d, 0xe2, 0x3b, 0xdf, 0x77, 0x66, 0x1a]);
var input = new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
for (var i = 0; i < 200; i++) {
input = nacl.scalarMult.base(input);
}
t.equal(enc(input), enc(golden));
t.end();
});
test('nacl.scalarMult and nacl.scalarMult.base random test vectors', function(t) {
randomVectors.forEach(function(vec) {
var pk1 = dec(vec[0]);
var sk1 = dec(vec[1]);
var pk2 = dec(vec[2]);
var sk2 = dec(vec[3]);
var out = dec(vec[4]);
var jpk1 = nacl.scalarMult.base(sk1);
t.equal(enc(jpk1), enc(pk1));
var jpk2 = nacl.scalarMult.base(sk2);
t.equal(enc(jpk2), enc(pk2));
var jout1 = nacl.scalarMult(sk1, pk2);
t.equal(enc(jout1), enc(out));
var jout2 = nacl.scalarMult(sk2, pk1);
t.equal(enc(jout2), enc(out));
});
t.end();
});

View File

@ -1,22 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var randomVectors = require('./data/box.random');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.box random test vectors', function(t) {
var nonce = new Uint8Array(nacl.box.nonceLength);
randomVectors.forEach(function(vec) {
var pk1 = dec(vec[0]);
var sk2 = dec(vec[1]);
var msg = dec(vec[2]);
var goodBox = dec(vec[3]);
var box = nacl.box(msg, nonce, pk1, sk2);
t.equal(enc(box), enc(goodBox));
var openedBox = nacl.box.open(goodBox, nonce, pk1, sk2);
t.equal(enc(openedBox), enc(msg));
});
t.end();
});

View File

@ -1,78 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.box.keyPair', function(t) {
var keys = nacl.box.keyPair();
t.ok(keys.secretKey && keys.secretKey.length === nacl.box.secretKeyLength, 'has secret key');
t.ok(keys.publicKey && keys.publicKey.length === nacl.box.publicKeyLength, 'has public key');
t.notEqual(enc(keys.secretKey), enc(keys.publicKey));
t.end();
});
test('nacl.box.keyPair.fromSecretKey', function(t) {
var k1 = nacl.box.keyPair();
var k2 = nacl.box.keyPair.fromSecretKey(k1.secretKey);
t.equal(enc(k2.secretKey), enc(k1.secretKey));
t.equal(enc(k2.publicKey), enc(k1.publicKey));
t.end();
});
test('nacl.box and nacl.box.open', function(t) {
var clientKeys = nacl.box.keyPair();
var serverKeys = nacl.box.keyPair();
var nonce = new Uint8Array(nacl.box.nonceLength);
for (i = 0; i < nonce.length; i++) nonce[i] = (32+i) & 0xff;
var msg = nacl.util.decodeUTF8('message to encrypt');
var clientBox = nacl.box(msg, nonce, serverKeys.publicKey, clientKeys.secretKey);
var clientMsg = nacl.box.open(clientBox, nonce, clientKeys.publicKey, serverKeys.secretKey);
t.equal(nacl.util.encodeUTF8(clientMsg), nacl.util.encodeUTF8(msg));
var serverBox = nacl.box(msg, nonce, clientKeys.publicKey, serverKeys.secretKey);
t.equal(enc(clientBox), enc(serverBox));
var serverMsg = nacl.box.open(serverBox, nonce, serverKeys.publicKey, clientKeys.secretKey);
t.equal(nacl.util.encodeUTF8(serverMsg), nacl.util.encodeUTF8(msg));
t.end();
});
test('nacl.box.open with invalid box', function(t) {
var clientKeys = nacl.box.keyPair();
var serverKeys = nacl.box.keyPair();
var nonce = new Uint8Array(nacl.box.nonceLength);
t.equal(nacl.box.open(new Uint8Array(0), nonce, serverKeys.publicKey, clientKeys.secretKey), false);
t.equal(nacl.box.open(new Uint8Array(10), nonce, serverKeys.publicKey, clientKeys.secretKey), false);
t.equal(nacl.box.open(new Uint8Array(100), nonce, serverKeys.publicKey, clientKeys.secretKey), false);
t.end();
});
test('nacl.box.open with invalid nonce', function(t) {
var clientKeys = nacl.box.keyPair();
var serverKeys = nacl.box.keyPair();
var nonce = new Uint8Array(nacl.box.nonceLength);
for (i = 0; i < nonce.length; i++) nonce[i] = i & 0xff;
var msg = nacl.util.decodeUTF8('message to encrypt');
var box = nacl.box(msg, nonce, clientKeys.publicKey, serverKeys.secretKey);
t.equal(nacl.util.encodeUTF8(nacl.box.open(box, nonce, serverKeys.publicKey, clientKeys.secretKey)),
nacl.util.encodeUTF8(msg));
nonce[0] = 255;
t.equal(nacl.box.open(box, nonce, serverKeys.publicKey, clientKeys.secretKey), false);
t.end();
});
test('nacl.box.open with invalid keys', function(t) {
var clientKeys = nacl.box.keyPair();
var serverKeys = nacl.box.keyPair();
var nonce = new Uint8Array(nacl.box.nonceLength);
var msg = nacl.util.decodeUTF8('message to encrypt');
var box = nacl.box(msg, nonce, clientKeys.publicKey, serverKeys.secretKey);
t.equal(nacl.util.encodeUTF8(nacl.box.open(box, nonce, serverKeys.publicKey, clientKeys.secretKey)),
nacl.util.encodeUTF8(msg));
t.equal(nacl.util.encodeUTF8(nacl.box.open(box, nonce, clientKeys.publicKey, serverKeys.secretKey)),
nacl.util.encodeUTF8(msg));
badPublicKey = new Uint8Array(nacl.box.publicKeyLength);
t.equal(nacl.box.open(box, nonce, badPublicKey, clientKeys.secretKey), false);
badSecretKey = new Uint8Array(nacl.box.secretKeyLength);
t.equal(nacl.box.open(box, nonce, serverKeys.publicKey, badSecretKey), false);
t.end();
});

View File

@ -1,17 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var randomVectors = require('./data/hash.random');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.hash random test vectors', function(t) {
randomVectors.forEach(function(vec) {
var msg = dec(vec[0]);
var goodHash = dec(vec[1]);
var hash = nacl.hash(msg);
t.equal(enc(hash), enc(goodHash));
});
t.end();
});

View File

@ -1,29 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var specVectors = require('./data/hash.spec');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.hash length', function(t) {
t.equal(nacl.hash(new Uint8Array(0)).length, 64);
t.equal(nacl.hash(new Uint8Array(100)).length, 64);
t.end();
});
test('nacl.hash exceptions for bad types', function(t) {
t.throws(function() { nacl.hash('string'); }, TypeError, 'should throw TypeError for string type');
t.throws(function() { nacl.hash([1,2,3]); }, TypeError, 'should throw TypeError for array type');
t.end();
});
test('nacl.hash specified test vectors', function(t) {
specVectors.forEach(function(vec) {
var goodHash = new Uint8Array(vec[0]);
var msg = new Uint8Array(vec[1]);
var hash = nacl.hash(msg);
t.equal(enc(hash), enc(goodHash));
});
t.end();
});

View File

@ -1,38 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var specVectors = require('./data/sign.spec');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.sign and nacl.sign.open specified vectors', function(t) {
specVectors.forEach(function(vec) {
var keys = nacl.sign.keyPair.fromSecretKey(dec(vec[0]));
var msg = dec(vec[1]);
var goodSig = dec(vec[2]);
var signedMsg = nacl.sign(msg, keys.secretKey);
t.equal(enc(signedMsg.subarray(0, nacl.sign.signatureLength)), enc(goodSig), 'signatures must be equal');
var openedMsg = nacl.sign.open(signedMsg, keys.publicKey);
t.equal(enc(openedMsg), enc(msg), 'messages must be equal');
});
t.end();
});
test('nacl.sign.detached and nacl.sign.detached.verify some specified vectors', function(t) {
specVectors.forEach(function(vec, i) {
// We don't need to test all, as internals are already tested above.
if (i % 100 !== 0) return;
var keys = nacl.sign.keyPair.fromSecretKey(dec(vec[0]));
var msg = dec(vec[1]);
var goodSig = dec(vec[2]);
var sig = nacl.sign.detached(msg, keys.secretKey);
t.equal(enc(sig), enc(goodSig), 'signatures must be equal');
var result = nacl.sign.detached.verify(msg, sig, keys.publicKey);
t.ok(result, 'signature must be verified');
});
t.end();
});

View File

@ -1,80 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../' + (process.env.NACL_SRC || 'nacl.min.js'));
var test = require('tape');
var enc = nacl.util.encodeBase64,
dec = nacl.util.decodeBase64;
test('nacl.sign.keyPair', function(t) {
var keys = nacl.sign.keyPair();
t.ok(keys.secretKey && keys.secretKey.length === nacl.sign.secretKeyLength, 'has secret key');
t.ok(keys.publicKey && keys.publicKey.length === nacl.sign.publicKeyLength, 'has public key');
t.notEqual(enc(keys.secretKey), enc(keys.publicKey));
var newKeys = nacl.sign.keyPair();
t.notEqual(enc(newKeys.secretKey), enc(keys.secretKey), 'two keys differ');
t.end();
});
test('nacl.sign.keyPair.fromSecretKey', function(t) {
var k1 = nacl.sign.keyPair();
var k2 = nacl.sign.keyPair.fromSecretKey(k1.secretKey);
t.equal(enc(k2.secretKey), enc(k1.secretKey));
t.equal(enc(k2.publicKey), enc(k1.publicKey));
t.end();
});
test('nacl.sign.keyPair.fromSeed', function(t) {
var seed = nacl.randomBytes(nacl.sign.seedLength);
var k1 = nacl.sign.keyPair.fromSeed(seed);
var k2 = nacl.sign.keyPair.fromSeed(seed);
t.equal(k1.secretKey.length, nacl.sign.secretKeyLength);
t.equal(k1.publicKey.length, nacl.sign.publicKeyLength);
t.equal(k2.secretKey.length, nacl.sign.secretKeyLength);
t.equal(k2.publicKey.length, nacl.sign.publicKeyLength);
t.equal(enc(k2.secretKey), enc(k1.secretKey));
t.equal(enc(k2.publicKey), enc(k1.publicKey));
var seed2 = nacl.randomBytes(nacl.sign.seedLength);
var k3 = nacl.sign.keyPair.fromSeed(seed2);
t.equal(k3.secretKey.length, nacl.sign.secretKeyLength);
t.equal(k3.publicKey.length, nacl.sign.publicKeyLength);
t.notEqual(enc(k3.secretKey), enc(k1.secretKey));
t.notEqual(enc(k3.publicKey), enc(k1.publicKey));
t.throws(function() { nacl.sign.keyPair.fromSeed(seed2.subarray(0, 16)) }, Error, 'should throw error for wrong seed size');
t.end();
});
test('nacl.sign and nacl.sign.open', function(t) {
var k = nacl.sign.keyPair();
var m = new Uint8Array(100);
for (var i = 0; i < m.length; i++) m[i] = i & 0xff;
var sm = nacl.sign(m, k.secretKey);
t.ok(sm.length > m.length, 'signed message length should be greater than message length');
var om = nacl.sign.open(sm, k.publicKey);
t.deepEqual(om, m);
t.throws(function() { nacl.sign.open(sm, k.publicKey.subarray(1)) }, Error, 'throws error for wrong public key size');
var badPublicKey = new Uint8Array(k.publicKey.length);
om = nacl.sign.open(sm, badPublicKey);
t.equal(om, null, 'opened message must be null when using wrong public key');
for (i = 80; i < 90; i++) sm[i] = 0;
om = nacl.sign.open(sm, k.publicKey);
t.equal(om, null, 'opened message must be null when opening bad signed message');
t.end();
});
test('nacl.sign.detached and nacl.sign.detached.verify', function(t) {
var k = nacl.sign.keyPair();
var m = new Uint8Array(100);
for (var i = 0; i < m.length; i++) m[i] = i & 0xff;
var sig = nacl.sign.detached(m, k.secretKey);
t.ok(sig.length === nacl.sign.signatureLength, 'signature must have correct length');
var result = nacl.sign.detached.verify(m, sig, k.publicKey);
t.ok(result, 'signature must be verified');
t.throws(function() { nacl.sign.detached.verify(m, sig, k.publicKey.subarray(1)) }, Error, 'throws error for wrong public key size');
t.throws(function() { nacl.sign.detached.verify(m, sig.subarray(1), k.publicKey) }, Error, 'throws error for wrong signature size');
var badPublicKey = new Uint8Array(k.publicKey.length);
result = nacl.sign.detached.verify(m, sig, badPublicKey);
t.equal(result, false, 'signature must not be verified with wrong public key');
for (i = 0; i < 10; i++) sig[i] = 0;
result = nacl.sign.detached.verify(m, sig, k.publicKey);
t.equal(result, false, 'bad signature must not be verified');
t.end();
});

View File

@ -1,11 +0,0 @@
This directory contains benchmarks that run in Node.js and browsers.
To run benchmarks in Node.js, from the root project directory:
$ npm run bench
alternatively, in this directory, run:
$ node bench.js
To run benchmarks in a browser, open bench.html.

View File

@ -1,13 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf8">
<title>TweetNaCl-js Benchmarks (fast)</title>
</head>
<body>
<h1>TweetNaCl-js Benchmarks (fast)</h1>
<script src="../../nacl-fast.min.js"></script>
<script src="helpers.js"></script>
<script src="bench.js"></script>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf8">
<title>TweetNaCl-js Benchmarks</title>
</head>
<body>
<h1>TweetNaCl-js Benchmarks</h1>
<script src="../../nacl.min.js"></script>
<script src="helpers.js"></script>
<script src="bench.js"></script>
</body>
</html>

View File

@ -1,164 +0,0 @@
var nacl = (typeof window !== 'undefined') ? window.nacl : require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var helpers = (typeof require !== 'undefined') ? require('./helpers') : window.helpers;
var log = helpers.log;
if (!nacl) throw new Error('nacl not loaded');
function benchmark(fn, bytes, num) {
if (!num) num = 1000;
var i, elapsed, start = new Date();
while (1) {
for (i = 0; i < num; i++) fn();
elapsed = (new Date()) - start;
if (elapsed < 500) {
num += num*1000/elapsed/2;
} else {
break;
}
}
log.print(' ' + ((bytes*num/1024/1024*1000)/elapsed).toFixed(3), 'MB/s');
log.print(' ' + ((num*1000)/elapsed).toFixed(3), 'ops/s');
}
function benchmarkOps(fn, num) {
var i, elapsed, start = new Date();
while (1) {
for (i = 0; i < num; i++) {
fn();
}
elapsed = (new Date()) - start;
if (elapsed < 500) {
num += num*1000/elapsed/2;
} else {
break;
}
}
log.print(' ' + ((num*1000)/elapsed).toFixed(3), 'ops/s');
}
function crypto_stream_xor_benchmark() {
log.start('Benchmarking crypto_stream_xor');
var m = new Uint8Array(1024),
n = new Uint8Array(24),
k = new Uint8Array(32),
out = new Uint8Array(1024);
for (i = 0; i < 1024; i++) m[i] = i & 255;
for (i = 0; i < 24; i++) n[i] = i;
for (i = 0; i < 32; i++) k[i] = i;
benchmark(function(){
nacl.lowlevel.crypto_stream_xor(out, 0, m, 0, m.length, n, k);
}, m.length);
}
function crypto_onetimeauth_benchmark() {
log.start('Benchmarking crypto_onetimeauth');
var m = new Uint8Array(1024),
out = new Uint8Array(1024),
k = new Uint8Array([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1]);
for (i = 0; i < 1024; i++) {
m[i] = i & 255;
}
benchmark(function(){
nacl.lowlevel.crypto_onetimeauth(out, 0, m, 0, m.length, k);
}, m.length);
}
function crypto_secretbox_benchmark() {
log.start('Benchmarking crypto_secretbox');
var i, k = new Uint8Array(32), n = new Uint8Array(24),
m = new Uint8Array(1024), c = new Uint8Array(1024);
for (i = 0; i < 32; i++) k[i] = 1;
for (i = 0; i < 24; i++) n[i] = 2;
for (i = 0; i < 1024; i++) m[i] = 3;
benchmark(function() {
nacl.lowlevel.crypto_secretbox(c, m, m.length, n, k);
}, m.length);
}
function secretbox_seal_open_benchmark() {
var key = new Uint8Array(32),
nonce = new Uint8Array(24),
msg = new Uint8Array(1024),
box, i;
for (i = 0; i < 32; i++) key[i] = 1;
for (i = 0; i < 24; i++) nonce[i] = 2;
for (i = 0; i < 1024; i++) msg[i] = 3;
log.start('Benchmarking secretbox');
benchmark(function() {
box = nacl.secretbox(msg, nonce, key);
}, msg.length);
log.start('Benchmarking secretbox.open');
benchmark(function() {
nacl.secretbox.open(box, nonce, key);
}, msg.length);
}
function crypto_scalarmult_base_benchmark() {
log.start('Benchmarking crypto_scalarmult_base');
var n = new Uint8Array(32), q = new Uint8Array(32);
for (var i = 0; i < 32; i++) n[i] = i;
benchmarkOps(function() {
nacl.lowlevel.crypto_scalarmult_base(q, n);
}, 10);
}
function box_seal_open_benchmark() {
var pk1 = new Uint8Array(32), sk1 = new Uint8Array(32),
pk2 = new Uint8Array(32), sk2 = new Uint8Array(32);
nacl.lowlevel.crypto_box_keypair(pk1, sk1);
nacl.lowlevel.crypto_box_keypair(pk2, sk2);
var nonce = nacl.util.decodeUTF8('123456789012345678901234');
var msg = nacl.util.decodeUTF8((new Array(1024)).join('a'));
var box = null;
log.start('Benchmarking box');
benchmark(function() {
box = nacl.box(msg, nonce, pk1, sk2);
}, msg.length, 20);
log.start('Benchmarking box.open');
benchmark(function() {
nacl.box.open(box, nonce, pk2, sk1);
}, msg.length, 20);
}
function sign_open_benchmark() {
var k = nacl.sign.keyPair();
var sk = k.secretKey;
var pk = k.publicKey;
var msg = nacl.util.decodeUTF8((new Array(128)).join('a'));
var sm;
log.start('Benchmarking sign');
benchmark(function() {
sm = nacl.sign(msg, sk);
}, msg.length, 20);
log.start('Benchmarking sign.open');
benchmark(function() {
nacl.sign.open(sm, pk);
}, msg.length, 20);
}
function crypto_hash_benchmark() {
log.start('Benchmarking crypto_hash (1024 bytes)');
var m = new Uint8Array(1024), out = new Uint8Array(64);
for (i = 0; i < m.length; i++) m[i] = i & 255;
benchmark(function(){
nacl.lowlevel.crypto_hash(out, m, m.length);
}, m.length);
log.start('Benchmarking crypto_hash (16 KiB)');
m = new Uint8Array(16*1024);
for (i = 0; i < m.length; i++) m[i] = i & 255;
benchmark(function(){
nacl.lowlevel.crypto_hash(out, m, m.length);
}, m.length);
}
crypto_stream_xor_benchmark();
crypto_onetimeauth_benchmark();
crypto_secretbox_benchmark();
crypto_hash_benchmark();
secretbox_seal_open_benchmark();
crypto_scalarmult_base_benchmark();
box_seal_open_benchmark();
sign_open_benchmark();

View File

@ -1,62 +0,0 @@
// Test helpers.
(function(exports) {
function NodeLogger() {
this.print = function() {
console.log.apply(console, arguments);
};
this.ok = function() {
process.stdout.write('.');
};
this.error = function() {
console.error.apply(console, arguments);
process.exit(1);
};
this.start = this.print.bind(this, '\n');
}
function BrowserLogger() {
var el = document.createElement('pre');
document.body.appendChild(el);
function escape(s) {
var reps = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;'
};
return String(s).replace(/[&<>"'\/]/g, function (x) { return reps[x]; });
}
this.print = function() {
console.log.apply(console, arguments);
var s = escape(arguments.length ? Array.prototype.slice.call(arguments).join(' ') : '');
el.innerHTML += s + '\n';
};
this.ok = function() {
// No console output.
el.innerHTML += '.';
};
this.error = function() {
console.error.apply(console, arguments);
var s = escape(arguments.length ? Array.prototype.slice.call(arguments) : '');
el.innerHTML += 'ERROR: ' + s + '\n';
};
this.start = this.print.bind(this, '\n');
}
if (typeof window !== 'undefined')
exports.log = new BrowserLogger();
else
exports.log = new NodeLogger();
})(typeof exports !== 'undefined' ? exports : (window.helpers = window.helpers || {}));

View File

@ -1,2 +0,0 @@
_bundle.js
_bundle-quick.js

View File

@ -1,9 +0,0 @@
This directory contains test files for browsers.
In the root project directory:
$ npm run browser
to build new _bundle.js.
Then open test.html.

View File

@ -1,11 +0,0 @@
<!doctype html>
<title>TweetNaCl.js Tests</title>
<body>
<h1>Select test to run:</h1>
<ul>
<li><a href="test.html"><b>Full</b> nacl.min.js</a></li>
<li><a href="test-fast.html"><b>Full</b> nacl-fast.min.js</a></li>
<li>--</li>
<li><a href="test-quick.html"><b>Quick</b> nacl.min.js</a></li>
<li><a href="test-quick-fast.html"><b>Quick</b> nacl-fast.min.js</a></li>
</body>

View File

@ -1 +0,0 @@
var _ = require('tap-browser-color')();

View File

@ -1,12 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TweetNaCl-js Tests (fast)</title>
</head>
<body>
<h1>TweetNaCl-js Tests (fast)</h1>
<script src="../../nacl-fast.min.js"></script>
<script src="_bundle.js"></script>
</body>
</html>

View File

@ -1,12 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TweetNaCl-js Tests (fast)</title>
</head>
<body>
<h1>TweetNaCl-js Tests (fast)</h1>
<script src="../../nacl-fast.min.js"></script>
<script src="_bundle-quick.js"></script>
</body>
</html>

View File

@ -1,12 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TweetNaCl-js Tests</title>
</head>
<body>
<h1>TweetNaCl-js Tests</h1>
<script src="../../nacl.min.js"></script>
<script src="_bundle-quick.js"></script>
</body>
</html>

View File

@ -1,12 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TweetNaCl-js Tests</title>
</head>
<body>
<h1>TweetNaCl-js Tests</h1>
<script src="../../nacl.min.js"></script>
<script src="_bundle.js"></script>
</body>
</html>

View File

@ -1 +0,0 @@
window.nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));

View File

@ -1,6 +0,0 @@
cscalarmult
cbox
csecretbox
csign
chash
*.o

View File

@ -1,54 +0,0 @@
var nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var crypto = require('crypto');
var spawn = require('child_process').spawn;
var path = require('path');
var test = require('tape');
function csecretbox(msg, n, k, callback) {
var hexk = (new Buffer(k)).toString('hex');
var hexn = (new Buffer(n)).toString('hex');
var p = spawn(path.resolve(__dirname, 'csecretbox'), [hexk, hexn]);
var result = [];
p.stdout.on('data', function(data) {
result.push(data);
});
p.on('close', function(code) {
return callback(Buffer.concat(result).toString('base64'));
});
p.on('error', function(err) {
throw err;
});
p.stdin.write(msg);
p.stdin.end();
}
test('nacl.secretbox (C)', function(t) {
var k = new Uint8Array(nacl.secretbox.keyLength),
n = new Uint8Array(nacl.secretbox.nonceLength),
i;
for (i = 0; i < 32; i++) k[i] = i;
for (i = 0; i < 24; i++) n[i] = i;
function check(num, maxNum, next) {
var msg = nacl.randomBytes(num);
var box = nacl.util.encodeBase64(nacl.secretbox(msg, n, k));
csecretbox(new Buffer(msg), n, k, function(boxFromC) {
t.equal(box, boxFromC, 'secretboxes should be equal');
t.notEqual(nacl.secretbox.open(nacl.util.decodeBase64(boxFromC), n, k), false, 'opening should succeed');
if (num >= maxNum) {
if (next) next();
return;
}
check(num+1, maxNum, next);
});
}
check(0, 1024, function() {
check(16418, 16500, function() {
check(1000000, 0, function() {
t.end();
});
});
});
});

View File

@ -1,44 +0,0 @@
var NUMBER_OF_TESTS = 1000;
var nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var execFile = require('child_process').execFile;
var path = require('path');
var test = require('tape');
function cscalarmult(n, p, callback) {
var hexN = (new Buffer(n)).toString('hex');
var hexP = (new Buffer(p)).toString('hex');
execFile(path.resolve(__dirname, 'cscalarmult'), [hexN, hexP], function(err, stdout) {
if (err) throw err;
callback(stdout.toString('utf8'));
});
}
test('nacl.scalarMult (C)', function(t) {
var k1 = {
publicKey: nacl.util.decodeBase64('JRAWWRKVfZS2U/QiV+X2+PaabPfAB4H9p+BZkBN8ji8='),
secretKey: nacl.util.decodeBase64('5g1pBmI3HL5GAjtt3/2FZDQVfGSMNohngN7OVSizBVE=')
};
function check(num) {
var k2 = nacl.box.keyPair();
var q1 = nacl.scalarMult(k1.secretKey, k2.publicKey);
var q2 = nacl.scalarMult(k2.secretKey, k1.publicKey);
t.equal(nacl.util.encodeBase64(q1), nacl.util.encodeBase64(q2),
'scalarMult results should be equal');
hexQ = (new Buffer(q1)).toString('hex');
cscalarmult(k1.secretKey, k2.publicKey, function(cQ) {
t.equal(hexQ, cQ);
if (num >= NUMBER_OF_TESTS) {
t.end();
return;
}
check(num+1);
});
}
check(0);
});

View File

@ -1,51 +0,0 @@
var nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var crypto = require('crypto');
var spawn = require('child_process').spawn;
var path = require('path');
var test = require('tape');
function cbox(msg, sk, pk, n, callback) {
var hexsk = (new Buffer(sk)).toString('hex');
var hexpk = (new Buffer(pk)).toString('hex');
var hexn = (new Buffer(n)).toString('hex');
var p = spawn(path.resolve(__dirname, 'cbox'), [hexsk, hexpk, hexn]);
var result = [];
p.stdout.on('data', function(data) {
result.push(data);
});
p.on('close', function(code) {
return callback(Buffer.concat(result).toString('base64'));
});
p.on('error', function(err) {
throw err;
});
p.stdin.write(msg);
p.stdin.end();
}
test('nacl.box (C)', function(t) {
var k1 = nacl.box.keyPair();
function check(num, maxNum, next) {
var sk2 = nacl.randomBytes(nacl.box.secretKeyLength);
var msg = nacl.randomBytes(num);
var nonce = nacl.randomBytes(24);
var box = nacl.util.encodeBase64(nacl.box(msg, nonce, k1.publicKey, sk2));
cbox(new Buffer(msg), sk2, k1.publicKey, nonce, function(boxFromC) {
t.equal(box, boxFromC, 'boxes should be equal');
t.notEqual(nacl.box.open(nacl.util.decodeBase64(boxFromC), nonce, k1.publicKey, sk2),
false, 'opening box should succeed');
if (num >= maxNum) {
if (next) next();
return;
}
check(num+1, maxNum, next);
});
}
check(0, 1024, function() {
check(16417, 16500, function() {
t.end();
});
});
});

View File

@ -1,39 +0,0 @@
var nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var crypto = require('crypto');
var spawn = require('child_process').spawn;
var path = require('path');
var test = require('tape');
function chash(msg, callback) {
var p = spawn(path.resolve(__dirname, 'chash'));
var result = [];
p.stdout.on('data', function(data) {
result.push(data);
});
p.on('close', function(code) {
return callback(Buffer.concat(result).toString('utf8'));
});
p.on('error', function(err) {
throw err;
});
p.stdin.write(msg);
p.stdin.end();
}
test('nacl.hash (C)', function(t) {
function check(num) {
var msg = nacl.randomBytes(num);
var h = nacl.hash(msg);
var hexH = (new Buffer(h)).toString('hex');
chash(new Buffer(msg), function(hexCH) {
t.equal(hexH, hexCH, 'hashes should be equal');
if (num >= 1000) {
t.end();
return;
}
check(num+1);
});
}
check(0);
});

View File

@ -1,44 +0,0 @@
var nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var crypto = require('crypto');
var spawn = require('child_process').spawn;
var path = require('path');
var test = require('tape');
function csign(sk, msg, callback) {
var hexsk = (new Buffer(sk)).toString('hex');
var p = spawn(path.resolve(__dirname, 'csign'), [hexsk]);
var result = [];
p.stdout.on('data', function(data) {
result.push(data);
});
p.on('close', function(code) {
callback(Buffer.concat(result).toString('base64'));
});
p.on('error', function(err) {
throw err;
});
p.stdin.write(msg);
p.stdin.end();
}
test('nacl.sign (C)', function(t) {
function check(num) {
var keys = nacl.sign.keyPair();
var msg = nacl.randomBytes(num);
var signedMsg = nacl.util.encodeBase64(nacl.sign(msg, keys.secretKey));
csign(keys.secretKey, new Buffer(msg), function(signedFromC) {
t.equal(signedMsg, signedFromC, 'signed messages should be equal');
var openedMsg = nacl.sign.open(nacl.util.decodeBase64(signedFromC), keys.publicKey);
t.notEqual(openedMsg, null, 'open should succeed');
t.equal(nacl.util.encodeBase64(openedMsg), nacl.util.encodeBase64(msg),
'messages should be equal');
if (num >= 100) {
t.end();
return;
}
check(num+1);
});
}
check(0);
});

View File

@ -1,52 +0,0 @@
var nacl = require('../../' + (process.env.NACL_SRC || 'nacl.min.js'));
var crypto = require('crypto');
var spawn = require('child_process').spawn;
var execFile = require('child_process').execFile;
var path = require('path');
var test = require('tape');
function csign(sk, msg, callback) {
var hexsk = (new Buffer(sk)).toString('hex');
var p = spawn(path.resolve(__dirname, 'csign'), [hexsk]);
var result = [];
p.stdout.on('data', function(data) {
result.push(data);
});
p.on('close', function(code) {
callback(Buffer.concat(result).toString('base64'));
});
p.on('error', function(err) {
throw err;
});
p.stdin.write(msg);
p.stdin.end();
}
function csignkeypair(callback) {
execFile(path.resolve(__dirname, 'csign-keypair'), [], function(err, stdout) {
if (err) throw err;
callback(stdout.toString('utf8'));
});
}
test('nacl.sign (C) with keypair from C', function(t) {
function check(num) {
csignkeypair(function(hexSecretKey) {
var secretKey = new Uint8Array(nacl.sign.secretKeyLength);
var b = new Buffer(hexSecretKey, 'hex');
for (var i = 0; i < b.length; i++) secretKey[i] = b[i];
var msg = nacl.randomBytes(num);
var signedMsg = nacl.util.encodeBase64(nacl.sign(msg, secretKey));
csign(secretKey, new Buffer(msg), function(signedFromC) {
t.equal(signedMsg, signedFromC, 'signed messages should be equal');
if (num >= 100) {
t.end();
return;
}
check(num+1);
});
});
}
check(0);
});

View File

@ -1,38 +0,0 @@
CFLAGS=-O3
all: csecretbox cscalarmult cbox chash csign csign-keypair
csecretbox: csecretbox.o tweetnacl.o
cscalarmult: cscalarmult.o tweetnacl.o
cbox: cbox.o tweetnacl.o
chash: chash.o tweetnacl.o
csign: csign.o tweetnacl.o
csign-keypair: csign-keypair.o tweetnacl.o
test: test_secretbox test_scalarmult test_box test_hash test_sign test_signkeypair clean
test_secretbox: csecretbox
node ./00-secretbox.js
test_scalarmult: cscalarmult
node ./01-scalarmult.js
test_box: cbox
node ./02-box.js
test_hash: chash
node ./03-hash.js
test_sign: csign
node ./04-sign.js
test_signkeypair: csign csign-keypair
node ./05-sign-keypair.js
clean:
rm *.o csecretbox cscalarmult cbox chash csign csign-keypair

View File

@ -1,8 +0,0 @@
Tests that call C programs based on original tweetnacl.c,
and compare results with TweetNaCl-js.
Work only in Node.js.
To run, in this directory:
$ make test

View File

@ -1,105 +0,0 @@
/*
* Written by @dchest. Public domain.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tweetnacl.h"
size_t
readmsg(unsigned char **out)
{
unsigned char buf[4096];
size_t n, have = 0;
unsigned char *msg = calloc(1, crypto_box_ZEROBYTES);
if (msg == NULL)
err(1, NULL);
have = crypto_box_ZEROBYTES;
while (!feof(stdin)) {
n = fread(buf, 1, sizeof(buf), stdin);
if (n == 0)
break;
if ((msg = realloc(msg, have + n)) == NULL)
err(1, NULL);
memcpy(&msg[have], buf, n);
have += n;
}
*out = msg;
return have;
}
unsigned char
dechexchar(char c)
{
if (c >= '0' && c <= '9')
return (c - 0x30);
else if (c >= 'A' && c <= 'F')
return (c - 0x37);
else if (c >= 'a' && c <= 'f')
return (c - 0x57);
else
errx(1, "not a hex char");
}
unsigned char *
decodehex(const char *h)
{
unsigned char *r, *p, a, b;
size_t i, len;
len = strlen(h);
if ((r = malloc(len / 2)) == NULL)
err(1, NULL);
p = r;
if (len % 2 != 0)
errx(1, "bad hex string length");
for (i = 0; i < len; i += 2)
*p++ = ((dechexchar(h[i]) * 16) & 0xf0) + (dechexchar(h[i+1]) & 0xf);
return r;
}
void
randombytes(unsigned char *x, long long y)
{
errx(1, "we don't use randombytes");
}
int
main(int argc, const char **argv)
{
unsigned char *sk, *pk, *n, *m, *c;
size_t mlen, clen;
if (argc < 2) {
puts("usage: cbox SK PK NONCE");
return 1;
}
if (strlen(argv[1]) != crypto_box_SECRETKEYBYTES * 2)
errx(1, "bad SK length");
sk = decodehex(argv[1]);
if (strlen(argv[2]) != crypto_box_PUBLICKEYBYTES * 2)
errx(1, "bad PK length");
pk = decodehex(argv[2]);
if (strlen(argv[3]) != crypto_box_NONCEBYTES * 2)
errx(1, "bad NONCE length");
n = decodehex(argv[3]);
mlen = readmsg(&m);
if ((c = calloc(mlen, 1)) == NULL)
err(1, NULL);
crypto_box(c, m, mlen, n, pk, sk);
c += crypto_box_BOXZEROBYTES;
clen = mlen - crypto_box_BOXZEROBYTES;
if (fwrite(c, 1, clen, stdout) != clen)
err(1, NULL);
return 0;
}

View File

@ -1,49 +0,0 @@
/*
* This program sings a message using tweetnacl.c.
* Written by @dchest. Public domain.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tweetnacl.h"
size_t
readmsg(unsigned char **out)
{
unsigned char buf[4096];
unsigned char *msg = NULL;
size_t n, have = 0;
while (!feof(stdin)) {
n = fread(buf, 1, sizeof(buf), stdin);
if (n == 0)
break;
if ((msg = realloc(msg, have + n)) == NULL)
err(1, NULL);
memcpy(&msg[have], buf, n);
have += n;
}
*out = msg;
return have;
}
void
randombytes(unsigned char *x, long long y)
{
errx(1, "we don't use randombytes");
}
int
main(int argc, const char **argv)
{
unsigned char *m, h[crypto_hash_BYTES];
size_t mlen, i;
mlen = readmsg(&m);
crypto_hash(h, m, mlen);
for (i = 0; i < sizeof(h); i++)
printf("%02x", h[i]);
return 0;
}

View File

@ -1,73 +0,0 @@
/*
* Written by @dchest. Public domain.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tweetnacl.h"
unsigned char
dechexchar(char c)
{
if (c >= '0' && c <= '9')
return (c - 0x30);
else if (c >= 'A' && c <= 'F')
return (c - 0x37);
else if (c >= 'a' && c <= 'f')
return (c - 0x57);
else
errx(1, "not a hex char");
}
unsigned char *
decodehex(const char *h)
{
unsigned char *r, *p, a, b;
size_t i, len;
len = strlen(h);
if (len == 0 || len % 2 != 0)
errx(1, "bad hex string length");
if ((r = malloc(len / 2)) == NULL)
err(1, NULL);
p = r;
for (i = 0; i < len; i += 2)
*p++ = ((dechexchar(h[i]) * 16) & 0xf0) + (dechexchar(h[i+1]) & 0xf);
return r;
}
void
randombytes(unsigned char *x, long long y)
{
errx(1, "we don't use randombytes");
}
int
main(int argc, const char **argv)
{
unsigned char *n, *p, q[crypto_scalarmult_BYTES];
size_t i;
if (argc < 2) {
puts("usage: cscalarmult N P");
return 1;
}
if (strlen(argv[1]) != crypto_scalarmult_SCALARBYTES * 2)
errx(1, "bad N length");
n = decodehex(argv[1]);
if (strlen(argv[2]) != crypto_scalarmult_SCALARBYTES * 2)
errx(1, "bad P length");
p = decodehex(argv[2]);
crypto_scalarmult(q, n, p);
for (i = 0; i < sizeof(q); i++)
printf("%02x", q[i]);
return 0;
}

View File

@ -1,108 +0,0 @@
/*
* Written by @dchest. Public domain.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tweetnacl.h"
size_t
readmsg(unsigned char **out)
{
unsigned char buf[4096];
size_t n, have = 0;
unsigned char *msg = calloc(1, crypto_secretbox_ZEROBYTES);
if (msg == NULL)
err(1, NULL);
have = crypto_secretbox_ZEROBYTES;
while (!feof(stdin)) {
n = fread(buf, 1, sizeof(buf), stdin);
if (n == 0)
break;
if ((msg = realloc(msg, have + n)) == NULL)
err(1, NULL);
memcpy(&msg[have], buf, n);
have += n;
}
*out = msg;
return have;
}
unsigned char
dechexchar(char c)
{
if (c >= '0' && c <= '9')
return (c - 0x30);
else if (c >= 'A' && c <= 'F')
return (c - 0x37);
else if (c >= 'a' && c <= 'f')
return (c - 0x57);
else
errx(1, "not a hex char");
}
unsigned char *
decodehex(const char *h)
{
unsigned char *r, *p, a, b;
size_t i, len;
len = strlen(h);
if ((r = malloc(len / 2)) == NULL)
err(1, NULL);
p = r;
if (len % 2 != 0)
errx(1, "bad hex string length");
for (i = 0; i < len; i += 2)
*p++ = ((dechexchar(h[i]) * 16) & 0xf0) + (dechexchar(h[i+1]) & 0xf);
return r;
}
void
randombytes(unsigned char *x, long long y)
{
errx(1, "we don't use randombytes");
}
int
main(int argc, const char **argv)
{
unsigned char *k, *n, *m, *c;
size_t mlen, clen;
if (argc < 2) {
puts("usage: csecretbox KEY NONCE");
return 1;
}
if (strlen(argv[1]) != crypto_secretbox_KEYBYTES * 2)
errx(1, "bad KEY length");
k = decodehex(argv[1]);
if (strlen(argv[2]) != crypto_secretbox_NONCEBYTES * 2)
errx(1, "bad NONCE length");
n = decodehex(argv[2]);
mlen = readmsg(&m);
#if 0
FILE *f = fopen("csecretbox_debug.txt", "w");
fwrite(m, 1, mlen, f);
fclose(f);
#endif
if ((c = calloc(mlen, 1)) == NULL)
err(1, NULL);
crypto_secretbox(c, m, mlen, n, k);
c += crypto_secretbox_BOXZEROBYTES;
clen = mlen - crypto_secretbox_BOXZEROBYTES;
if (fwrite(c, 1, clen, stdout) != clen)
err(1, NULL);
return 0;
}

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