kopia lustrzana https://gitlab.com/rysiekpl/libresilient
cli: signed-integrity - started implementing the gen-integrity action (ref. #66)
rodzic
5af7012da7
commit
c0f6f62cfe
|
@ -10,6 +10,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function, converting binary to base64
|
||||||
|
* this need not be extremely fast, since it will only be used on digests
|
||||||
|
*
|
||||||
|
* binary_data - data to convert to base64
|
||||||
|
*/
|
||||||
|
let binToBase64 = (binary_data) => {
|
||||||
|
return btoa(
|
||||||
|
(new Uint8Array(binary_data))
|
||||||
|
.reduce((bin, byte)=>{
|
||||||
|
return bin += String.fromCharCode(byte)
|
||||||
|
}, '')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generate an ECDSA P-384 keypair and export it as a JWK
|
* generate an ECDSA P-384 keypair and export it as a JWK
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#json_web_key
|
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#json_web_key
|
||||||
|
@ -30,7 +46,7 @@ let genKeypair = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* derive a public key from a provided public key file
|
* get a public key from a provided private key file
|
||||||
*
|
*
|
||||||
* keyfile - a path to a file containing the private key
|
* keyfile - a path to a file containing the private key
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +58,7 @@ let getPubkey = async (keyfile) => {
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
var keydata = JSON.parse(await Deno.readTextFile(keyfile));
|
var keydata = JSON.parse(await Deno.readTextFile(keyfile));
|
||||||
// the key can be eitehr in a CryptoKeyPair structure, or directly in CryptoKey structure
|
// the key can be either in a CryptoKeyPair structure, or directly in CryptoKey structure
|
||||||
// standardize!
|
// standardize!
|
||||||
if ("privateKey" in keydata) {
|
if ("privateKey" in keydata) {
|
||||||
keydata = keydata.privateKey
|
keydata = keydata.privateKey
|
||||||
|
@ -54,12 +70,131 @@ let getPubkey = async (keyfile) => {
|
||||||
keydata.key_ops = ['verify']
|
keydata.key_ops = ['verify']
|
||||||
|
|
||||||
// import the key, thus making sure data is valid and makes sense
|
// import the key, thus making sure data is valid and makes sense
|
||||||
let key = await crypto.subtle.importKey("jwk", keydata, {name: 'ECDSA', namedCurve: 'P-384'}, true, ['verify'])
|
let key = await crypto.subtle.importKey(
|
||||||
|
"jwk",
|
||||||
|
keydata,
|
||||||
|
{
|
||||||
|
name: 'ECDSA',
|
||||||
|
namedCurve: 'P-384'
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
['verify']
|
||||||
|
)
|
||||||
|
|
||||||
// export it again
|
// export it again
|
||||||
return JSON.stringify(await crypto.subtle.exportKey("jwk", key))
|
return JSON.stringify(await crypto.subtle.exportKey("jwk", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get integrity digests for a given path
|
||||||
|
*
|
||||||
|
* path - path to a file whose digest is to be generated
|
||||||
|
* algos - array of SubtleCrypto.digest-compatible algorithm names
|
||||||
|
*/
|
||||||
|
let getFileIntegrity = async (path, algos) => {
|
||||||
|
|
||||||
|
var result = []
|
||||||
|
|
||||||
|
// open the file and get some info on it
|
||||||
|
const file = await Deno.open(
|
||||||
|
path,
|
||||||
|
{ read: true }
|
||||||
|
);
|
||||||
|
const fileInfo = await file.stat();
|
||||||
|
|
||||||
|
// are we working with a file?
|
||||||
|
if (fileInfo.isFile) {
|
||||||
|
|
||||||
|
//console.log(`+-- reading: ${path}`)
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
var content = new Uint8Array()
|
||||||
|
var buf = new Uint8Array(1000);
|
||||||
|
|
||||||
|
// read the first batch
|
||||||
|
var numread = file.readSync(buf);
|
||||||
|
|
||||||
|
// read the rest, if there is anything to read
|
||||||
|
while (numread !== null) {
|
||||||
|
//console.log(` +-- read: ${numread}`)
|
||||||
|
//console.log(` +-- length: ${content.length}`)
|
||||||
|
|
||||||
|
// there has to be a better way...
|
||||||
|
var new_content = new Uint8Array(content.length + numread);
|
||||||
|
//console.log(` +-- new length: ${new_content.length}`)
|
||||||
|
new_content.set(content)
|
||||||
|
if (buf.length === numread) {
|
||||||
|
new_content.set(buf, content.length)
|
||||||
|
} else {
|
||||||
|
new_content.set(buf.slice(0, numread), content.length)
|
||||||
|
}
|
||||||
|
content = new_content
|
||||||
|
|
||||||
|
// read some more
|
||||||
|
numread = file.readSync(buf);
|
||||||
|
}
|
||||||
|
//console.log(' +-- done.')
|
||||||
|
|
||||||
|
//console.log('+-- calculating digests')
|
||||||
|
for (const algo of algos) {
|
||||||
|
//console.log(` +-- ${algo}`)
|
||||||
|
var digest = algo.toLowerCase().replace('-', '') + '-' + binToBase64(await crypto.subtle.digest(algo, content))
|
||||||
|
//console.log(digest)
|
||||||
|
result.push(digest)
|
||||||
|
}
|
||||||
|
//console.log(`+-- file done: ${path}`)
|
||||||
|
|
||||||
|
// we are not working with a file
|
||||||
|
} else {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// putting this in a try-catch block as the file
|
||||||
|
// is apparently being auto-closed?
|
||||||
|
// https://issueantenna.com/repo/denoland/deno/issues/15442
|
||||||
|
try {
|
||||||
|
await file.close();
|
||||||
|
} catch (BadResource) {}
|
||||||
|
|
||||||
|
// return the result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generate integrity files for provided paths
|
||||||
|
*
|
||||||
|
* paths - paths to files for which integrity files are to be generated
|
||||||
|
* keyfile - path of the file containing the private key to use
|
||||||
|
* extension - file extension to use when saving integrity files (default: ".integrity")
|
||||||
|
*/
|
||||||
|
let genSignedIntegrity = async (paths, keyfile, extension='.integrity') => {
|
||||||
|
|
||||||
|
// load the key
|
||||||
|
var keydata = JSON.parse(await Deno.readTextFile(keyfile));
|
||||||
|
|
||||||
|
// the key can be either in a CryptoKeyPair structure, or directly in CryptoKey structure
|
||||||
|
// standardize!
|
||||||
|
if ("privateKey" in keydata) {
|
||||||
|
keydata = keydata.privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// import the key, thus making sure data is valid and makes sense
|
||||||
|
let privkey = await crypto.subtle.importKey(
|
||||||
|
"jwk",
|
||||||
|
keydata,
|
||||||
|
{
|
||||||
|
name: 'ECDSA',
|
||||||
|
namedCurve: 'P-384'
|
||||||
|
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
['sign']
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: implement shit
|
||||||
|
}
|
||||||
|
|
||||||
// this never changes
|
// this never changes
|
||||||
const pluginName = "signed-integrity"
|
const pluginName = "signed-integrity"
|
||||||
const pluginDescription = "Fetching signed integrity data and using it to verify content.\nCLI used to generate subresource integrity tokens and save them in integrity files."
|
const pluginDescription = "Fetching signed integrity data and using it to verify content.\nCLI used to generate subresource integrity tokens and save them in integrity files."
|
||||||
|
@ -78,6 +213,25 @@ const pluginActions = {
|
||||||
description: "file containing the private key in JSON Web Key format"
|
description: "file containing the private key in JSON Web Key format"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"gen-integrity": {
|
||||||
|
run: genSignedIntegrity,
|
||||||
|
description: "generate integrity files for given paths",
|
||||||
|
arguments: {
|
||||||
|
'_': {
|
||||||
|
name: "file",
|
||||||
|
description: "paths to generate signed integrity files for"
|
||||||
|
},
|
||||||
|
keyfile: {
|
||||||
|
description: "path to the file containing a private key in JSON Web Key format",
|
||||||
|
string: true
|
||||||
|
},
|
||||||
|
extension: {
|
||||||
|
description: "file extension to use when saving integrity files",
|
||||||
|
default: '.integrity',
|
||||||
|
string: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue