Successfully reported this slideshow.

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Related Audiobooks

Free with a 14 day trial from Scribd

See all

Dodging WebCrypto API Landmines

  1. 1. Dodging Web Crypto API Landmines
  2. 2. @erniewturner
  3. 3. Finalized! January 2017 WebCrypto API - W3C JavaScript API for performing basic cryptographic operations in web applications, such as hashing, signature generation and verification, and encryption and decryption. @erniewturner
  4. 4. Web Browser ServerIn Transit Why WebCrypto API @erniewturner
  5. 5. Web Browser SSL ServerIn Transit Why WebCrypto API @erniewturner
  6. 6. Cryptographically Strong PRNG window.crypto.getRandomValues() @erniewturner
  7. 7. Subtle Crypto window.crypto.subtle.* @erniewturner It is named SubtleCrypto to reflect the fact that many of these algorithms have subtle usage requirements in order to provide the required algorithmic security guarantees. -W3C It is named SubtleCrypto to reflect the fact that many of these algorithms have subtle usage requirements in order to provide the required algorithmic security guarantees. -W3C
  8. 8. Subtle Crypto Developers making use of the SubtleCrypto interface are expected to be aware of the security concerns associated with both the design and implementation of the various algorithms provided. The raw algorithms are provided in order to allow developers maximum flexibility in implementing a variety of protocols and applications, each of which may represent the composition and security parameters in a unique manner that necessitate the use of the raw algorithms. -MDN @erniewturner
  9. 9. Subtle Crypto Methods are generic and take crypto algorithms as strings or objects Nearly all operations return Promises Only available over HTTPS @erniewturner
  10. 10. Subtle Crypto decrypt deriveKey digest encrypt exportKey generateKey importKey sign unwrapKey verify wrapKey @erniewturner
  11. 11. Subtle Crypto digest exportKey generateKey sign unwrapKey verify wrapKey @erniewturner decrypt deriveKey encrypt importKey
  12. 12. Subtle Crypto RSA ECDSA ECDH SHA HMAC @erniewturner AES PBKDF2
  13. 13. Subtle Crypto RSA ECDSA ECDH SHA HMAC @erniewturner AES PBKDF2
  14. 14. Typed Arrays Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array Array-like view into binary data @erniewturner
  15. 15. Uint8Array @erniewturner 10110010100101010100110010101010
  16. 16. Uint8Array @erniewturner 10110010 10010101 01001100 10101010
  17. 17. Uint8Array @erniewturner 178 149 76 170
  18. 18. Uint8Array @erniewturner 178 149 76 170, ,, ][
  19. 19. const empty = new Uint8Array(32); Uint8Array [0, 0, 0, ...] const fixed = new Uint8Array([35, 183, 21, 111]); [35, 183, 21, 111] const text = UTF8.encode('text'); [116, 101, 120, 116] const flag = UTF8.encode('!'); [240, 159, 135, 179, 240, 159, 135, 180] @erniewturner
  20. 20. CryptoKey @erniewturner
  21. 21. Symmetric Key Algorithm @erniewturner AES-256 GCM DecryptEncrypt Symmetric Key Alice Bob
  22. 22. AES-256 GCM @erniewturner 256 bit = 32 byte 96 bit = 12 byte AES-GCM Crypto Key Initialization Vector
  23. 23. Military Grade AES-256 GCM
  24. 24. PBKDF2 @erniewturner Password Based Key Derivation Function 2 User Password Crypto Key
  25. 25. PBKDF2 SHA-256 User password Salt AES-GCM Crypto Key PBKDF2 @erniewturner
  26. 26. Browser Support Edge IE Chrome Firefox Safari PRNG Algorithms @erniewturner Typed Arrays
  27. 27. ENCRYPT Example Project @erniewturner …….. Password
  28. 28. Encryption @erniewturner
  29. 29. User Adds Data User Enters Password Convert Password to CryptoKey Derive AES Key Encrypt Data Encrypt @erniewturner
  30. 30. function encryptData(secretData: string, password: string){ const dataAsBytes = UTF8.encode(secretData); const passwordAsBytes = UTF8.encode(password); }
  31. 31. Dat Passcod Derive AES EncryImport function encryptData(secretData: string, password: string){ const dataAsBytes = UTF8.encode(secretData); const passwordAsBytes = UTF8.encode(password); }
  32. 32. function encryptData(secretData: string, password: string){ const dataAsBytes = UTF8.encode(secretData); const passwordAsBytes = UTF8.encode(password); }
  33. 33. function encryptData(secretData: string, passwo const dataAsBytes = UTF8.encode(secretData); const passwordAsBytes = UTF8.encode(password) } Data to Encrypt byte array form User Passcode in binary formbyte array form
  34. 34. User Password Import Key User Passcode Crypto Key importKey @erniewturner byte array form
  35. 35. @erniewturner importKey window.crypto.subtle.importKey( format: string, keyData: Uint8Array, algo: object|string, extractable: boolean, usages: string[] )
  36. 36. function encryptData(secretData: string, password: string){ const dataAsBytes = UT8.encode(secretData); const passwordAsBytes = UTF8.encode(password); window.crypto.subtle.importKey( “raw", passwordAsBytes, 'PBKDF2', false [‘deriveKey’] ) .then((passwordKey: CryptoKey) => { }); }
  37. 37. function encryptData(secretData: string, password: string){ const dataAsBytes = UT8.encode(secretData); const passwordAsBytes = UTF8.encode(password); window.crypto.subtle.importKey( “raw", passwordAsBytes, 'PBKDF2', false [‘deriveKey’] ) .then((passwordKey: CryptoKey) => { }); }
  38. 38. function encryptData(secretData: string, password: string){ const dataAsBytes = UT8.encode(secretData); const passwordAsBytes = UTF8.encode(password); window.crypto.subtle.importKey( “raw", passwordAsBytes, 'PBKDF2', false [‘deriveKey’] ) .then((passwordKey: CryptoKey) => { }); }
  39. 39. function encryptData(secretData: string, passwor const dataAsBytes = UT8.encode(secretData); const passwordAsBytes = UTF8.encode(password); window.crypto.subtle.importKey( “raw", passwordAsBytes, 'PBKDF2', false [‘deriveKey’] ) .then((passwordKey: CryptoKey) => { }); } Data to Encrypt byte array form User Password Crypto Key
  40. 40. deriveKey @erniewturner PBKDF2 SHA-256 User Password Crypto Key Salt AES-GCM Crypto Key
  41. 41. deriveKey window.crypto.subtle.deriveKey( algorithm: object, masterKey: CryptoKey, derivedKeyAlgorithm: object, extractable: boolean, usages: string[] ) @erniewturner
  42. 42. .then((passwordKey: CryptoKey) => { const salt = window.crypto.getRandomValues(new Uint8Array(12)); return window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 250000, hash: {name: 'SHA-256'} }, passwordKey, {name: 'AES-GCM', length: 256}, false, ['encrypt']); }) .then((aesKey: CryptoKey) => { });
  43. 43. .then((passwordKey: CryptoKey) => { const salt = window.crypto.getRandomValues(new Uint8Array(32)); return window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 250000, hash: {name: 'SHA-256'} }, passwordKey, {name: 'AES-GCM', length: 256}, false, ['encrypt']); }) .then((aesKey: CryptoKey) => { });
  44. 44. .then((passwordKey: CryptoKey) => { const salt = window.crypto.getRandomValues(new Uint8Array(32)); return window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 250000, hash: {name: 'SHA-256'} }, passwordKey, {name: 'AES-GCM', length: 256}, false, ['encrypt']); }) .then((aesKey: CryptoKey) => { });
  45. 45. .then((passwordKey: CryptoKey) => { const salt = window.crypto.getRandomValues(new Uint8Array(32)); return window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 250000, hash: {name: 'SHA-256'} }, passwordKey, {name: 'AES-GCM', length: 256}, false, ['encrypt']); }) .then((aesKey: CryptoKey) => { });
  46. 46. .then((passwordKey: CryptoKey) => { const salt = window.crypto.getRandomValues(new Uint8Array(32)); return window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 250000, hash: {name: 'SHA-256'} }, passwordKey, {name: 'AES-GCM', length: 256}, false, ['encrypt']); }) .then((aesKey: CryptoKey) => { });
  47. 47. Data to Encrypt byte array form .then((passwordKey: CryptoKey) => { const salt = window.crypto.getRandomValues(new U return window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 250000, hash: {name: 'SHA-256'} }, passwordKey, {name: 'AES-GCM', length: 256}, }) .then((aesKey: CryptoKey) => { }); AES-GCM Crypto Key
  48. 48. Encrypt @erniewturner byte array form Encrypted DataEncrypt AES-GCM Crypto Key Initialization Vector Data to Encrypt
  49. 49. Encrypt window.crypto.subtle.encrypt( algorithm: object, key: CryptoKey, data: Uint8Array ) @erniewturner
  50. 50. .then((aesKey: CryptoKey) => { const iv = window.crypto.getRandomValues(new Uint8Array(12)); return window.crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, aesKey, dataAsBytes); }) .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); });
  51. 51. .then((aesKey: CryptoKey) => { const iv = window.crypto.getRandomValues(new Uint8Array(12)); return window.crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, aesKey, dataAsBytes); }) .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); });
  52. 52. .then((aesKey: CryptoKey) => { const iv = window.crypto.getRandomValues(new Uint8Array(12)); return window.crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, aesKey, dataAsBytes); }) .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); });
  53. 53. .then((aesKey: CryptoKey) => { const iv = window.crypto.getRandomValues(new Uint8Array(12)); return window.crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, aesKey, dataAsBytes); }) .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); });
  54. 54. Encrypted Data .then((aesKey: CryptoKey) => { const iv = window.crypto.getRandomValues(new Uint8Ar return window.crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, aesKey, dataAsBytes); }) .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedConte }); byte array form
  55. 55. Storage 256 bit = 32 byte (Uint8Array) 96 bit = 12 byte (Uint8Array) > 0 bytes (Uint8Array) @erniewturner Salt Encrypted Data Initialization Vector PBKDF2 AES-GCM
  56. 56. Storage 32B Salt 12B IV Encrypted Data+ + @erniewturner
  57. 57. Encrypted Data32B Salt 12B IV Storage @erniewturner
  58. 58. Storage @erniewturner Base64 String TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB…..…
  59. 59. .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); const encryptedPackage = concat( salt, iv, encryptedBytes ); return Base64.fromByteArray(encryptedPackage); });
  60. 60. .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); const encryptedPackage = concat( salt, iv, encryptedBytes ); return Base64.fromByteArray(encryptedPackage); });
  61. 61. .then((encryptedContent: ArrayBuffer) => { const encryptedBytes = new Uint8Array(encryptedContent); const encryptedPackage = concat( salt, iv, encryptedBytes ); return Base64.fromByteArray(encryptedPackage); });
  62. 62. Decryption @erniewturner
  63. 63. Get Encrypted Data User Enters Password Convert Password to CryptoKey Derive AES Key Decrypt Data Decrypt @erniewturner
  64. 64. DECRYPT Decryption @erniewturner …….. Password
  65. 65. Decryption EncryptedData32BSalt12BIV @erniewturner Decrypted Data Decrypt PBKDF2 SHA-256 Crypto Key Salt AES-GCM Crypto Key Initialization Vector Encrypted Data
  66. 66. function decryptData(encryptedData: string, password: string){ const encryptedBytes = Base64.toByteArray(encryptedData); const salt = encryptedBytes.slice(0, 32); const IV = encryptedBytes.slice(32, 12); const encryptedData = encryptedBytes.slice(32 + 12); }
  67. 67. function decryptData(encryptedData: string, password: string){ const encryptedBytes = Base64.toByteArray(encryptedData); const salt = encryptedBytes.slice(0, 32); const IV = encryptedBytes.slice(32, 12); const encryptedData = encryptedBytes.slice(32 + 12); }
  68. 68. function decryptData(encryptedData: string, password: string){ const encryptedBytes = Base64.toByteArray(encryptedData); const salt = encryptedBytes.slice(0, 32); const IV = encryptedBytes.slice(32, 12); const encryptedData = encryptedBytes.slice(32 + 12); }
  69. 69. Decrypt window.crypto.decrypt( algorithm: object, key: CryptoKey, data: Uint8Array ); @erniewturner
  70. 70. return window.crypto.subtle.importKey(...) .then(() => window.crypto.subtle.deriveKey(...)) .then((aesKey: CryptoKey) => { return window.crypto.subtle.decrypt({ name: 'AES-GCM', iv, }, aesKey, encryptedData); }) .then((decryptedContent: ArrayBuffer) => { const decryptedBytes = new Uint8Array(decryptedContent); }); }
  71. 71. return window.crypto.subtle.importKey(...) .then(() => window.crypto.subtle.deriveKey(...)) .then((aesKey: CryptoKey) => { return window.crypto.subtle.decrypt({ name: 'AES-GCM', iv, }, aesKey, encryptedData); }) .then((decryptedContent: ArrayBuffer) => { const decryptedBytes = new Uint8Array(decryptedContent); }); }
  72. 72. return window.crypto.subtle.importKey(...) .then(() => window.crypto.subtle.deriveKey(...)) .then((aesKey: CryptoKey) => { return window.crypto.subtle.decrypt({ name: 'AES-GCM', iv, }, aesKey, encryptedData); }) .then((decryptedContent: ArrayBuffer) => { const decryptedBytes = new Uint8Array(decryptedContent); }); }
  73. 73. return window.crypto.subtle.importKey(...) .then(() => window.crypto.subtle.deriveKey(...)) .then((aesKey: CryptoKey) => { return window.crypto.subtle.decrypt({ name: ‘AES-GCM', iv, }, aesKey, encryptedData); }) .then((decryptedContent: ArrayBuffer) => { const decryptedBytes = new Uint8Array(decryptedContent); }); }
  74. 74. Important Notes Key derivation will not fail if user enters wrong password PBKDF2 Iterations must be the same on encrypt as on decrypt There is no “forgot password” support @erniewturner There is no way to feature detect which algorithms are supported
  75. 75. POLYFILL You CANNOT polyfill random number generation. Stanford Javascript Crypto Library @erniewturner
  76. 76. Title PBKDF2 
 (250K) Native SJCL 1203ms 132ms Native SJCL Performance @erniewturner Desktop - MBP 2016
  77. 77. Title PBKDF2 
 (250K) Native SJCL 4310ms 137ms Native SJCL Performance @erniewturner MOBILE - Pixel 1
  78. 78. Title AES 
 (1 KB) Native SJCL 3.2ms 0.3ms Native SJCL Performance @erniewturner Desktop - MBP 2016
  79. 79. Title AES 
 (1 KB) Native SJCL 4.8ms 1.1ms Native SJCL Performance @erniewturner MOBILE - Pixel 1
  80. 80. Title AES 
 (10MB) Native SJCL 3015ms 27ms Native SJCL Performance @erniewturner Desktop - MBP 2016
  81. 81. Title AES 
 (10MB) Native SJCL 7781ms 195ms Native SJCL Performance @erniewturner MOBILE - Pixel 1
  82. 82. WEB WORKERS Bytes to encrypt/decrypt
 User password bytes Encrypted/decrypted bytes MAIN THREAD WEB WORKER
  83. 83. Thank You @erniewturner ernieturner @ironcorelabs ironcorelabs.com Ernie Turner

×