@@ -388,10 +388,14 @@ function readPassphrase(passphrase, minEntropy, callback) {
388388 }
389389}
390390
391- function publicKeyFromId ( id ) {
391+ function keyFromId ( id ) {
392392 return new Uint8Array ( Base58 . decode ( id ) . slice ( 0 , 32 ) ) ;
393393}
394394
395+ function keyPairFromSecret ( secret ) {
396+ return nacl . box . keyPair . fromSecretKey ( keyFromId ( secret ) ) ;
397+ }
398+
395399function validateKey ( key ) {
396400 if ( ! key || ! ( key . length >= 40 && key . length <= 50 )
397401 || ! / ^ (?: [ A - Z a - z 0 - 9 + \/ ] { 4 } ) * (?: [ A - Z a - z 0 - 9 + \/ ] { 2 } = = | [ A - Z a - z 0 - 9 + \/ ] { 3 } = ) ? $ /
@@ -435,7 +439,7 @@ function printId(id) {
435439 }
436440}
437441
438- function saveId ( email , id ) {
442+ function saveId ( email , id , keyPair ) {
439443 var profileDirectory = path . resolve ( home ( ) , '.mlck' ) ;
440444
441445 try {
@@ -446,11 +450,16 @@ function saveId(email, id) {
446450 }
447451 }
448452
449- var profile = {
450- version : '0.1' ,
451- email : email ,
452- id : id
453- } ;
453+ var profile = { version : '0.1' } ;
454+
455+ if ( keyPair ) {
456+ // Store only the secret key. If it's compromised, you have to get a new
457+ // one. No other information is leaked.
458+ profile . secret = miniLockId ( keyPair . secretKey ) ;
459+ } else {
460+ profile . email = email ;
461+ profile . id = id ;
462+ }
454463
455464 fs . writeFileSync ( path . resolve ( profileDirectory , 'profile.json' ) ,
456465 JSON . stringify ( profile ) ) ;
@@ -493,7 +502,7 @@ function makeHeader(ids, senderInfo, fileInfo) {
493502 debug ( "Adding recipient " + id ) ;
494503
495504 var nonce = nacl . randomBytes ( 24 ) ;
496- var publicKey = publicKeyFromId ( id ) ;
505+ var publicKey = keyFromId ( id ) ;
497506
498507 debug ( "Using nonce " + hex ( nonce ) ) ;
499508
@@ -544,7 +553,7 @@ function extractDecryptInfo(header, secretKey) {
544553
545554 decryptInfo . fileInfo = nacl . util . decodeBase64 ( decryptInfo . fileInfo ) ;
546555 decryptInfo . fileInfo = nacl . box . open ( decryptInfo . fileInfo , nonce ,
547- publicKeyFromId ( decryptInfo . senderID ) , secretKey ) ;
556+ keyFromId ( decryptInfo . senderID ) , secretKey ) ;
548557
549558 decryptInfo . fileInfo = JSON . parse (
550559 nacl . util . encodeUTF8 ( decryptInfo . fileInfo )
@@ -618,19 +627,33 @@ function decryptChunk(chunk, decryptor, output, hash) {
618627}
619628
620629function encryptFile ( ids , email , passphrase , file , outputFile , includeSelf ,
621- anonymous , checkId , callback ) {
630+ anonymous , checkId , keyPair , callback ) {
622631 debug ( "Begin file encryption" ) ;
623632
624- if ( anonymous ) {
625- // Generate a random passphrase.
626- email = 'Anonymous' ;
627- passphrase = new Buffer ( nacl . randomBytes ( 32 ) ) . toString ( 'base64' ) ;
628- }
633+ var keyPairFunc = null ;
629634
630- debug ( "Generating key pair with email " + email
631- + " and passphrase " + passphrase ) ;
635+ if ( anonymous || ! keyPair ) {
636+ if ( anonymous ) {
637+ // Generate a random passphrase.
638+ email = 'Anonymous' ;
639+ passphrase = new Buffer ( nacl . randomBytes ( 32 ) ) . toString ( 'base64' ) ;
640+ }
632641
633- getKeyPair ( passphrase , email , function ( keyPair ) {
642+ debug ( "Generating key pair with email " + email
643+ + " and passphrase " + passphrase ) ;
644+
645+ keyPairFunc = function ( callback ) {
646+ getKeyPair ( passphrase , email , callback ) ;
647+ } ;
648+ } else {
649+ keyPairFunc = function ( callback ) {
650+ async ( function ( ) {
651+ callback ( keyPair ) ;
652+ } ) ;
653+ } ;
654+ }
655+
656+ keyPairFunc ( function ( keyPair ) {
634657 debug ( "Our public key is " + hex ( keyPair . publicKey ) ) ;
635658 debug ( "Our secret key is " + hex ( keyPair . secretKey ) ) ;
636659
@@ -821,13 +844,28 @@ function encryptFile(ids, email, passphrase, file, outputFile, includeSelf,
821844 } ) ;
822845}
823846
824- function decryptFile ( email , passphrase , file , outputFile , checkId , callback ) {
847+ function decryptFile ( email , passphrase , file , outputFile , checkId , keyPair ,
848+ callback ) {
825849 debug ( "Begin file decryption" ) ;
826850
827- debug ( "Generating key pair with email " + email
828- + " and passphrase " + passphrase ) ;
851+ var keyPairFunc = null ;
829852
830- getKeyPair ( passphrase , email , function ( keyPair ) {
853+ if ( ! keyPair ) {
854+ debug ( "Generating key pair with email " + email
855+ + " and passphrase " + passphrase ) ;
856+
857+ keyPairFunc = function ( callback ) {
858+ getKeyPair ( passphrase , email , callback ) ;
859+ } ;
860+ } else {
861+ keyPairFunc = function ( callback ) {
862+ async ( function ( ) {
863+ callback ( keyPair ) ;
864+ } ) ;
865+ } ;
866+ }
867+
868+ keyPairFunc ( function ( keyPair ) {
831869 debug ( "Our public key is " + hex ( keyPair . publicKey ) ) ;
832870 debug ( "Our secret key is " + hex ( keyPair . secretKey ) ) ;
833871
@@ -1023,6 +1061,7 @@ function handleIdCommand() {
10231061 var defaultOptions = {
10241062 'passphrase' : null ,
10251063 'save' : false ,
1064+ 'save-key' : false ,
10261065 } ;
10271066
10281067 var shortcuts = {
@@ -1039,12 +1078,17 @@ function handleIdCommand() {
10391078 var passphrase = options . passphrase ;
10401079
10411080 var save = options . save ;
1081+ var saveKey = options [ 'save-key' ] ;
10421082
10431083 if ( email === undefined ) {
10441084 loadProfile ( ) ;
10451085
1046- if ( profile ) {
1047- printId ( profile . id ) ;
1086+ if ( profile && ( profile . id || profile . secret ) ) {
1087+ if ( profile . id ) {
1088+ printId ( profile . id ) ;
1089+ } else {
1090+ printId ( miniLockId ( keyPairFromSecret ( profile . secret ) . publicKey ) ) ;
1091+ }
10481092 } else {
10491093 console . error ( 'No profile data available.' ) ;
10501094 }
@@ -1060,13 +1104,15 @@ function handleIdCommand() {
10601104
10611105 debug ( "Using passphrase " + passphrase ) ;
10621106
1063- generateId ( email , passphrase , function ( error , id ) {
1107+ generateId ( email , passphrase , function ( error , id , keyPair ) {
10641108 if ( error ) {
10651109 logError ( error ) ;
10661110 die ( ) ;
10671111 }
10681112
1069- if ( save ) {
1113+ if ( saveKey ) {
1114+ saveId ( email , id , keyPair ) ;
1115+ } else if ( save ) {
10701116 saveId ( email , id ) ;
10711117 }
10721118
@@ -1118,32 +1164,40 @@ function handleEncryptCommand() {
11181164
11191165 loadProfile ( ) ;
11201166
1121- if ( typeof email !== 'string' && profile ) {
1122- email = profile . email ;
1123- }
1167+ var keyPair = ! anonymous && typeof email !== 'string'
1168+ && profile && profile . secret && keyPairFromSecret ( profile . secret ) ;
11241169
1125- if ( ! anonymous && typeof email !== 'string' ) {
1126- die ( 'Email required.' ) ;
1127- }
1170+ if ( ! keyPair ) {
1171+ if ( typeof email !== 'string' && profile ) {
1172+ email = profile . email ;
1173+ }
1174+
1175+ if ( ! anonymous && typeof email !== 'string' ) {
1176+ die ( 'Email required.' ) ;
1177+ }
11281178
1129- if ( ! anonymous && typeof passphrase !== 'string' && ! process . stdin . isTTY ) {
1130- die ( 'No passphrase given; no terminal available.' ) ;
1179+ if ( ! anonymous && typeof passphrase !== 'string' && ! process . stdin . isTTY ) {
1180+ die ( 'No passphrase given; no terminal available.' ) ;
1181+ }
11311182 }
11321183
1133- var checkId = ! anonymous && profile && email === profile . email && profile . id ;
1184+ var checkId = ! anonymous && ! keyPair && profile && email === profile . email
1185+ && profile . id ;
11341186
1135- readPassphrase ( anonymous ? '' : passphrase , 0 , function ( error , passphrase ) {
1187+ readPassphrase ( anonymous || keyPair ? '' : passphrase , 0 ,
1188+ function ( error , passphrase ) {
11361189 if ( error ) {
11371190 logError ( error ) ;
11381191 die ( ) ;
11391192 }
11401193
1141- if ( ! anonymous ) {
1194+ if ( ! anonymous && ! keyPair ) {
11421195 debug ( "Using passphrase " + passphrase ) ;
11431196 }
11441197
11451198 encryptFile ( ids , email , passphrase , file , outputFile , includeSelf ,
1146- anonymous , checkId , function ( error , keyPair , length , filename ) {
1199+ anonymous , checkId , keyPair ,
1200+ function ( error , keyPair , length , filename ) {
11471201 if ( error ) {
11481202 if ( error === ERR_ID_CHECK_FAILED ) {
11491203 console . error ( 'Incorrect passphrase for ' + email ) ;
@@ -1196,29 +1250,34 @@ function handleDecryptCommand() {
11961250
11971251 loadProfile ( ) ;
11981252
1199- if ( typeof email !== 'string' && profile ) {
1200- email = profile . email ;
1201- }
1253+ var keyPair = typeof email !== 'string'
1254+ && profile && profile . secret && keyPairFromSecret ( profile . secret ) ;
12021255
1203- if ( typeof email !== 'string' ) {
1204- die ( 'Email required.' ) ;
1205- }
1256+ if ( ! keyPair ) {
1257+ if ( typeof email !== 'string' && profile ) {
1258+ email = profile . email ;
1259+ }
12061260
1207- if ( typeof passphrase !== 'string' && ! process . stdin . isTTY ) {
1208- die ( 'No passphrase given; no terminal available.' ) ;
1261+ if ( typeof email !== 'string' ) {
1262+ die ( 'Email required.' ) ;
1263+ }
1264+
1265+ if ( typeof passphrase !== 'string' && ! process . stdin . isTTY ) {
1266+ die ( 'No passphrase given; no terminal available.' ) ;
1267+ }
12091268 }
12101269
1211- var checkId = profile && email === profile . email && profile . id ;
1270+ var checkId = ! keyPair && profile && email === profile . email && profile . id ;
12121271
1213- readPassphrase ( passphrase , 0 , function ( error , passphrase ) {
1272+ readPassphrase ( keyPair ? '' : passphrase , 0 , function ( error , passphrase ) {
12141273 if ( error ) {
12151274 logError ( error ) ;
12161275 die ( ) ;
12171276 }
12181277
12191278 debug ( "Using passphrase " + passphrase ) ;
12201279
1221- decryptFile ( email , passphrase , file , outputFile , checkId ,
1280+ decryptFile ( email , passphrase , file , outputFile , checkId , keyPair ,
12221281 function ( error , keyPair , length , filename , senderId ,
12231282 originalFilename ) {
12241283 if ( error ) {
0 commit comments