diff --git a/chromeKeePassXC/background/aes.js b/chromeKeePassXC/background/aes.js new file mode 100644 index 0000000..b2eb073 --- /dev/null +++ b/chromeKeePassXC/background/aes.js @@ -0,0 +1,780 @@ +/* + * aes.js: implements AES - Advanced Encryption Standard + * from the SlowAES project, http://code.google.com/p/slowaes/ + * + * Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), + * Mark Percival ( http://mpercival.com ), + * + * Ported from C code written by Laurent Haan ( http://www.progressive-coding.com ) + * + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/ + */ + +var slowAES = { + /* + * START AES SECTION + */ + aes:{ + // structure of valid key sizes + keySize:{ + SIZE_128:16, + SIZE_192:24, + SIZE_256:32 + }, + + // Rijndael S-box + sbox:[ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ], + + // Rijndael Inverted S-box + rsbox: + [ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb + , 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb + , 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e + , 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 + , 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 + , 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 + , 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 + , 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b + , 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 + , 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e + , 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b + , 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 + , 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f + , 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef + , 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 + , 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ], + + /* rotate the word eight bits to the left */ + rotate:function(word) + { + var c = word[0]; + for (var i = 0; i < 3; i++) + word[i] = word[i+1]; + word[3] = c; + + return word; + }, + + // Rijndael Rcon + Rcon:[ + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, + 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, + 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, + 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, + 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, + 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, + 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, + 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, + 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, + 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, + 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, + 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, + 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, + 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, + 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, + 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb ], + + G2X: [ + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, + 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, + 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, + 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, + 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, + 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, + 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, + 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, + 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, + 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1b, 0x19, 0x1f, 0x1d, + 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05, + 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, + 0x23, 0x21, 0x27, 0x25, 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, + 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45, 0x7b, 0x79, 0x7f, 0x7d, + 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65, + 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, + 0x83, 0x81, 0x87, 0x85, 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, + 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 0xdb, 0xd9, 0xdf, 0xdd, + 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, + 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, + 0xe3, 0xe1, 0xe7, 0xe5 + ], + + G3X: [ + 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, + 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, + 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 0x60, 0x63, 0x66, 0x65, + 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, + 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, + 0x44, 0x47, 0x42, 0x41, 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, + 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 0xf0, 0xf3, 0xf6, 0xf5, + 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, + 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, + 0xb4, 0xb7, 0xb2, 0xb1, 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, + 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 0x9b, 0x98, 0x9d, 0x9e, + 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a, + 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, + 0xbf, 0xbc, 0xb9, 0xba, 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, + 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea, 0xcb, 0xc8, 0xcd, 0xce, + 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda, + 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, + 0x4f, 0x4c, 0x49, 0x4a, 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, + 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, 0x3b, 0x38, 0x3d, 0x3e, + 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a, + 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, + 0x1f, 0x1c, 0x19, 0x1a + ], + + G9X: [ + 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, + 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, + 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 0x3b, 0x32, 0x29, 0x20, + 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, + 0xc7, 0xce, 0xd5, 0xdc, 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, + 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, 0xe6, 0xef, 0xf4, 0xfd, + 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91, + 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, + 0x21, 0x28, 0x33, 0x3a, 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, + 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, 0xec, 0xe5, 0xfe, 0xf7, + 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b, + 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, + 0x10, 0x19, 0x02, 0x0b, 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, + 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, 0x47, 0x4e, 0x55, 0x5c, + 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, + 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, + 0xf6, 0xff, 0xe4, 0xed, 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, + 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 0xa1, 0xa8, 0xb3, 0xba, + 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, + 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, + 0x5d, 0x54, 0x4f, 0x46 + ], + + GBX: [ + 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, + 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, + 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, 0x7b, 0x70, 0x6d, 0x66, + 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12, + 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, + 0xbf, 0xb4, 0xa9, 0xa2, 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, + 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f, 0x46, 0x4d, 0x50, 0x5b, + 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f, + 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, + 0xf9, 0xf2, 0xef, 0xe4, 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, + 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54, 0xf7, 0xfc, 0xe1, 0xea, + 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e, + 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, + 0x33, 0x38, 0x25, 0x2e, 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, + 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5, 0x3c, 0x37, 0x2a, 0x21, + 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55, + 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, + 0x75, 0x7e, 0x63, 0x68, 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, + 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 0x7a, 0x71, 0x6c, 0x67, + 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13, + 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, + 0xbe, 0xb5, 0xa8, 0xa3 + ], + + GDX: [ + 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, + 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, + 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, 0xbb, 0xb6, 0xa1, 0xac, + 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0, + 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, + 0x37, 0x3a, 0x2d, 0x20, 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, + 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, 0xbd, 0xb0, 0xa7, 0xaa, + 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6, + 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, + 0x8a, 0x87, 0x90, 0x9d, 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, + 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d, 0xda, 0xd7, 0xc0, 0xcd, + 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91, + 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, + 0x56, 0x5b, 0x4c, 0x41, 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, + 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a, 0xb1, 0xbc, 0xab, 0xa6, + 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa, + 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, + 0xeb, 0xe6, 0xf1, 0xfc, 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, + 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 0x0c, 0x01, 0x16, 0x1b, + 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47, + 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, + 0x80, 0x8d, 0x9a, 0x97 + ], + + GEX: [ + 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, + 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, + 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, 0xdb, 0xd5, 0xc7, 0xc9, + 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81, + 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, + 0x73, 0x7d, 0x6f, 0x61, 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, + 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 0x4d, 0x43, 0x51, 0x5f, + 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17, + 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, + 0x3e, 0x30, 0x22, 0x2c, 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, + 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc, 0x41, 0x4f, 0x5d, 0x53, + 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b, + 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, + 0xe9, 0xe7, 0xf5, 0xfb, 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, + 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0, 0x7a, 0x74, 0x66, 0x68, + 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20, + 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, + 0xa4, 0xaa, 0xb8, 0xb6, 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, + 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, 0x37, 0x39, 0x2b, 0x25, + 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, + 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, + 0x9f, 0x91, 0x83, 0x8d + ], + + // Key Schedule Core + core:function(word,iteration) + { + /* rotate the 32-bit word 8 bits to the left */ + word = this.rotate(word); + /* apply S-Box substitution on all 4 parts of the 32-bit word */ + for (var i = 0; i < 4; ++i) + word[i] = this.sbox[word[i]]; + /* XOR the output of the rcon operation with i to the first part (leftmost) only */ + word[0] = word[0]^this.Rcon[iteration]; + return word; + }, + + /* Rijndael's key expansion + * expands an 128,192,256 key into an 176,208,240 bytes key + * + * expandedKey is a pointer to an char array of large enough size + * key is a pointer to a non-expanded key + */ + expandKey:function(key,size) + { + var expandedKeySize = (16*(this.numberOfRounds(size)+1)); + + /* current expanded keySize, in bytes */ + var currentSize = 0; + var rconIteration = 1; + var t = []; // temporary 4-byte variable + + var expandedKey = []; + for(var i = 0;i < expandedKeySize;i++) + expandedKey[i] = 0; + + /* set the 16,24,32 bytes of the expanded key to the input key */ + for (var j = 0; j < size; j++) + expandedKey[j] = key[j]; + currentSize += size; + + while (currentSize < expandedKeySize) + { + /* assign the previous 4 bytes to the temporary value t */ + for (var k = 0; k < 4; k++) + t[k] = expandedKey[(currentSize - 4) + k]; + + /* every 16,24,32 bytes we apply the core schedule to t + * and increment rconIteration afterwards + */ + if(currentSize % size == 0) + t = this.core(t, rconIteration++); + + /* For 256-bit keys, we add an extra sbox to the calculation */ + if(size == this.keySize.SIZE_256 && ((currentSize % size) == 16)) + for(var l = 0; l < 4; l++) + t[l] = this.sbox[t[l]]; + + /* We XOR t with the four-byte block 16,24,32 bytes before the new expanded key. + * This becomes the next four bytes in the expanded key. + */ + for(var m = 0; m < 4; m++) { + expandedKey[currentSize] = expandedKey[currentSize - size] ^ t[m]; + currentSize++; + } + } + return expandedKey; + }, + + // Adds (XORs) the round key to the state + addRoundKey:function(state,roundKey) + { + for (var i = 0; i < 16; i++) + state[i] ^= roundKey[i]; + return state; + }, + + // Creates a round key from the given expanded key and the + // position within the expanded key. + createRoundKey:function(expandedKey,roundKeyPointer) + { + var roundKey = []; + for (var i = 0; i < 4; i++) + for (var j = 0; j < 4; j++) + roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j]; + return roundKey; + }, + + /* substitute all the values from the state with the value in the SBox + * using the state value as index for the SBox + */ + subBytes:function(state,isInv) + { + for (var i = 0; i < 16; i++) + state[i] = isInv?this.rsbox[state[i]]:this.sbox[state[i]]; + return state; + }, + + /* iterate over the 4 rows and call shiftRow() with that row */ + shiftRows:function(state,isInv) + { + for (var i = 0; i < 4; i++) + state = this.shiftRow(state,i*4, i,isInv); + return state; + }, + + /* each iteration shifts the row to the left by 1 */ + shiftRow:function(state,statePointer,nbr,isInv) + { + for (var i = 0; i < nbr; i++) + { + if(isInv) + { + var tmp = state[statePointer + 3]; + for (var j = 3; j > 0; j--) + state[statePointer + j] = state[statePointer + j-1]; + state[statePointer] = tmp; + } + else + { + var tmp = state[statePointer]; + for (var j = 0; j < 3; j++) + state[statePointer + j] = state[statePointer + j+1]; + state[statePointer + 3] = tmp; + } + } + return state; + }, + + // galois multiplication of 8 bit characters a and b + galois_multiplication:function(a,b) + { + var p = 0; + for(var counter = 0; counter < 8; counter++) + { + if((b & 1) == 1) + p ^= a; + if(p > 0x100) p ^= 0x100; + var hi_bit_set = (a & 0x80); //keep p 8 bit + a <<= 1; + if(a > 0x100) a ^= 0x100; //keep a 8 bit + if(hi_bit_set == 0x80) + a ^= 0x1b; + if(a > 0x100) a ^= 0x100; //keep a 8 bit + b >>= 1; + if(b > 0x100) b ^= 0x100; //keep b 8 bit + } + return p; + }, + + // galois multipication of the 4x4 matrix + mixColumns:function(state,isInv) + { + var column = []; + /* iterate over the 4 columns */ + for (var i = 0; i < 4; i++) + { + /* construct one column by iterating over the 4 rows */ + for (var j = 0; j < 4; j++) + column[j] = state[(j*4)+i]; + /* apply the mixColumn on one column */ + column = this.mixColumn(column,isInv); + /* put the values back into the state */ + for (var k = 0; k < 4; k++) + state[(k*4)+i] = column[k]; + } + return state; + }, + + // galois multipication of 1 column of the 4x4 matrix + mixColumn:function(column,isInv) + { + var mult = []; + if(isInv) + mult = [14,9,13,11]; + else + mult = [2,1,1,3]; + var cpy = []; + for(var i = 0; i < 4; i++) + cpy[i] = column[i]; + + column[0] = this.galois_multiplication(cpy[0],mult[0]) ^ + this.galois_multiplication(cpy[3],mult[1]) ^ + this.galois_multiplication(cpy[2],mult[2]) ^ + this.galois_multiplication(cpy[1],mult[3]); + column[1] = this.galois_multiplication(cpy[1],mult[0]) ^ + this.galois_multiplication(cpy[0],mult[1]) ^ + this.galois_multiplication(cpy[3],mult[2]) ^ + this.galois_multiplication(cpy[2],mult[3]); + column[2] = this.galois_multiplication(cpy[2],mult[0]) ^ + this.galois_multiplication(cpy[1],mult[1]) ^ + this.galois_multiplication(cpy[0],mult[2]) ^ + this.galois_multiplication(cpy[3],mult[3]); + column[3] = this.galois_multiplication(cpy[3],mult[0]) ^ + this.galois_multiplication(cpy[2],mult[1]) ^ + this.galois_multiplication(cpy[1],mult[2]) ^ + this.galois_multiplication(cpy[0],mult[3]); + return column; + }, + + // applies the 4 operations of the forward round in sequence + round:function(state, roundKey) + { + state = this.subBytes(state,false); + state = this.shiftRows(state,false); + state = this.mixColumns(state,false); + state = this.addRoundKey(state, roundKey); + return state; + }, + + // applies the 4 operations of the inverse round in sequence + invRound:function(state,roundKey) + { + state = this.shiftRows(state,true); + state = this.subBytes(state,true); + state = this.addRoundKey(state, roundKey); + state = this.mixColumns(state,true); + return state; + }, + + /* + * Perform the initial operations, the standard round, and the final operations + * of the forward aes, creating a round key for each round + */ + main:function(state,expandedKey,nbrRounds) + { + state = this.addRoundKey(state, this.createRoundKey(expandedKey,0)); + for (var i = 1; i < nbrRounds; i++) + state = this.round(state, this.createRoundKey(expandedKey,16*i)); + state = this.subBytes(state,false); + state = this.shiftRows(state,false); + state = this.addRoundKey(state, this.createRoundKey(expandedKey,16*nbrRounds)); + return state; + }, + + /* + * Perform the initial operations, the standard round, and the final operations + * of the inverse aes, creating a round key for each round + */ + invMain:function(state, expandedKey, nbrRounds) + { + state = this.addRoundKey(state, this.createRoundKey(expandedKey,16*nbrRounds)); + for (var i = nbrRounds-1; i > 0; i--) + state = this.invRound(state, this.createRoundKey(expandedKey,16*i)); + state = this.shiftRows(state,true); + state = this.subBytes(state,true); + state = this.addRoundKey(state, this.createRoundKey(expandedKey,0)); + return state; + }, + + numberOfRounds:function(size) + { + var nbrRounds; + switch (size) /* set the number of rounds */ + { + case this.keySize.SIZE_128: + nbrRounds = 10; + break; + case this.keySize.SIZE_192: + nbrRounds = 12; + break; + case this.keySize.SIZE_256: + nbrRounds = 14; + break; + default: + return null; + break; + } + return nbrRounds; + }, + + // encrypts a 128 bit input block against the given key of size specified + encrypt:function(input,key,size) + { + var output = []; + var block = []; /* the 128 bit block to encode */ + var nbrRounds = this.numberOfRounds(size); + /* Set the block values, for the block: + * a0,0 a0,1 a0,2 a0,3 + * a1,0 a1,1 a1,2 a1,3 + * a2,0 a2,1 a2,2 a2,3 + * a3,0 a3,1 a3,2 a3,3 + * the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 + */ + for (var i = 0; i < 4; i++) /* iterate over the columns */ + for (var j = 0; j < 4; j++) /* iterate over the rows */ + block[(i+(j*4))] = input[(i*4)+j]; + + /* expand the key into an 176, 208, 240 bytes key */ + var expandedKey = this.expandKey(key, size); /* the expanded key */ + /* encrypt the block using the expandedKey */ + block = this.main(block, expandedKey, nbrRounds); + for (var k = 0; k < 4; k++) /* unmap the block again into the output */ + for (var l = 0; l < 4; l++) /* iterate over the rows */ + output[(k*4)+l] = block[(k+(l*4))]; + return output; + }, + + // decrypts a 128 bit input block against the given key of size specified + decrypt:function(input, key, size) + { + var output = []; + var block = []; /* the 128 bit block to decode */ + var nbrRounds = this.numberOfRounds(size); + /* Set the block values, for the block: + * a0,0 a0,1 a0,2 a0,3 + * a1,0 a1,1 a1,2 a1,3 + * a2,0 a2,1 a2,2 a2,3 + * a3,0 a3,1 a3,2 a3,3 + * the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 + */ + for (var i = 0; i < 4; i++) /* iterate over the columns */ + for (var j = 0; j < 4; j++) /* iterate over the rows */ + block[(i+(j*4))] = input[(i*4)+j]; + /* expand the key into an 176, 208, 240 bytes key */ + var expandedKey = this.expandKey(key, size); + /* decrypt the block using the expandedKey */ + block = this.invMain(block, expandedKey, nbrRounds); + for (var k = 0; k < 4; k++)/* unmap the block again into the output */ + for (var l = 0; l < 4; l++)/* iterate over the rows */ + output[(k*4)+l] = block[(k+(l*4))]; + return output; + } + }, + /* + * END AES SECTION + */ + + /* + * START MODE OF OPERATION SECTION + */ + //structure of supported modes of operation + modeOfOperation:{ + OFB:0, + CFB:1, + CBC:2 + }, + + // get the next block of 16 bytes (aes operates on 128bits) + getNextBlock: function(bytesIn,start,end,mode) + { + if(end - start > 16) + end = start + 16; + + return bytesIn.slice(start, end); + }, + + /* + * Mode of Operation Encryption + * bytesIn - Input String as array of bytes + * mode - mode of type modeOfOperation + * key - a number array of length 'size' + * size - the bit length of the key + * iv - the 128 bit number array Initialization Vector + */ + encrypt: function (bytesIn, mode, key, iv) + { + var size = key.length; + if(iv.length%16) + { + throw 'iv length must be 128 bits.'; + } + // the AES input/output + var byteArray = []; + var input = []; + var output = []; + var ciphertext = []; + var cipherOut = []; + // char firstRound + var firstRound = true; + if (mode == this.modeOfOperation.CBC) + this.padBytesIn(bytesIn); + if (bytesIn !== null) + { + for (var j = 0;j < Math.ceil(bytesIn.length/16); j++) + { + var start = j*16; + var end = j*16+16; + if(j*16+16 > bytesIn.length) + end = bytesIn.length; + byteArray = this.getNextBlock(bytesIn,start,end,mode); + if (mode == this.modeOfOperation.CFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (var i = 0; i < 16; i++) + ciphertext[i] = byteArray[i] ^ output[i]; + for(var k = 0;k < end-start;k++) + cipherOut.push(ciphertext[k]); + input = ciphertext; + } + else if (mode == this.modeOfOperation.OFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (var i = 0; i < 16; i++) + ciphertext[i] = byteArray[i] ^ output[i]; + for(var k = 0;k < end-start;k++) + cipherOut.push(ciphertext[k]); + input = output; + } + else if (mode == this.modeOfOperation.CBC) + { + for (var i = 0; i < 16; i++) + input[i] = byteArray[i] ^ ((firstRound) ? iv[i] : ciphertext[i]); + firstRound = false; + ciphertext = this.aes.encrypt(input, key, size); + // always 16 bytes because of the padding for CBC + for(var k = 0;k < 16;k++) + cipherOut.push(ciphertext[k]); + } + } + } + return cipherOut; + }, + + /* + * Mode of Operation Decryption + * cipherIn - Encrypted String as array of bytes + * originalsize - The unencrypted string length - required for CBC + * mode - mode of type modeOfOperation + * key - a number array of length 'size' + * size - the bit length of the key + * iv - the 128 bit number array Initialization Vector + */ + decrypt:function(cipherIn,mode,key,iv) + { + var size = key.length; + if(iv.length%16) + { + throw 'iv length must be 128 bits.'; + } + // the AES input/output + var ciphertext = []; + var input = []; + var output = []; + var byteArray = []; + var bytesOut = []; + // char firstRound + var firstRound = true; + if (cipherIn !== null) + { + for (var j = 0;j < Math.ceil(cipherIn.length/16); j++) + { + var start = j*16; + var end = j*16+16; + if(j*16+16 > cipherIn.length) + end = cipherIn.length; + ciphertext = this.getNextBlock(cipherIn,start,end,mode); + if (mode == this.modeOfOperation.CFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (i = 0; i < 16; i++) + byteArray[i] = output[i] ^ ciphertext[i]; + for(var k = 0;k < end-start;k++) + bytesOut.push(byteArray[k]); + input = ciphertext; + } + else if (mode == this.modeOfOperation.OFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (i = 0; i < 16; i++) + byteArray[i] = output[i] ^ ciphertext[i]; + for(var k = 0;k < end-start;k++) + bytesOut.push(byteArray[k]); + input = output; + } + else if(mode == this.modeOfOperation.CBC) + { + output = this.aes.decrypt(ciphertext, key, size); + for (i = 0; i < 16; i++) + byteArray[i] = ((firstRound) ? iv[i] : input[i]) ^ output[i]; + firstRound = false; + for(var k = 0;k < end-start;k++) + bytesOut.push(byteArray[k]); + input = ciphertext; + } + } + if(mode == this.modeOfOperation.CBC) + this.unpadBytesOut(bytesOut); + } + return bytesOut; + }, + padBytesIn: function(data) { + var len = data.length; + var padByte = 16 - (len % 16); + for (var i = 0; i < padByte; i++) { + data.push(padByte); + } + }, + unpadBytesOut: function(data) { + var padCount = 0; + var padByte = -1; + var blockSize = 16; + for (var i = data.length - 1; i >= data.length-1 - blockSize; i--) { + if (data[i] <= blockSize) { + if (padByte == -1) + padByte = data[i]; + if (data[i] != padByte) { + padCount = 0; + break; + } + padCount++; + } else + break; + if (padCount == padByte) + break; + } + if (padCount > 0) + data.splice(data.length - padCount, padCount); + } + /* + * END MODE OF OPERATION SECTION + */ +}; diff --git a/chromeKeePassXC/background/browserAction.js b/chromeKeePassXC/background/browserAction.js new file mode 100644 index 0000000..d9647f2 --- /dev/null +++ b/chromeKeePassXC/background/browserAction.js @@ -0,0 +1,266 @@ +var browserAction = {}; + +var BLINK_TIMEOUT_DEFAULT = 7500; +var BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT = -1; +var BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT = 1; + +browserAction.show = function(callback, tab) { + var data = {}; + if(!page.tabs[tab.id] || page.tabs[tab.id].stack.length == 0) { + browserAction.showDefault(callback, tab); + return; + } + else { + data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1]; + } + + chrome.browserAction.setIcon({ + tabId: tab.id, + path: "/icons/19x19/" + browserAction.generateIconName(data.iconType, data.icon) + }); + + if(data.popup) { + chrome.browserAction.setPopup({ + tabId: tab.id, + popup: "popups/" + data.popup + }); + } +} + +browserAction.update = function(interval) { + if(!page.tabs[page.currentTabId] || page.tabs[page.currentTabId].stack.length == 0) { + return; + } + + var data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1]; + + if(typeof data.visibleForMilliSeconds != "undefined") { + if(data.visibleForMilliSeconds <= 0) { + browserAction.stackPop(page.currentTabId); + browserAction.show(null, {"id": page.currentTabId}); + page.clearCredentials(page.currentTabId); + return; + } + data.visibleForMilliSeconds -= interval; + } + + if(data.intervalIcon) { + data.intervalIcon.counter += 1; + if(data.intervalIcon.counter < data.intervalIcon.max) { + return; + } + + data.intervalIcon.counter = 0; + data.intervalIcon.index += 1; + + if(data.intervalIcon.index > data.intervalIcon.icons.length - 1) { + data.intervalIcon.index = 0; + } + + chrome.browserAction.setIcon({ + tabId: page.currentTabId, + path: "/icons/19x19/" + browserAction.generateIconName(null, data.intervalIcon.icons[data.intervalIcon.index]) + }); + } +} + +browserAction.showDefault = function(callback, tab) { + var stackData = { + level: 1, + iconType: "normal", + popup: "popup.html" + } + if(!keepass.isConfigured() || keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable || page.tabs[tab.id].errorMessage) { + stackData.iconType = "cross"; + } + + if(page.tabs[tab.id].loginList.length > 0) { + stackData.iconType = "questionmark"; + stackData.popup = "popup_login.html"; + } + + browserAction.stackUnshift(stackData, tab.id); + + browserAction.show(null, tab); +} + +browserAction.stackAdd = function(callback, tab, icon, popup, level, push, visibleForMilliSeconds, visibleForPageUpdates, redirectOffset, dontShow) { + var id = tab.id || page.currentTabId; + + if(!level) { + level = 1; + } + + var stackData = { + "level": level, + "icon": icon + } + + if(popup) { + stackData.popup = popup; + } + + if(visibleForMilliSeconds) { + stackData.visibleForMilliSeconds = visibleForMilliSeconds; + } + + if(visibleForPageUpdates) { + stackData.visibleForPageUpdates = visibleForPageUpdates; + } + + if(redirectOffset) { + stackData.redirectOffset = redirectOffset; + } + + if(push) { + browserAction.stackPush(stackData, id); + } + else { + browserAction.stackUnshift(stackData, id); + } + + if(!dontShow) { + browserAction.show(null, {"id": id}); + } +} + + +browserAction.removeLevelFromStack = function(callback, tab, level, type, dontShow) { + if(!page.tabs[tab.id]) { + return; + } + + if(!type) { + type = "<="; + } + + var newStack = []; + for(var i = 0; i < page.tabs[tab.id].stack.length; i++) { + if( + (type == "<" && page.tabs[tab.id].stack[i].level >= level) || + (type == "<=" && page.tabs[tab.id].stack[i].level > level) || + (type == "=" && page.tabs[tab.id].stack[i].level != level) || + (type == "==" && page.tabs[tab.id].stack[i].level != level) || + (type == "!=" && page.tabs[tab.id].stack[i].level == level) || + (type == ">" && page.tabs[tab.id].stack[i].level <= level) || + (type == ">=" && page.tabs[tab.id].stack[i].level < level) + ) { + newStack.push(page.tabs[tab.id].stack[i]); + } + } + + page.tabs[tab.id].stack = newStack; + + if(!dontShow) { + browserAction.show(callback, tab); + } +} + +browserAction.stackPop = function(tabId) { + var id = tabId || page.currentTabId; + + page.tabs[id].stack.pop(); +}; + +browserAction.stackPush = function(data, tabId) { + var id = tabId || page.currentTabId; + + browserAction.removeLevelFromStack(null, {"id": id}, data.level, "<=", true); + page.tabs[id].stack.push(data); +}; + +browserAction.stackUnshift = function(data, tabId) { + var id = tabId || page.currentTabId; + + browserAction.removeLevelFromStack(null, {"id": id}, data.level, "<=", true); + page.tabs[id].stack.unshift(data); +}; + + +browserAction.removeRememberPopup = function(callback, tab, removeImmediately) { + if(!page.tabs[tab.id]) { + return; + } + + if(page.tabs[tab.id].stack.length == 0) { + page.clearCredentials(tab.id); + return; + } + var data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1]; + + if(removeImmediately || !isNaN(data.visibleForPageUpdates)) { + var currentMS = Date.now(); + if( removeImmediately || (data.visibleForPageUpdates <= 0 && data.redirectOffset > 0)) { + browserAction.stackPop(tab.id); + browserAction.show(null, {"id": tab.id}); + page.clearCredentials(tab.id); + return; + } + else if (!isNaN(data.visibleForPageUpdates) && data.redirectOffset > 0 && currentMS >= data.redirectOffset) { + data.visibleForPageUpdates = data.visibleForPageUpdates - 1; + } + } +}; + +browserAction.setRememberPopup = function(tabId, username, password, url, usernameExists, credentialsList) { + var settings = typeof(localStorage.settings)=='undefined' ? {} : JSON.parse(localStorage.settings); + + var id = tabId || page.currentTabId; + + var timeoutMinMillis = parseInt(getValueOrDefault(settings, "blinkMinTimeout", BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT, 0)) ; + if (timeoutMinMillis > 0) { + timeoutMinMillis += Date.now(); + } + var blinkTimeout = getValueOrDefault(settings, "blinkTimeout", BLINK_TIMEOUT_DEFAULT, 0); + var pageUpdateAllowance = getValueOrDefault(settings, "allowedRedirect", BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT, 0); + + var stackData = { + visibleForMilliSeconds: blinkTimeout, + visibleForPageUpdates: pageUpdateAllowance, + redirectOffset: timeoutMinMillis, + level: 10, + intervalIcon: { + index: 0, + counter: 0, + max: 2, + icons: ["icon_remember_red_background_19x19.png", "icon_remember_red_lock_19x19.png"] + }, + icon: "icon_remember_red_background_19x19.png", + popup: "popup_remember.html" + } + + browserAction.stackPush(stackData, id); + + page.tabs[id].credentials = { + "username": username, + "password": password, + "url": url, + "usernameExists": usernameExists, + "list": credentialsList + }; + + browserAction.show(null, {"id": id}); +} + +function getValueOrDefault(settings, key, defaultVal, min) { + try { + var val = settings[key]; + if (isNaN(val) || val < min) { + val = defaultVal; + } + return val; + } catch(e) { return defaultVal; } +} + +browserAction.generateIconName = function(iconType, icon) { + if(icon) { + return icon; + } + + var name = "icon_"; + name += (keepass.keePassXCUpdateAvailable()) ? "new_" : ""; + name += (!iconType || iconType == "normal") ? "normal" : iconType; + name += "_19x19.png"; + + return name; +} \ No newline at end of file diff --git a/chromeKeePassXC/background/cryptoHelpers.js b/chromeKeePassXC/background/cryptoHelpers.js new file mode 100644 index 0000000..ebcfac4 --- /dev/null +++ b/chromeKeePassXC/background/cryptoHelpers.js @@ -0,0 +1,181 @@ +/* + * cryptoHelpers.js: implements AES - Advanced Encryption Standard + * from the SlowAES project, http://code.google.com/p/slowaes/ + * + * Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), + * Mark Percival ( http://mpercival.com ), + * Johan Sundstrom ( http://ecmanaut.blogspot.com ), + * John Resig ( http://ejohn.org ) + * + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/ + */ + +var cryptoHelpers = { + // encodes a unicode string to UTF8 (8 bit characters are critical to AES functioning properly) + encode_utf8:function(s) + { + try{return unescape(encodeURIComponent(s));} + catch(e){throw 'error during utf8 encoding: cryptoHelpers.encode_utf8.';} + }, + + // decodes a UTF8 string back to unicode + decode_utf8:function(s) + { + try{return decodeURIComponent(escape(s));} + catch(e){throw('error during utf8 decoding: cryptoHelpers.decode_utf8.');} + }, + + //convert a number array to a hex string + toHex:function() + { + var array = []; + if(arguments.length == 1 && arguments[0].constructor == Array) + array = arguments[0]; + else + array = arguments; + var ret = ''; + for(var i = 0;i < array.length;i++) + ret += (array[i] < 16 ? '0' : '') + array[i].toString(16); + return ret.toLowerCase(); + }, + + //convert a hex string to a number array + toNumbers:function(s) + { + var ret = []; + s.replace(/(..)/g,function(s){ + ret.push(parseInt(s,16)); + }); + return ret; + }, + + // get a random number in the range [min,max] + getRandom:function(min,max) + { + if(min === null) + min = 0; + if(max === null) + max = 1; + return Math.floor(Math.random()*(max+1)) + min; + }, + + generateSharedKey:function(len) + { + if(len === null) + len = 16; + var key = []; + for(var i = 0; i < len*2; i++) + key.push(this.getRandom(0,255)); + return key; + }, + + generatePrivateKey:function(s,size) + { + var sha = jsHash.sha2.arr_sha256(s); + return sha.slice(0,size); + }, + + convertStringToByteArray: function(s) + { + var byteArray = []; + for(var i = 0;i < s.length;i++) + { + byteArray.push(s.charCodeAt(i)); + } + return byteArray; + }, + + convertByteArrayToString: function(byteArray) + { + var s = ''; + for(var i = 0;i < byteArray.length;i++) + { + s += String.fromCharCode(byteArray[i]) + } + return s; + }, + + base64: { + // Takes a Nx16x1 byte array and converts it to Base64 + chars: [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + '=', // for decoding purposes + ], + + encode_line: function(flatArr){ + var b64 = ''; + + for (var i = 0; i < flatArr.length; i += 3){ + b64 += this.chars[flatArr[i] >> 2]; + b64 += this.chars[((flatArr[i] & 3) << 4) | (flatArr[i + 1] >> 4)]; + if (!(flatArr[i + 1] == null)){ + b64 += this.chars[((flatArr[i + 1] & 15) << 2) | (flatArr[i + 2] >> 6)]; + }else{ + b64 += '='; + } + if (!(flatArr[i + 2] == null)){ + b64 += this.chars[flatArr[i + 2] & 63]; + }else{ + b64 += '='; + } + } + return b64; + }, + + encode: function(flatArr) + { + var b64 = this.encode_line(flatArr); + // OpenSSL is super particular about line breaks + var broken_b64 = b64.slice(0, 64) + '\n'; + for (var i = 1; i < (Math.ceil(b64.length / 64)); i++) + { + broken_b64 += b64.slice(i * 64, i * 64 + 64) + (Math.ceil(b64.length / 64) == i + 1 ? '': '\n'); + } + return broken_b64; + }, + + decode: function(string) + { + string = string.replace(/[\r\n\t ]+/g, '') + '===='; // drop all whitespaces and pad with '=' (end of b64 marker) + var flatArr = []; + var c = []; + var b = []; + for (var i = 0; ; i = i + 4){ + c[0] = this.chars.indexOf(string.charAt(i)); + if(c[0] == 64){ + return flatArr; + } + c[1] = this.chars.indexOf(string.charAt(i + 1)); + c[2] = this.chars.indexOf(string.charAt(i + 2)); + c[3] = this.chars.indexOf(string.charAt(i + 3)); + + if( + (c[0] < 0) || // char1 is wrong + (c[1] < 0) || (c[1] == 64) || // char2 is wrong + (c[2] < 0) || // char3 is neither an valid char nor '=' + (c[3] < 0) // char4 is neither an valid char nor '=' + ){ + throw 'error during base64 decoding at pos '+i+': cryptoHelpers.base64.decode.'; + } + + flatArr.push((c[0] << 2) | (c[1] >> 4)); + if(c[2] >= 0 && c[2] < 64){ + flatArr.push(((c[1] & 15) << 4) | (c[2] >> 2)); + if(c[3] >= 0 && c[2] < 64){ + flatArr.push(((c[2] & 3) << 6) | c[3]); + } + } + } + }, + + }, + +}; diff --git a/chromeKeePassXC/background/event.js b/chromeKeePassXC/background/event.js new file mode 100644 index 0000000..2817d1e --- /dev/null +++ b/chromeKeePassXC/background/event.js @@ -0,0 +1,245 @@ +var event = {}; + + +event.onMessage = function(request, sender, callback) { + if (request.action in event.messageHandlers) { + //console.log("onMessage(" + request.action + ") for #" + sender.tab.id); + + if(!sender.hasOwnProperty('tab') || sender.tab.id < 1) { + sender.tab = {}; + sender.tab.id = page.currentTabId; + } + + event.invoke(event.messageHandlers[request.action], callback, sender.tab.id, request.args); + + // onMessage closes channel for callback automatically + // if this method does not return true + if(callback) { + return true; + } + } +} + +/** + * Get interesting information about the given tab. + * Function adapted from AdBlock-Plus. + * + * @param {function} handler to call after invoke + * @param {function} callback to call after handler or null + * @param {integer} senderTabId + * @param {array} args + * @param {bool} secondTime + * @returns null (asynchronous) + */ +event.invoke = function(handler, callback, senderTabId, args, secondTime) { + if(senderTabId < 1) { + return; + } + + if(!page.tabs[senderTabId]) { + page.createTabEntry(senderTabId); + } + + // remove information from no longer existing tabs + page.removePageInformationFromNotExistingTabs(); + + chrome.tabs.get(senderTabId, function(tab) { + //chrome.tabs.query({"active": true, "windowId": chrome.windows.WINDOW_ID_CURRENT}, function(tabs) { + //if (tabs.length === 0) + // return; // For example: only the background devtools or a popup are opened + //var tab = tabs[0]; + + if(!tab) { + return; + } + + if (!tab.url) { + // Issue 6877: tab URL is not set directly after you opened a window + // using window.open() + if (!secondTime) { + window.setTimeout(function() { + event.invoke(handler, callback, senderTabId, args, true); + }, 250); + } + return; + } + + if(!page.tabs[tab.id]) { + page.createTabEntry(tab.id); + } + + args = args || []; + + args.unshift(tab); + args.unshift(callback); + + if(handler) { + handler.apply(this, args); + } + else { + console.log("undefined handler for tab " + tab.id); + } + }); +} + + +event.onShowAlert = function(callback, tab, message) { + if( page.settings.supressAlerts ){ console.log(message); } + else { alert(message); } +} + +event.onLoadSettings = function(callback, tab) { + page.settings = (typeof(localStorage.settings) == 'undefined') ? {} : JSON.parse(localStorage.settings); +} + +event.onLoadKeyRing = function(callback, tab) { + keepass.keyRing = (typeof(localStorage.keyRing) == 'undefined') ? {} : JSON.parse(localStorage.keyRing); + if(keepass.isAssociated() && !keepass.keyRing[keepass.associated.hash]) { + keepass.associated = { + "value": false, + "hash": null + }; + } +} + +event.onGetSettings = function(callback, tab) { + event.onLoadSettings(); + callback({ data: page.settings }); +} + +event.onSaveSettings = function(callback, tab, settings) { + localStorage.settings = JSON.stringify(settings); + event.onLoadSettings(); +} + +event.onGetStatus = function(callback, tab) { + keepass.testAssociation(tab); + + var configured = keepass.isConfigured(); + var keyId = null; + if (configured) { + keyId = keepass.keyRing[keepass.databaseHash].id; + } + + browserAction.showDefault(null, tab); + + callback({ + identifier: keyId, + configured: configured, + databaseClosed: keepass.isDatabaseClosed, + keePassXCAvailable: keepass.isKeePassXCAvailable, + encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized, + associated: keepass.isAssociated(), + error: page.tabs[tab.id].errorMessage + }); +} + +event.onPopStack = function(callback, tab) { + browserAction.stackPop(tab.id); + browserAction.show(null, tab); +} + +event.onGetTabInformation = function(callback, tab) { + var id = tab.id || page.currentTabId; + + callback(page.tabs[id]); +} + +event.onGetConnectedDatabase = function(callback, tab) { + callback({ + "count": Object.keys(keepass.keyRing).length, + "identifier": (keepass.keyRing[keepass.associated.hash]) ? keepass.keyRing[keepass.associated.hash].id : null + }); +} + +event.onGetKeePassXCVersions = function(callback, tab) { + if(keepass.currentKeePassXC.version == 0) { + keepass.getDatabaseHash(tab); + } + callback({"current": keepass.currentKeePassXC.version, "latest": keepass.latestKeePassXC.version}); +} + +event.onCheckUpdateKeePassXC = function(callback, tab) { + keepass.checkForNewKeePassXCVersion(); + callback({"current": keepass.currentKeePassXC.version, "latest": keepass.latestKeePassXC.version}); +} + +event.onUpdateAvailableKeePassXC = function(callback, tab) { + callback(keepass.keePassXCUpdateAvailable()); +} + +event.onRemoveCredentialsFromTabInformation = function(callback, tab) { + var id = tab.id || page.currentTabId; + + page.clearCredentials(id); +} + +event.onSetRememberPopup = function(callback, tab, username, password, url, usernameExists, credentialsList) { + browserAction.setRememberPopup(tab.id, username, password, url, usernameExists, credentialsList); +} + +event.onLoginPopup = function(callback, tab, logins) { + var stackData = { + level: 1, + iconType: "questionmark", + popup: "popup_login.html" + } + browserAction.stackUnshift(stackData, tab.id); + + page.tabs[tab.id].loginList = logins; + + browserAction.show(null, tab); +} + +event.onHTTPAuthPopup = function(callback, tab, data) { + var stackData = { + level: 1, + iconType: "questionmark", + popup: "popup_httpauth.html" + } + browserAction.stackUnshift(stackData, tab.id); + + page.tabs[tab.id].loginList = data; + + browserAction.show(null, tab); +} + +event.onMultipleFieldsPopup = function(callback, tab) { + var stackData = { + level: 1, + iconType: "normal", + popup: "popup_multiple-fields.html" + } + browserAction.stackUnshift(stackData, tab.id); + + browserAction.show(null, tab); +} + + +// all methods named in this object have to be declared BEFORE this! +event.messageHandlers = { + 'add_credentials': keepass.addCredentials, + 'alert': event.onShowAlert, + 'associate': keepass.associate, + 'check_update_keepassxc': event.onCheckUpdateKeePassXC, + 'get_connected_database': event.onGetConnectedDatabase, + 'get_keepassxc_versions': event.onGetKeePassXCVersions, + 'get_settings': event.onGetSettings, + 'get_status': event.onGetStatus, + 'get_tab_information': event.onGetTabInformation, + 'load_keyring': event.onLoadKeyRing, + 'load_settings': event.onLoadSettings, + 'pop_stack': event.onPopStack, + 'popup_login': event.onLoginPopup, + 'popup_multiple-fields': event.onMultipleFieldsPopup, + 'remove_credentials_from_tab_information': event.onRemoveCredentialsFromTabInformation, + 'retrieve_credentials': keepass.retrieveCredentials, + 'show_default_browseraction': browserAction.showDefault, + 'update_credentials': keepass.updateCredentials, + 'save_settings': event.onSaveSettings, + 'set_remember_credentials': event.onSetRememberPopup, + 'stack_add': browserAction.stackAdd, + 'update_available_keepassxc': event.onUpdateAvailableKeePassXC, + 'generate_password': keepass.generatePassword, + 'copy_password': keepass.copyPassword +}; \ No newline at end of file diff --git a/chromeKeePassXC/background/httpauth.js b/chromeKeePassXC/background/httpauth.js new file mode 100644 index 0000000..762f8c9 --- /dev/null +++ b/chromeKeePassXC/background/httpauth.js @@ -0,0 +1,65 @@ +var httpAuth = httpAuth || {}; + +httpAuth.pendingCallbacks = []; +httpAuth.requestId = ""; +httpAuth.callback = null; +httpAuth.tabId = 0; +httpAuth.url = null; +httpAuth.isProxy = false; +httpAuth.proxyUrl = null; + + +httpAuth.handleRequest = function (details, callback) { + if(httpAuth.requestId == details.requestId || !page.tabs[details.tabId]) { + callback({}); + } + else { + httpAuth.requestId = details.requestId; + httpAuth.pendingCallbacks.push(callback); + httpAuth.processPendingCallbacks(details); + } +} + +httpAuth.processPendingCallbacks = function(details) { + httpAuth.callback = httpAuth.pendingCallbacks.pop(); + httpAuth.tabId = details.tabId; + httpAuth.url = details.url; + httpAuth.isProxy = details.isProxy; + + if(details.challenger){ + httpAuth.proxyUrl = details.challenger.host; + } + + // WORKAROUND: second parameter should be tab, but is an own object with tab-id + // but in background.js only tab.id is used. To get tabs we could use + // chrome.tabs.get(tabId, callback) <-- but what should callback be? + + var url = (httpAuth.isProxy && httpAuth.proxyUrl) ? httpAuth.proxyUrl : httpAuth.url; + + keepass.retrieveCredentials(httpAuth.loginOrShowCredentials, { "id" : details.tabId }, url, url, true); +} + +httpAuth.loginOrShowCredentials = function(logins) { + // at least one login found --> use first to login + if (logins.length > 0) { + var url = (httpAuth.isProxy && httpAuth.proxyUrl) ? httpAuth.proxyUrl : httpAuth.url; + event.onHTTPAuthPopup(null, {"id": httpAuth.tabId}, {"logins": logins, "url": url}); + //generate popup-list for HTTP Auth usernames + descriptions + + if(page.settings.autoFillAndSend) { + httpAuth.callback({ + authCredentials: { + username: logins[0].Login, + password: logins[0].Password + } + }); + } + else { + httpAuth.callback({}); + } + } + // no logins found + else { + httpAuth.callback({}); + } +} diff --git a/chromeKeePassXC/background/init.js b/chromeKeePassXC/background/init.js new file mode 100644 index 0000000..8cb456e --- /dev/null +++ b/chromeKeePassXC/background/init.js @@ -0,0 +1,152 @@ +// since version 2.0 the extension is using a keyRing instead of a single key-name-pair +keepass.convertKeyToKeyRing(); +// load settings +page.initSettings(); +// create tab information structure for every opened tab +page.initOpenedTabs(); +// initial connection with KeePassXC +keepass.connectToNative(); +keepass.getDatabaseHash(null); +// set initial tab-ID +chrome.tabs.query({"active": true, "windowId": chrome.windows.WINDOW_ID_CURRENT}, function(tabs) { + if (tabs.length === 0) + return; // For example: only the background devtools or a popup are opened + page.currentTabId = tabs[0].id; +}); +// Milliseconds for intervall (e.g. to update browserAction) +var _interval = 250; + + +/** + * Generate information structure for created tab and invoke all needed + * functions if tab is created in foreground + * @param {object} tab + */ +chrome.tabs.onCreated.addListener(function(tab) { + if(tab.id > 0) { + //console.log("chrome.tabs.onCreated(" + tab.id+ ")"); + if(tab.selected) { + page.currentTabId = tab.id; + event.invoke(page.switchTab, null, tab.id, []); + } + } +}); + +/** + * Remove information structure of closed tab for freeing memory + * @param {integer} tabId + * @param {object} removeInfo + */ +chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) { + delete page.tabs[tabId]; + if(page.currentTabId == tabId) { + page.currentTabId = -1; + } +}); + +/** + * Remove stored credentials on switching tabs. + * Invoke functions to retrieve credentials for focused tab + * @param {object} activeInfo + */ +chrome.tabs.onActivated.addListener(function(activeInfo) { + // remove possible credentials from old tab information + page.clearCredentials(page.currentTabId, true); + browserAction.removeRememberPopup(null, {"id": page.currentTabId}, true); + + chrome.tabs.get(activeInfo.tabId, function(info) { + //console.log(info.id + ": " + info.url); + if(info && info.id) { + page.currentTabId = info.id; + if(info.status == "complete") { + //console.log("event.invoke(page.switchTab, null, "+info.id + ", []);"); + event.invoke(page.switchTab, null, info.id, []); + } + } + }); +}); + +/** + * Update browserAction on every update of the page + * @param {integer} tabId + * @param {object} changeInfo + */ +chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if(changeInfo.status == "complete") { + event.invoke(browserAction.removeRememberPopup, null, tabId, []); + } +}); + + +/** + * Retrieve Credentials and try auto-login for HTTPAuth requests + */ +chrome.webRequest.onAuthRequired.addListener(httpAuth.handleRequest, + { urls: [""] }, ["asyncBlocking"] +); + +/** + * Interaction between background-script and front-script + */ +chrome.extension.onMessage.addListener(event.onMessage); + + +/** + * Add context menu entry for filling in username + password + */ +chrome.contextMenus.create({ + "title": "Fill &User + Pass", + "contexts": [ "editable" ], + "onclick": function(info, tab) { + chrome.tabs.sendMessage(tab.id, { + action: "fill_user_pass" + }); + } +}); + +/** + * Add context menu entry for filling in only password which matches for given username + */ +chrome.contextMenus.create({ + "title": "Fill &Pass Only", + "contexts": [ "editable" ], + "onclick": function(info, tab) { + chrome.tabs.sendMessage(tab.id, { + action: "fill_pass_only" + }); + } +}); + +/** + * Add context menu entry for creating icon for generate-password dialog + */ +chrome.contextMenus.create({ + "title": "Show Password &Generator Icons", + "contexts": [ "editable" ], + "onclick": function(info, tab) { + chrome.tabs.sendMessage(tab.id, { + action: "activate_password_generator" + }); + } +}); + +/** + * Add context menu entry for creating icon for generate-password dialog + */ +chrome.contextMenus.create({ + "title": "&Save credentials", + "contexts": [ "editable" ], + "onclick": function(info, tab) { + chrome.tabs.sendMessage(tab.id, { + action: "remember_credentials" + }); + } +}); + + +/** + * Interval which updates the browserAction (e.g. blinking icon) + */ +window.setInterval(function() { + browserAction.update(_interval); +}, _interval); \ No newline at end of file diff --git a/chromeKeePassXC/background/keepass original.js b/chromeKeePassXC/background/keepass original.js new file mode 100644 index 0000000..12cafdf --- /dev/null +++ b/chromeKeePassXC/background/keepass original.js @@ -0,0 +1,632 @@ +var keepass = {}; + +keepass.associated = {"value": false, "hash": null}; +keepass.isDatabaseClosed = false; +keepass.isKeePassHttpAvailable = false; +keepass.isEncryptionKeyUnrecognized = false; +keepass.currentKeePassHttp = {"version": 0, "versionParsed": 0}; +keepass.latestKeePassHttp = (typeof(localStorage.latestKeePassHttp) == 'undefined') ? {"version": 0, "versionParsed": 0, "lastChecked": null} : JSON.parse(localStorage.latestKeePassHttp); +keepass.keySize = 8; // wtf? stupid cryptoHelpers +keepass.pluginUrlDefault = "http://localhost:19455/"; +keepass.latestVersionUrl = "https://passifox.appspot.com/kph/latest-version.txt"; +keepass.cacheTimeout = 30 * 1000; // milliseconds +keepass.databaseHash = "no-hash"; //no-hash = keepasshttp is too old and does not return a hash value +keepass.keyRing = (typeof(localStorage.keyRing) == 'undefined') ? {} : JSON.parse(localStorage.keyRing); +keepass.keyId = "chromeipass-cryptokey-name"; +keepass.keyBody = "chromeipass-key"; +keepass.to_s = cryptoHelpers.convertByteArrayToString; +keepass.to_b = cryptoHelpers.convertStringToByteArray; + + +keepass.addCredentials = function(callback, tab, username, password, url) { + keepass.updateCredentials(callback, tab, null, username, password, url); +} + +keepass.updateCredentials = function(callback, tab, entryId, username, password, url) { + page.debug("keepass.updateCredentials(callback, {1}, {2}, {3}, [password], {4})", tab.id, entryId, username, url); + + // unset error message + page.tabs[tab.id].errorMessage = null; + + // is browser associated to keepass? + if(!keepass.testAssociation(tab)) { + browserAction.showDefault(null, tab); + callback("error"); + return; + } + + // build request + var request = { + RequestType: "set-login" + }; + var verifier = keepass.setVerifier(request); + var id = verifier[0]; + var key = verifier[1]; + var iv = request.Nonce; + + + request.Login = keepass.encrypt(cryptoHelpers.encode_utf8(username), key, iv); + + request.Password = keepass.encrypt(cryptoHelpers.encode_utf8(password), key, iv); + request.Url = keepass.encrypt(url, key, iv); + request.SubmitUrl = keepass.encrypt(url, key, iv); + + if(entryId) { + request.Uuid = keepass.encrypt(entryId, key, iv); + } + + // send request + var result = keepass.send(request); + var status = result[0]; + var response = result[1]; + + // verify response + var code = "error"; + if(keepass.checkStatus(status, tab)) { + var r = JSON.parse(response); + if (keepass.verifyResponse(r, key, id)) { + code = "success"; + } + else { + code = "error"; + } + } + + callback(code); +} + +keepass.retrieveCredentials = function (callback, tab, url, submiturl, forceCallback, triggerUnlock) { + page.debug("keepass.retrieveCredentials(callback, {1}, {2}, {3}, {4})", tab.id, url, submiturl, forceCallback); + + // unset error message + page.tabs[tab.id].errorMessage = null; + + // is browser associated to keepass? + if(!keepass.testAssociation(tab, triggerUnlock)) { + browserAction.showDefault(null, tab); + if(forceCallback) { + callback([]); + } + return; + } + + // build request + var request = { + "RequestType": "get-logins", + "SortSelection": "true", + "TriggerUnlock": (triggerUnlock === true) ? "true" : "false" + }; + var verifier = keepass.setVerifier(request); + var id = verifier[0]; + var key = verifier[1]; + var iv = request.Nonce; + request.Url = keepass.encrypt(url, key, iv); + + if(submiturl) { + request.SubmitUrl = keepass.encrypt(submiturl, key, iv); + } + + // send request + var result = keepass.send(request); + var status = result[0]; + var response = result[1]; + var entries = []; + + // verify response + if(keepass.checkStatus(status, tab)) { + var r = JSON.parse(response); + + keepass.setCurrentKeePassHttpVersion(r.Version); + + if (keepass.verifyResponse(r, key, id)) { + var rIv = r.Nonce; + for (var i = 0; i < r.Entries.length; i++) { + keepass.decryptEntry(r.Entries[i], key, rIv); + } + entries = r.Entries; + keepass.updateLastUsed(keepass.databaseHash); + if(entries.length == 0) { + //questionmark-icon is not triggered, so we have to trigger for the normal symbol + browserAction.showDefault(null, tab); + } + } + else { + console.log("RetrieveCredentials for " + url + " rejected"); + } + } + else { + browserAction.showDefault(null, tab); + } + + page.debug("keepass.retrieveCredentials() => entries.length = {1}", entries.length); + + callback(entries); +} + +keepass.generatePassword = function (callback, tab, forceCallback) { + // is browser associated to keepass? + if(!keepass.testAssociation(tab)) { + browserAction.showDefault(null, tab); + if(forceCallback) { + callback([]); + } + return; + } + + if(keepass.currentKeePassHttp.versionParsed < 1400) { + callback([]); + return; + } + + // build request + var request = { + RequestType: "generate-password" + }; + var verifier = keepass.setVerifier(request); + var id = verifier[0]; + var key = verifier[1]; + + // send request + var result = keepass.send(request); + var status = result[0]; + var response = result[1]; + var passwords = []; + + // verify response + if(keepass.checkStatus(status, tab)) { + var r = JSON.parse(response); + + keepass.setCurrentKeePassHttpVersion(r.Version); + + if (keepass.verifyResponse(r, key, id)) { + var rIv = r.Nonce; + + if(r.Entries) { + for (var i = 0; i < r.Entries.length; i++) { + keepass.decryptEntry(r.Entries[i], key, rIv); + } + passwords = r.Entries; + keepass.updateLastUsed(keepass.databaseHash); + } + else { + console.log("No entries returned. Is KeePassHttp up-to-date?"); + } + } + else { + console.log("GeneratePassword rejected"); + } + } + else { + browserAction.showDefault(null, tab); + } + + callback(passwords); +} + +keepass.copyPassword = function(callback, tab, password) { + var bg = chrome.extension.getBackgroundPage(); + var c2c = bg.document.getElementById("copy2clipboard"); + if(!c2c) { + var input = document.createElement('input'); + input.type = "text"; + input.id = "copy2clipboard"; + bg.document.getElementsByTagName('body')[0].appendChild(input); + c2c = bg.document.getElementById("copy2clipboard"); + } + + c2c.value = password; + c2c.select(); + document.execCommand("copy"); + c2c.value = ""; + callback(true); +} + +keepass.associate = function(callback, tab) { + if(keepass.isAssociated()) { + return; + } + + keepass.getDatabaseHash(tab); + + if(keepass.isDatabaseClosed || !keepass.isKeePassHttpAvailable) { + return; + } + + page.tabs[tab.id].errorMessage = null; + + var rawKey = cryptoHelpers.generateSharedKey(keepass.keySize * 2); + var key = keepass.b64e(rawKey); + + var request = { + RequestType: "associate", + Key: key + }; + + keepass.setVerifier(request, key); + + var result = keepass.send(request); + + if(keepass.checkStatus(result[0], tab)) { + var r = JSON.parse(result[1]); + + if(r.Version) { + keepass.currentKeePassHttp = { + "version": r.Version, + "versionParsed": parseInt(r.Version.replace(/\./g,"")) + }; + } + + var id = r.Id; + if(!keepass.verifyResponse(r, key)) { + page.tabs[tab.id].errorMessage = "KeePass association failed, try again."; + } + else { + keepass.setCryptoKey(id, key); + keepass.associated.value = true; + keepass.associated.hash = r.Hash || 0; + } + + browserAction.show(callback, tab); + } +} + +keepass.isConfigured = function() { + if(typeof(keepass.databaseHash) == "undefined") { + keepass.getDatabaseHash(); + } + return (keepass.databaseHash in keepass.keyRing); +} + +keepass.isAssociated = function() { + return (keepass.associated.value && keepass.associated.hash && keepass.associated.hash == keepass.databaseHash); +} + +keepass.send = function(request) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", keepass.getPluginUrl(), false); + xhr.setRequestHeader("Content-Type", "application/json"); + try { + var r = JSON.stringify(request); + page.debug("Request: {1}", r); + xhr.send(r); + } + catch (e) { + console.log("KeePassHttp: " + e); + } + page.debug("Response: {1} => {2}", xhr.status, xhr.responseText); + return [xhr.status, xhr.responseText]; +} + +keepass.checkStatus = function (status, tab) { + var success = (status >= 200 && status <= 299); + keepass.isDatabaseClosed = false; + keepass.isKeePassHttpAvailable = true; + + if(tab && page.tabs[tab.id]) { + delete page.tabs[tab.id].errorMessage; + } + if (!success) { + keepass.associated.value = false; + keepass.associated.hash = null; + if(tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = "Unknown error: " + status; + } + console.log("Error: "+ status); + if (status == 503) { + keepass.isDatabaseClosed = true; + console.log("KeePass database is not opened"); + if(tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = "KeePass database is not opened."; + } + } + else if (status == 0) { + keepass.isKeePassHttpAvailable = false; + console.log("Could not connect to keepass"); + if(tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = "Is KeePassHttp installed and is KeePass running?"; + } + } + } + + page.debug("keepass.checkStatus({1}, [tabID]) => {2}", status, success); + + return success; +} + +keepass.convertKeyToKeyRing = function() { + if(keepass.keyId in localStorage && keepass.keyBody in localStorage && !("keyRing" in localStorage)) { + var hash = keepass.getDatabaseHash(null); + keepass.saveKey(hash, localStorage[keepass.keyId], localStorage[keepass.keyBody]); + } + + if("keyRing" in localStorage) { + delete localStorage[keepass.keyId]; + delete localStorage[keepass.keyBody]; + } +} + +keepass.saveKey = function(hash, id, key) { + if(!(hash in keepass.keyRing)) { + keepass.keyRing[hash] = { + "id": id, + "key": key, + "icon": "blue", + "created": new Date(), + "last-used": new Date() + } + } + else { + keepass.keyRing[hash].id = id; + keepass.keyRing[hash].key = key; + } + localStorage.keyRing = JSON.stringify(keepass.keyRing); +} + +keepass.updateLastUsed = function(hash) { + if((hash in keepass.keyRing)) { + keepass.keyRing[hash].lastUsed = new Date(); + localStorage.keyRing = JSON.stringify(keepass.keyRing); + } +} + +keepass.deleteKey = function(hash) { + delete keepass.keyRing[hash]; + localStorage.keyRing = JSON.stringify(keepass.keyRing); +} + +keepass.getIconColor = function() { + return ((keepass.databaseHash in keepass.keyRing) && keepass.keyRing[keepass.databaseHash].icon) ? keepass.keyRing[keepass.databaseHash].icon : "blue"; +} + +keepass.getPluginUrl = function() { + if(page.settings.hostname && page.settings.port) { + return "http://" + page.settings.hostname + ":" + page.settings.port; + } + return keepass.pluginUrlDefault; +} + +keepass.setCurrentKeePassHttpVersion = function(version) { + if(version) { + keepass.currentKeePassHttp = { + "version": version, + "versionParsed": parseInt(version.replace(/\./g,"")) + }; + } +} + +keepass.keePassHttpUpdateAvailable = function() { + if(page.settings.checkUpdateKeePassHttp && page.settings.checkUpdateKeePassHttp > 0) { + var lastChecked = (keepass.latestKeePassHttp.lastChecked) ? new Date(keepass.latestKeePassHttp.lastChecked) : new Date("11/21/1986"); + var daysSinceLastCheck = Math.floor(((new Date()).getTime()-lastChecked.getTime())/86400000); + if(daysSinceLastCheck >= page.settings.checkUpdateKeePassHttp) { + keepass.checkForNewKeePassHttpVersion(); + } + } + + return (keepass.currentKeePassHttp.versionParsed > 0 && keepass.currentKeePassHttp.versionParsed < keepass.latestKeePassHttp.versionParsed); +} + +keepass.checkForNewKeePassHttpVersion = function() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", keepass.latestVersionUrl, false); + xhr.setRequestHeader("Content-Type", "application/json"); + try { + xhr.send(); + var $version = xhr.responseText; + if($version.substring(0, 1) == ":") { + $version = $version.substring(xhr.responseText.indexOf("KeePassHttp") + 12); + $version = $version.substring(0, $version.indexOf(":") - 1); + keepass.latestKeePassHttp.version = $version; + keepass.latestKeePassHttp.versionParsed = parseInt($version.replace(/\./g,"")); + } + else { + $version = -1; + } + } + catch (e) { + console.log("Error: " + e); + } + + if($version != -1) { + localStorage.latestKeePassHttp = JSON.stringify(keepass.latestKeePassHttp); + } + keepass.latestKeePassHttp.lastChecked = new Date(); +} + +keepass.testAssociation = function (tab, triggerUnlock) { + keepass.getDatabaseHash(tab, triggerUnlock); + + if(keepass.isDatabaseClosed || !keepass.isKeePassHttpAvailable) { + return false; + } + + if(keepass.isAssociated()) { + return true; + } + + var request = { + "RequestType": "test-associate", + "TriggerUnlock": (triggerUnlock === true) ? "true" : false + }; + var verifier = keepass.setVerifier(request); + + if(!verifier) { + keepass.associated.value = false; + keepass.associated.hash = null; + return false; + } + + var result = keepass.send(request); + var status = result[0]; + var response = result[1]; + + if(keepass.checkStatus(status, tab)) { + var r = JSON.parse(response); + var id = verifier[0]; + var key = verifier[1]; + + if(r.Version) { + keepass.currentKeePassHttp = { + "version": r.Version, + "versionParsed": parseInt(r.Version.replace(/\./g,"")) + }; + } + + keepass.isEncryptionKeyUnrecognized = false; + if(!keepass.verifyResponse(r, key, id)) { + var hash = r.Hash || 0; + keepass.deleteKey(hash); + keepass.isEncryptionKeyUnrecognized = true; + console.log("Encryption key is not recognized!"); + page.tabs[tab.id].errorMessage = "Encryption key is not recognized."; + keepass.associated.value = false; + keepass.associated.hash = null; + } + else if(!keepass.isAssociated()) { + console.log("Association was not successful"); + page.tabs[tab.id].errorMessage = "Association was not successful."; + } + } + + return keepass.isAssociated(); +} + +keepass.getDatabaseHash = function (tab, triggerUnlock) { + var request = { + "RequestType": "test-associate", + "TriggerUnlock": (triggerUnlock === true) ? "true" : false + }; + + var oldDatabaseHash = keepass.databaseHash; + + var result = keepass.send(request); + if(keepass.checkStatus(result[0], tab)) { + var response = JSON.parse(result[1]); + keepass.setCurrentKeePassHttpVersion(response.Version); + keepass.databaseHash = response.Hash || "no-hash"; + } + else { + keepass.databaseHash = "no-hash"; + } + + if(oldDatabaseHash && oldDatabaseHash != keepass.databaseHash) { + //console.log("clear association (old db hash != new db hash ==> " + oldDatabaseHash + " != " + keepass.databaseHash); + keepass.associated.value = false; + keepass.associated.hash = null; + } + + return keepass.databaseHash; +} + +keepass.setVerifier = function(request, inputKey) { + var key = inputKey || null; + var id = null; + + if(!key) { + var info = keepass.getCryptoKey(); + if (info == null) { + return null; + } + id = info[0]; + key = info[1]; + } + + if(id) { + request.Id = id; + } + + var iv = cryptoHelpers.generateSharedKey(keepass.keySize); + request.Nonce = keepass.b64e(iv); + + //var decodedKey = keepass.b64d(key); + request.Verifier = keepass.encrypt(request.Nonce, key, request.Nonce); + + return [id, key]; +} + +keepass.verifyResponse = function(response, key, id) { + keepass.associated.value = response.Success; + if (!response.Success) { + keepass.associated.hash = null; + return false; + } + + keepass.associated.hash = keepass.databaseHash; + + var iv = response.Nonce; + var value = keepass.decrypt(response.Verifier, key, iv, true); + + keepass.associated.value = (value == iv); + + if(id) { + keepass.associated.value = (keepass.associated.value && id == response.Id); + } + + keepass.associated.hash = (keepass.associated.value) ? keepass.databaseHash : null; + + return keepass.isAssociated(); + +} + +keepass.b64e = function(d) { + return btoa(keepass.to_s(d)); +} + +keepass.b64d = function(d) { + return keepass.to_b(atob(d)); +} + +keepass.getCryptoKey = function() { + if(!(keepass.databaseHash in keepass.keyRing)) { + return null; + } + + var id = keepass.keyRing[keepass.databaseHash].id; + var key = null; + + if(id) { + key = keepass.keyRing[keepass.databaseHash].key; + } + + return key ? [id, key] : null; +} + +keepass.setCryptoKey = function(id, key) { + keepass.saveKey(keepass.databaseHash, id, key); +} + +keepass.encrypt = function(input, key, iv) { + return keepass.b64e( + slowAES.encrypt( + keepass.to_b(input), + slowAES.modeOfOperation.CBC, + keepass.b64d(key), + keepass.b64d(iv) + ) + ); +} + +keepass.decrypt = function(input, key, iv, toStr) { + var output = slowAES.decrypt( + keepass.b64d(input), + slowAES.modeOfOperation.CBC, + keepass.b64d(key), + keepass.b64d(iv) + ); + + return toStr ? keepass.to_s(output) : output; +} + +keepass.decryptEntry = function (e, key, iv) { + e.Uuid = keepass.decrypt(e.Uuid, key, iv, true); + e.Name = UTF8.decode(keepass.decrypt(e.Name, key, iv, true)); + e.Login = UTF8.decode(keepass.decrypt(e.Login, key, iv, true)); + e.Password = UTF8.decode(keepass.decrypt(e.Password, key, iv, true)); + + if(e.StringFields) { + for(var i = 0; i < e.StringFields.length; i++) { + e.StringFields[i].Key = UTF8.decode(keepass.decrypt(e.StringFields[i].Key, key, iv, true)) + e.StringFields[i].Value = UTF8.decode(keepass.decrypt(e.StringFields[i].Value, key, iv, true)) + } + } +} diff --git a/chromeKeePassXC/background/keepass.js b/chromeKeePassXC/background/keepass.js new file mode 100644 index 0000000..da22f77 --- /dev/null +++ b/chromeKeePassXC/background/keepass.js @@ -0,0 +1,642 @@ +var keepass = {}; + +keepass.associated = {"value": false, "hash": null}; +keepass.isConnected = false; +keepass.isDatabaseClosed = false; +keepass.isKeePassXCAvailable = false; +keepass.useSecretBox = false; +keepass.isEncryptionKeyUnrecognized = false; +keepass.currentKeePassXC = {"version": 0, "versionParsed": 0}; +keepass.latestKeePassXC = (typeof(localStorage.latestKeePassXC) == 'undefined') ? {"version": 0, "versionParsed": 0, "lastChecked": null} : JSON.parse(localStorage.latestKeePassXC); +keepass.requiredKeePassXC = 212; +keepass.nativeHostName = "com.varjolintu.chromekeepassxc"; +keepass.nativePort = null; +keepass.keySize = 8; // wtf? stupid cryptoHelpers +keepass.latestVersionUrl = "https://raw.githubusercontent.com/keepassxreboot/keepassxc/develop/CHANGELOG"; +keepass.cacheTimeout = 30 * 1000; // milliseconds +keepass.databaseHash = "no-hash"; //no-hash = keepasshttp is too old and does not return a hash value +keepass.keyRing = (typeof(localStorage.keyRing) == 'undefined') ? {} : JSON.parse(localStorage.keyRing); +keepass.keyId = "chromekeepassxc-cryptokey-name"; +keepass.keyBody = "chromekeepassxc-key"; +keepass.to_s = cryptoHelpers.convertByteArrayToString; +keepass.to_b = cryptoHelpers.convertStringToByteArray; + + +keepass.addCredentials = function(callback, tab, username, password, url) { + keepass.updateCredentials(callback, tab, null, username, password, url); +} + +keepass.updateCredentials = function(callback, tab, entryId, username, password, url) { + page.debug("keepass.updateCredentials(callback, {1}, {2}, {3}, [password], {4})", tab.id, entryId, username, url); + + // unset error message + page.tabs[tab.id].errorMessage = null; + + // is browser associated to keepass? + if(!keepass.testAssociation(tab)) { + browserAction.showDefault(null, tab); + callback("error"); + return; + } + + // build request + var request = { + RequestType: "set-login" + }; + var verifier = keepass.setVerifier(request); + var id = verifier[0]; + var key = verifier[1]; + var iv = request.Nonce; + + + request.Login = keepass.encrypt(nacl.util.encode_UTF8(username), key, iv); + + request.Password = keepass.encrypt(nacl.util.encode_UTF8(password), key, iv); + request.Url = keepass.encrypt(url, key, iv); + request.SubmitUrl = keepass.encrypt(url, key, iv); + + if(entryId) { + request.Uuid = keepass.encrypt(entryId, key, iv); + } + + // send request + var result = keepass.send(request); + var status = result[0]; + var response = result[1]; + + // verify response + var code = "error"; + if(keepass.checkStatus(status, tab)) { + var r = JSON.parse(response); + if (keepass.verifyResponse(r, key, id)) { + code = "success"; + } + else { + code = "error"; + } + } + + callback(code); +} + +keepass.retrieveCredentials = function (callback, tab, url, submiturl, forceCallback, triggerUnlock) { + page.debug("keepass.retrieveCredentials(callback, {1}, {2}, {3}, {4})", tab.id, url, submiturl, forceCallback); + + // is browser associated to keepass? + if(!keepass.testAssociation(tab)) { + browserAction.showDefault(null, tab); + callback("error"); + return; + } + + // unset error message + page.tabs[tab.id].errorMessage = null; + + if (!keepass.isConnected) { + return; + } + + var entries = []; + message = { + "action": "get-logins", + "url": url + }; + + var verifier = keepass.setVerifier(message); + var id = verifier[0]; + var key = verifier[1]; + var iv = message.nonce; + var entries = []; + + keepass.callbackOnId(keepass.nativePort.onMessage, "get-logins-reply", function(response) { + if (response) { + keepass.setcurrentKeePassXCVersion(response.version); + + if (keepass.verifyResponse(response, key, id)) { + var rIv = response.nonce; + for (var i = 0; i < response.entries.length; i++) { + keepass.decryptEntry(response.entries[i], key, rIv); + } + entries = response.entries; + keepass.updateLastUsed(keepass.databaseHash); + if(entries.length == 0) { + //questionmark-icon is not triggered, so we have to trigger for the normal symbol + browserAction.showDefault(null, tab); + } + callback(entries); + } + else { + console.log("RetrieveCredentials for " + url + " rejected"); + } + } + else { + browserAction.showDefault(null, tab); + } + }); + keepass.nativePort.postMessage(message); + page.debug("keepass.retrieveCredentials() => entries.length = {1}", entries.length); +} + +// Handles the replies with callback provided +keepass.handleReply = function (msg) { + var reply; + if (msg.action == "generate-reply") { + console.log("Handling generate-reply"); + var a = []; + var response = JSON.stringify({ login: (msg.password.length * 8), password: msg.password }); + keepass.updateLastUsed(keepass.databaseHash); + a.push(response); + reply = a; + } + /*else if (msg.action == "get-logins-reply") { + console.log("Handling get-logins-reply"); + if (msg.count) { + var a = []; + for (var i = 0; i < msg.count; i++) { + var response = JSON.stringify({ login: msg.entries[i].login, name: msg.entries[i].name, password: msg.entries[i].password }); + keepass.updateLastUsed(keepass.databaseHash); + console.log(response); + a.push(response); + } + reply = a; + } + }*/ + else { + reply = msg; + } + return reply; +} + +// Redirects the callback to a listener (handleReply()) +keepass.callbackOnId = function (ev, id, callback) { + var listener = ( function(port, id) { + var handler = function(msg) { + if(msg.action == id) { + var reply = keepass.handleReply(msg); + ev.removeListener(handler); + callback(reply); + } + } + return handler; + })(ev, id, callback); + ev.addListener(listener); +} + + +keepass.generatePassword = function (callback, tab, forceCallback) { + if (!keepass.isConnected) { + return; + } + + // is browser associated to keepass? + if(!keepass.testAssociation(tab)) { + browserAction.showDefault(null, tab); + callback("error"); + return; + } + + if(keepass.currentKeePassXC.versionParsed < keepass.requiredKeePassXC) { + callback([]); + return; + } + + var passwords = []; + message = { "action": "generate-password" }; + keepass.callbackOnId(keepass.nativePort.onMessage, "generate-reply", callback); + keepass.nativePort.postMessage(message); +} + +keepass.copyPassword = function(callback, tab, password) { + var bg = chrome.extension.getBackgroundPage(); + var c2c = bg.document.getElementById("copy2clipboard"); + if(!c2c) { + var input = document.createElement('input'); + input.type = "text"; + input.id = "copy2clipboard"; + bg.document.getElementsByTagName('body')[0].appendChild(input); + c2c = bg.document.getElementById("copy2clipboard"); + } + + c2c.value = password; + c2c.select(); + document.execCommand("copy"); + c2c.value = ""; + callback(true); +} + +keepass.associate = function(callback, tab) { + if(keepass.isAssociated()) { + return; + } + + keepass.getDatabaseHash(callback, tab); + + if(keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) { + return; + } + + page.tabs[tab.id].errorMessage = null; + + var rawKey = nacl.randomBytes(keepass.keySize * 2); + //var rawKey = [41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56]; // This is just for testing with the test client. KSorLC0uLzAxMjM0NTY3OA== )*+,-./012345678 + console.log(rawKey); + var key = keepass.b64e(rawKey); + + var request = { + action: "associate", + key: key + }; + + keepass.setVerifier(request, key); + + keepass.callbackOnId(keepass.nativePort.onMessage, "associate-reply", function(response) { + if (response.version) { + keepass.currentKeePassXC = { + "version": response.version, + "versionParsed": parseInt(response.version.replace(/\./g,""))} + ; + } + + var id = response.id; + if(!keepass.verifyResponse(response, key)) { + page.tabs[tab.id].errorMessage = "KeePassXC association failed, try again."; + } + else { + keepass.setCryptoKey(id, key); + keepass.associated.value = true; + keepass.associated.hash = response.hash || 0; + } + + browserAction.show(callback, tab); + }); + + keepass.nativePort.postMessage(request); +} + +keepass.testAssociation = function (tab, triggerUnlock) { + keepass.getDatabaseHash(null, tab, triggerUnlock); + + if(keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) { + return false; + } + + if(keepass.isAssociated()) { + return true; + } + + var message = { + "action": "test-associate", + }; + var verifier = keepass.setVerifier(message); + + if(!verifier) { + keepass.associated.value = false; + keepass.associated.hash = null; + return false; + } + + keepass.callbackOnId(keepass.nativePort.onMessage, "test-associate-reply", function(response) { + if (response) { + var id = verifier[0]; + var key = verifier[1]; + + if(response.version) { + keepass.currentKeePassXC = { + "version": response.version, + "versionParsed": parseInt(response.version.replace(/\./g,"")) + }; + } + + keepass.isEncryptionKeyUnrecognized = false; + if(!keepass.verifyResponse(response, key, id)) { + var hash = response.hash || 0; + keepass.deleteKey(hash); + keepass.isEncryptionKeyUnrecognized = true; + console.log("Encryption key is not recognized!"); + page.tabs[tab.id].errorMessage = "Encryption key is not recognized."; + keepass.associated.value = false; + keepass.associated.hash = null; + } + else if(!keepass.isAssociated()) { + console.log("Association was not successful"); + page.tabs[tab.id].errorMessage = "Association was not successful."; + } + } + }); + keepass.nativePort.postMessage(message); + + return keepass.isAssociated(); +} + +keepass.getDatabaseHash = function (callback, tab, triggerUnlock) { + if (!keepass.isConnected) { + return; + } + + message = { "action": "get-databasehash" }; + keepass.callbackOnId(keepass.nativePort.onMessage, "hash-reply", function(response) { + console.log("hash-reply received: "+ response.hash); + var oldDatabaseHash = keepass.databaseHash; + keepass.setcurrentKeePassXCVersion(response.version); + keepass.databaseHash = response.hash || "no-hash"; + + if(oldDatabaseHash && oldDatabaseHash != keepass.databaseHash) { + keepass.associated.value = false; + keepass.associated.hash = null; + } + + statusOK(); + return keepass.databaseHash; + }); + keepass.nativePort.postMessage(message); +} + +keepass.isConfigured = function() { + if(typeof(keepass.databaseHash) == "undefined") { + keepass.getDatabaseHash(); + } + return (keepass.databaseHash in keepass.keyRing); +} + +keepass.isAssociated = function() { + return (keepass.associated.value && keepass.associated.hash && keepass.associated.hash == keepass.databaseHash); +} + +// Needed? +keepass.checkStatus = function (status, tab) { + var success = (status >= 200 && status <= 299); + keepass.isDatabaseClosed = false; + keepass.isKeePassXCAvailable = true; + + if(tab && page.tabs[tab.id]) { + delete page.tabs[tab.id].errorMessage; + } + if (!success) { + keepass.associated.value = false; + keepass.associated.hash = null; + if(tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = "Unknown error: " + status; + } + console.log("Error: "+ status); + if (status == 503) { + keepass.isDatabaseClosed = true; + console.log("KeePass database is not opened"); + if(tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = "KeePass database is not opened."; + } + } + else if (status == 0) { + keepass.isKeePassXCAvailable = false; + console.log("Could not connect to keepass"); + if(tab && page.tabs[tab.id]) { + page.tabs[tab.id].errorMessage = "Is KeePassXC installed and running?"; + } + } + } + + page.debug("keepass.checkStatus({1}, [tabID]) => {2}", status, success); + + return success; +} + +keepass.convertKeyToKeyRing = function() { + if(keepass.keyId in localStorage && keepass.keyBody in localStorage && !("keyRing" in localStorage)) { + //var hash = keepass.getDatabaseHash(null); + //keepass.saveKey(hash, localStorage[keepass.keyId], localStorage[keepass.keyBody]); + + keepass.getDatabaseHash(function(hash) { + keepass.saveKey(hash, localStorage[keepass.keyId], localStorage[keepass.keyBody]); + }, null); + } + + if("keyRing" in localStorage) { + delete localStorage[keepass.keyId]; + delete localStorage[keepass.keyBody]; + } +} + +keepass.saveKey = function(hash, id, key) { + if(!(hash in keepass.keyRing)) { + keepass.keyRing[hash] = { + "id": id, + "key": key, + "icon": "blue", + "created": new Date(), + "last-used": new Date() + } + } + else { + keepass.keyRing[hash].id = id; + keepass.keyRing[hash].key = key; + } + localStorage.keyRing = JSON.stringify(keepass.keyRing); +} + +keepass.updateLastUsed = function(hash) { + if((hash in keepass.keyRing)) { + keepass.keyRing[hash].lastUsed = new Date(); + localStorage.keyRing = JSON.stringify(keepass.keyRing); + } +} + +keepass.deleteKey = function(hash) { + delete keepass.keyRing[hash]; + localStorage.keyRing = JSON.stringify(keepass.keyRing); +} + +keepass.getIconColor = function() { + return ((keepass.databaseHash in keepass.keyRing) && keepass.keyRing[keepass.databaseHash].icon) ? keepass.keyRing[keepass.databaseHash].icon : "blue"; +} + +keepass.setcurrentKeePassXCVersion = function(version) { + if(version) { + keepass.currentKeePassXC = { + "version": version, + "versionParsed": parseInt(version.replace(/\./g,"")) + }; + } +} + +keepass.keePassXCUpdateAvailable = function() { + if(page.settings.checkUpdateKeePassXC && page.settings.checkUpdateKeePassXC > 0) { + var lastChecked = (keepass.latestKeePassXC.lastChecked) ? new Date(keepass.latestKeePassXC.lastChecked) : new Date("11/21/1986"); + var daysSinceLastCheck = Math.floor(((new Date()).getTime()-lastChecked.getTime())/86400000); + if(daysSinceLastCheck >= page.settings.checkUpdateKeePassXC) { + keepass.checkForNewKeePassXCVersion(); + } + } + + return (keepass.currentKeePassXC.versionParsed > 0 && keepass.currentKeePassXC.versionParsed < keepass.latestKeePassXC.versionParsed); +} + +keepass.checkForNewKeePassXCVersion = function() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", keepass.latestVersionUrl, true); + xhr.onload = function(e) { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + var $version = xhr.responseText; + if($version.substring(0, 1) == "2") { + $version = $version.substring(0, $version.indexOf(" ")); + keepass.latestKeePassXC.version = $version; + keepass.latestKeePassXC.versionParsed = parseInt($version.replace(/\./g,"")); + } + } + else { + $version = -1; + } + } + + if($version != -1) { + localStorage.latestKeePassXC = JSON.stringify(keepass.latestKeePassXC); + } + }; + + xhr.onerror = function(e) { + console.log("checkForNewKeePassXCVersion error: " + e); + } + + xhr.send(); + keepass.latestKeePassXC.lastChecked = new Date(); +} + +keepass.connectToNative = function() { + if (!keepass.isConnected) { + keepass.nativeConnect(); + } +} + +function statusOK() { + keepass.isDatabaseClosed = false; + keepass.isKeePassXCAvailable = true; +} + +keepass.onNativeMessage = function (response) { + console.log("Received message: " + JSON.stringify(response)); +} + +function onDisconnected() { + console.log("Failed to connect: " + chrome.runtime.lastError.message); + keepass.nativePort = null; + keepass.isConnected = false; +} + +keepass.nativeConnect = function() { + console.log("Connecting to native messaging host " + keepass.nativeHostName) + keepass.nativePort = chrome.runtime.connectNative(keepass.nativeHostName); + keepass.nativePort.onMessage.addListener(keepass.onNativeMessage); + keepass.nativePort.onDisconnect.addListener(onDisconnected); + keepass.isConnected = true; +} + +keepass.setVerifier = function(request, inputKey) { + var key = inputKey || null; + var id = null; + + if(!key) { + var info = keepass.getCryptoKey(); + if (info == null) { + return null; + } + id = info[0]; + key = info[1]; + } + + if(id) { + request.id = id; + } + + //var iv = cryptoHelpers.generateSharedKey(keepass.keySize); + var nonce = nacl.randomBytes(keepass.keySize * 2); + request.nonce = keepass.b64e(nonce); + + //var decodedKey = keepass.b64d(key); + request.verifier = keepass.encrypt(request.nonce, key, request.nonce); + + var test = keepass.encrypt("Aeh9maerCjE5v5V8Tz2YxA==", key, "Aeh9maerCjE5v5V8Tz2YxA=="); + console.log(test); + + return [id, key]; +} + +keepass.verifyResponse = function(response, key, id) { + keepass.associated.value = response.success; + if (!response.success) { + keepass.associated.hash = null; + return false; + } + + keepass.associated.hash = keepass.databaseHash; + + var nonce = response.nonce; + var value = keepass.decrypt(response.verifier, key, nonce, true); + + keepass.associated.value = (value == nonce); + + if(id) { + keepass.associated.value = (keepass.associated.value && id == response.id); + } + + keepass.associated.hash = (keepass.associated.value) ? keepass.databaseHash : null; + + return keepass.isAssociated(); + +} + +keepass.b64e = function(d) { + //return nacl.util.encodeBase64(d); + return btoa(keepass.to_s(d)); +} + +keepass.b64d = function(d) { + //return nacl.util.decodeBase64(d); + return keepass.to_b(atob(d)); +} + +keepass.getCryptoKey = function() { + if(!(keepass.databaseHash in keepass.keyRing)) { + return null; + } + + var id = keepass.keyRing[keepass.databaseHash].id; + var key = null; + + if(id) { + key = keepass.keyRing[keepass.databaseHash].key; + } + + return key ? [id, key] : null; +} + +keepass.setCryptoKey = function(id, key) { + keepass.saveKey(keepass.databaseHash, id, key); +} + +keepass.encrypt = function(input, key, nonce) { + return keepass.b64e(slowAES.encrypt(keepass.to_b(input), slowAES.modeOfOperation.CBC, keepass.b64d(key), keepass.b64d(nonce))); + //return keepass.b64e(nacl.secretbox(keepass.to_b(input), keepass.b64d(nonce), keepass.b64d(key))); +} + +keepass.decrypt = function(input, key, nonce, toStr) { + var output = slowAES.decrypt(keepass.b64d(input), slowAES.modeOfOperation.CBC, keepass.b64d(key), keepass.b64d(nonce)); + //var output = nacl.secretbox.open(keepass.b64d(input), keepass.b64d(nonce), keepass.b64d(key)); + return toStr ? keepass.to_s(output) : output; +} + +keepass.decryptEntry = function (box, key, nonce) { + var e = keepass.useSecretBox ? nacl.secretbox.open(box, nonce, key) : box; + if (e) + { + e.uuid = keepass.decrypt(e.uuid, key, nonce, true); + e.name = UTF8.decode(keepass.decrypt(e.name, key, nonce, true)); + e.login = UTF8.decode(keepass.decrypt(e.login, key, nonce, true)); + e.password = UTF8.decode(keepass.decrypt(e.password, key, nonce, true)); + + if(e.StringFields) { + for(var i = 0; i < e.StringFields.length; i++) { + e.StringFields[i].Key = UTF8.decode(keepass.decrypt(e.StringFields[i].Key, key, nonce, true)) + e.StringFields[i].Value = UTF8.decode(keepass.decrypt(e.StringFields[i].Value, key, nonce, true)) + } + } + } +} diff --git a/chromeKeePassXC/background/nacl-util.js b/chromeKeePassXC/background/nacl-util.js new file mode 100644 index 0000000..804068b --- /dev/null +++ b/chromeKeePassXC/background/nacl-util.js @@ -0,0 +1,51 @@ +// Written in 2014-2016 by Dmitry Chestnykh and Devi Mandiri. +// Public domain. +(function(root, f) { + 'use strict'; + if (typeof module !== 'undefined' && module.exports) module.exports = f(); + else if (root.nacl) root.nacl.util = f(); + else { + root.nacl = {}; + root.nacl.util = f(); + } +}(this, function() { + 'use strict'; + + var util = {}; + + util.decodeUTF8 = function(s) { + if (typeof s !== 'string') throw new TypeError('expected string'); + var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length); + for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i); + return b; + }; + + util.encodeUTF8 = function(arr) { + var i, s = []; + for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i])); + return decodeURIComponent(escape(s.join(''))); + }; + + util.encodeBase64 = function(arr) { + if (typeof btoa === 'undefined') { + return (new Buffer(arr)).toString('base64'); + } else { + var i, s = [], len = arr.length; + for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i])); + return btoa(s.join('')); + } + }; + + util.decodeBase64 = function(s) { + if (typeof atob === 'undefined') { + return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0)); + } else { + var i, d = atob(s), b = new Uint8Array(d.length); + for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i); + return b; + } + }; + + return util; + +})); diff --git a/chromeKeePassXC/background/nacl.min.js b/chromeKeePassXC/background/nacl.min.js new file mode 100755 index 0000000..cf032a3 --- /dev/null +++ b/chromeKeePassXC/background/nacl.min.js @@ -0,0 +1 @@ +!function(r){"use strict";function n(r,n){return r<>>32-n}function e(r,n){var e=255&r[n+3];return e=e<<8|255&r[n+2],e=e<<8|255&r[n+1],e<<8|255&r[n+0]}function t(r,n){var e=r[n]<<24|r[n+1]<<16|r[n+2]<<8|r[n+3],t=r[n+4]<<24|r[n+5]<<16|r[n+6]<<8|r[n+7];return new sr(e,t)}function o(r,n,e){var t;for(t=0;t<4;t++)r[n+t]=255&e,e>>>=8}function i(r,n,e){r[n]=e.hi>>24&255,r[n+1]=e.hi>>16&255,r[n+2]=e.hi>>8&255,r[n+3]=255&e.hi,r[n+4]=e.lo>>24&255,r[n+5]=e.lo>>16&255,r[n+6]=e.lo>>8&255,r[n+7]=255&e.lo}function f(r,n,e,t,o){var i,f=0;for(i=0;i>>8)-1}function a(r,n,e,t){return f(r,n,e,t,16)}function u(r,n,e,t){return f(r,n,e,t,32)}function c(r,t,i,f,a){var u,c,w,y=new Uint32Array(16),l=new Uint32Array(16),s=new Uint32Array(16),h=new Uint32Array(4);for(u=0;u<4;u++)l[5*u]=e(f,4*u),l[1+u]=e(i,4*u),l[6+u]=e(t,4*u),l[11+u]=e(i,16+4*u);for(u=0;u<16;u++)s[u]=l[u];for(u=0;u<20;u++){for(c=0;c<4;c++){for(w=0;w<4;w++)h[w]=l[(5*c+4*w)%16];for(h[1]^=n(h[0]+h[3]|0,7),h[2]^=n(h[1]+h[0]|0,9),h[3]^=n(h[2]+h[1]|0,13),h[0]^=n(h[3]+h[2]|0,18),w=0;w<4;w++)y[4*c+(c+w)%4]=h[w]}for(w=0;w<16;w++)l[w]=y[w]}if(a){for(u=0;u<16;u++)l[u]=l[u]+s[u]|0;for(u=0;u<4;u++)l[5*u]=l[5*u]-e(f,4*u)|0,l[6+u]=l[6+u]-e(t,4*u)|0;for(u=0;u<4;u++)o(r,4*u,l[5*u]),o(r,16+4*u,l[6+u])}else for(u=0;u<16;u++)o(r,4*u,l[u]+s[u]|0)}function w(r,n,e,t){return c(r,n,e,t,!1),0}function y(r,n,e,t){return c(r,n,e,t,!0),0}function l(r,n,e,t,o,i,f){var a,u,c=new Uint8Array(16),y=new Uint8Array(64);if(!o)return 0;for(u=0;u<16;u++)c[u]=0;for(u=0;u<8;u++)c[u]=i[u];for(;o>=64;){for(w(y,c,f,Br),u=0;u<64;u++)r[n+u]=(e?e[t+u]:0)^y[u];for(a=1,u=8;u<16;u++)a=a+(255&c[u])|0,c[u]=255&a,a>>>=8;o-=64,n+=64,e&&(t+=64)}if(o>0)for(w(y,c,f,Br),u=0;u>>=8}function b(r,n,e,t,o,i){var f,a,u,c,w=new Uint32Array(17),y=new Uint32Array(17),l=new Uint32Array(17),s=new Uint32Array(17),h=new Uint32Array(17);for(u=0;u<17;u++)y[u]=l[u]=0;for(u=0;u<16;u++)y[u]=i[u];for(y[3]&=15,y[4]&=252,y[7]&=15,y[8]&=252,y[11]&=15,y[12]&=252,y[15]&=15;o>0;){for(u=0;u<17;u++)s[u]=0;for(u=0;u<16&&u>>=8;for(c=c+l[16]|0,l[16]=3&c,c=5*(c>>>2)|0,u=0;u<16;u++)c=c+l[u]|0,l[u]=255&c,c>>>=8;c=c+l[16]|0,l[16]=c}for(u=0;u<17;u++)h[u]=l[u];for(g(l,Sr),f=0|-(l[16]>>>7),u=0;u<17;u++)l[u]^=f&(h[u]^l[u]);for(u=0;u<16;u++)s[u]=i[u+16];for(s[16]=0,g(l,s),u=0;u<16;u++)r[n+u]=l[u];return 0}function p(r,n,e,t,o,i){var f=new Uint8Array(16);return b(f,0,e,t,o,i),a(r,n,f,0)}function _(r,n,e,t,o){var i;if(e<32)return-1;for(v(r,0,n,0,e,t,o),b(r,16,r,32,e-32,r),i=0;i<16;i++)r[i]=0;return 0}function A(r,n,e,t,o){var i,f=new Uint8Array(32);if(e<32)return-1;if(h(f,0,32,t,o),0!==p(n,16,n,32,e-32,f))return-1;for(v(r,0,n,0,e,t,o),i=0;i<32;i++)r[i]=0;return 0}function U(r,n){var e;for(e=0;e<16;e++)r[e]=0|n[e]}function E(r){var n,e;for(e=0;e<16;e++)r[e]+=65536,n=Math.floor(r[e]/65536),r[(e+1)*(e<15?1:0)]+=n-1+37*(n-1)*(15===e?1:0),r[e]-=65536*n}function x(r,n,e){for(var t,o=~(e-1),i=0;i<16;i++)t=o&(r[i]^n[i]),r[i]^=t,n[i]^=t}function d(r,n){var e,t,o,i=hr(),f=hr();for(e=0;e<16;e++)f[e]=n[e];for(E(f),E(f),E(f),t=0;t<2;t++){for(i[0]=f[0]-65517,e=1;e<15;e++)i[e]=f[e]-65535-(i[e-1]>>16&1),i[e-1]&=65535;i[15]=f[15]-32767-(i[14]>>16&1),o=i[15]>>16&1,i[14]&=65535,x(f,i,1-o)}for(e=0;e<16;e++)r[2*e]=255&f[e],r[2*e+1]=f[e]>>8}function m(r,n){var e=new Uint8Array(32),t=new Uint8Array(32);return d(e,r),d(t,n),u(e,0,t,0)}function B(r){var n=new Uint8Array(32);return d(n,r),1&n[0]}function S(r,n){var e;for(e=0;e<16;e++)r[e]=n[2*e]+(n[2*e+1]<<8);r[15]&=32767}function K(r,n,e){var t;for(t=0;t<16;t++)r[t]=n[t]+e[t]|0}function Y(r,n,e){var t;for(t=0;t<16;t++)r[t]=n[t]-e[t]|0}function T(r,n,e){var t,o,i=new Float64Array(31);for(t=0;t<31;t++)i[t]=0;for(t=0;t<16;t++)for(o=0;o<16;o++)i[t+o]+=n[t]*e[o];for(t=0;t<15;t++)i[t]+=38*i[t+16];for(t=0;t<16;t++)r[t]=i[t];E(r),E(r)}function L(r,n){T(r,n,n)}function k(r,n){var e,t=hr();for(e=0;e<16;e++)t[e]=n[e];for(e=253;e>=0;e--)L(t,t),2!==e&&4!==e&&T(t,t,n);for(e=0;e<16;e++)r[e]=t[e]}function z(r,n){var e,t=hr();for(e=0;e<16;e++)t[e]=n[e];for(e=250;e>=0;e--)L(t,t),1!==e&&T(t,t,n);for(e=0;e<16;e++)r[e]=t[e]}function R(r,n,e){var t,o,i=new Uint8Array(32),f=new Float64Array(80),a=hr(),u=hr(),c=hr(),w=hr(),y=hr(),l=hr();for(o=0;o<31;o++)i[o]=n[o];for(i[31]=127&n[31]|64,i[0]&=248,S(f,e),o=0;o<16;o++)u[o]=f[o],w[o]=a[o]=c[o]=0;for(a[0]=w[0]=1,o=254;o>=0;--o)t=i[o>>>3]>>>(7&o)&1,x(a,u,t),x(c,w,t),K(y,a,c),Y(a,a,c),K(c,u,w),Y(u,u,w),L(w,y),L(l,a),T(a,c,a),T(c,u,y),K(y,a,c),Y(a,a,c),L(u,a),Y(c,w,l),T(a,c,Ar),K(a,a,w),T(c,c,a),T(a,w,l),T(w,u,f),L(u,y),x(a,u,t),x(c,w,t);for(o=0;o<16;o++)f[o+16]=a[o],f[o+32]=c[o],f[o+48]=u[o],f[o+64]=w[o];var s=f.subarray(32),h=f.subarray(16);return k(s,s),T(h,h,s),d(r,h),0}function P(r,n){return R(r,n,br)}function N(r,n){return vr(n,32),P(r,n)}function O(r,n,e){var t=new Uint8Array(32);return R(t,e,n),y(r,gr,t,Br)}function C(r,n,e,t,o,i){var f=new Uint8Array(32);return O(f,o,i),Kr(r,n,e,t,f)}function F(r,n,e,t,o,i){var f=new Uint8Array(32);return O(f,o,i),Yr(r,n,e,t,f)}function M(){var r,n,e,t=0,o=0,i=0,f=0,a=65535;for(e=0;e>>16,i+=n&a,f+=n>>>16;return o+=t>>>16,i+=o>>>16,f+=i>>>16,new sr(i&a|f<<16,t&a|o<<16)}function G(r,n){return new sr(r.hi>>>n,r.lo>>>n|r.hi<<32-n)}function Z(){var r,n=0,e=0;for(r=0;r>>n|r.lo<>>n|r.hi<>>n|r.hi<>>n|r.lo<=128;){for(f=0;f<16;f++)y[f]=t(n,8*f+l);for(f=0;f<80;f++){for(a=0;a<8;a++)c[a]=w[a];for(o=M(w[7],D(w[4]),I(w[4],w[5],w[6]),Tr[f],y[f%16]),c[7]=M(o,X(w[0]),V(w[0],w[1],w[2])),c[3]=M(c[3],o),a=0;a<8;a++)w[(a+1)%8]=c[a];if(f%16===15)for(a=0;a<16;a++)y[a]=M(y[a],y[(a+9)%16],j(y[(a+1)%16]),H(y[(a+14)%16]))}for(f=0;f<8;f++)w[f]=M(w[f],u[f]),u[f]=w[f];l+=128,e-=128}for(f=0;f<8;f++)i(r,8*f,u[f]);return e}function Q(r,n,e){var t,o=new Uint8Array(64),f=new Uint8Array(256),a=e;for(t=0;t<64;t++)o[t]=Lr[t];for(J(o,n,e),e%=128,t=0;t<256;t++)f[t]=0;for(t=0;t=0;--o)t=e[o/8|0]>>(7&o)&1,$(r,n,t),W(n,r),W(r,r),$(r,n,t)}function er(r,n){var e=[hr(),hr(),hr(),hr()];U(e[0],xr),U(e[1],dr),U(e[2],_r),T(e[3],xr,dr),nr(r,e,n)}function tr(r,n,e){var t,o=new Uint8Array(64),i=[hr(),hr(),hr(),hr()];for(e||vr(n,32),Q(o,n,32),o[0]&=248,o[31]&=127,o[31]|=64,er(i,o),rr(r,i),t=0;t<32;t++)n[t+32]=r[t];return 0}function or(r,n){var e,t,o,i;for(t=63;t>=32;--t){for(e=0,o=t-32,i=t-12;o>8,n[o]-=256*e;n[o]+=e,n[t]=0}for(e=0,o=0;o<32;o++)n[o]+=e-(n[31]>>4)*kr[o],e=n[o]>>8,n[o]&=255;for(o=0;o<32;o++)n[o]-=e*kr[o];for(t=0;t<32;t++)n[t+1]+=n[t]>>8,r[t]=255&n[t]}function ir(r){var n,e=new Float64Array(64);for(n=0;n<64;n++)e[n]=r[n];for(n=0;n<64;n++)r[n]=0;or(r,e)}function fr(r,n,e,t){var o,i,f=new Uint8Array(64),a=new Uint8Array(64),u=new Uint8Array(64),c=new Float64Array(64),w=[hr(),hr(),hr(),hr()];Q(f,t,32),f[0]&=248,f[31]&=127,f[31]|=64;var y=e+64;for(o=0;o>7&&Y(r[0],pr,r[0]),T(r[3],r[0],r[1]),0)}function ur(r,n,e,t){var o,i,f=new Uint8Array(32),a=new Uint8Array(64),c=[hr(),hr(),hr(),hr()],w=[hr(),hr(),hr(),hr()];if(i=-1,e<64)return-1;if(ar(w,t))return-1;for(o=0;o=0},r.sign.keyPair=function(){var r=new Uint8Array(Xr),n=new Uint8Array(Dr);return tr(r,n),{publicKey:r,secretKey:n}},r.sign.keyPair.fromSecretKey=function(r){if(yr(r),r.length!==Dr)throw new Error("bad secret key size");for(var n=new Uint8Array(Xr),e=0;e 1) { + console.log(page.sprintf(arguments[0], arguments)); + } + else { + console.log(arguments[0]); + } +}; + +page.sprintf = function(input, args) { + return input.replace(/{(\d+)}/g, function(match, number) { + return typeof args[number] != 'undefined' + ? (typeof args[number] == 'object' ? JSON.stringify(args[number]) : args[number]) + : match + ; + }); +} + +page.debugDummy = function() {}; + +page.debug = page.debugDummy; + +page.setDebug = function(bool) { + if(bool) { + page.debug = page.debugConsole; + return "Debug mode enabled"; + } + else { + page.debug = page.debugDummy; + return "Debug mode disabled"; + } +}; diff --git a/chromeKeePassXC/background/sjcl.js b/chromeKeePassXC/background/sjcl.js new file mode 100644 index 0000000..3bc60d3 --- /dev/null +++ b/chromeKeePassXC/background/sjcl.js @@ -0,0 +1,573 @@ +/** @fileOverview Javascript cryptography implementation. + * + * Crush to remove comments, shorten variable names and + * generally reduce transmission size. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +"use strict"; +/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */ +/*global document, window, escape, unescape */ + +/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */ +var sjcl = { + /** @namespace Symmetric ciphers. */ + cipher: {}, + + /** @namespace Hash functions. Right now only SHA256 is implemented. */ + hash: {}, + + /** @namespace Key exchange functions. Right now only SRP is implemented. */ + keyexchange: {}, + + /** @namespace Block cipher modes of operation. */ + mode: {}, + + /** @namespace Miscellaneous. HMAC and PBKDF2. */ + misc: {}, + + /** + * @namespace Bit array encoders and decoders. + * + * @description + * The members of this namespace are functions which translate between + * SJCL's bitArrays and other objects (usually strings). Because it + * isn't always clear which direction is encoding and which is decoding, + * the method names are "fromBits" and "toBits". + */ + codec: {}, + + /** @namespace Exceptions. */ + exception: { + /** @class Ciphertext is corrupt. */ + corrupt: function(message) { + this.toString = function() { return "CORRUPT: "+this.message; }; + this.message = message; + }, + + /** @class Invalid parameter. */ + invalid: function(message) { + this.toString = function() { return "INVALID: "+this.message; }; + this.message = message; + }, + + /** @class Bug or missing feature in SJCL. */ + bug: function(message) { + this.toString = function() { return "BUG: "+this.message; }; + this.message = message; + }, + + /** @class Something isn't ready. */ + notReady: function(message) { + this.toString = function() { return "NOT READY: "+this.message; }; + this.message = message; + } + } +}; +/** @fileOverview Low-level AES implementation. + * + * This file contains a low-level implementation of AES, optimized for + * size and for efficiency on several browsers. It is based on + * OpenSSL's aes_core.c, a public-domain implementation by Vincent + * Rijmen, Antoon Bosselaers and Paulo Barreto. + * + * An older version of this implementation is available in the public + * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh, + * Stanford University 2008-2010 and BSD-licensed for liability + * reasons. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** + * Schedule out an AES key for both encryption and decryption. This + * is a low-level class. Use a cipher mode to do bulk encryption. + * + * @constructor + * @param {Array} key The key as an array of 4, 6 or 8 words. + * + * @class Advanced Encryption Standard (low-level interface) + */ +sjcl.cipher.aes = function (key) { + if (!this._tables[0][0][0]) { + this._precompute(); + } + + var i, j, tmp, + encKey, decKey, + sbox = this._tables[0][4], decTable = this._tables[1], + keyLen = key.length, rcon = 1; + + if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { + throw new sjcl.exception.invalid("invalid aes key size"); + } + + this._key = [encKey = key.slice(0), decKey = []]; + + // schedule encryption keys + for (i = keyLen; i < 4 * keyLen + 28; i++) { + tmp = encKey[i-1]; + + // apply sbox + if (i%keyLen === 0 || (keyLen === 8 && i%keyLen === 4)) { + tmp = sbox[tmp>>>24]<<24 ^ sbox[tmp>>16&255]<<16 ^ sbox[tmp>>8&255]<<8 ^ sbox[tmp&255]; + + // shift rows and add rcon + if (i%keyLen === 0) { + tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24; + rcon = rcon<<1 ^ (rcon>>7)*283; + } + } + + encKey[i] = encKey[i-keyLen] ^ tmp; + } + + // schedule decryption keys + for (j = 0; i; j++, i--) { + tmp = encKey[j&3 ? i : i - 4]; + if (i<=4 || j<4) { + decKey[j] = tmp; + } else { + decKey[j] = decTable[0][sbox[tmp>>>24 ]] ^ + decTable[1][sbox[tmp>>16 & 255]] ^ + decTable[2][sbox[tmp>>8 & 255]] ^ + decTable[3][sbox[tmp & 255]]; + } + } +}; + +sjcl.cipher.aes.prototype = { + // public + /* Something like this might appear here eventually + name: "AES", + blockSize: 4, + keySizes: [4,6,8], + */ + + /** + * Encrypt an array of 4 big-endian words. + * @param {Array} data The plaintext. + * @return {Array} The ciphertext. + */ + encrypt:function (data) { return this._crypt(data,0); }, + + /** + * Decrypt an array of 4 big-endian words. + * @param {Array} data The ciphertext. + * @return {Array} The plaintext. + */ + decrypt:function (data) { return this._crypt(data,1); }, + + /** + * The expanded S-box and inverse S-box tables. These will be computed + * on the client so that we don't have to send them down the wire. + * + * There are two tables, _tables[0] is for encryption and + * _tables[1] is for decryption. + * + * The first 4 sub-tables are the expanded S-box with MixColumns. The + * last (_tables[01][4]) is the S-box itself. + * + * @private + */ + _tables: [[[],[],[],[],[]],[[],[],[],[],[]]], + + /** + * Expand the S-box tables. + * + * @private + */ + _precompute: function () { + var encTable = this._tables[0], decTable = this._tables[1], + sbox = encTable[4], sboxInv = decTable[4], + i, x, xInv, d=[], th=[], x2, x4, x8, s, tEnc, tDec; + + // Compute double and third tables + for (i = 0; i < 256; i++) { + th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i; + } + + for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { + // Compute sbox + s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4; + s = s>>8 ^ s&255 ^ 99; + sbox[x] = s; + sboxInv[s] = x; + + // Compute MixColumns + x8 = d[x4 = d[x2 = d[x]]]; + tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100; + tEnc = d[s]*0x101 ^ s*0x1010100; + + for (i = 0; i < 4; i++) { + encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8; + decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8; + } + } + + // Compactify. Considerable speedup on Firefox. + for (i = 0; i < 5; i++) { + encTable[i] = encTable[i].slice(0); + decTable[i] = decTable[i].slice(0); + } + }, + + /** + * Encryption and decryption core. + * @param {Array} input Four words to be encrypted or decrypted. + * @param dir The direction, 0 for encrypt and 1 for decrypt. + * @return {Array} The four encrypted or decrypted words. + * @private + */ + _crypt:function (input, dir) { + if (input.length !== 4) { + throw new sjcl.exception.invalid("invalid aes block size"); + } + + var key = this._key[dir], + // state variables a,b,c,d are loaded with pre-whitened data + a = input[0] ^ key[0], + b = input[dir ? 3 : 1] ^ key[1], + c = input[2] ^ key[2], + d = input[dir ? 1 : 3] ^ key[3], + a2, b2, c2, + + nInnerRounds = key.length/4 - 2, + i, + kIndex = 4, + out = [0,0,0,0], + table = this._tables[dir], + + // load up the tables + t0 = table[0], + t1 = table[1], + t2 = table[2], + t3 = table[3], + sbox = table[4]; + + // Inner rounds. Cribbed from OpenSSL. + for (i = 0; i < nInnerRounds; i++) { + a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex]; + b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1]; + c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2]; + d = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3]; + kIndex += 4; + a=a2; b=b2; c=c2; + } + + // Last round. + for (i = 0; i < 4; i++) { + out[dir ? 3&-i : i] = + sbox[a>>>24 ]<<24 ^ + sbox[b>>16 & 255]<<16 ^ + sbox[c>>8 & 255]<<8 ^ + sbox[d & 255] ^ + key[kIndex++]; + a2=a; a=b; b=c; c=d; d=a2; + } + + return out; + } +}; + +/** @fileOverview Arrays of bits, encoded as arrays of Numbers. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** @namespace Arrays of bits, encoded as arrays of Numbers. + * + * @description + *

+ * These objects are the currency accepted by SJCL's crypto functions. + *

+ * + *

+ * Most of our crypto primitives operate on arrays of 4-byte words internally, + * but many of them can take arguments that are not a multiple of 4 bytes. + * This library encodes arrays of bits (whose size need not be a multiple of 8 + * bits) as arrays of 32-bit words. The bits are packed, big-endian, into an + * array of words, 32 bits at a time. Since the words are double-precision + * floating point numbers, they fit some extra data. We use this (in a private, + * possibly-changing manner) to encode the number of bits actually present + * in the last word of the array. + *

+ * + *

+ * Because bitwise ops clear this out-of-band data, these arrays can be passed + * to ciphers like AES which want arrays of words. + *

+ */ +sjcl.bitArray = { + /** + * Array slices in units of bits. + * @param {bitArray a} The array to slice. + * @param {Number} bstart The offset to the start of the slice, in bits. + * @param {Number} bend The offset to the end of the slice, in bits. If this is undefined, + * slice until the end of the array. + * @return {bitArray} The requested slice. + */ + bitSlice: function (a, bstart, bend) { + a = sjcl.bitArray._shiftRight(a.slice(bstart/32), 32 - (bstart & 31)).slice(1); + return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart); + }, + + /** + * Extract a number packed into a bit array. + * @param {bitArray} a The array to slice. + * @param {Number} bstart The offset to the start of the slice, in bits. + * @param {Number} length The length of the number to extract. + * @return {Number} The requested slice. + */ + extract: function(a, bstart, blength) { + // FIXME: this Math.floor is not necessary at all, but for some reason + // seems to suppress a bug in the Chromium JIT. + var x, sh = Math.floor((-bstart-blength) & 31); + if ((bstart + blength - 1 ^ bstart) & -32) { + // it crosses a boundary + x = (a[bstart/32|0] << (32 - sh)) ^ (a[bstart/32+1|0] >>> sh); + } else { + // within a single word + x = a[bstart/32|0] >>> sh; + } + return x & ((1< 0 && len) { + a[l-1] = sjcl.bitArray.partial(len, a[l-1] & 0x80000000 >> (len-1), 1); + } + return a; + }, + + /** + * Make a partial word for a bit array. + * @param {Number} len The number of bits in the word. + * @param {Number} x The bits. + * @param {Number} [0] _end Pass 1 if x has already been shifted to the high side. + * @return {Number} The partial word. + */ + partial: function (len, x, _end) { + if (len === 32) { return x; } + return (_end ? x|0 : x << (32-len)) + len * 0x10000000000; + }, + + /** + * Get the number of bits used by a partial word. + * @param {Number} x The partial word. + * @return {Number} The number of bits used by the partial word. + */ + getPartial: function (x) { + return Math.round(x/0x10000000000) || 32; + }, + + /** + * Compare two arrays for equality in a predictable amount of time. + * @param {bitArray} a The first array. + * @param {bitArray} b The second array. + * @return {boolean} true if a == b; false otherwise. + */ + equal: function (a, b) { + if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) { + return false; + } + var x = 0, i; + for (i=0; i= 32; shift -= 32) { + out.push(carry); + carry = 0; + } + if (shift === 0) { + return out.concat(a); + } + + for (i=0; i>>shift); + carry = a[i] << (32-shift); + } + last2 = a.length ? a[a.length-1] : 0; + shift2 = sjcl.bitArray.getPartial(last2); + out.push(sjcl.bitArray.partial(shift+shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(),1)); + return out; + }, + + /** xor a block of 4 words together. + * @private + */ + _xor4: function(x,y) { + return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]]; + } +}; +/** @fileOverview CBC mode implementation + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** @namespace + * Dangerous: CBC mode with PKCS#5 padding. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + sjcl.mode.cbc = { + /** The name of the mode. + * @constant + */ + name: "cbc", + + /** Encrypt in CBC mode with PKCS#5 padding. + * @param {Object} prp The block cipher. It must have a block size of 16 bytes. + * @param {bitArray} plaintext The plaintext data. + * @param {bitArray} iv The initialization value. + * @param {bitArray} [adata=[]] The authenticated data. Must be empty. + * @return The encrypted data, an array of bytes. + * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified. + */ + encrypt: function(prp, plaintext, iv, adata) { + if (adata && adata.length) { + throw new sjcl.exception.invalid("cbc can't authenticate data"); + } + if (sjcl.bitArray.bitLength(iv) !== 128) { + throw new sjcl.exception.invalid("cbc iv must be 128 bits"); + } + var i, + w = sjcl.bitArray, + xor = w._xor4, + bl = w.bitLength(plaintext), + bp = 0, + output = []; + + if (bl&7) { + throw new sjcl.exception.invalid("pkcs#5 padding only works for multiples of a byte"); + } + + for (i=0; bp+128 <= bl; i+=4, bp+=128) { + /* Encrypt a non-final block */ + iv = prp.encrypt(xor(iv, plaintext.slice(i,i+4))); + output.splice(i,0,iv[0],iv[1],iv[2],iv[3]); + } + + /* Construct the pad. */ + bl = (16 - ((bl >> 3) & 15)) * 0x1010101; + + /* Pad and encrypt. */ + iv = prp.encrypt(xor(iv,w.concat(plaintext,[bl,bl,bl,bl]).slice(i,i+4))); + output.splice(i,0,iv[0],iv[1],iv[2],iv[3]); + return output; + }, + + /** Decrypt in CBC mode. + * @param {Object} prp The block cipher. It must have a block size of 16 bytes. + * @param {bitArray} ciphertext The ciphertext data. + * @param {bitArray} iv The initialization value. + * @param {bitArray} [adata=[]] The authenticated data. It must be empty. + * @return The decrypted data, an array of bytes. + * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified. + * @throws {sjcl.exception.corrupt} if if the message is corrupt. + */ + decrypt: function(prp, ciphertext, iv, adata) { + if (adata && adata.length) { + throw new sjcl.exception.invalid("cbc can't authenticate data"); + } + if (sjcl.bitArray.bitLength(iv) !== 128) { + throw new sjcl.exception.invalid("cbc iv must be 128 bits"); + } + if ((sjcl.bitArray.bitLength(ciphertext) & 127) || !ciphertext.length) { + throw new sjcl.exception.corrupt("cbc ciphertext must be a positive multiple of the block size"); + } + var i, + w = sjcl.bitArray, + xor = w._xor4, + bi, bo, + output = []; + + adata = adata || []; + + for (i=0; i 16) { + throw new sjcl.exception.corrupt("pkcs#5 padding corrupt"); + } + bo = bi * 0x1010101; + if (!w.equal(w.bitSlice([bo,bo,bo,bo], 0, bi*8), + w.bitSlice(output, output.length*32 - bi*8, output.length*32))) { + throw new sjcl.exception.corrupt("pkcs#5 padding corrupt"); + } + + return w.bitSlice(output, 0, output.length*32 - bi*8); + } + }; diff --git a/chromeKeePassXC/background/utf8.js b/chromeKeePassXC/background/utf8.js new file mode 100644 index 0000000..95c70da --- /dev/null +++ b/chromeKeePassXC/background/utf8.js @@ -0,0 +1,28 @@ +var UTF8 = { + decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while (i < utftext.length) { + c = utftext.charCodeAt(i); + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode( + ((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + return string; + } +} diff --git a/chromeKeePassXC/bootstrap-btn.css b/chromeKeePassXC/bootstrap-btn.css new file mode 100644 index 0000000..0a2b69a --- /dev/null +++ b/chromeKeePassXC/bootstrap-btn.css @@ -0,0 +1,468 @@ +.b2c-btn { + display: inline-block; + *display: inline; + padding: 4px 14px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + *line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #bbbbbb; + *border: 0; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-bottom-color: #a2a2a2; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.b2c-btn:hover, +.b2c-btn:active, +.b2c-btn.active, +.b2c-btn.disabled, +.b2c-btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.b2c-btn:active, +.b2c-btn.active { + background-color: #cccccc \9; +} + +.b2c-btn:first-child { + *margin-left: 0; +} + +.b2c-btn:hover { + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + *background-color: #d9d9d9; + /* Buttons in IE7 don't get borders, so darken on hover */ + + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.b2c-btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.b2c-btn.active, +.b2c-btn:active { + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.b2c-btn.disabled, +.b2c-btn[disabled] { + cursor: default; + background-color: #e6e6e6; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.b2c-btn-large { + padding: 9px 14px; + font-size: 16px; + line-height: normal; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.b2c-btn-large [class^="icon-"] { + margin-top: 2px; +} + +.b2c-btn-small { + padding: 3px 9px; + font-size: 12px; + line-height: 18px; +} + +.b2c-btn-small [class^="icon-"] { + margin-top: 0; +} + +.b2c-btn-mini { + padding: 2px 6px; + font-size: 11px; + line-height: 17px; +} + +.b2c-btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.b2c-btn-block + .b2c-btn-block { + margin-top: 5px; +} + +input[type="submit"].b2c-btn-block, +input[type="reset"].b2c-btn-block, +input[type="button"].b2c-btn-block { + width: 100%; +} + +.b2c-btn-primary.active, +.b2c-btn-warning.active, +.b2c-btn-danger.active, +.b2c-btn-success.active, +.b2c-btn-info.active, +.b2c-btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.b2c-btn { + border-color: #c5c5c5; + border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); +} + +.b2c-btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} + +.b2c-btn-primary:hover, +.b2c-btn-primary:active, +.b2c-btn-primary.active, +.b2c-btn-primary.disabled, +.b2c-btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.b2c-btn-primary:active, +.b2c-btn-primary.active { + background-color: #003399 \9; +} + +.b2c-btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} + +.b2c-btn-warning:hover, +.b2c-btn-warning:active, +.b2c-btn-warning.active, +.b2c-btn-warning.disabled, +.b2c-btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.b2c-btn-warning:active, +.b2c-btn-warning.active { + background-color: #c67605 \9; +} + +.b2c-btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} + +.b2c-btn-danger:hover, +.b2c-btn-danger:active, +.b2c-btn-danger.active, +.b2c-btn-danger.disabled, +.b2c-btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.b2c-btn-danger:active, +.b2c-btn-danger.active { + background-color: #942a25 \9; +} + +.b2c-btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} + +.b2c-btn-success:hover, +.b2c-btn-success:active, +.b2c-btn-success.active, +.b2c-btn-success.disabled, +.b2c-btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.b2c-btn-success:active, +.b2c-btn-success.active { + background-color: #408140 \9; +} + +.b2c-btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} + +.b2c-btn-info:hover, +.b2c-btn-info:active, +.b2c-btn-info.active, +.b2c-btn-info.disabled, +.b2c-btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.b2c-btn-info:active, +.b2c-btn-info.active { + background-color: #24748c \9; +} + +.b2c-btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-image: -moz-linear-gradient(top, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} + +.b2c-btn-inverse:hover, +.b2c-btn-inverse:active, +.b2c-btn-inverse.active, +.b2c-btn-inverse.disabled, +.b2c-btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.b2c-btn-inverse:active, +.b2c-btn-inverse.active { + background-color: #080808 \9; +} + +button.b2c-btn, +input[type="submit"].b2c-btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.b2c-btn::-moz-focus-inner, +input[type="submit"].b2c-btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.b2c-btn.b2c-btn-large, +input[type="submit"].b2c-btn.b2c-btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.b2c-btn.b2c-btn-small, +input[type="submit"].b2c-btn.b2c-btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.b2c-btn.b2c-btn-mini, +input[type="submit"].b2c-btn.b2c-btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.b2c-input-append { + display: inline-block; + margin-bottom: 10px; + white-space: nowrap; + vertical-align: middle; +} + +.b2c-input-append input, +.b2c-input-append select, +.b2c-input-append .b2c-uneditable-input, +.b2c-input-append .b2c-dropdown-menu, +.b2c-input-append .b2c-popover { + font-size: 14px; +} + +.b2c-input-append input, +.b2c-input-append select, +.b2c-input-append .b2c-uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.b2c-input-append input:focus, +.b2c-input-append select:focus, +.b2c-input-append .b2c-b2c-uneditable-input:focus { + z-index: 2; +} + +.b2c-input-append .b2c-add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.b2c-input-append .b2c-add-on, +.b2c-input-append .b2c-btn, +.b2c-input-append .b2c-btn-group > .b2c-dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.b2c-input-append .b2c-active { + background-color: #a9dba9; + border-color: #46a546; +} + +.b2c-input-append input, +.b2c-input-append select, +.b2c-input-append .b2c-uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.b2c-input-append .b2c-add-on, +.b2c-input-append .b2c-btn, +.b2c-input-append .b2c-btn-group { + margin-left: -1px; +} + +.b2c-input-append .b2c-add-on:last-child, +.b2c-input-append .b2c-btn:last-child, +.b2c-input-append .b2c-btn-group:last-child > .b2c-dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} \ No newline at end of file diff --git a/chromeKeePassXC/chromekeepassxc.css b/chromeKeePassXC/chromekeepassxc.css new file mode 100644 index 0000000..df158c7 --- /dev/null +++ b/chromeKeePassXC/chromekeepassxc.css @@ -0,0 +1,172 @@ +.cip-ui-autocomplete li.cip-ui-menu-item { + text-align: left !important; + font-size: 12px !important; + font-weight: normal !important; + font-style: normal !important; + font-family: Verdana, Arial, sans-serif !important; + color: #222222 !important; +} +.cip-ui-dialog-titlebar-close { + visibility: hidden !important; +} +.cip-ui-widget-overlay { + background: none !important; + z-index: 2147483601 !important; +} +.cip-ui-dialog { + z-index: 2147483602 !important; +} +.cip-ui-dialog .cip-ui-dialog-title { + font-size:.8em !important; +} +.cip-ui-dialog .cip-ui-dialog-titlebar { + padding:.1em .5em !important; +} +.cip-ui-dialog-content { + font-size: .8em !important; +} + +#cip-genpw-dialog { + text-align: left !important; +} +#cip-genpw-dialog button { + height: 26px !important; +} +.cip-genpw-clearfix:after { + clear: both; + line-height: 0; + content: ""; + display: table; +} +.cip-genpw-icon { + position: absolute; + cursor: pointer; +} +.cip-genpw-icon.cip-icon-key-small { + width: 16px; + height: 16px; + background-image: url(chrome-extension://__MSG_@@extension_id__/icons/key_16x16.png); +} +.cip-genpw-icon.cip-icon-key-big { + width: 24px; + height: 24px; + background-image: url(chrome-extension://__MSG_@@extension_id__/icons/key_24x24.png); +} +.cip-genpw-password-frame { + margin-top: 5px !important; + margin-bottom: 5px !important; +} +.cip-genpw-password-frame > * { + height: 20px !important; + font-size: 11px !important; +} +.cip-genpw-textfield { + background: none !important; + font-size: 11px !important; + display: inline !important; + border: 1px solid rgb(170, 170, 170) !important; + padding: 1px 2px !important; + max-width: none !important; + min-width: 0 !important; + font-size: 1em !important; + width: 240px !important; + padding-left: 5px !important; +} +#cip-genpw-quality { + width: 50px !important; + padding-top: 1px !important; + padding-bottom: 1px !important; +} +.cip-genpw-label { + display: block !important; + margin: 5px 0 !important; +} +.cip-genpw-checkbox { + vertical-align: middle !important; +} +#cip-genpw-btn-fillin { + margin-right: 5px; +} + +.b2c-modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 2147483645; +} +.b2c-modal-backdrop:after { + content:''; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: #000000; + opacity: 0.8; + filter: alpha(opacity=80); +} +#b2c-cipDefine-fields { + z-index: 2147483646; +} +#b2c-cipDefine-description { + z-index: 2147483646; + color: #efefef; + border: 2px solid #555555; + padding: 7px 5px; + position: absolute; + top: 100px; + left: 150px; + text-align: left; + cursor: pointer; + background-color:rgba(255,255,255,0.3); + font-size: 15px; +} +#b2c-cipDefine-description div:first-of-type { + margin-top: 0; + padding-top: 0; + text-align: left; + color: #efefef; + padding-bottom: 8px; + font-weight: bold; + font-size: 160%; +} +#b2c-cipDefine-description p { + margin-top: 10px; + padding-top: 10px; + color: #efefef; + border-top: 2px solid #666666; + line-height: 110%; +} +#b2c-help { + margin-bottom: 3px; +} +.b2c-fixed-field { + position: absolute; + border: 2px solid #efefef; + cursor: pointer; + z-index: 2147483646; + text-align: left; + font-weight: bold; + background-color:rgba(239,239,239,0.4); +} +.b2c-fixed-hover-field { + border: 2px solid orange; + background-color:rgba(255,165,239,0.4); +} +.b2c-fixed-password-field { + border: 2px solid red; + color: #efefef; + background-color:rgba(255,0,0,0.4); +} +.b2c-fixed-username-field { + border: 2px solid limegreen; + color: #efefef; + background-color:rgba(50,205,50,0.4); +} +.b2c-fixed-string-field { + border: 2px solid deepskyblue; + color: #efefef; + background-color:rgba(30,144,255,0.4); +} \ No newline at end of file diff --git a/chromeKeePassXC/chromekeepassxc.js b/chromeKeePassXC/chromekeepassxc.js new file mode 100644 index 0000000..7f4654e --- /dev/null +++ b/chromeKeePassXC/chromekeepassxc.js @@ -0,0 +1,1739 @@ +// contains already called method names +var _called = {}; + +chrome.extension.onMessage.addListener(function(req, sender, callback) { + if ('action' in req) { + if(req.action == "fill_user_pass_with_specific_login") { + if(cip.credentials[req.id]) { + var combination = null; + if (cip.u) { + cip.setValueWithChange(cip.u, cip.credentials[req.id].login); + combination = cipFields.getCombination("username", cip.u); + cip.u.focus(); + } + if (cip.p) { + cip.setValueWithChange(cip.p, cip.credentials[req.id].password); + combination = cipFields.getCombination("password", cip.p); + } + + var list = {}; + if(cip.fillInStringFields(combination.fields, cip.credentials[req.id].StringFields, list)) { + cipForm.destroy(false, {"password": list.list[0], "username": list.list[1]}); + } + } + // wish I could clear out _logins and _u, but a subsequent + // selection may be requested. + } + else if (req.action == "fill_user_pass") { + cip.receiveCredentialsIfNecessary(); + cip.fillInFromActiveElement(false); + } + else if (req.action == "fill_pass_only") { + cip.receiveCredentialsIfNecessary(); + cip.fillInFromActiveElementPassOnly(false); + } + else if (req.action == "activate_password_generator") { + cip.initPasswordGenerator(cipFields.getAllFields()); + } + else if(req.action == "remember_credentials") { + cip.contextMenuRememberCredentials(); + } + else if (req.action == "choose_credential_fields") { + cipDefine.init(); + } + else if (req.action == "clear_credentials") { + cipEvents.clearCredentials(); + } + else if (req.action == "activated_tab") { + cipEvents.triggerActivatedTab(); + } + else if (req.action == "redetect_fields") { + chrome.extension.sendMessage({ + "action": "get_settings", + }, function(response) { + cip.settings = response.data; + cip.initCredentialFields(true); + }); + } + } +}); + +// Hotkeys for every page +// ctrl + shift + p = fill only password +// ctrl + shift + u = fill username + password +window.addEventListener("keydown", function(e) { + if (e.ctrlKey && e.shiftKey) { + if (e.key == "KeyP" || e.keyIdentifier == "U+0050") { // P + e.preventDefault(); + cip.receiveCredentialsIfNecessary(); + cip.fillInFromActiveElementPassOnly(false); + } else if (e.key == "KeyU" || e.keyIdentifier == "U+0055") { // U + e.preventDefault(); + cip.receiveCredentialsIfNecessary(); + cip.fillInFromActiveElement(false); + var field =_f(cipFields.combinations[0].username); + cipAutocomplete.init(field); + field.click(); + } + } +}, false); + +function _f(fieldId) { + var field = (fieldId) ? cIPJQ("input[data-cip-id='"+fieldId+"']:first") : []; + return (field.length > 0) ? field : null; +} + +function _fs(fieldId) { + var field = (fieldId) ? cIPJQ("input[data-cip-id='"+fieldId+"']:first,select[data-cip-id='"+fieldId+"']:first").first() : []; + return (field.length > 0) ? field : null; +} + + + +var cipAutocomplete = {}; + +// objects of username + description for autocomplete +cipAutocomplete.elements = []; + +cipAutocomplete.init = function(field) { + if(field.hasClass("cip-ui-autocomplete-input")) { + //_f(credentialInputs[i].username).autocomplete("source", autocompleteSource); + field.autocomplete("destroy"); + } + + field + .autocomplete({ + minLength: 0, + source: cipAutocomplete.onSource, + select: cipAutocomplete.onSelect, + open: cipAutocomplete.onOpen + }); + field + .click(cipAutocomplete.onClick) + .blur(cipAutocomplete.onBlur) + .focus(cipAutocomplete.onFocus); +} + +cipAutocomplete.onClick = function() { + cIPJQ(this).autocomplete("search", cIPJQ(this).val()); +} + +cipAutocomplete.onOpen = function(event, ui) { + // NOT BEAUTIFUL! + // modifies ALL ui-autocomplete menus of class .cip-ui-menu + cIPJQ("ul.cip-ui-autocomplete.cip-ui-menu").css("z-index", 2147483636); +} + +cipAutocomplete.onSource = function (request, callback) { + var matches = cIPJQ.map( cipAutocomplete.elements, function(tag) { + if ( tag.label.toUpperCase().indexOf(request.term.toUpperCase()) === 0 ) { + return tag; + } + }); + callback(matches); +} + +cipAutocomplete.onSelect = function (e, ui) { + e.preventDefault(); + cip.setValueWithChange(cIPJQ(this), ui.item.value); + var fieldId = cipFields.prepareId(cIPJQ(this).attr("data-cip-id")); + var combination = cipFields.getCombination("username", fieldId); + combination.loginId = ui.item.loginId; + cip.fillInCredentials(combination, true, false); + cIPJQ(this).data("fetched", true); +} + +cipAutocomplete.onBlur = function() { + if(cIPJQ(this).data("fetched") == true) { + cIPJQ(this).data("fetched", false); + } + else { + var fieldId = cipFields.prepareId(cIPJQ(this).attr("data-cip-id")); + var fields = cipFields.getCombination("username", fieldId); + if(_f(fields.password) && _f(fields.password).data("unchanged") != true && cIPJQ(this).val() != "") { + cip.fillInCredentials(fields, true, true); + } + } +} + +cipAutocomplete.onFocus = function() { + cip.u = cIPJQ(this); + + if(cIPJQ(this).val() == "") { + cIPJQ(this).autocomplete("search", ""); + } +} + + + +var cipPassword = {}; + +cipPassword.observedIcons = []; +cipPassword.observingLock = false; + +cipPassword.init = function() { + if("initPasswordGenerator" in _called) { + return; + } + + _called.initPasswordGenerator = true; + + window.setInterval(function() { + cipPassword.checkObservedElements(); + }, 400); +} + +cipPassword.initField = function(field, inputs, pos) { + if(!field || field.length != 1) { + return; + } + if(field.data("cip-password-generator")) { + return; + } + + field.data("cip-password-generator", true); + + cipPassword.createIcon(field); + cipPassword.createDialog(); + + var $found = false; + if(inputs) { + for(var i = pos + 1; i < inputs.length; i++) { + if(inputs[i] && inputs[i].attr("type") && inputs[i].attr("type").toLowerCase() == "password") { + field.data("cip-genpw-next-field-id", inputs[i].data("cip-id")); + field.data("cip-genpw-next-is-password-field", (i == 0)); + $found = true; + break; + } + } + } + + field.data("cip-genpw-next-field-exists", $found); +} + +cipPassword.createDialog = function() { + if("passwordCreateDialog" in _called) { + return; + } + + _called.passwordCreateDialog = true; + + var $dialog = cIPJQ("
") + .attr("id", "cip-genpw-dialog"); + + var $divFloat = cIPJQ("
").addClass("cip-genpw-clearfix"); + var $btnGenerate = cIPJQ("