corrade-nucleus-nucleons – Blame information for rev 26

Subversion Repositories:
Rev:
Rev Author Line No. Line
22 office 1 /**
2 * Javascript implementation of PKCS#1 PSS signature padding.
3 *
4 * @author Stefan Siegl
5 *
6 * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
7 */
8 var forge = require('./forge');
9 require('./random');
10 require('./util');
11  
12 // shortcut for PSS API
13 var pss = module.exports = forge.pss = forge.pss || {};
14  
15 /**
16 * Creates a PSS signature scheme object.
17 *
18 * There are several ways to provide a salt for encoding:
19 *
20 * 1. Specify the saltLength only and the built-in PRNG will generate it.
21 * 2. Specify the saltLength and a custom PRNG with 'getBytesSync' defined that
22 * will be used.
23 * 3. Specify the salt itself as a forge.util.ByteBuffer.
24 *
25 * @param options the options to use:
26 * md the message digest object to use, a forge md instance.
27 * mgf the mask generation function to use, a forge mgf instance.
28 * [saltLength] the length of the salt in octets.
29 * [prng] the pseudo-random number generator to use to produce a salt.
30 * [salt] the salt to use when encoding.
31 *
32 * @return a signature scheme object.
33 */
34 pss.create = function(options) {
35 // backwards compatibility w/legacy args: hash, mgf, sLen
36 if(arguments.length === 3) {
37 options = {
38 md: arguments[0],
39 mgf: arguments[1],
40 saltLength: arguments[2]
41 };
42 }
43  
44 var hash = options.md;
45 var mgf = options.mgf;
46 var hLen = hash.digestLength;
47  
48 var salt_ = options.salt || null;
49 if(typeof salt_ === 'string') {
50 // assume binary-encoded string
51 salt_ = forge.util.createBuffer(salt_);
52 }
53  
54 var sLen;
55 if('saltLength' in options) {
56 sLen = options.saltLength;
57 } else if(salt_ !== null) {
58 sLen = salt_.length();
59 } else {
60 throw new Error('Salt length not specified or specific salt not given.');
61 }
62  
63 if(salt_ !== null && salt_.length() !== sLen) {
64 throw new Error('Given salt length does not match length of given salt.');
65 }
66  
67 var prng = options.prng || forge.random;
68  
69 var pssobj = {};
70  
71 /**
72 * Encodes a PSS signature.
73 *
74 * This function implements EMSA-PSS-ENCODE as per RFC 3447, section 9.1.1.
75 *
76 * @param md the message digest object with the hash to sign.
77 * @param modsBits the length of the RSA modulus in bits.
78 *
79 * @return the encoded message as a binary-encoded string of length
80 * ceil((modBits - 1) / 8).
81 */
82 pssobj.encode = function(md, modBits) {
83 var i;
84 var emBits = modBits - 1;
85 var emLen = Math.ceil(emBits / 8);
86  
87 /* 2. Let mHash = Hash(M), an octet string of length hLen. */
88 var mHash = md.digest().getBytes();
89  
90 /* 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. */
91 if(emLen < hLen + sLen + 2) {
92 < hLen + sLen + 2) { throw new Error('Message is too long to encrypt.');
93 < hLen + sLen + 2) { }
94  
95 < hLen + sLen + 2) { /* 4. Generate a random octet string salt of length sLen; if sLen = 0,
96 < hLen + sLen + 2) { * then salt is the empty string. */
97 < hLen + sLen + 2) { var salt;
98 < hLen + sLen + 2) { if(salt_ === null) {
99 < hLen + sLen + 2) { salt = prng.getBytesSync(sLen);
100 < hLen + sLen + 2) { } else {
101 < hLen + sLen + 2) { salt = salt_.bytes();
102 < hLen + sLen + 2) { }
103  
104 < hLen + sLen + 2) { /* 5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; */
105 < hLen + sLen + 2) { var m_ = new forge.util.ByteBuffer();
106 < hLen + sLen + 2) { m_.fillWithByte(0, 8);
107 < hLen + sLen + 2) { m_.putBytes(mHash);
108 < hLen + sLen + 2) { m_.putBytes(salt);
109  
110 < hLen + sLen + 2) { /* 6. Let H = Hash(M'), an octet string of length hLen. */
111 < hLen + sLen + 2) { hash.start();
112 < hLen + sLen + 2) { hash.update(m_.getBytes());
113 < hLen + sLen + 2) { var h = hash.digest().getBytes();
114  
115 < hLen + sLen + 2) { /* 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
116 < hLen + sLen + 2) { * zero octets. The length of PS may be 0. */
117 < hLen + sLen + 2) { var ps = new forge.util.ByteBuffer();
118 < hLen + sLen + 2) { ps.fillWithByte(0, emLen - sLen - hLen - 2);
119  
120 < hLen + sLen + 2) { /* 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
121 < hLen + sLen + 2) { * emLen - hLen - 1. */
122 < hLen + sLen + 2) { ps.putByte(0x01);
123 < hLen + sLen + 2) { ps.putBytes(salt);
124 < hLen + sLen + 2) { var db = ps.getBytes();
125  
126 < hLen + sLen + 2) { /* 9. Let dbMask = MGF(H, emLen - hLen - 1). */
127 < hLen + sLen + 2) { var maskLen = emLen - hLen - 1;
128 < hLen + sLen + 2) { var dbMask = mgf.generate(h, maskLen);
129  
130 < hLen + sLen + 2) { /* 10. Let maskedDB = DB \xor dbMask. */
131 < hLen + sLen + 2) { var maskedDB = '';
132 < hLen + sLen + 2) { for(i = 0; i < maskLen; i++) {
133 < hLen + sLen + 2) {< maskLen; i++) { maskedDB += String.fromCharCode(db.charCodeAt(i) ^ dbMask.charCodeAt(i));
134 < hLen + sLen + 2) {< maskLen; i++) { }
135  
136 < hLen + sLen + 2) {< maskLen; i++) { /* 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
137 < hLen + sLen + 2) {< maskLen; i++) { * maskedDB to zero. */
138 < hLen + sLen + 2) {< maskLen; i++) { var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
139 < hLen + sLen + 2) {< maskLen; i++) { maskedDB = String.fromCharCode(maskedDB.charCodeAt(0) & ~mask) +
140 < hLen + sLen + 2) {< maskLen; i++) { maskedDB.substr(1);
141  
142 < hLen + sLen + 2) {< maskLen; i++) { /* 12. Let EM = maskedDB || H || 0xbc.
143 < hLen + sLen + 2) {< maskLen; i++) { * 13. Output EM. */
144 < hLen + sLen + 2) {< maskLen; i++) { return maskedDB + h + String.fromCharCode(0xbc);
145 < hLen + sLen + 2) {< maskLen; i++) { };
146  
147 < hLen + sLen + 2) {< maskLen; i++) { /**
148 < hLen + sLen + 2) {< maskLen; i++) { * Verifies a PSS signature.
149 < hLen + sLen + 2) {< maskLen; i++) { *
150 < hLen + sLen + 2) {< maskLen; i++) { * This function implements EMSA-PSS-VERIFY as per RFC 3447, section 9.1.2.
151 < hLen + sLen + 2) {< maskLen; i++) { *
152 < hLen + sLen + 2) {< maskLen; i++) { * @param mHash the message digest hash, as a binary-encoded string, to
153 < hLen + sLen + 2) {< maskLen; i++) { * compare against the signature.
154 < hLen + sLen + 2) {< maskLen; i++) { * @param em the encoded message, as a binary-encoded string
155 < hLen + sLen + 2) {< maskLen; i++) { * (RSA decryption result).
156 < hLen + sLen + 2) {< maskLen; i++) { * @param modsBits the length of the RSA modulus in bits.
157 < hLen + sLen + 2) {< maskLen; i++) { *
158 < hLen + sLen + 2) {< maskLen; i++) { * @return true if the signature was verified, false if not.
159 < hLen + sLen + 2) {< maskLen; i++) { */
160 < hLen + sLen + 2) {< maskLen; i++) { pssobj.verify = function(mHash, em, modBits) {
161 < hLen + sLen + 2) {< maskLen; i++) { var i;
162 < hLen + sLen + 2) {< maskLen; i++) { var emBits = modBits - 1;
163 < hLen + sLen + 2) {< maskLen; i++) { var emLen = Math.ceil(emBits / 8);
164  
165 < hLen + sLen + 2) {< maskLen; i++) { /* c. Convert the message representative m to an encoded message EM
166 < hLen + sLen + 2) {< maskLen; i++) { * of length emLen = ceil((modBits - 1) / 8) octets, where modBits
167 < hLen + sLen + 2) {< maskLen; i++) { * is the length in bits of the RSA modulus n */
168 < hLen + sLen + 2) {< maskLen; i++) { em = em.substr(-emLen);
169  
170 < hLen + sLen + 2) {< maskLen; i++) { /* 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. */
171 < hLen + sLen + 2) {< maskLen; i++) { if(emLen < hLen + sLen + 2) {
172 < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Inconsistent parameters to PSS signature verification.');
173 < hLen + sLen + 2) {< maskLen; i++) { }
174  
175 < hLen + sLen + 2) {< maskLen; i++) { /* 4. If the rightmost octet of EM does not have hexadecimal value
176 < hLen + sLen + 2) {< maskLen; i++) { * 0xbc, output "inconsistent" and stop. */
177 < hLen + sLen + 2) {< maskLen; i++) { if(em.charCodeAt(emLen - 1) !== 0xbc) {
178 < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Encoded message does not end in 0xBC.');
179 < hLen + sLen + 2) {< maskLen; i++) { }
180  
181 < hLen + sLen + 2) {< maskLen; i++) { /* 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
182 < hLen + sLen + 2) {< maskLen; i++) { * let H be the next hLen octets. */
183 < hLen + sLen + 2) {< maskLen; i++) { var maskLen = emLen - hLen - 1;
184 < hLen + sLen + 2) {< maskLen; i++) { var maskedDB = em.substr(0, maskLen);
185 < hLen + sLen + 2) {< maskLen; i++) { var h = em.substr(maskLen, hLen);
186  
187 < hLen + sLen + 2) {< maskLen; i++) { /* 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
188 < hLen + sLen + 2) {< maskLen; i++) { * maskedDB are not all equal to zero, output "inconsistent" and stop. */
189 < hLen + sLen + 2) {< maskLen; i++) { var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
190 < hLen + sLen + 2) {< maskLen; i++) { if((maskedDB.charCodeAt(0) & mask) !== 0) {
191 < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Bits beyond keysize not zero as expected.');
192 < hLen + sLen + 2) {< maskLen; i++) { }
193  
194 < hLen + sLen + 2) {< maskLen; i++) { /* 7. Let dbMask = MGF(H, emLen - hLen - 1). */
195 < hLen + sLen + 2) {< maskLen; i++) { var dbMask = mgf.generate(h, maskLen);
196  
197 < hLen + sLen + 2) {< maskLen; i++) { /* 8. Let DB = maskedDB \xor dbMask. */
198 < hLen + sLen + 2) {< maskLen; i++) { var db = '';
199 < hLen + sLen + 2) {< maskLen; i++) { for(i = 0; i < maskLen; i++) {
200 < hLen + sLen + 2) {< maskLen; i++) { db += String.fromCharCode(maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i));
201 < hLen + sLen + 2) {< maskLen; i++) { }
202  
203 < hLen + sLen + 2) {< maskLen; i++) { /* 9. Set the leftmost 8emLen - emBits bits of the leftmost octet
204 < hLen + sLen + 2) {< maskLen; i++) { * in DB to zero. */
205 < hLen + sLen + 2) {< maskLen; i++) { db = String.fromCharCode(db.charCodeAt(0) & ~mask) + db.substr(1);
206  
207 < hLen + sLen + 2) {< maskLen; i++) { /* 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
208 < hLen + sLen + 2) {< maskLen; i++) { * or if the octet at position emLen - hLen - sLen - 1 (the leftmost
209 < hLen + sLen + 2) {< maskLen; i++) { * position is "position 1") does not have hexadecimal value 0x01,
210 < hLen + sLen + 2) {< maskLen; i++) { * output "inconsistent" and stop. */
211 < hLen + sLen + 2) {< maskLen; i++) { var checkLen = emLen - hLen - sLen - 2;
212 < hLen + sLen + 2) {< maskLen; i++) { for(i = 0; i < checkLen; i++) {
213 < hLen + sLen + 2) {< maskLen; i++) { if(db.charCodeAt(i) !== 0x00) {
214 < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Leftmost octets not zero as expected');
215 < hLen + sLen + 2) {< maskLen; i++) { }
216 < hLen + sLen + 2) {< maskLen; i++) { }
217  
218 < hLen + sLen + 2) {< maskLen; i++) { if(db.charCodeAt(checkLen) !== 0x01) {
219 < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Inconsistent PSS signature, 0x01 marker not found');
220 < hLen + sLen + 2) {< maskLen; i++) { }
221  
222 < hLen + sLen + 2) {< maskLen; i++) { /* 11. Let salt be the last sLen octets of DB. */
223 < hLen + sLen + 2) {< maskLen; i++) { var salt = db.substr(-sLen);
224  
225 < hLen + sLen + 2) {< maskLen; i++) { /* 12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
226 < hLen + sLen + 2) {< maskLen; i++) { var m_ = new forge.util.ByteBuffer();
227 < hLen + sLen + 2) {< maskLen; i++) { m_.fillWithByte(0, 8);
228 < hLen + sLen + 2) {< maskLen; i++) { m_.putBytes(mHash);
229 < hLen + sLen + 2) {< maskLen; i++) { m_.putBytes(salt);
230  
231 < hLen + sLen + 2) {< maskLen; i++) { /* 13. Let H' = Hash(M'), an octet string of length hLen. */
232 < hLen + sLen + 2) {< maskLen; i++) { hash.start();
233 < hLen + sLen + 2) {< maskLen; i++) { hash.update(m_.getBytes());
234 < hLen + sLen + 2) {< maskLen; i++) { var h_ = hash.digest().getBytes();
235  
236 < hLen + sLen + 2) {< maskLen; i++) { /* 14. If H = H', output "consistent." Otherwise, output "inconsistent." */
237 < hLen + sLen + 2) {< maskLen; i++) { return h === h_;
238 < hLen + sLen + 2) {< maskLen; i++) { };
239  
240 < hLen + sLen + 2) {< maskLen; i++) { return pssobj;
241 < hLen + sLen + 2) {< maskLen; i++) {};