Repository: pbhogan/scrypt Branch: master Commit: 2491ad3c9865 Files: 60 Total size: 152.2 KB Directory structure: gitextract_x0o4l56k/ ├── .document ├── .github/ │ └── workflows/ │ └── ruby.yml ├── .gitignore ├── .rdoc_options ├── .rspec ├── .rubocop.yml ├── .ruby-gemset ├── .ruby-version ├── CHANGELOG.md ├── COPYING ├── Gemfile ├── README.md ├── Rakefile ├── certs/ │ ├── pbhogan.pem │ └── stakach.pem ├── checksum/ │ └── scrypt-3.0.6.gem.sha512 ├── ext/ │ ├── alt-impl/ │ │ ├── crypto_scrypt-nosse.c │ │ └── crypto_scrypt-ref.c │ └── scrypt/ │ ├── Rakefile │ ├── cpusupport.h │ ├── crypto_scrypt.c │ ├── crypto_scrypt.h │ ├── crypto_scrypt_internal.h │ ├── crypto_scrypt_smix.c │ ├── crypto_scrypt_smix.h │ ├── crypto_scrypt_smix_sse2.c │ ├── crypto_scrypt_smix_sse2.h │ ├── insecure_memzero.c │ ├── insecure_memzero.h │ ├── memlimit.c │ ├── memlimit.h │ ├── scrypt_calibrate.c │ ├── scrypt_calibrate.h │ ├── scrypt_ext.c │ ├── scrypt_ext.h │ ├── scrypt_platform.h │ ├── scryptenc_cpuperf.c │ ├── scryptenc_cpuperf.h │ ├── sha256.c │ ├── sha256.h │ ├── sysendian.h │ ├── warnp.c │ └── warnp.h ├── lib/ │ ├── scrypt/ │ │ ├── engine.rb │ │ ├── errors.rb │ │ ├── password.rb │ │ ├── scrypt_ext.rb │ │ ├── security_utils.rb │ │ └── version.rb │ └── scrypt.rb ├── scrypt.gemspec └── spec/ ├── fixtures/ │ └── test_vectors.yml ├── scrypt/ │ ├── engine_spec.rb │ ├── ffi_spec.rb │ ├── integration_spec.rb │ ├── password_spec.rb │ └── utils_spec.rb ├── spec_helper.rb └── support/ ├── shared_examples.rb └── test_helpers.rb ================================================ FILE CONTENTS ================================================ ================================================ FILE: .document ================================================ README.md COPYING /lib/**/*.rb ================================================ FILE: .github/workflows/ruby.yml ================================================ # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby name: Ruby on: push: branches: [ "master" ] pull_request: branches: [ "master" ] permissions: contents: read jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: # Testing on Ubuntu for the time being to avoid potential cost issues with macOS runners os: [ubuntu-latest] ruby-version: ['2.3', '2.4', '2.5', '2.6', ' 2.7', '3.0', '3.1', '3.2', '3.3', '3.4', head, jruby, jruby-head, truffleruby, truffleruby-head] steps: - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run tests run: bundle exec rake ================================================ FILE: .gitignore ================================================ # Created by https://www.gitignore.io/api/c,ruby,macos,rubymine,visualstudiocode # Edit at https://www.gitignore.io/?templates=c,ruby,macos,rubymine,visualstudiocode ### C ### # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf ### macOS ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Ruby ### *.gem *.rbc /.config /coverage/ /InstalledFiles /pkg/ /spec/reports/ /spec/examples.txt /test/tmp/ /test/version_tmp/ /tmp/ # Used by dotenv library to load environment variables. # .env # Ignore Byebug command history file. .byebug_history ## Specific to RubyMotion: .dat* .repl_history build/ *.bridgesupport build-iPhoneOS/ build-iPhoneSimulator/ ## Specific to RubyMotion (use of CocoaPods): # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # vendor/Pods/ ## Documentation cache and generated files: /.yardoc/ /_yardoc/ /doc/ /rdoc/ ## Environment normalization: /.bundle/ /vendor/bundle /lib/bundler/man/ # for a library or gem, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: Gemfile.lock # .ruby-version # .ruby-gemset # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc ### RubyMine ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf # Generated files .idea/**/contentModel.xml # Sensitive or high-churn files .idea/**/dataSources/ .idea/**/dataSources.ids .idea/**/dataSources.local.xml .idea/**/sqlDataSources.xml .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml # Gradle .idea/**/gradle.xml .idea/**/libraries # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/modules.xml # .idea/*.iml # .idea/modules # *.iml # *.ipr # CMake cmake-build-*/ # Mongo Explorer plugin .idea/**/mongoSettings.xml # File-based project format *.iws # IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties # Editor-based Rest Client .idea/httpRequests # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser ### RubyMine Patch ### # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 # *.iml # modules.xml # .idea/misc.xml # *.ipr # Sonarlint plugin .idea/**/sonarlint/ # SonarQube Plugin .idea/**/sonarIssues.xml # Markdown Navigator plugin .idea/**/markdown-navigator.xml .idea/**/markdown-navigator/ ### VisualStudioCode ### .vscode/* # !.vscode/settings.json # !.vscode/tasks.json # !.vscode/launch.json # !.vscode/extensions.json ### VisualStudioCode Patch ### # Ignore all local history of files .history # End of https://www.gitignore.io/api/c,ruby,macos,rubymine,visualstudiocode *.bundle /.idea ================================================ FILE: .rdoc_options ================================================ --- main_page: README.md title: SCrypt Ruby Documentation charset: UTF-8 encoding: UTF-8 force_update: true line_numbers: true markup: rdoc op_dir: html visibility: :private ================================================ FILE: .rspec ================================================ --require spec_helper --color --format documentation ================================================ FILE: .rubocop.yml ================================================ # require: rubocop-rspec plugins: - rubocop-performance - rubocop-rake - rubocop-rspec AllCops: Include: - Rakefile - ext/**/Rakefile - lib/**/*.{rb,rake} - spec/**/*.rb NewCops: enable TargetRubyVersion: 2.3 Layout/LineLength: Max: 120 Naming/VariableNumber: EnforcedStyle: snake_case ================================================ FILE: .ruby-gemset ================================================ scrypt ================================================ FILE: .ruby-version ================================================ ruby-3.4.5 ================================================ FILE: CHANGELOG.md ================================================ 3.0.7 ----- Changes: * Replaced `scanf` usage to avoid the need for a runtime dependency with the upcoming Ruby 2.7. * **Development:** Added Rubocop and rules to the project. * Refactored: * Extracted (organized) `Engine`, `Errors` and `Password` class/modules into dedicated files in `scrypt` sub-directory. * Logic and syntax cleanup and formatting. 3.0.6 ----- Fixed: * Expanded complication flags in support of macOS Mojave. 3.0.5 ----- Changes: * Make `rake` development dependency not runtime 3.0.4 ----- Fixed: * Compilation on Archlinux 3.0.3 ----- Fixed: * ~Compilation on Archlinux~ 3.0.2 ----- Fixed: * ~~Compilation on Archlinux~~ 3.0.1 ----- Fixed: * Windows support was broken in 3.0.0 3.0.0 ----- Breaking Changes: * None Added: * Updated of core scrypt ext code: https://github.com/pbhogan/scrypt/pull/53 * Support for platforms other than x86 such as ARM 2.1.1 ----- Changes: * Uses more secure defaults: Increased max_mem from 1MB to 16MB, and salt_len from 8 to 32 bytes. * See discussion at https://github.com/pbhogan/scrypt/issues/25 2.0.1 ----- Changes: * Adds a `:cost` option for specifying a cost string (e.g. `'400$8$19$'`) from the `calibrate` method (https://github.com/pbhogan/scrypt/commit/95ce6e3e37f4b2e8681a544713bfe783d2d69466) 2.0.0 ----- Breaking Changes: * `SCrypt::Password#hash` has been renamed to `#checksum` (https://github.com/pbhogan/scrypt/commit/a1a60e06ec9d863c3156ac06fda32ce82cddd759) ================================================ FILE: COPYING ================================================ Copyright 2010 Patrick Hogan Original implementation of scrypt by Colin Percival. This product includes software developed by Coda Hale. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Gemfile ================================================ # frozen_string_literal: true source 'https://rubygems.org' gemspec group :development, :test do gem 'irb' gem 'rdoc', '~> 6' gem 'rake', '~> 13' gem 'rspec', '~> 3' if RUBY_VERSION >= '3.0.0' gem 'rubocop', '~> 1' gem 'rubocop-performance', '~> 1' gem 'rubocop-rake', '~> 0.7' gem 'rubocop-rspec', '~> 3' end end ================================================ FILE: README.md ================================================ # scrypt A Ruby library providing a secure password hashing solution using the scrypt key derivation function. [![Gem Version](https://badge.fury.io/rb/scrypt.svg)](https://badge.fury.io/rb/scrypt) [![Ruby](https://github.com/pbhogan/scrypt/actions/workflows/ruby.yml/badge.svg)](https://github.com/pbhogan/scrypt/actions/workflows/ruby.yml) ## About scrypt The scrypt key derivation function is designed to be far more secure against hardware brute-force attacks than alternative functions such as PBKDF2 or bcrypt. It accomplishes this by being deliberately memory-intensive, making it expensive to implement in hardware. **Key Features:** - Memory-hard function that resists ASIC and FPGA attacks - Configurable computational cost, memory usage, and parallelization - Drop-in replacement for bcrypt in most applications - Production-ready and battle-tested **Resources:** - [Original scrypt paper](http://www.tarsnap.com/scrypt.html) - [GitHub repository](http://github.com/pbhogan/scrypt) ## Why you should use scrypt ![KDF comparison](https://github.com/tarcieri/scrypt/raw/modern-readme/kdf-comparison.png) The designers of scrypt estimate that on modern (2009) hardware, if 5 seconds are spent computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly 4,000 times greater than the cost of a similar attack against bcrypt (to find the same password), and 20,000 times greater than a similar attack against PBKDF2. ## Installation Add this line to your application's Gemfile: ```ruby gem 'scrypt' ``` And then execute: ```bash bundle install ``` Or install it yourself as: ```bash gem install scrypt ``` ## Basic Usage The scrypt gem works similarly to ruby-bcrypt with a few minor differences, especially regarding the cost factor configuration. ```ruby require "scrypt" # Hash a user's password password = SCrypt::Password.create("my grand secret") # => "400$8$36$78f4ae6983f76119$37ec6ce55a2b928dc56ff9a7d0cdafbd7dbde49d9282c38a40b1434e88f24cf5" # Compare passwords password == "my grand secret" # => true password == "a paltry guess" # => false ``` ### Configuration Options `Password.create` accepts several options to customize the key length, salt size, and computational cost limits: * **`:key_len`** - Length in bytes of the generated key. Default: 32 bytes (256 bits). Range: 16-512 bytes. * **`:salt_size`** - Size in bytes of the random salt. Default: 32 bytes (256 bits). Range: 8-32 bytes. * **`:max_time`** - Maximum computation time in seconds. Default: 0.2 seconds. * **`:max_mem`** - Maximum memory usage in bytes. Default: 16 MB. Set to 0 for no limit (minimum 1 MB). * **`:max_memfrac`** - Maximum memory as a fraction of available resources. Default: 0.5. Range: 0-0.5. * **`:cost`** - Explicit cost string from `calibrate` method (e.g., `'400$8$19$'`). When provided, `max_*` options are ignored. **Note:** Default options result in approximately 200ms computation time with 16 MB memory usage. ## Advanced Usage ### Engine Methods The scrypt gem provides low-level access to the scrypt algorithm through the `SCrypt::Engine` class: ```ruby require "scrypt" # Calibrate scrypt parameters for your system SCrypt::Engine.calibrate # => "400$8$25$" # Generate a salt with default parameters salt = SCrypt::Engine.generate_salt # => "400$8$26$b62e0f787a5fc373" # Hash a secret with a specific salt SCrypt::Engine.hash_secret("my grand secret", salt) # => "400$8$26$b62e0f787a5fc373$0399ccd4fa26642d92741b17c366b7f6bd12ccea5214987af445d2bed97bc6a2" # Calibrate with custom memory limits and save for future use SCrypt::Engine.calibrate!(max_mem: 16 * 1024 * 1024) # => "4000$8$4$" # Subsequent salt generation will use the calibrated parameters SCrypt::Engine.generate_salt # => "4000$8$4$c6d101522d3cb045" ``` ### Password Creation with Custom Options ```ruby # Create password with custom parameters password = SCrypt::Password.create("my secret", { key_len: 64, salt_size: 16, max_time: 0.5, max_mem: 32 * 1024 * 1024 }) # Create password with pre-calibrated cost cost = SCrypt::Engine.calibrate(max_time: 0.1) password = SCrypt::Password.create("my secret", cost: cost) ``` ## Usage in Rails (and the like) ```ruby ## Usage in Rails (and similar frameworks) # Store password safely in the user model user.update_attribute(:password, SCrypt::Password.create("my grand secret")) # Read it back later user.reload! password = SCrypt::Password.new(user.password) password == "my grand secret" # => true ``` ## Security Considerations * **Memory Safety**: The scrypt algorithm requires significant memory, making it resistant to hardware-based attacks * **Time-Memory Trade-off**: Higher memory requirements make it expensive to parallelize attacks * **Parameter Selection**: Use `calibrate` to find optimal parameters for your system's performance requirements * **Salt Generation**: Always use cryptographically secure random salts (handled automatically) ## Performance Tuning The scrypt parameters can be tuned based on your security and performance requirements: ```ruby # For high-security applications (slower) password = SCrypt::Password.create("secret", max_time: 1.0, max_mem: 64 * 1024 * 1024) # For faster authentication (less secure) password = SCrypt::Password.create("secret", max_time: 0.1, max_mem: 8 * 1024 * 1024) # Calibrate once and reuse parameters SCrypt::Engine.calibrate!(max_time: 0.5) # All subsequent Password.create calls will use these parameters ``` ## Error Handling The library raises specific exceptions for different error conditions: ```ruby begin SCrypt::Password.new("invalid_hash_format") rescue SCrypt::Errors::InvalidHash => e puts "Invalid hash format: #{e.message}" end begin SCrypt::Engine.hash_secret(nil, "salt") rescue SCrypt::Errors::InvalidSecret => e puts "Invalid secret: #{e.message}" end ``` ## Contributing 1. Fork the repository 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create a new Pull Request ## Acknowledgments ### Original scrypt Algorithm - **Colin Percival** and **Tarsnap** for creating the scrypt key derivation function and providing the reference implementation - The original scrypt paper: [Stronger Key Derivation via Sequential Memory-Hard Functions](http://www.tarsnap.com/scrypt.html) ### Core Collaborators - **Patrick Hogan** ([@pbhogan](https://github.com/pbhogan)) - **Stephen von Takach** ([@stakach](https://github.com/stakach)) - **Rene van Paassen** ([@repagh](https://github.com/repagh)) - **Johanns Gregorian** ([@johanns](https://github.com/johanns)) ### Special Thanks - The Ruby community for testing and feedback - Contributors who have submitted bug reports, feature requests, and patches - The cryptography community for security reviews and recommendations ## License This project is licensed under the BSD-3-Clause License - see the [COPYING](COPYING) file for details. ================================================ FILE: Rakefile ================================================ # frozen_string_literal: true require 'bundler/setup' require 'bundler/gem_tasks' require 'digest/sha2' require 'ffi' require 'ffi-compiler/compile_task' require 'fileutils' require 'rake' require 'rake/clean' require 'rdoc/task' require 'rspec/core/rake_task' require 'rubygems' require 'rubygems/package_task' require './lib/scrypt/version' task default: %i[clean compile_ffi spec] desc 'Run all specs' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = ['--color', '--backtrace', '--format', 'documentation'] end desc 'Generate checksum for built gem' task :checksum do built_gem_path = "pkg/scrypt-#{SCrypt::VERSION}.gem" unless File.exist?(built_gem_path) puts "Gem file not found: #{built_gem_path}" puts "Run 'rake build' first to create the gem." exit 1 end checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path)) checksum_path = "checksum/scrypt-#{SCrypt::VERSION}.gem.sha512" # Ensure checksum directory exists FileUtils.mkdir_p(File.dirname(checksum_path)) File.write(checksum_path, checksum) puts "Checksum written to: #{checksum_path}" end desc 'Compile FFI extension' namespace :ffi_compiler do FFI::Compiler::CompileTask.new('ext/scrypt/scrypt_ext') do |t| target_cpu = RbConfig::CONFIG['target_cpu'] t.cflags << '-Wall -std=c99' t.cflags << '-msse -msse2' if t.platform.arch.include?('86') t.cflags << '-D_GNU_SOURCE=1' if RbConfig::CONFIG['host_os'].downcase =~ /mingw/ t.cflags << '-D_POSIX_C_SOURCE=200809L' if RbConfig::CONFIG['host_os'].downcase =~ /linux/ if 1.size == 4 && target_cpu =~ /i386|x86_32/ && t.platform.mac? t.cflags << '-arch i386' t.ldflags << '-arch i386' elsif 1.size == 8 && target_cpu =~ /i686|x86_64/ && t.platform.mac? t.cflags << '-arch x86_64' t.ldflags << '-arch x86_64' end t.add_define 'WINDOWS_OS' if FFI::Platform.windows? end end task compile_ffi: ['ffi_compiler:default'] CLEAN.include('ext/scrypt/*{.o,.log,.so,.bundle}') CLEAN.include('lib/**/*{.o,.log,.so,.bundle}') desc 'Generate RDoc documentation' RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_dir = 'doc/rdoc' rdoc.options << '--force-update' rdoc.options << '-V' rdoc.template = ENV['TEMPLATE'] if ENV['TEMPLATE'] end desc 'Run all specs' RSpec::Core::RakeTask.new do |_t| # Task automatically runs specs based on RSpec defaults end def gem_spec @gem_spec ||= Gem::Specification.load('scrypt.gemspec') end Gem::PackageTask.new(gem_spec) do |pkg| pkg.need_zip = true pkg.need_tar = true pkg.package_dir = 'pkg' end ================================================ FILE: certs/pbhogan.pem ================================================ -----BEGIN CERTIFICATE----- MIIEbDCCAtSgAwIBAgIBATANBgkqhkiG9w0BAQsFADA+MRAwDgYDVQQDDAdwYmhv Z2FuMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNjb20w HhcNMjUwNzIyMTM1NDMxWhcNMjYwNzIyMTM1NDMxWjA+MRAwDgYDVQQDDAdwYmhv Z2FuMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNjb20w ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCSWjGKTBcJNoRa0QeIh0Au 6uAZ2daA43tu9Kst/DTB3OdsDfZnnJu9NftgmNTw7di9ZTQFH4+BrQg2/HodvZdP +HvZecC3ZxXu/US8f+ryz70BrFz1ve+xW11bKNDlw+KkCVmlZobqYQMUuCXjU6wD s5lGHGXiiK5tDK9US1rFqK5aV35gibpPaNRBRlEdmGKABmhhdNQw6fVDzfIWxIvE IgeTVTvnSAmVfO8F1pH73fRL3zGWXirK8gg09i118POoT7FLtvgU6QzZu46DPhph YMgLRK+RM+aAvVa0IH/cD94SrgHXrK82SXKgrccnYTUajiO+BemUWY82dRWWQnW6 5SSs1KYlNqo2OE5Fx6cSmcCdXermtidRj9SVVK6cp3Z+V4dNCMwe5d0PkmhbcLqH xxIb9GFU0Tl7qVnyzuTAHkEbhGM6vTlOnggQzmDQJGT/urpUnYXkPqYiOFB58UWR zoUJzGbzGkoyt+8IK2Wi4T0FM+iIjM+nWKfHyx1BKA8CAwEAAaN1MHMwCQYDVR0T BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEF+cJzNMLtyKh6xGnlyx97tiHCl MBwGA1UdEQQVMBOBEXBiaG9nYW5AZ21haWwuY29tMBwGA1UdEgQVMBOBEXBiaG9n YW5AZ21haWwuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAsiXgPScyfZqqrUkaykumD XtahlA0BcS9fchzkf21qd8w34L5I8gyz3fvAJXPAvSqZPSWEn6ud/TOxhXmG6+K/ rZ7J1iKbGpk3/yMmmkyTLsItloc4xABTRjyJ4Cy1wuJ4kdwgIW/kHTwWG0PFA1YF ZoqZijAXbTQ4Z+wTcRiUjVpbizB4MtmaQb+Mrr+zxeEmRv/B8iZdkRCZ02C1u+T+ bwvmftjB0xuKvPmHP1KpSnFh3I4uvlykDMVSNwE0pKlGsVrzkJFf68R2YZJgDwiH GmXe0sAsp14rX0I+JyC6a35TPrC7o1PH1zWLSJn2hBIbA/fObf8CAwNRIVHUTTUt kr/2/0UpZZ6O3j7S8lovBRVi6Cv+P6avNYOiQpOH0kJyaSw2LMLl87D7t3JYQ0+8 rTgQJuEZY0TADK4NMJujsMEok9mO6Qfj4d+dVYdpFfuFgYf/LVnpT4sZ8bKVn0PQ KtQ2gCSB20nUFBsuf/VRbsMWL+MxzesANGDEIrEXNS0= -----END CERTIFICATE----- ================================================ FILE: certs/stakach.pem ================================================ -----BEGIN CERTIFICATE----- MIIEfDCCAuSgAwIBAgIBATANBgkqhkiG9w0BAQsFADBCMQ4wDAYDVQQDDAVzdGV2 ZTEbMBkGCgmSJomT8ixkARkWC2FjYXByb2plY3RzMRMwEQYKCZImiZPyLGQBGRYD Y29tMB4XDTE5MTExMzIzMDMwMFoXDTIwMTExMjIzMDMwMFowQjEOMAwGA1UEAwwF c3RldmUxGzAZBgoJkiaJk/IsZAEZFgthY2Fwcm9qZWN0czETMBEGCgmSJomT8ixk ARkWA2NvbTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALtarCg2Ux+m XjcGQ6XVxRbVwwVUxGWIBfpKXzq23WOXk0NkRooyYLuxSfkA/PFVd5OLZTMP+ULr KgM9w7+xR/Xhs/qxib/84u54H8rqHTCozYmldR5QpYTwsB4BeG0HpSbD0PGL+4N+ oa50wRyvwVBAZMG4F4QYUGnQlwBpLSm4Fhm6xhKqD9DDhbS5jgIg/S3Cr4dqghUG kqsIGjKd6X9wIOIqF1IWLZkXiwN1IcKCJ9FO2iTBEo7UidJXROO5xs5D0Vr3iyiw F3tmhpq1C7KkXkv0AxAxRK3SmdpIiagRukvdNFEAcpkgX6qUg62G8KMRGc3dP9lx tBP8IonLEcpLktQakuqsV4YETQaKQb5F4WADxh1tvIPcYJUxHsw3sdHZeDywT3De LPCNTbuBseIF33hj2qiZ77XMusgVxiqG1eaCD0X58zeVTd7ZDZUFuVKlgAudhyOi O30rMiCHNIchQqwVNLah0Tu4KAF7PGAwJhu01qMXOEl9WCEtApOS6wIDAQABo30w ezAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUjrc6E0haseLehiw2 JME0lZzbYKMwIAYDVR0RBBkwF4EVc3RldmVAYWNhcHJvamVjdHMuY29tMCAGA1Ud EgQZMBeBFXN0ZXZlQGFjYXByb2plY3RzLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEA fpmwkaIoCUA8A5IDHTcGWDX5gFYwhrRLMPwvdbU3r9RUeo7slWjek1OCAH4gyLUM K+OeWIYyQjOzeRHllNapY3AnOWbwXX7rVrYa0OxFd8JgXgVS/XJR4elzNJ0lb8En szSDkj4xl2Yn7FDZBsT+Oq19zMKFZNSF3SYumTuLMq3AdJa3vO2Gg4I1r8oSfZBB 5V81o2GU9YTGCNrl57dmq+Iop1qVU9jF60wEXyiOz/Fkhvk+kdz2PveH8nhlpBiw t3kOzg645P903giemoqlYZJ1XTmBqHLhflfTSxNie6my4izqFQgB9UxtUeesRtaJ 5y48Vz2twr2OQfw+1lM//SY/H9rPJkaOPDM7AlPodnZvYrr1hAlwXUebgmOq+Mvm 13PWwCGItI0lMBPfAfadtZKJQNvzl4K4Iq76ksQy3tobcrYw1r4cKTyqvrrrKhWn 93B950TkkA8h64SLwNEzV3ayjvGKTI95l0cz0B1STPIwvQecQI2j1y8/DzyztNXO -----END CERTIFICATE----- ================================================ FILE: checksum/scrypt-3.0.6.gem.sha512 ================================================ fb1b89bddfd5fa440994aad7f3edc6cce588c41018bf60ba82eab927863e243025c2e25094be2dfefdbe21a022dec62bdd072284ff83e472071791ff69bcb949 ================================================ FILE: ext/alt-impl/crypto_scrypt-nosse.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "scrypt_platform.h" #include #ifndef __MINGW32__ #include #endif #include #include #include #include #include "sha256.h" #include "sysendian.h" #include "crypto_scrypt.h" static void blkcpy(void *, void *, size_t); static void blkxor(void *, void *, size_t); static void salsa20_8(uint32_t[16]); static void blockmix_salsa8(uint32_t *, uint32_t *, uint32_t *, size_t); static uint64_t integerify(void *, size_t); static void smix(uint8_t *, size_t, uint64_t, uint32_t *, uint32_t *); static void blkcpy(void * dest, void * src, size_t len) { size_t * D = dest; size_t * S = src; size_t L = len / sizeof(size_t); size_t i; for (i = 0; i < L; i++) D[i] = S[i]; } static void blkxor(void * dest, void * src, size_t len) { size_t * D = dest; size_t * S = src; size_t L = len / sizeof(size_t); size_t i; for (i = 0; i < L; i++) D[i] ^= S[i]; } /** * salsa20_8(B): * Apply the salsa20/8 core to the provided block. */ static void salsa20_8(uint32_t B[16]) { uint32_t x[16]; size_t i; blkcpy(x, B, 64); for (i = 0; i < 8; i += 2) { #define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) /* Operate on columns. */ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); /* Operate on rows. */ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); #undef R } for (i = 0; i < 16; i++) B[i] += x[i]; } /** * blockmix_salsa8(Bin, Bout, X, r): * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r * bytes in length; the output Bout must also be the same size. The * temporary space X must be 64 bytes. */ static void blockmix_salsa8(uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) { size_t i; /* 1: X <-- B_{2r - 1} */ blkcpy(X, &Bin[(2 * r - 1) * 16], 64); /* 2: for i = 0 to 2r - 1 do */ for (i = 0; i < 2 * r; i += 2) { /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 16], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 8], X, 64); /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 16 + 16], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 8 + r * 16], X, 64); } } /** * integerify(B, r): * Return the result of parsing B_{2r-1} as a little-endian integer. */ static uint64_t integerify(void * B, size_t r) { uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64); return (((uint64_t)(X[1]) << 32) + X[0]); } /** * smix(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. */ static void smix(uint8_t * B, size_t r, uint64_t N, uint32_t * V, uint32_t * XY) { uint32_t * X = XY; uint32_t * Y = &XY[32 * r]; uint32_t * Z = &XY[64 * r]; uint64_t i; uint64_t j; size_t k; /* 1: X <-- B */ for (k = 0; k < 32 * r; k++) X[k] = le32dec(&B[4 * k]); /* 2: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 3: V_i <-- X */ blkcpy(&V[i * (32 * r)], X, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(X, Y, Z, r); /* 3: V_i <-- X */ blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(Y, X, Z, r); } /* 6: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 7: j <-- Integerify(X) mod N */ j = integerify(X, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(X, &V[j * (32 * r)], 128 * r); blockmix_salsa8(X, Y, Z, r); /* 7: j <-- Integerify(X) mod N */ j = integerify(Y, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(Y, &V[j * (32 * r)], 128 * r); blockmix_salsa8(Y, X, Z, r); } /* 10: B' <-- X */ for (k = 0; k < 32 * r; k++) le32enc(&B[4 * k], X[k]); } /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N * must be a power of 2 greater than 1. * * Return 0 on success; or -1 on error. */ int crypto_scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, uint8_t * buf, size_t buflen) { void * B0, * V0, * XY0; uint8_t * B; uint32_t * V; uint32_t * XY; size_t r = _r, p = _p; uint32_t i; /* Sanity-check parameters. */ #if SIZE_MAX > UINT32_MAX if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { errno = EFBIG; goto err0; } #endif if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { errno = EFBIG; goto err0; } if (((N & (N - 1)) != 0) || (N < 2)) { errno = EINVAL; goto err0; } if ((r > SIZE_MAX / 128 / p) || #if SIZE_MAX / 256 <= UINT32_MAX (r > SIZE_MAX / 256) || #endif (N > SIZE_MAX / 128 / r)) { errno = ENOMEM; goto err0; } /* Allocate memory. */ #ifdef HAVE_POSIX_MEMALIGN if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) goto err0; B = (uint8_t *)(B0); if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) goto err1; XY = (uint32_t *)(XY0); #ifndef MAP_ANON if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) goto err2; V = (uint32_t *)(V0); #endif #else if ((B0 = malloc(128 * r * p + 63)) == NULL) goto err0; B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) goto err1; XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); #ifndef MAP_ANON if ((V0 = malloc(128 * r * N + 63)) == NULL) goto err2; V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); #endif #endif #ifdef MAP_ANON if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, #ifdef MAP_NOCORE MAP_ANON | MAP_PRIVATE | MAP_NOCORE, #else MAP_ANON | MAP_PRIVATE, #endif -1, 0)) == MAP_FAILED) goto err2; V = (uint32_t *)(V0); #endif /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ PBKDF2_scrypt_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); /* 2: for i = 0 to p - 1 do */ for (i = 0; i < p; i++) { /* 3: B_i <-- MF(B_i, N) */ smix(&B[i * 128 * r], r, N, V, XY); } /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ PBKDF2_scrypt_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); /* Free memory. */ #ifdef MAP_ANON if (munmap(V0, 128 * r * N)) goto err2; #else free(V0); #endif free(XY0); free(B0); /* Success! */ return (0); err2: free(XY0); err1: free(B0); err0: /* Failure! */ return (-1); } ================================================ FILE: ext/alt-impl/crypto_scrypt-ref.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "scrypt_platform.h" #include #include #include #include #include "sha256.h" #include "sysendian.h" #include "crypto_scrypt.h" static void blkcpy(uint8_t *, uint8_t *, size_t); static void blkxor(uint8_t *, uint8_t *, size_t); static void salsa20_8(uint8_t[64]); static void blockmix_salsa8(uint8_t *, uint8_t *, size_t); static uint64_t integerify(uint8_t *, size_t); static void smix(uint8_t *, size_t, uint64_t, uint8_t *, uint8_t *); static void blkcpy(uint8_t * dest, uint8_t * src, size_t len) { size_t i; for (i = 0; i < len; i++) dest[i] = src[i]; } static void blkxor(uint8_t * dest, uint8_t * src, size_t len) { size_t i; for (i = 0; i < len; i++) dest[i] ^= src[i]; } /** * salsa20_8(B): * Apply the salsa20/8 core to the provided block. */ static void salsa20_8(uint8_t B[64]) { uint32_t B32[16]; uint32_t x[16]; size_t i; /* Convert little-endian values in. */ for (i = 0; i < 16; i++) B32[i] = le32dec(&B[i * 4]); /* Compute x = doubleround^4(B32). */ for (i = 0; i < 16; i++) x[i] = B32[i]; for (i = 0; i < 8; i += 2) { #define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) /* Operate on columns. */ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); /* Operate on rows. */ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); #undef R } /* Compute B32 = B32 + x. */ for (i = 0; i < 16; i++) B32[i] += x[i]; /* Convert little-endian values out. */ for (i = 0; i < 16; i++) le32enc(&B[4 * i], B32[i]); } /** * blockmix_salsa8(B, Y, r): * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in * length; the temporary space Y must also be the same size. */ static void blockmix_salsa8(uint8_t * B, uint8_t * Y, size_t r) { uint8_t X[64]; size_t i; /* 1: X <-- B_{2r - 1} */ blkcpy(X, &B[(2 * r - 1) * 64], 64); /* 2: for i = 0 to 2r - 1 do */ for (i = 0; i < 2 * r; i++) { /* 3: X <-- H(X \xor B_i) */ blkxor(X, &B[i * 64], 64); salsa20_8(X); /* 4: Y_i <-- X */ blkcpy(&Y[i * 64], X, 64); } /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ for (i = 0; i < r; i++) blkcpy(&B[i * 64], &Y[(i * 2) * 64], 64); for (i = 0; i < r; i++) blkcpy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64); } /** * integerify(B, r): * Return the result of parsing B_{2r-1} as a little-endian integer. */ static uint64_t integerify(uint8_t * B, size_t r) { uint8_t * X = &B[(2 * r - 1) * 64]; return (le64dec(X)); } /** * smix(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the * temporary storage V must be 128rN bytes in length; the temporary storage * XY must be 256r bytes in length. The value N must be a power of 2. */ static void smix(uint8_t * B, size_t r, uint64_t N, uint8_t * V, uint8_t * XY) { uint8_t * X = XY; uint8_t * Y = &XY[128 * r]; uint64_t i; uint64_t j; /* 1: X <-- B */ blkcpy(X, B, 128 * r); /* 2: for i = 0 to N - 1 do */ for (i = 0; i < N; i++) { /* 3: V_i <-- X */ blkcpy(&V[i * (128 * r)], X, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(X, Y, r); } /* 6: for i = 0 to N - 1 do */ for (i = 0; i < N; i++) { /* 7: j <-- Integerify(X) mod N */ j = integerify(X, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(X, &V[j * (128 * r)], 128 * r); blockmix_salsa8(X, Y, r); } /* 10: B' <-- X */ blkcpy(B, X, 128 * r); } /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N * must be a power of 2. * * Return 0 on success; or -1 on error. */ int crypto_scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, uint8_t * buf, size_t buflen) { uint8_t * B; uint8_t * V; uint8_t * XY; size_t r = _r, p = _p; uint32_t i; /* Sanity-check parameters. */ #if SIZE_MAX > UINT32_MAX if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { errno = EFBIG; goto err0; } #endif if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { errno = EFBIG; goto err0; } if (((N & (N - 1)) != 0) || (N == 0)) { errno = EINVAL; goto err0; } if ((r > SIZE_MAX / 128 / p) || #if SIZE_MAX / 256 <= UINT32_MAX (r > SIZE_MAX / 256) || #endif (N > SIZE_MAX / 128 / r)) { errno = ENOMEM; goto err0; } /* Allocate memory. */ if ((B = malloc(128 * r * p)) == NULL) goto err0; if ((XY = malloc(256 * r)) == NULL) goto err1; if ((V = malloc(128 * r * N)) == NULL) goto err2; /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ PBKDF2_scrypt_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); /* 2: for i = 0 to p - 1 do */ for (i = 0; i < p; i++) { /* 3: B_i <-- MF(B_i, N) */ smix(&B[i * 128 * r], r, N, V, XY); } /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ PBKDF2_scrypt_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); /* Free memory. */ free(V); free(XY); free(B); /* Success! */ return (0); err2: free(XY); err1: free(B); err0: /* Failure! */ return (-1); } ================================================ FILE: ext/scrypt/Rakefile ================================================ require 'ffi-compiler/compile_task' target_cpu = RbConfig::CONFIG['target_cpu'] FFI::Compiler::CompileTask.new('scrypt_ext') do |t| t.cflags << '-Wall -std=c99' t.cflags << '-msse -msse2' if t.platform.arch.include? '86' t.cflags << '-D_GNU_SOURCE=1' if RbConfig::CONFIG['host_os'].downcase =~ /mingw/ t.cflags << '-D_POSIX_C_SOURCE=200809L' if RbConfig::CONFIG['host_os'].downcase =~ /linux/ if 1.size == 4 && target_cpu =~ /i386|x86_32/ && t.platform.mac? t.cflags << '-arch i386' t.ldflags << '-arch i386' elsif 1.size == 8 && target_cpu =~ /i686|x86_64/ && t.platform.mac? t.cflags << '-arch x86_64' t.ldflags << '-arch x86_64' end t.export '../../lib/scrypt/scrypt_ext.rb' t.add_define 'WINDOWS_OS' if FFI::Platform.windows? end ================================================ FILE: ext/scrypt/cpusupport.h ================================================ #ifndef _CPUSUPPORT_H_ #define _CPUSUPPORT_H_ /* * To enable support for non-portable CPU features at compile time, one or * more CPUSUPPORT_ARCH_FEATURE macros should be defined. This can be done * directly on the compiler command line via -D CPUSUPPORT_ARCH_FEATURE or * -D CPUSUPPORT_ARCH_FEATURE=1; or a file can be created with the * necessary #define lines and then -D CPUSUPPORT_CONFIG_FILE=cpuconfig.h * (or similar) can be provided to include that file here. */ #ifdef CPUSUPPORT_CONFIG_FILE #include CPUSUPPORT_CONFIG_FILE #endif /** * The CPUSUPPORT_FEATURE macro declares the necessary variables and * functions for detecting CPU feature support at run time. The function * defined in the macro acts to cache the result of the ..._detect function * using the ..._present and ..._init variables. The _detect function and the * _present and _init variables are turn defined by CPUSUPPORT_FEATURE_DECL in * appropriate cpusupport_foo_bar.c file. * * In order to allow CPUSUPPORT_FEATURE to be used for features which do not * have corresponding CPUSUPPORT_FEATURE_DECL blocks in another source file, * we abuse the C preprocessor: If CPUSUPPORT_${enabler} is defined to 1, then * we access _present_1, _init_1, and _detect_1; but if it is not defined, we * access _present_CPUSUPPORT_${enabler} etc., which we define as static, thus * preventing the compiler from emitting a reference to an external symbol. * * In this way, it becomes possible to issue CPUSUPPORT_FEATURE invocations * for nonexistent features without running afoul of the requirement that * "If an identifier declared with external linkage is used... in the entire * program there shall be exactly one external definition" (C99 standard, 6.9 * paragraph 5). In practice, this means that users of the cpusupport code * can omit build and runtime detection files without changing the framework * code. */ #define CPUSUPPORT_FEATURE__(arch_feature, enabler, enabled) \ static int cpusupport_ ## arch_feature ## _present ## _CPUSUPPORT_ ## enabler; \ static int cpusupport_ ## arch_feature ## _init ## _CPUSUPPORT_ ## enabler; \ static inline int cpusupport_ ## arch_feature ## _detect ## _CPUSUPPORT_ ## enabler(void) { return (0); } \ extern int cpusupport_ ## arch_feature ## _present_ ## enabled; \ extern int cpusupport_ ## arch_feature ## _init_ ## enabled; \ int cpusupport_ ## arch_feature ## _detect_ ## enabled(void); \ \ static inline int \ cpusupport_ ## arch_feature(void) \ { \ \ if (cpusupport_ ## arch_feature ## _present_ ## enabled) \ return (1); \ else if (cpusupport_ ## arch_feature ## _init_ ## enabled) \ return (0); \ cpusupport_ ## arch_feature ## _present_ ## enabled = \ cpusupport_ ## arch_feature ## _detect_ ## enabled(); \ cpusupport_ ## arch_feature ## _init_ ## enabled = 1; \ return (cpusupport_ ## arch_feature ## _present_ ## enabled); \ } \ static void (* cpusupport_ ## arch_feature ## _dummyptr)(void); \ static inline void \ cpusupport_ ## arch_feature ## _dummyfunc(void) \ { \ \ (void)cpusupport_ ## arch_feature ## _present ## _CPUSUPPORT_ ## enabler; \ (void)cpusupport_ ## arch_feature ## _init ## _CPUSUPPORT_ ## enabler; \ (void)cpusupport_ ## arch_feature ## _detect ## _CPUSUPPORT_ ## enabler; \ (void)cpusupport_ ## arch_feature ## _present_ ## enabled; \ (void)cpusupport_ ## arch_feature ## _init_ ## enabled; \ (void)cpusupport_ ## arch_feature ## _detect_ ## enabled; \ (void)cpusupport_ ## arch_feature ## _dummyptr; \ } \ static void (* cpusupport_ ## arch_feature ## _dummyptr)(void) = cpusupport_ ## arch_feature ## _dummyfunc; \ struct cpusupport_ ## arch_feature ## _dummy #define CPUSUPPORT_FEATURE_(arch_feature, enabler, enabled) \ CPUSUPPORT_FEATURE__(arch_feature, enabler, enabled) #define CPUSUPPORT_FEATURE(arch, feature, enabler) \ CPUSUPPORT_FEATURE_(arch ## _ ## feature, enabler, CPUSUPPORT_ ## enabler) /* * CPUSUPPORT_FEATURE_DECL(arch, feature): * Macro which defines variables and provides a function declaration for * detecting the presence of "feature" on the "arch" architecture. The * function body following this macro expansion must return nonzero if the * feature is present, or zero if the feature is not present or the detection * fails for any reason. */ #define CPUSUPPORT_FEATURE_DECL(arch, feature) \ int cpusupport_ ## arch ## _ ## feature ## _present_1 = 0; \ int cpusupport_ ## arch ## _ ## feature ## _init_1 = 0; \ int \ cpusupport_ ## arch ## _ ## feature ## _detect_1(void) /* * List of features. If a feature here is not enabled by the appropriate * CPUSUPPORT_ARCH_FEATURE macro being defined, it has no effect; but if the * relevant macro may be defined (e.g., by Build/cpusupport.sh successfully * compiling Build/cpusupport-ARCH-FEATURE.c) then the C file containing the * corresponding run-time detection code (cpusupport_arch_feature.c) must be * compiled and linked in. */ CPUSUPPORT_FEATURE(x86, aesni, X86_AESNI); CPUSUPPORT_FEATURE(x86, sse2, X86_SSE2); #endif /* !_CPUSUPPORT_H_ */ ================================================ FILE: ext/scrypt/crypto_scrypt.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ /* #include "bsdtar_platform.h" */ #include #if !defined(WINDOWS_OS) #include #ifndef HAVE_MMAP #define HAVE_MMAP 1 #endif #endif #include #include #include #include #include "cpusupport.h" #include "sha256.h" //#include "warnp.h" #include "crypto_scrypt_smix.h" #include "crypto_scrypt_smix_sse2.h" #include "crypto_scrypt.h" #include "warnp.h" static void (*smix_func)(uint8_t *, size_t, uint64_t, void *, void *) = NULL; /** * _crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen, smix): * Perform the requested scrypt computation, using ${smix} as the smix routine. */ static int _crypto_scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, uint8_t * buf, size_t buflen, void (*smix)(uint8_t *, size_t, uint64_t, void *, void *)) { void * B0, * V0, * XY0; uint8_t * B; uint32_t * V; uint32_t * XY; size_t r = _r, p = _p; uint32_t i; /* Sanity-check parameters. */ #if SIZE_MAX > UINT32_MAX if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { errno = EFBIG; goto err0; } #endif if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { errno = EFBIG; goto err0; } if (((N & (N - 1)) != 0) || (N < 2)) { errno = EINVAL; goto err0; } if ((r > SIZE_MAX / 128 / p) || #if SIZE_MAX / 256 <= UINT32_MAX (r > (SIZE_MAX - 64) / 256) || #endif (N > SIZE_MAX / 128 / r)) { errno = ENOMEM; goto err0; } /* Allocate memory. */ #ifdef HAVE_POSIX_MEMALIGN if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) goto err0; B = (uint8_t *)(B0); if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) goto err1; XY = (uint32_t *)(XY0); #if !defined(MAP_ANON) || !defined(HAVE_MMAP) if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) goto err2; V = (uint32_t *)(V0); #endif #else if ((B0 = malloc(128 * r * p + 63)) == NULL) goto err0; B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) goto err1; XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); #if !defined(MAP_ANON) || !defined(HAVE_MMAP) if ((V0 = malloc(128 * r * N + 63)) == NULL) goto err2; V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); #endif #endif #if defined(MAP_ANON) && defined(HAVE_MMAP) if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, #ifdef MAP_NOCORE MAP_ANON | MAP_PRIVATE | MAP_NOCORE, #else MAP_ANON | MAP_PRIVATE, #endif -1, 0)) == MAP_FAILED) goto err2; V = (uint32_t *)(V0); #endif /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); /* 2: for i = 0 to p - 1 do */ for (i = 0; i < p; i++) { /* 3: B_i <-- MF(B_i, N) */ (smix)(&B[i * 128 * r], r, N, V, XY); } /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); /* Free memory. */ #if defined(MAP_ANON) && defined(HAVE_MMAP) if (munmap(V0, 128 * r * N)) goto err2; #else free(V0); #endif free(XY0); free(B0); /* Success! */ return (0); err2: free(XY0); err1: free(B0); err0: /* Failure! */ return (-1); } #define TESTLEN 64 static struct scrypt_test { const char * passwd; const char * salt; uint64_t N; uint32_t r; uint32_t p; uint8_t result[TESTLEN]; } testcase = { .passwd = "pleaseletmein", .salt = "SodiumChloride", .N = 16, .r = 8, .p = 1, .result = { 0x25, 0xa9, 0xfa, 0x20, 0x7f, 0x87, 0xca, 0x09, 0xa4, 0xef, 0x8b, 0x9f, 0x77, 0x7a, 0xca, 0x16, 0xbe, 0xb7, 0x84, 0xae, 0x18, 0x30, 0xbf, 0xbf, 0xd3, 0x83, 0x25, 0xaa, 0xbb, 0x93, 0x77, 0xdf, 0x1b, 0xa7, 0x84, 0xd7, 0x46, 0xea, 0x27, 0x3b, 0xf5, 0x16, 0xa4, 0x6f, 0xbf, 0xac, 0xf5, 0x11, 0xc5, 0xbe, 0xba, 0x4c, 0x4a, 0xb3, 0xac, 0xc7, 0xfa, 0x6f, 0x46, 0x0b, 0x6c, 0x0f, 0x47, 0x7b, } }; static int testsmix(void (*smix)(uint8_t *, size_t, uint64_t, void *, void *)) { uint8_t hbuf[TESTLEN]; /* Perform the computation. */ if (_crypto_scrypt( (const uint8_t *)testcase.passwd, strlen(testcase.passwd), (const uint8_t *)testcase.salt, strlen(testcase.salt), testcase.N, testcase.r, testcase.p, hbuf, TESTLEN, smix)) return (-1); /* Does it match? */ return (memcmp(testcase.result, hbuf, TESTLEN)); } static void selectsmix(void) { #ifdef CPUSUPPORT_X86_SSE2 /* If we're running on an SSE2-capable CPU, try that code. */ if (cpusupport_x86_sse2()) { /* If SSE2ized smix works, use it. */ if (!testsmix(crypto_scrypt_smix_sse2)) { smix_func = crypto_scrypt_smix_sse2; return; } warn0("Disabling broken SSE2 scrypt support - please report bug!"); } #endif /* If generic smix works, use it. */ if (!testsmix(crypto_scrypt_smix)) { smix_func = crypto_scrypt_smix; return; } warn0("Generic scrypt code is broken - please report bug!"); /* If we get here, something really bad happened. */ abort(); } /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N * must be a power of 2 greater than 1. * * Return 0 on success; or -1 on error. */ int crypto_scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p, uint8_t * buf, size_t buflen) { if (smix_func == NULL) selectsmix(); return (_crypto_scrypt(passwd, passwdlen, salt, saltlen, N, _r, _p, buf, buflen, smix_func)); } ================================================ FILE: ext/scrypt/crypto_scrypt.h ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef _CRYPTO_SCRYPT_H_ #define _CRYPTO_SCRYPT_H_ #include #include /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, * p, buflen) and write the result into buf. The parameters r, p, and buflen * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N * must be a power of 2 greater than 1. * * Return 0 on success; or -1 on error. */ int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, uint32_t, uint32_t, uint8_t *, size_t); #endif /* !_CRYPTO_SCRYPT_H_ */ ================================================ FILE: ext/scrypt/crypto_scrypt_internal.h ================================================ ================================================ FILE: ext/scrypt/crypto_scrypt_smix.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include #include #include "sysendian.h" #include "crypto_scrypt_smix.h" static void blkcpy(void *, const void *, size_t); static void blkxor(void *, const void *, size_t); static void salsa20_8(uint32_t[16]); static void blockmix_salsa8(const uint32_t *, uint32_t *, uint32_t *, size_t); static uint64_t integerify(const void *, size_t); static void blkcpy(void * dest, const void * src, size_t len) { size_t * D = dest; const size_t * S = src; size_t L = len / sizeof(size_t); size_t i; for (i = 0; i < L; i++) D[i] = S[i]; } static void blkxor(void * dest, const void * src, size_t len) { size_t * D = dest; const size_t * S = src; size_t L = len / sizeof(size_t); size_t i; for (i = 0; i < L; i++) D[i] ^= S[i]; } /** * salsa20_8(B): * Apply the salsa20/8 core to the provided block. */ static void salsa20_8(uint32_t B[16]) { uint32_t x[16]; size_t i; blkcpy(x, B, 64); for (i = 0; i < 8; i += 2) { #define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) /* Operate on columns. */ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); /* Operate on rows. */ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); #undef R } for (i = 0; i < 16; i++) B[i] += x[i]; } /** * blockmix_salsa8(Bin, Bout, X, r): * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r * bytes in length; the output Bout must also be the same size. The * temporary space X must be 64 bytes. */ static void blockmix_salsa8(const uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) { size_t i; /* 1: X <-- B_{2r - 1} */ blkcpy(X, &Bin[(2 * r - 1) * 16], 64); /* 2: for i = 0 to 2r - 1 do */ for (i = 0; i < 2 * r; i += 2) { /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 16], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 8], X, 64); /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 16 + 16], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 8 + r * 16], X, 64); } } /** * integerify(B, r): * Return the result of parsing B_{2r-1} as a little-endian integer. */ static uint64_t integerify(const void * B, size_t r) { const uint32_t * X = (const void *)((uintptr_t)(B) + (2 * r - 1) * 64); return (((uint64_t)(X[1]) << 32) + X[0]); } /** * crypto_scrypt_smix(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. */ void crypto_scrypt_smix(uint8_t * B, size_t r, uint64_t N, void * _V, void * XY) { uint32_t * X = XY; uint32_t * Y = (void *)((uint8_t *)(XY) + 128 * r); uint32_t * Z = (void *)((uint8_t *)(XY) + 256 * r); uint32_t * V = _V; uint64_t i; uint64_t j; size_t k; /* 1: X <-- B */ for (k = 0; k < 32 * r; k++) X[k] = le32dec(&B[4 * k]); /* 2: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 3: V_i <-- X */ blkcpy(&V[i * (32 * r)], X, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(X, Y, Z, r); /* 3: V_i <-- X */ blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(Y, X, Z, r); } /* 6: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 7: j <-- Integerify(X) mod N */ j = integerify(X, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(X, &V[j * (32 * r)], 128 * r); blockmix_salsa8(X, Y, Z, r); /* 7: j <-- Integerify(X) mod N */ j = integerify(Y, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(Y, &V[j * (32 * r)], 128 * r); blockmix_salsa8(Y, X, Z, r); } /* 10: B' <-- X */ for (k = 0; k < 32 * r; k++) le32enc(&B[4 * k], X[k]); } ================================================ FILE: ext/scrypt/crypto_scrypt_smix.h ================================================ #ifndef _CRYPTO_SCRYPT_SMIX_H_ #define _CRYPTO_SCRYPT_SMIX_H_ /** * crypto_scrypt_smix(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. */ void crypto_scrypt_smix(uint8_t *, size_t, uint64_t, void *, void *); #endif /* !_CRYPTO_SCRYPT_SMIX_H_ */ ================================================ FILE: ext/scrypt/crypto_scrypt_smix_sse2.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "cpusupport.h" #ifdef CPUSUPPORT_X86_SSE2 #include #include #include "sysendian.h" #include "crypto_scrypt_smix_sse2.h" static void blkcpy(void *, const void *, size_t); static void blkxor(void *, const void *, size_t); static void salsa20_8(__m128i *); static void blockmix_salsa8(const __m128i *, __m128i *, __m128i *, size_t); static uint64_t integerify(const void *, size_t); static void blkcpy(void * dest, const void * src, size_t len) { __m128i * D = dest; const __m128i * S = src; size_t L = len / 16; size_t i; for (i = 0; i < L; i++) D[i] = S[i]; } static void blkxor(void * dest, const void * src, size_t len) { __m128i * D = dest; const __m128i * S = src; size_t L = len / 16; size_t i; for (i = 0; i < L; i++) D[i] = _mm_xor_si128(D[i], S[i]); } /** * salsa20_8(B): * Apply the salsa20/8 core to the provided block. */ static void salsa20_8(__m128i B[4]) { __m128i X0, X1, X2, X3; __m128i T; size_t i; X0 = B[0]; X1 = B[1]; X2 = B[2]; X3 = B[3]; for (i = 0; i < 8; i += 2) { /* Operate on "columns". */ T = _mm_add_epi32(X0, X3); X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); T = _mm_add_epi32(X1, X0); X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); T = _mm_add_epi32(X2, X1); X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13)); X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); T = _mm_add_epi32(X3, X2); X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); /* Rearrange data. */ X1 = _mm_shuffle_epi32(X1, 0x93); X2 = _mm_shuffle_epi32(X2, 0x4E); X3 = _mm_shuffle_epi32(X3, 0x39); /* Operate on "rows". */ T = _mm_add_epi32(X0, X1); X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7)); X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); T = _mm_add_epi32(X3, X0); X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); T = _mm_add_epi32(X2, X3); X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13)); X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); T = _mm_add_epi32(X1, X2); X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); /* Rearrange data. */ X1 = _mm_shuffle_epi32(X1, 0x39); X2 = _mm_shuffle_epi32(X2, 0x4E); X3 = _mm_shuffle_epi32(X3, 0x93); } B[0] = _mm_add_epi32(B[0], X0); B[1] = _mm_add_epi32(B[1], X1); B[2] = _mm_add_epi32(B[2], X2); B[3] = _mm_add_epi32(B[3], X3); } /** * blockmix_salsa8(Bin, Bout, X, r): * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r * bytes in length; the output Bout must also be the same size. The * temporary space X must be 64 bytes. */ static void blockmix_salsa8(const __m128i * Bin, __m128i * Bout, __m128i * X, size_t r) { size_t i; /* 1: X <-- B_{2r - 1} */ blkcpy(X, &Bin[8 * r - 4], 64); /* 2: for i = 0 to 2r - 1 do */ for (i = 0; i < r; i++) { /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 8], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[i * 4], X, 64); /* 3: X <-- H(X \xor B_i) */ blkxor(X, &Bin[i * 8 + 4], 64); salsa20_8(X); /* 4: Y_i <-- X */ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ blkcpy(&Bout[(r + i) * 4], X, 64); } } /** * integerify(B, r): * Return the result of parsing B_{2r-1} as a little-endian integer. * Note that B's layout is permuted compared to the generic implementation. */ static uint64_t integerify(const void * B, size_t r) { const uint32_t * X = (const void *)((uintptr_t)(B) + (2 * r - 1) * 64); return (((uint64_t)(X[13]) << 32) + X[0]); } /** * crypto_scrypt_smix_sse2(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. * * Use SSE2 instructions. */ void crypto_scrypt_smix_sse2(uint8_t * B, size_t r, uint64_t N, void * V, void * XY) { __m128i * X = XY; __m128i * Y = (void *)((uintptr_t)(XY) + 128 * r); __m128i * Z = (void *)((uintptr_t)(XY) + 256 * r); uint32_t * X32 = (void *)X; uint64_t i, j; size_t k; /* 1: X <-- B */ for (k = 0; k < 2 * r; k++) { for (i = 0; i < 16; i++) { X32[k * 16 + i] = le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]); } } /* 2: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 3: V_i <-- X */ blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(X, Y, Z, r); /* 3: V_i <-- X */ blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r), Y, 128 * r); /* 4: X <-- H(X) */ blockmix_salsa8(Y, X, Z, r); } /* 6: for i = 0 to N - 1 do */ for (i = 0; i < N; i += 2) { /* 7: j <-- Integerify(X) mod N */ j = integerify(X, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); blockmix_salsa8(X, Y, Z, r); /* 7: j <-- Integerify(X) mod N */ j = integerify(Y, r) & (N - 1); /* 8: X <-- H(X \xor V_j) */ blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); blockmix_salsa8(Y, X, Z, r); } /* 10: B' <-- X */ for (k = 0; k < 2 * r; k++) { for (i = 0; i < 16; i++) { le32enc(&B[(k * 16 + (i * 5 % 16)) * 4], X32[k * 16 + i]); } } } #endif /* CPUSUPPORT_X86_SSE2 */ ================================================ FILE: ext/scrypt/crypto_scrypt_smix_sse2.h ================================================ #ifndef _CRYPTO_SCRYPT_SMIX_SSE2_H_ #define _CRYPTO_SCRYPT_SMIX_SSE2_H_ /** * crypto_scrypt_smix_sse2(B, r, N, V, XY): * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; * the temporary storage V must be 128rN bytes in length; the temporary * storage XY must be 256r + 64 bytes in length. The value N must be a * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a * multiple of 64 bytes. * * Use SSE2 instructions. */ void crypto_scrypt_smix_sse2(uint8_t *, size_t, uint64_t, void *, void *); #endif /* !_CRYPTO_SCRYPT_SMIX_SSE2_H_ */ ================================================ FILE: ext/scrypt/insecure_memzero.c ================================================ #include #include #include "insecure_memzero.h" /* Function which does the zeroing. */ static void insecure_memzero_func(volatile void * buf, size_t len) { volatile uint8_t * _buf = buf; size_t i; for (i = 0; i < len; i++) _buf[i] = 0; } /* Pointer to memory-zeroing function. */ void (* volatile insecure_memzero_ptr)(volatile void *, size_t) = insecure_memzero_func; ================================================ FILE: ext/scrypt/insecure_memzero.h ================================================ #ifndef _INSECURE_MEMZERO_H_ #define _INSECURE_MEMZERO_H_ #include /* Pointer to memory-zeroing function. */ extern void (* volatile insecure_memzero_ptr)(volatile void *, size_t); /** * insecure_memzero(buf, len): * Attempt to zero ${len} bytes at ${buf} in spite of optimizing compilers' * best (standards-compliant) attempts to remove the buffer-zeroing. In * particular, to avoid performing the zeroing, a compiler would need to * use optimistic devirtualization; recognize that non-volatile objects do not * need to be treated as volatile, even if they are accessed via volatile * qualified pointers; and perform link-time optimization; in addition to the * dead-code elimination which often causes buffer-zeroing to be elided. * * Note however that zeroing a buffer does not guarantee that the data held * in the buffer is not stored elsewhere; in particular, there may be copies * held in CPU registers or in anonymous allocations on the stack, even if * every named variable is successfully sanitized. Solving the "wipe data * from the system" problem will require a C language extension which does not * yet exist. * * For more information, see: * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html * http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html */ static inline void insecure_memzero(volatile void * buf, size_t len) { (insecure_memzero_ptr)(buf, len); } #endif /* !_INSECURE_MEMZERO_H_ */ ================================================ FILE: ext/scrypt/memlimit.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "scrypt_platform.h" #include #ifndef __MINGW32__ #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYSCTL_HW_USERMEM #include #endif #ifdef HAVE_SYS_SYSINFO_H #include #endif #include #include #include #include #ifdef DEBUG #include #endif #include "memlimit.h" #ifdef HAVE_SYSCTL_HW_USERMEM static int memlimit_sysctl_hw_usermem(size_t * memlimit) { int mib[2]; uint8_t usermembuf[8]; size_t usermemlen = 8; uint64_t usermem; /* Ask the kernel how much RAM we have. */ mib[0] = CTL_HW; mib[1] = HW_USERMEM; if (sysctl(mib, 2, usermembuf, &usermemlen, NULL, 0)) return (1); /* * Parse as either a uint64_t or a uint32_t based on the length of * output the kernel reports having copied out. It appears that all * systems providing a sysctl interface for reading integers copy * them out as system-endian values, so we don't need to worry about * parsing them. */ if (usermemlen == sizeof(uint64_t)) usermem = *(uint64_t *)usermembuf; else if (usermemlen == sizeof(uint32_t)) usermem = *(uint32_t *)usermembuf; else return (1); /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (usermem > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = usermem; #else *memlimit = usermem; #endif /* Success! */ return (0); } #endif /* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */ #ifndef HAVE_STRUCT_SYSINFO #undef HAVE_SYSINFO #endif /* If we don't HAVE_STRUCT_SYSINFO_TOTALRAM, we can't use sysinfo. */ #ifndef HAVE_STRUCT_SYSINFO_TOTALRAM #undef HAVE_SYSINFO #endif #ifdef HAVE_SYSINFO static int memlimit_sysinfo(size_t * memlimit) { struct sysinfo info; uint64_t totalmem; /* Get information from the kernel. */ if (sysinfo(&info)) return (1); totalmem = info.totalram; /* If we're on a modern kernel, adjust based on mem_unit. */ #ifdef HAVE_STRUCT_SYSINFO_MEM_UNIT totalmem = totalmem * info.mem_unit; #endif /* Return the value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (totalmem > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = totalmem; #else *memlimit = totalmem; #endif /* Success! */ return (0); } #endif /* HAVE_SYSINFO */ static int memlimit_rlimit(size_t * memlimit) { #ifndef __MINGW32__ struct rlimit rl; uint64_t memrlimit; /* Find the least of... */ memrlimit = (uint64_t)(-1); /* ... RLIMIT_AS... */ #ifdef RLIMIT_AS if (getrlimit(RLIMIT_AS, &rl)) return (1); if ((rl.rlim_cur != RLIM_INFINITY) && ((uint64_t)rl.rlim_cur < memrlimit)) memrlimit = rl.rlim_cur; #endif /* ... RLIMIT_DATA... */ if (getrlimit(RLIMIT_DATA, &rl)) return (1); if ((rl.rlim_cur != RLIM_INFINITY) && ((uint64_t)rl.rlim_cur < memrlimit)) memrlimit = rl.rlim_cur; /* ... and RLIMIT_RSS. */ #ifdef RLIMIT_RSS if (getrlimit(RLIMIT_RSS, &rl)) return (1); if ((rl.rlim_cur != RLIM_INFINITY) && ((uint64_t)rl.rlim_cur < memrlimit)) memrlimit = rl.rlim_cur; #endif /* Return the value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (memrlimit > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = memrlimit; #else *memlimit = memrlimit; #endif #else *memlimit = SIZE_MAX; #endif /* Success! */ return (0); } #ifdef _SC_PHYS_PAGES /* Some systems define _SC_PAGESIZE instead of _SC_PAGE_SIZE. */ #ifndef _SC_PAGE_SIZE #define _SC_PAGE_SIZE _SC_PAGESIZE #endif static int memlimit_sysconf(size_t * memlimit) { long pagesize; long physpages; uint64_t totalmem; /* Set errno to 0 in order to distinguish "no limit" from "error". */ errno = 0; /* Read the two limits. */ if (((pagesize = sysconf(_SC_PAGE_SIZE)) == -1) || ((physpages = sysconf(_SC_PHYS_PAGES)) == -1)) { /* Did an error occur? */ if (errno != 0) return (1); /* If not, there is no limit. */ totalmem = (uint64_t)(-1); } else { /* Compute the limit. */ totalmem = (uint64_t)(pagesize) * (uint64_t)(physpages); } /* Return the value, but clamp to SIZE_MAX if necessary. */ #if UINT64_MAX > SIZE_MAX if (totalmem > SIZE_MAX) *memlimit = SIZE_MAX; else *memlimit = totalmem; #else *memlimit = totalmem; #endif /* Success! */ return (0); } #endif int memtouse(size_t maxmem, double maxmemfrac, size_t * memlimit) { size_t sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit; size_t sysconf_memlimit; size_t memlimit_min; size_t memavail; /* Get memory limits. */ #ifdef HAVE_SYSCTL_HW_USERMEM if (memlimit_sysctl_hw_usermem(&sysctl_memlimit)) return (1); #else sysctl_memlimit = (size_t)(-1); #endif #ifdef HAVE_SYSINFO if (memlimit_sysinfo(&sysinfo_memlimit)) return (1); #else sysinfo_memlimit = (size_t)(-1); #endif if (memlimit_rlimit(&rlimit_memlimit)) return (1); #ifdef _SC_PHYS_PAGES if (memlimit_sysconf(&sysconf_memlimit)) return (1); #else sysconf_memlimit = (size_t)(-1); #endif #ifdef DEBUG fprintf(stderr, "Memory limits are %zu %zu %zu %zu\n", sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit, sysconf_memlimit); #endif /* Find the smallest of them. */ memlimit_min = (size_t)(-1); if (memlimit_min > sysctl_memlimit) memlimit_min = sysctl_memlimit; if (memlimit_min > sysinfo_memlimit) memlimit_min = sysinfo_memlimit; if (memlimit_min > rlimit_memlimit) memlimit_min = rlimit_memlimit; if (memlimit_min > sysconf_memlimit) memlimit_min = sysconf_memlimit; /* Only use the specified fraction of the available memory. */ if ((maxmemfrac > 0.5) || (maxmemfrac == 0.0)) maxmemfrac = 0.5; memavail = maxmemfrac * memlimit_min; /* Don't use more than the specified maximum. */ if ((maxmem > 0) && (memavail > maxmem)) memavail = maxmem; /* But always allow at least 1 MiB. */ if (memavail < 1048576) memavail = 1048576; #ifdef DEBUG fprintf(stderr, "Allowing up to %zu memory to be used\n", memavail); #endif /* Return limit via the provided pointer. */ *memlimit = memavail; return (0); } ================================================ FILE: ext/scrypt/memlimit.h ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef _MEMLIMIT_H_ #define _MEMLIMIT_H_ #include /** * memtouse(maxmem, maxmemfrac, memlimit): * Examine the system and return via memlimit the amount of RAM which should * be used -- the specified fraction of the available RAM, but no more than * maxmem, and no less than 1MiB. */ int memtouse(size_t, double, size_t *); #endif /* !_MEMLIMIT_H_ */ ================================================ FILE: ext/scrypt/scrypt_calibrate.c ================================================ /* * scrypt_calibrate.c * scrypt * * Created by Patrick Hogan on 12/15/10. * Copyright 2010 __MyCompanyName__. All rights reserved. * */ #include "scrypt_platform.h" #include #include #include #include #include #include #include "memlimit.h" #include "scryptenc_cpuperf.h" #include "sysendian.h" #include "scrypt_calibrate.h" static int pickparams(size_t maxmem, double maxmemfrac, double maxtime, int * logN, uint32_t * r, uint32_t * p) { size_t memlimit; double opps; double opslimit; double maxN, maxrp; int rc; /* Figure out how much memory to use. */ if (memtouse(maxmem, maxmemfrac, &memlimit)) return (1); /* Figure out how fast the CPU is. */ if ((rc = scryptenc_cpuperf(&opps)) != 0) return (rc); opslimit = opps * maxtime; /* Allow a minimum of 2^15 salsa20/8 cores. */ if (opslimit < 32768) opslimit = 32768; /* Fix r = 8 for now. */ *r = 8; /* * The memory limit requires that 128Nr <= memlimit, while the CPU * limit requires that 4Nrp <= opslimit. If opslimit < memlimit/32, * opslimit imposes the stronger limit on N. */ #ifdef DEBUG fprintf(stderr, "Requiring 128Nr <= %zu, 4Nrp <= %f\n", memlimit, opslimit); #endif if (opslimit < memlimit/32) { /* Set p = 1 and choose N based on the CPU limit. */ *p = 1; maxN = opslimit / (*r * 4); for (*logN = 1; *logN < 63; *logN += 1) { if ((uint64_t)(1) << *logN > maxN / 2) break; } } else { /* Set N based on the memory limit. */ maxN = memlimit / (*r * 128); for (*logN = 1; *logN < 63; *logN += 1) { if ((uint64_t)(1) << *logN > maxN / 2) break; } /* Choose p based on the CPU limit. */ maxrp = (opslimit / 4) / ((uint64_t)(1) << *logN); if (maxrp > 0x3fffffff) maxrp = 0x3fffffff; *p = (uint32_t)(maxrp) / *r; } #ifdef DEBUG fprintf(stderr, "N = %zu r = %d p = %d\n", (size_t)(1) << *logN, (int)(*r), (int)(*p)); #endif /* Success! */ return (0); } int calibrate(size_t maxmem, double maxmemfrac, double maxtime, uint64_t * n, uint32_t * r, uint32_t * p) { int logN = 0; int result = pickparams( maxmem, maxmemfrac, maxtime, & logN, r, p ); if (result == 0) { *n = (uint64_t)(1) << logN; } return result; } ================================================ FILE: ext/scrypt/scrypt_calibrate.h ================================================ /* * scrypt_calibrate.h * scrypt * * Created by Patrick Hogan on 12/15/10. * */ #ifndef _SCRYPT_CALIBRATE_H_ #define _SCRYPT_CALIBRATE_H_ #include #include int calibrate( size_t maxmem, double maxmemfrac, double maxtime, uint64_t * n, uint32_t * r, uint32_t * p ); #endif ================================================ FILE: ext/scrypt/scrypt_ext.c ================================================ #include "scrypt_ext.h" #include "scrypt_calibrate.h" #include "crypto_scrypt.h" typedef struct { uint64_t n; uint32_t r; uint32_t p; } Calibration; RBFFI_EXPORT int sc_calibrate(size_t maxmem, double maxmemfrac, double maxtime, Calibration *result) { return calibrate(maxmem, maxmemfrac, maxtime, &result->n, &result->r, &result->p); // 0 == success } ================================================ FILE: ext/scrypt/scrypt_ext.h ================================================ #ifndef SCRYPT_EXT_H #define SCRYPT_EXT_H 1 #ifndef RBFFI_EXPORT # ifdef __cplusplus # define RBFFI_EXPORT extern "C" # else # define RBFFI_EXPORT # endif #endif #endif /* SCRYPT_EXT_H */ ================================================ FILE: ext/scrypt/scrypt_platform.h ================================================ #ifndef _SCRYPT_PLATFORM_H_ #define _SCRYPT_PLATFORM_H_ #endif ================================================ FILE: ext/scrypt/scryptenc_cpuperf.c ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #include "scrypt_platform.h" #include #include #include #include #include "crypto_scrypt.h" #include "scryptenc_cpuperf.h" #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) struct timespec { long tv_sec; long tv_nsec; }; #endif #ifdef HAVE_CLOCK_GETTIME static clock_t clocktouse; static int getclockres(double * resd) { struct timespec res; /* * Try clocks in order of preference until we find one which works. * (We assume that if clock_getres works, clock_gettime will, too.) * The use of if/else/if/else/if/else rather than if/elif/elif/else * is ugly but legal, and allows us to #ifdef things appropriately. */ #ifdef CLOCK_VIRTUAL if (clock_getres(CLOCK_VIRTUAL, &res) == 0) clocktouse = CLOCK_VIRTUAL; else #endif #ifdef CLOCK_MONOTONIC if (clock_getres(CLOCK_MONOTONIC, &res) == 0) clocktouse = CLOCK_MONOTONIC; else #endif if (clock_getres(CLOCK_REALTIME, &res) == 0) clocktouse = CLOCK_REALTIME; else return (-1); /* Convert clock resolution to a double. */ *resd = res.tv_sec + res.tv_nsec * 0.000000001; return (0); } static int getclocktime(struct timespec * ts) { if (clock_gettime(clocktouse, ts)) return (-1); return (0); } #else static int getclockres(double * resd) { *resd = 1.0 / CLOCKS_PER_SEC; return (0); } static int getclocktime(struct timespec * ts) { struct timeval tv; if (gettimeofday(&tv, NULL)) return (-1); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; return (0); } #endif static int getclockdiff(struct timespec * st, double * diffd) { struct timespec en; if (getclocktime(&en)) return (1); *diffd = (en.tv_nsec - st->tv_nsec) * 0.000000001 + (en.tv_sec - st->tv_sec); return (0); } /** * scryptenc_cpuperf(opps): * Estimate the number of salsa20/8 cores which can be executed per second, * and return the value via opps. */ int scryptenc_cpuperf(double * opps) { struct timespec st; double resd, diffd; uint64_t i = 0; /* Get the clock resolution. */ if (getclockres(&resd)) return (2); #ifdef DEBUG fprintf(stderr, "Clock resolution is %f\n", resd); #endif /* Loop until the clock ticks. */ if (getclocktime(&st)) return (2); do { /* Do an scrypt. */ if (crypto_scrypt(NULL, 0, NULL, 0, 16, 1, 1, NULL, 0)) return (3); /* Has the clock ticked? */ if (getclockdiff(&st, &diffd)) return (2); if (diffd > 0) break; } while (1); /* Count how many scrypts we can do before the next tick. */ if (getclocktime(&st)) return (2); do { /* Do an scrypt. */ if (crypto_scrypt(NULL, 0, NULL, 0, 128, 1, 1, NULL, 0)) return (3); /* We invoked the salsa20/8 core 512 times. */ i += 512; /* Check if we have looped for long enough. */ if (getclockdiff(&st, &diffd)) return (2); if (diffd > resd) break; } while (1); #ifdef DEBUG fprintf(stderr, "%ju salsa20/8 cores performed in %f seconds\n", (uintmax_t)i, diffd); #endif /* We can do approximately i salsa20/8 cores per diffd seconds. */ *opps = i / diffd; return (0); } ================================================ FILE: ext/scrypt/scryptenc_cpuperf.h ================================================ /*- * Copyright 2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef _SCRYPTENC_CPUPERF_H_ #define _SCRYPTENC_CPUPERF_H_ /** * scryptenc_cpuperf(opps): * Estimate the number of salsa20/8 cores which can be executed per second, * and return the value via opps. */ int scryptenc_cpuperf(double *); #endif /* !_SCRYPTENC_CPUPERF_H_ */ ================================================ FILE: ext/scrypt/sha256.c ================================================ #include #include #include #include "insecure_memzero.h" #include "sysendian.h" #include "sha256.h" /* * Encode a length len/4 vector of (uint32_t) into a length len vector of * (uint8_t) in big-endian form. Assumes len is a multiple of 4. */ static void be32enc_vect(uint8_t * dst, const uint32_t * src, size_t len) { size_t i; /* Sanity-check. */ assert(len % 4 == 0); /* Encode vector, one word at a time. */ for (i = 0; i < len / 4; i++) be32enc(dst + i * 4, src[i]); } /* * Decode a big-endian length len vector of (uint8_t) into a length * len/4 vector of (uint32_t). Assumes len is a multiple of 4. */ static void be32dec_vect(uint32_t * dst, const uint8_t * src, size_t len) { size_t i; /* Sanity-check. */ assert(len % 4 == 0); /* Decode vector, one word at a time. */ for (i = 0; i < len / 4; i++) dst[i] = be32dec(src + i * 4); } /* SHA256 round constants. */ static const uint32_t K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; /* Elementary functions used by SHA256 */ #define Ch(x, y, z) ((x & (y ^ z)) ^ z) #define Maj(x, y, z) ((x & (y | z)) | (y & z)) #define SHR(x, n) (x >> n) #define ROTR(x, n) ((x >> n) | (x << (32 - n))) #define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) #define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) #define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) #define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) /* SHA256 round function */ #define RND(a, b, c, d, e, f, g, h, k) \ h += S1(e) + Ch(e, f, g) + k; \ d += h; \ h += S0(a) + Maj(a, b, c); /* Adjusted round function for rotating state */ #define RNDr(S, W, i, ii) \ RND(S[(64 - i) % 8], S[(65 - i) % 8], \ S[(66 - i) % 8], S[(67 - i) % 8], \ S[(68 - i) % 8], S[(69 - i) % 8], \ S[(70 - i) % 8], S[(71 - i) % 8], \ W[i + ii] + K[i + ii]) /* Message schedule computation */ #define MSCH(W, ii, i) \ W[i + ii + 16] = s1(W[i + ii + 14]) + W[i + ii + 9] + s0(W[i + ii + 1]) + W[i + ii] /* * SHA256 block compression function. The 256-bit state is transformed via * the 512-bit input block to produce a new state. */ static void SHA256_Transform(uint32_t state[static restrict 8], const uint8_t block[static restrict 64], uint32_t W[static restrict 64], uint32_t S[static restrict 8]) { int i; /* 1. Prepare the first part of the message schedule W. */ be32dec_vect(W, block, 64); /* 2. Initialize working variables. */ memcpy(S, state, 32); /* 3. Mix. */ for (i = 0; i < 64; i += 16) { RNDr(S, W, 0, i); RNDr(S, W, 1, i); RNDr(S, W, 2, i); RNDr(S, W, 3, i); RNDr(S, W, 4, i); RNDr(S, W, 5, i); RNDr(S, W, 6, i); RNDr(S, W, 7, i); RNDr(S, W, 8, i); RNDr(S, W, 9, i); RNDr(S, W, 10, i); RNDr(S, W, 11, i); RNDr(S, W, 12, i); RNDr(S, W, 13, i); RNDr(S, W, 14, i); RNDr(S, W, 15, i); if (i == 48) break; MSCH(W, 0, i); MSCH(W, 1, i); MSCH(W, 2, i); MSCH(W, 3, i); MSCH(W, 4, i); MSCH(W, 5, i); MSCH(W, 6, i); MSCH(W, 7, i); MSCH(W, 8, i); MSCH(W, 9, i); MSCH(W, 10, i); MSCH(W, 11, i); MSCH(W, 12, i); MSCH(W, 13, i); MSCH(W, 14, i); MSCH(W, 15, i); } /* 4. Mix local working variables into global state. */ for (i = 0; i < 8; i++) state[i] += S[i]; } static const uint8_t PAD[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Add padding and terminating bit-count. */ static void SHA256_Pad(SHA256_CTX * ctx, uint32_t tmp32[static restrict 72]) { size_t r; /* Figure out how many bytes we have buffered. */ r = (ctx->count >> 3) & 0x3f; /* Pad to 56 mod 64, transforming if we finish a block en route. */ if (r < 56) { /* Pad to 56 mod 64. */ memcpy(&ctx->buf[r], PAD, 56 - r); } else { /* Finish the current block and mix. */ memcpy(&ctx->buf[r], PAD, 64 - r); SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); /* The start of the final block is all zeroes. */ memset(&ctx->buf[0], 0, 56); } /* Add the terminating bit-count. */ be64enc(&ctx->buf[56], ctx->count); /* Mix in the final block. */ SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); } /* Magic initialization constants. */ static const uint32_t initstate[8] = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; /** * SHA256_Init(ctx): * Initialize the SHA256 context ${ctx}. */ void SHA256_Init(SHA256_CTX * ctx) { /* Zero bits processed so far. */ ctx->count = 0; /* Initialize state. */ memcpy(ctx->state, initstate, sizeof(initstate)); } /** * SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the SHA256 context ${ctx}. */ static void _SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len, uint32_t tmp32[static restrict 72]) { uint32_t r; const uint8_t * src = in; /* Return immediately if we have nothing to do. */ if (len == 0) return; /* Number of bytes left in the buffer from previous updates. */ r = (ctx->count >> 3) & 0x3f; /* Update number of bits. */ ctx->count += (uint64_t)(len) << 3; /* Handle the case where we don't need to perform any transforms. */ if (len < 64 - r) { memcpy(&ctx->buf[r], src, len); return; } /* Finish the current block. */ memcpy(&ctx->buf[r], src, 64 - r); SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]); src += 64 - r; len -= 64 - r; /* Perform complete blocks. */ while (len >= 64) { SHA256_Transform(ctx->state, src, &tmp32[0], &tmp32[64]); src += 64; len -= 64; } /* Copy left over data into buffer. */ memcpy(ctx->buf, src, len); } /* Wrapper function for intermediate-values sanitization. */ void SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len) { uint32_t tmp32[72]; /* Call the real function. */ _SHA256_Update(ctx, in, len, tmp32); /* Clean the stack. */ insecure_memzero(tmp32, 288); } /** * SHA256_Final(digest, ctx): * Output the SHA256 hash of the data input to the context ${ctx} into the * buffer ${digest}. */ static void _SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx, uint32_t tmp32[static restrict 72]) { /* Add padding. */ SHA256_Pad(ctx, tmp32); /* Write the hash. */ be32enc_vect(digest, ctx->state, 32); } /* Wrapper function for intermediate-values sanitization. */ void SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx) { uint32_t tmp32[72]; /* Call the real function. */ _SHA256_Final(digest, ctx, tmp32); /* Clear the context state. */ insecure_memzero(ctx, sizeof(SHA256_CTX)); /* Clean the stack. */ insecure_memzero(tmp32, 288); } /** * SHA256_Buf(in, len, digest): * Compute the SHA256 hash of ${len} bytes from $in} and write it to ${digest}. */ void SHA256_Buf(const void * in, size_t len, uint8_t digest[32]) { SHA256_CTX ctx; uint32_t tmp32[72]; SHA256_Init(&ctx); _SHA256_Update(&ctx, in, len, tmp32); _SHA256_Final(digest, &ctx, tmp32); /* Clean the stack. */ insecure_memzero(&ctx, sizeof(SHA256_CTX)); insecure_memzero(tmp32, 288); } /** * HMAC_SHA256_Init(ctx, K, Klen): * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from * ${K}. */ static void _HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen, uint32_t tmp32[static restrict 72], uint8_t pad[static restrict 64], uint8_t khash[static restrict 32]) { const uint8_t * K = _K; size_t i; /* If Klen > 64, the key is really SHA256(K). */ if (Klen > 64) { SHA256_Init(&ctx->ictx); _SHA256_Update(&ctx->ictx, K, Klen, tmp32); _SHA256_Final(khash, &ctx->ictx, tmp32); K = khash; Klen = 32; } /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ SHA256_Init(&ctx->ictx); memset(pad, 0x36, 64); for (i = 0; i < Klen; i++) pad[i] ^= K[i]; _SHA256_Update(&ctx->ictx, pad, 64, tmp32); /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ SHA256_Init(&ctx->octx); memset(pad, 0x5c, 64); for (i = 0; i < Klen; i++) pad[i] ^= K[i]; _SHA256_Update(&ctx->octx, pad, 64, tmp32); } /* Wrapper function for intermediate-values sanitization. */ void HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) { uint32_t tmp32[72]; uint8_t pad[64]; uint8_t khash[32]; /* Call the real function. */ _HMAC_SHA256_Init(ctx, _K, Klen, tmp32, pad, khash); /* Clean the stack. */ insecure_memzero(tmp32, 288); insecure_memzero(khash, 32); insecure_memzero(pad, 64); } /** * HMAC_SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}. */ static void _HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len, uint32_t tmp32[static restrict 72]) { /* Feed data to the inner SHA256 operation. */ _SHA256_Update(&ctx->ictx, in, len, tmp32); } /* Wrapper function for intermediate-values sanitization. */ void HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len) { uint32_t tmp32[72]; /* Call the real function. */ _HMAC_SHA256_Update(ctx, in, len, tmp32); /* Clean the stack. */ insecure_memzero(tmp32, 288); } /** * HMAC_SHA256_Final(digest, ctx): * Output the HMAC-SHA256 of the data input to the context ${ctx} into the * buffer ${digest}. */ static void _HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx, uint32_t tmp32[static restrict 72], uint8_t ihash[static restrict 32]) { /* Finish the inner SHA256 operation. */ _SHA256_Final(ihash, &ctx->ictx, tmp32); /* Feed the inner hash to the outer SHA256 operation. */ _SHA256_Update(&ctx->octx, ihash, 32, tmp32); /* Finish the outer SHA256 operation. */ _SHA256_Final(digest, &ctx->octx, tmp32); } /* Wrapper function for intermediate-values sanitization. */ void HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx) { uint32_t tmp32[72]; uint8_t ihash[32]; /* Call the real function. */ _HMAC_SHA256_Final(digest, ctx, tmp32, ihash); /* Clean the stack. */ insecure_memzero(tmp32, 288); insecure_memzero(ihash, 32); } /** * HMAC_SHA256_Buf(K, Klen, in, len, digest): * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of * length ${Klen}, and write the result to ${digest}. */ void HMAC_SHA256_Buf(const void * K, size_t Klen, const void * in, size_t len, uint8_t digest[32]) { HMAC_SHA256_CTX ctx; uint32_t tmp32[72]; uint8_t tmp8[96]; _HMAC_SHA256_Init(&ctx, K, Klen, tmp32, &tmp8[0], &tmp8[64]); _HMAC_SHA256_Update(&ctx, in, len, tmp32); _HMAC_SHA256_Final(digest, &ctx, tmp32, &tmp8[0]); /* Clean the stack. */ insecure_memzero(&ctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(tmp32, 288); insecure_memzero(tmp8, 96); } /** * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). */ void PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) { HMAC_SHA256_CTX Phctx, PShctx, hctx; uint32_t tmp32[72]; uint8_t tmp8[96]; size_t i; uint8_t ivec[4]; uint8_t U[32]; uint8_t T[32]; uint64_t j; int k; size_t clen; /* Sanity-check. */ assert(dkLen <= 32 * (size_t)(UINT32_MAX)); /* Compute HMAC state after processing P. */ _HMAC_SHA256_Init(&Phctx, passwd, passwdlen, tmp32, &tmp8[0], &tmp8[64]); /* Compute HMAC state after processing P and S. */ memcpy(&PShctx, &Phctx, sizeof(HMAC_SHA256_CTX)); _HMAC_SHA256_Update(&PShctx, salt, saltlen, tmp32); /* Iterate through the blocks. */ for (i = 0; i * 32 < dkLen; i++) { /* Generate INT(i + 1). */ be32enc(ivec, (uint32_t)(i + 1)); /* Compute U_1 = PRF(P, S || INT(i)). */ memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); _HMAC_SHA256_Update(&hctx, ivec, 4, tmp32); _HMAC_SHA256_Final(U, &hctx, tmp32, tmp8); /* T_i = U_1 ... */ memcpy(T, U, 32); for (j = 2; j <= c; j++) { /* Compute U_j. */ memcpy(&hctx, &Phctx, sizeof(HMAC_SHA256_CTX)); _HMAC_SHA256_Update(&hctx, U, 32, tmp32); _HMAC_SHA256_Final(U, &hctx, tmp32, tmp8); /* ... xor U_j ... */ for (k = 0; k < 32; k++) T[k] ^= U[k]; } /* Copy as many bytes as necessary into buf. */ clen = dkLen - i * 32; if (clen > 32) clen = 32; memcpy(&buf[i * 32], T, clen); } /* Clean the stack. */ insecure_memzero(&Phctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(&PShctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(&hctx, sizeof(HMAC_SHA256_CTX)); insecure_memzero(tmp32, 288); insecure_memzero(tmp8, 96); insecure_memzero(U, 32); insecure_memzero(T, 32); } ================================================ FILE: ext/scrypt/sha256.h ================================================ #ifndef _SHA256_H_ #define _SHA256_H_ #include #include /* * Use #defines in order to avoid namespace collisions with anyone else's * SHA256 code (e.g., the code in OpenSSL). */ #define SHA256_Init libcperciva_SHA256_Init #define SHA256_Update libcperciva_SHA256_Update #define SHA256_Final libcperciva_SHA256_Final #define SHA256_Buf libcperciva_SHA256_Buf #define SHA256_CTX libcperciva_SHA256_CTX #define HMAC_SHA256_Init libcperciva_HMAC_SHA256_Init #define HMAC_SHA256_Update libcperciva_HMAC_SHA256_Update #define HMAC_SHA256_Final libcperciva_HMAC_SHA256_Final #define HMAC_SHA256_Buf libcperciva_HMAC_SHA256_Buf #define HMAC_SHA256_CTX libcperciva_HMAC_SHA256_CTX /* Context structure for SHA256 operations. */ typedef struct { uint32_t state[8]; uint64_t count; uint8_t buf[64]; } SHA256_CTX; /** * SHA256_Init(ctx): * Initialize the SHA256 context ${ctx}. */ void SHA256_Init(SHA256_CTX *); /** * SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the SHA256 context ${ctx}. */ void SHA256_Update(SHA256_CTX *, const void *, size_t); /** * SHA256_Final(digest, ctx): * Output the SHA256 hash of the data input to the context ${ctx} into the * buffer ${digest}. */ void SHA256_Final(uint8_t[32], SHA256_CTX *); /** * SHA256_Buf(in, len, digest): * Compute the SHA256 hash of ${len} bytes from $in} and write it to ${digest}. */ void SHA256_Buf(const void *, size_t, uint8_t[32]); /* Context structure for HMAC-SHA256 operations. */ typedef struct { SHA256_CTX ictx; SHA256_CTX octx; } HMAC_SHA256_CTX; /** * HMAC_SHA256_Init(ctx, K, Klen): * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from * ${K}. */ void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); /** * HMAC_SHA256_Update(ctx, in, len): * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}. */ void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); /** * HMAC_SHA256_Final(digest, ctx): * Output the HMAC-SHA256 of the data input to the context ${ctx} into the * buffer ${digest}. */ void HMAC_SHA256_Final(uint8_t[32], HMAC_SHA256_CTX *); /** * HMAC_SHA256_Buf(K, Klen, in, len, digest): * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of * length ${Klen}, and write the result to ${digest}. */ void HMAC_SHA256_Buf(const void *, size_t, const void *, size_t, uint8_t[32]); /** * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). */ void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, uint8_t *, size_t); #endif /* !_SHA256_H_ */ ================================================ FILE: ext/scrypt/sysendian.h ================================================ /*- * Copyright 2007-2009 Colin Percival * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file was originally written by Colin Percival as part of the Tarsnap * online backup system. */ #ifndef _SYSENDIAN_H_ #define _SYSENDIAN_H_ #include "scrypt_platform.h" /* If we don't have be64enc, the we have isn't usable. */ #if !HAVE_DECL_BE64ENC #undef HAVE_SYS_ENDIAN_H #endif #ifdef HAVE_SYS_ENDIAN_H #include #else #include static inline uint32_t be32dec(const void *pp) { const uint8_t *p = (uint8_t const *)pp; return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); } static inline void be32enc(void *pp, uint32_t x) { uint8_t * p = (uint8_t *)pp; p[3] = x & 0xff; p[2] = (x >> 8) & 0xff; p[1] = (x >> 16) & 0xff; p[0] = (x >> 24) & 0xff; } static inline uint64_t be64dec(const void *pp) { const uint8_t *p = (uint8_t const *)pp; return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); } static inline void be64enc(void *pp, uint64_t x) { uint8_t * p = (uint8_t *)pp; p[7] = x & 0xff; p[6] = (x >> 8) & 0xff; p[5] = (x >> 16) & 0xff; p[4] = (x >> 24) & 0xff; p[3] = (x >> 32) & 0xff; p[2] = (x >> 40) & 0xff; p[1] = (x >> 48) & 0xff; p[0] = (x >> 56) & 0xff; } static inline uint32_t le32dec(const void *pp) { const uint8_t *p = (uint8_t const *)pp; return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); } static inline void le32enc(void *pp, uint32_t x) { uint8_t * p = (uint8_t *)pp; p[0] = x & 0xff; p[1] = (x >> 8) & 0xff; p[2] = (x >> 16) & 0xff; p[3] = (x >> 24) & 0xff; } static inline uint64_t le64dec(const void *pp) { const uint8_t *p = (uint8_t const *)pp; return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); } static inline void le64enc(void *pp, uint64_t x) { uint8_t * p = (uint8_t *)pp; p[0] = x & 0xff; p[1] = (x >> 8) & 0xff; p[2] = (x >> 16) & 0xff; p[3] = (x >> 24) & 0xff; p[4] = (x >> 32) & 0xff; p[5] = (x >> 40) & 0xff; p[6] = (x >> 48) & 0xff; p[7] = (x >> 56) & 0xff; } #endif /* !HAVE_SYS_ENDIAN_H */ #endif /* !_SYSENDIAN_H_ */ ================================================ FILE: ext/scrypt/warnp.c ================================================ #include #include #include #include #include #include "warnp.h" static int initialized = 0; static char * name = NULL; /* Free the name string. */ static void done(void) { free(name); name = NULL; } /** * warnp_setprogname(progname): * Set the program name to be used by warn() and warnx() to ${progname}. */ void warnp_setprogname(const char * progname) { const char * p; /* Free the name if we already have one. */ free(name); /* Find the last segment of the program name. */ for (p = progname; progname[0] != '\0'; progname++) if (progname[0] == '/') p = progname + 1; /* Copy the name string. */ name = malloc(strlen(p) + 1); if (name == NULL) { /* No cleanup handler needs to be registered on failure. */ return; } strncpy(name, p, strlen(p) + 1); name[strlen(p)] = '\0'; /* Ensure null termination */ /* If we haven't already done so, register our exit handler. */ if (initialized == 0) { atexit(done); initialized = 1; } } void warn(const char * fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)"); if (fmt != NULL) { fprintf(stderr, ": "); vfprintf(stderr, fmt, ap); } fprintf(stderr, ": %s\n", strerror(errno)); va_end(ap); } void warnx(const char * fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)"); if (fmt != NULL) { fprintf(stderr, ": "); vfprintf(stderr, fmt, ap); } fprintf(stderr, "\n"); va_end(ap); } ================================================ FILE: ext/scrypt/warnp.h ================================================ #ifndef _WARNP_H_ #define _WARNP_H_ #include /* Avoid namespace collisions with BSD . */ #define warn libcperciva_warn #define warnx libcperciva_warnx /** * warnp_setprogname(progname): * Set the program name to be used by warn() and warnx() to ${progname}. */ void warnp_setprogname(const char *); #define WARNP_INIT do { \ if (argv[0] != NULL) \ warnp_setprogname(argv[0]); \ } while (0) /* As in BSD . */ void warn(const char *, ...); void warnx(const char *, ...); /* * If compiled with DEBUG defined, print __FILE__ and __LINE__. */ #ifdef DEBUG #define warnline do { \ warnx("%s, %d", __FILE__, __LINE__); \ } while (0) #else #define warnline #endif /* * Call warn(3) or warnx(3) depending upon whether errno == 0; and clear * errno (so that the standard error message isn't repeated later). */ #define warnp(...) do { \ warnline; \ if (errno != 0) { \ warn(__VA_ARGS__); \ errno = 0; \ } else \ warnx(__VA_ARGS__); \ } while (0) /* * Call warnx(3) and set errno == 0. Unlike warnp, this should be used * in cases where we're reporting a problem which we discover ourselves * rather than one which is reported to us from a library or the kernel. */ #define warn0(...) do { \ warnline; \ warnx(__VA_ARGS__); \ errno = 0; \ } while (0) #endif /* !_WARNP_H_ */ ================================================ FILE: lib/scrypt/engine.rb ================================================ # frozen_string_literal: true require 'ffi' require 'openssl' module SCrypt module Ext # Bind the external functions attach_function :sc_calibrate, %i[size_t double double pointer], :int, blocking: true attach_function :crypto_scrypt, %i[pointer size_t pointer size_t uint64 uint32 uint32 pointer size_t], :int, blocking: true # Use blocking: true for CPU-intensive operations to avoid GIL issues end class Engine # Regular expressions for validation COST_PATTERN = /^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$$/.freeze SALT_PATTERN = /^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]{16,64}$/.freeze HASH_PATTERN = /^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]{16,64}\$[A-Za-z0-9]{32,1024}$/.freeze # Constants for salt handling OLD_STYLE_SALT_LENGTH = 40 SALT_MIN_LENGTH = 16 DEFAULTS = { key_len: 32, salt_size: 32, max_mem: 16 * 1024 * 1024, max_memfrac: 0.5, max_time: 0.2, cost: nil }.freeze # Class variable to store calibrated cost, separate from defaults @calibrated_cost = nil class << self attr_accessor :calibrated_cost end class Calibration < FFI::Struct layout :n, :uint64, :r, :uint32, :p, :uint32 end class << self def scrypt(secret, salt, *args) case args.length when 2 # args is [cost_string, key_len] cost_string, key_len = args cpu_cost, memory_cost, parallelization = parse_cost_string(cost_string) __sc_crypt(secret, salt, cpu_cost, memory_cost, parallelization, key_len) when 4 # args is [n, r, p, key_len] cpu_cost, memory_cost, parallelization, key_len = args __sc_crypt(secret, salt, cpu_cost, memory_cost, parallelization, key_len) else raise ArgumentError, 'invalid number of arguments (4 or 6)' end end # Given a secret and a valid salt (see SCrypt::Engine.generate_salt) calculates an scrypt password hash. def hash_secret(secret, salt, key_len = DEFAULTS[:key_len]) raise Errors::InvalidSecret, 'invalid secret' unless valid_secret?(secret) raise Errors::InvalidSalt, 'invalid salt' unless valid_salt?(salt) cost = autodetect_cost(salt) salt_only = extract_salt_from_string(salt) if old_style_hash?(salt_only) generate_old_style_hash(secret, salt, cost) else generate_new_style_hash(secret, salt, salt_only, cost, key_len) end end # Generates a random salt with a given computational cost. Uses a saved # cost if SCrypt::Engine.calibrate! has been called. # # Options: # :cost is a cost string returned by SCrypt::Engine.calibrate def generate_salt(options = {}) options = DEFAULTS.merge(options) cost = options[:cost] || @calibrated_cost || calibrate(options) salt = OpenSSL::Random.random_bytes(options[:salt_size]).unpack('H*').first.rjust(SALT_MIN_LENGTH, '0') salt = avoid_old_style_collision(salt) cost + salt end # Returns true if +cost+ is a valid cost, false if not. def valid_cost?(cost) !COST_PATTERN.match(cost).nil? end # Returns true if +salt+ is a valid salt, false if not. def valid_salt?(salt) !SALT_PATTERN.match(salt).nil? end # Returns true if +secret+ is a valid secret, false if not. def valid_secret?(secret) secret.respond_to?(:to_s) end # Returns the cost value which will result in computation limits less than the given options. # # Options: # :max_time specifies the maximum number of seconds the computation should take. # :max_mem specifies the maximum number of bytes the computation should take. # A value of 0 specifies no upper limit. The minimum is always 1 MB. # :max_memfrac specifies the maximum memory in a fraction of available resources to use. # Any value equal to 0 or greater than 0.5 will result in 0.5 being used. # # Example: # # # should take less than 200ms # SCrypt::Engine.calibrate(:max_time => 0.2) # def calibrate(options = {}) options = DEFAULTS.merge(options) '%x$%x$%x$' % __sc_calibrate(options[:max_mem], options[:max_memfrac], options[:max_time]) end # Calls SCrypt::Engine.calibrate and saves the cost string for future calls to # SCrypt::Engine.generate_salt. def calibrate!(options = {}) @calibrated_cost = calibrate(options) end # Computes the memory use of the given +cost+ def memory_use(cost) cpu_cost, memory_cost, parallelization = parse_cost_string(cost) (128 * memory_cost * parallelization) + (256 * memory_cost) + (128 * memory_cost * cpu_cost) end # Autodetects the cost from the salt string. def autodetect_cost(salt) salt[/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$/] end private # Extracts the salt portion from a salt string def extract_salt_from_string(salt) salt[/\$([A-Za-z0-9]{16,64})$/, 1] end # Checks if this is an old-style hash based on salt length def old_style_hash?(salt_only) salt_only.length == OLD_STYLE_SALT_LENGTH end # Generates old-style hash with SHA1 def generate_old_style_hash(secret, salt, cost) "#{salt}$#{Digest::SHA1.hexdigest(scrypt(secret.to_s, salt, cost, 256))}" end # Generates new-style hash def generate_new_style_hash(secret, salt, salt_only, cost, key_len) processed_salt = [salt_only.sub(/^(00)+/, '')].pack('H*') hash_bytes = scrypt(secret.to_s, processed_salt, cost, key_len) "#{salt}$#{hash_bytes.unpack('H*').first.rjust(key_len * 2, '0')}" end # Avoids collision with old-style hash detection def avoid_old_style_collision(salt) if salt.length == OLD_STYLE_SALT_LENGTH # If salt is 40 characters, the regexp will think that it is an old-style hash, so add a '0'. "0#{salt}" else salt end end # Parses a cost string into its component values def parse_cost_string(cost_string) cost_string.split('$').map { |component| component.to_i(16) } end def __sc_calibrate(max_mem, max_memfrac, max_time) raise ArgumentError, 'max_mem must be non-negative' if max_mem.negative? raise ArgumentError, 'max_memfrac must be between 0 and 1' unless (0..1).cover?(max_memfrac) raise ArgumentError, 'max_time must be positive' if max_time <= 0 calibration = Calibration.new ret_val = SCrypt::Ext.sc_calibrate(max_mem, max_memfrac, max_time, calibration) raise "calibration error: return value #{ret_val}" unless ret_val.zero? [calibration[:n], calibration[:r], calibration[:p]] end def __sc_crypt(secret, salt, cpu_cost, memory_cost, parallelization, key_len) raise ArgumentError, 'secret cannot be nil' if secret.nil? raise ArgumentError, 'salt cannot be nil' if salt.nil? raise ArgumentError, 'key_len must be positive' if key_len <= 0 raise ArgumentError, 'cpu_cost must be positive' if cpu_cost <= 0 raise ArgumentError, 'memory_cost must be positive' if memory_cost <= 0 raise ArgumentError, 'parallelization must be positive' if parallelization <= 0 result = nil FFI::MemoryPointer.new(:char, key_len) do |buffer| ret_val = SCrypt::Ext.crypto_scrypt( secret, secret.bytesize, salt, salt.bytesize, cpu_cost, memory_cost, parallelization, buffer, key_len ) raise "scrypt error: return value #{ret_val}" unless ret_val.zero? result = buffer.read_string(key_len) end result end end end end ================================================ FILE: lib/scrypt/errors.rb ================================================ # frozen_string_literal: true module SCrypt module Errors # The salt parameter provided is invalid. class InvalidSalt < StandardError; end # The hash parameter provided is invalid. class InvalidHash < StandardError; end # The secret parameter provided is invalid. class InvalidSecret < StandardError; end end end ================================================ FILE: lib/scrypt/password.rb ================================================ # frozen_string_literal: true module SCrypt # A password management class which allows you to safely store users' passwords and compare them. # # Example usage: # # include "scrypt" # # # hash a user's password # @password = Password.create("my grand secret") # @password #=> "2000$8$1$f5f2fa5fe5484a7091f1299768fbe92b5a7fbc77$6a385f22c54d92c314b71a4fd5ef33967c93d679" # # # store it safely # @user.update_attribute(:password, @password) # # # read it back # @user.reload! # @db_password = Password.new(@user.password) # # # compare it after retrieval # @db_password == "my grand secret" #=> true # @db_password == "a paltry guess" #=> false # class Password < String # Key length constraints MIN_KEY_LENGTH = 16 MAX_KEY_LENGTH = 512 # Salt size constraints MIN_SALT_SIZE = 8 MAX_SALT_SIZE = 32 # The hash portion of the stored password hash. attr_reader :digest # The salt of the store password hash attr_reader :salt # The cost factor used to create the hash. attr_reader :cost class << self # Hashes a secret, returning a SCrypt::Password instance. # Takes five options (optional), which will determine the salt/key's length and # the cost limits of the computation. # :key_len specifies the length in bytes of the key you want to generate. # The default is 32 bytes (256 bits). Minimum is 16 bytes (128 bits). Maximum is 512 bytes (4096 bits). # :salt_size specifies the size in bytes of the random salt you want to generate. # The default and minimum is 8 bytes (64 bits). Maximum is 32 bytes (256 bits). # :max_time specifies the maximum number of seconds the computation should take. # :max_mem specifies the maximum number of bytes the computation should take. # A value of 0 specifies no upper limit. The minimum is always 1 MB. # :max_memfrac specifies the maximum memory in a fraction of available resources to use. # Any value equal to 0 or greater than 0.5 will result in 0.5 being used. # The scrypt key derivation function is designed to be far more secure against hardware # brute-force attacks than alternative functions such as PBKDF2 or bcrypt. # The designers of scrypt estimate that on modern (2009) hardware, if 5 seconds are spent # computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly # 4000 times greater than the cost of a similar attack against bcrypt (to find the same password), # and 20000 times greater than a similar attack against PBKDF2. # Default options will result in calculation time of approx. 200 ms with 1 MB memory use. # # Example: # @password = SCrypt::Password.create("my secret", :max_time => 0.25) # def create(secret, options = {}) options = SCrypt::Engine::DEFAULTS.merge(options) options[:key_len] = clamp_key_length(options[:key_len]) options[:salt_size] = clamp_salt_size(options[:salt_size]) salt = SCrypt::Engine.generate_salt(options) hash = SCrypt::Engine.hash_secret(secret, salt, options[:key_len]) Password.new(hash) end private # Clamps key length to valid range def clamp_key_length(key_len) return MIN_KEY_LENGTH if key_len < MIN_KEY_LENGTH return MAX_KEY_LENGTH if key_len > MAX_KEY_LENGTH key_len end # Clamps salt size to valid range def clamp_salt_size(salt_size) return MIN_SALT_SIZE if salt_size < MIN_SALT_SIZE return MAX_SALT_SIZE if salt_size > MAX_SALT_SIZE salt_size end end # Initializes a SCrypt::Password instance with the data from a stored hash. def initialize(raw_hash) raise Errors::InvalidHash, 'invalid hash' unless valid_hash?(raw_hash) replace(raw_hash) @cost, @salt, @digest = split_hash(to_s) end # Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise. def ==(other) SecurityUtils.secure_compare(self, SCrypt::Engine.hash_secret(other, @cost + @salt, digest.length / 2)) end alias is_password? == private # Returns true if +h+ is a valid hash. def valid_hash?(h) !SCrypt::Engine::HASH_PATTERN.match(h).nil? end # call-seq: # split_hash(raw_hash) -> cost, salt, hash # # Splits +h+ into cost, salt, and hash and returns them in that order. def split_hash(hash_string) cpu_cost, version, memory_cost, salt, hash = hash_string.split('$') cost_string = "#{[cpu_cost, version, memory_cost].join('$')}$" [cost_string, salt, hash] end end end ================================================ FILE: lib/scrypt/scrypt_ext.rb ================================================ # frozen_string_literal: true require 'ffi' require 'ffi-compiler/loader' module SCrypt module Ext extend FFI::Library begin ffi_lib FFI::Compiler::Loader.find('scrypt_ext') rescue LoadError => e raise LoadError, "Failed to load scrypt extension library: #{e.message}" end end end ================================================ FILE: lib/scrypt/security_utils.rb ================================================ # frozen_string_literal: true # NOTE:: a verbatim copy of https://github.com/rails/rails/blob/c8c660002f4b0e9606de96325f20b95248b6ff2d/activesupport/lib/active_support/security_utils.rb # Please see the Rails license: https://github.com/rails/rails/blob/master/activesupport/MIT-LICENSE module SCrypt module SecurityUtils # Constant time string comparison. # # The values compared should be of fixed length, such as strings # that have already been processed by HMAC. This should not be used # on variable length plaintext strings because it could leak length info # via timing attacks. def self.secure_compare(a, b) return false unless a.bytesize == b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res.zero? end end end ================================================ FILE: lib/scrypt/version.rb ================================================ # frozen_string_literal: true module SCrypt VERSION = '3.1.0' end ================================================ FILE: lib/scrypt.rb ================================================ # frozen_string_literal: true # A wrapper for the scrypt algorithm. require 'scrypt/errors' require 'scrypt/scrypt_ext' require 'scrypt/security_utils' require 'scrypt/engine' require 'scrypt/password' ================================================ FILE: scrypt.gemspec ================================================ # frozen_string_literal: true $:.push File.expand_path('lib', __dir__) require 'scrypt/version' Gem::Specification.new do |s| s.name = 'scrypt' s.version = SCrypt::VERSION s.authors = ['Patrick Hogan', 'Stephen von Takach', 'Rene van Paassen', 'Johanns Gregorian'] s.email = ['pbhogan@gmail.com', 'steve@advancedcontrol.com.au', 'rene.vanpaassen@gmail.com', 'io+scrypt@jsg.io'] s.cert_chain = ['certs/pbhogan.pem'] s.license = 'BSD-3-Clause' s.signing_key = File.expand_path('~/.ssh/gem-private_key.pem') if $0 =~ /gem\z/ s.metadata['rubygems_mfa_required'] = 'true' s.homepage = 'https://github.com/pbhogan/scrypt' s.summary = 'scrypt password hashing algorithm.' s.description = <<-DESC The scrypt key derivation function is designed to be far more secure against hardware brute-force attacks than alternative functions such as PBKDF2 or bcrypt. DESC s.required_ruby_version = '>= 2.3.0' s.add_dependency 'ffi-compiler', '>= 1.0', '< 2.0' s.add_dependency 'rake', '~> 13' s.extensions = ['ext/scrypt/Rakefile'] s.files = %w[Rakefile scrypt.gemspec README.md COPYING] + Dir.glob('{lib,spec}/**/*') s.files += Dir.glob('ext/scrypt/*') s.require_paths = ['lib'] end ================================================ FILE: spec/fixtures/test_vectors.yml ================================================ # SCrypt Test Vectors # These are the official test vectors from the scrypt specification # Used to verify our implementation matches the reference scrypt_vectors: - description: "Empty string test" password: "" salt: "" n: 16 r: 1 p: 1 key_len: 64 expected: "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906" - description: "Standard test vector" password: "password" salt: "NaCl" n: 1024 r: 8 p: 16 key_len: 64 expected: "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640" - description: "High memory test vector" password: "pleaseletmein" salt: "SodiumChloride" n: 16384 r: 8 p: 1 key_len: 64 expected: "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887" - description: "Very high memory test (disabled on memory-constrained systems)" password: "pleaseletmein" salt: "SodiumChloride" n: 1048576 r: 8 p: 1 key_len: 64 expected: "2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4" skip_reason: "Memory limited systems (like Raspberry Pi) may fail this test" hash_secret_vectors: - description: "Empty string via hash_secret" password: "" salt: "10$1$1$0000000000000000" key_len: 64 expected_pattern: "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906" - description: "Standard test via hash_secret" password: "password" salt: "400$8$10$000000004e61436c" key_len: 64 expected_pattern: "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640" - description: "High memory test via hash_secret" password: "pleaseletmein" salt: "4000$8$1$536f6469756d43686c6f72696465" key_len: 64 expected_pattern: "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887" - description: "Very high memory test via hash_secret (disabled)" password: "pleaseletmein" salt: "100000$8$1$536f6469756d43686c6f72696465" key_len: 64 expected_pattern: "2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4" skip_reason: "Memory limited systems may fail this test" ================================================ FILE: spec/scrypt/engine_spec.rb ================================================ # frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper')) describe 'The SCrypt engine' do it 'calculates a valid cost factor' do first = SCrypt::Engine.calibrate(max_time: 0.2) expect(SCrypt::Engine.valid_cost?(first)).to equal(true) end end describe 'Generating SCrypt salts' do it 'produces strings' do expect(SCrypt::Engine.generate_salt).to be_an_instance_of(String) end it 'produces random data' do expect(SCrypt::Engine.generate_salt).not_to equal(SCrypt::Engine.generate_salt) end it 'uses the saved cost factor' do # Verify cost is different before saving cost = SCrypt::Engine.calibrate(max_time: 0.01) expect(SCrypt::Engine.generate_salt).not_to start_with(cost) cost = SCrypt::Engine.calibrate!(max_time: 0.01) expect(SCrypt::Engine.generate_salt).to start_with(cost) end it 'resets calibrated cost when setting new calibration' do # Set initial calibration first_cost = SCrypt::Engine.calibrate!(max_time: 0.01) expect(SCrypt::Engine.calibrated_cost).to eq(first_cost) # Set different calibration second_cost = SCrypt::Engine.calibrate!(max_time: 0.02) expect(SCrypt::Engine.calibrated_cost).to eq(second_cost) expect(SCrypt::Engine.calibrated_cost).not_to eq(first_cost) end end describe 'Autodetecting of salt cost' do it 'works' do expect(SCrypt::Engine.autodetect_cost('2a$08$c3$some_salt')).to eq('2a$08$c3$') end end describe 'Generating SCrypt hashes' do class MyInvalidSecret undef to_s end before do @salt = SCrypt::Engine.generate_salt @password = 'woo' end it 'produces a string' do expect(SCrypt::Engine.hash_secret(@password, @salt)).to be_an_instance_of(String) end it 'raises an InvalidSalt error if the salt is invalid' do expect { SCrypt::Engine.hash_secret(@password, 'nino') }.to raise_error(SCrypt::Errors::InvalidSalt) end it 'raises an InvalidSecret error if the secret is invalid' do expect { SCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.to raise_error(SCrypt::Errors::InvalidSecret) expect { SCrypt::Engine.hash_secret(nil, @salt) }.not_to raise_error expect { SCrypt::Engine.hash_secret(false, @salt) }.not_to raise_error end it 'calls #to_s on the secret and use the return value as the actual secret data' do expect(SCrypt::Engine.hash_secret(false, @salt)).to eq(SCrypt::Engine.hash_secret('false', @salt)) end end describe 'SCrypt test vectors' do it 'matches results of SCrypt function' do TEST_VECTORS['scrypt_vectors'].each do |vector| next if vector['skip_reason'] # Skip memory-intensive tests result = SCrypt::Engine.scrypt( vector['password'], vector['salt'], vector['n'], vector['r'], vector['p'], vector['key_len'] ).unpack('H*').first expect(result).to eq(vector['expected']), "Failed for: #{vector['description']}" end end it 'matches equivalent results sent through hash_secret() function' do TEST_VECTORS['hash_secret_vectors'].each do |vector| next if vector['skip_reason'] # Skip memory-intensive tests result = SCrypt::Engine.hash_secret( vector['password'], vector['salt'], vector['key_len'] ) # hash_secret returns: salt + '$' + hash_digest # So we expect: "salt$expected_pattern" expected_full_hash = "#{vector['salt']}$#{vector['expected_pattern']}" expect(result).to eq(expected_full_hash), "Failed for: #{vector['description']}" end end end describe 'Input validation' do describe '#calibrate' do it 'raises ArgumentError for negative max_mem' do expect do SCrypt::Engine.send(:__sc_calibrate, -1, 0.5, 0.2) end.to raise_error(ArgumentError, 'max_mem must be non-negative') end it 'raises ArgumentError for invalid max_memfrac' do expect do SCrypt::Engine.send(:__sc_calibrate, 1024, -0.1, 0.2) end.to raise_error(ArgumentError, 'max_memfrac must be between 0 and 1') expect do SCrypt::Engine.send(:__sc_calibrate, 1024, 1.1, 0.2) end.to raise_error(ArgumentError, 'max_memfrac must be between 0 and 1') end it 'raises ArgumentError for non-positive max_time' do expect do SCrypt::Engine.send(:__sc_calibrate, 1024, 0.5, 0) end.to raise_error(ArgumentError, 'max_time must be positive') expect do SCrypt::Engine.send(:__sc_calibrate, 1024, 0.5, -0.1) end.to raise_error(ArgumentError, 'max_time must be positive') end end describe '#scrypt' do it 'raises ArgumentError for nil secret' do expect do SCrypt::Engine.send(:__sc_crypt, nil, 'salt', 16, 1, 1, 32) end.to raise_error(ArgumentError, 'secret cannot be nil') end it 'raises ArgumentError for nil salt' do expect do SCrypt::Engine.send(:__sc_crypt, 'secret', nil, 16, 1, 1, 32) end.to raise_error(ArgumentError, 'salt cannot be nil') end it 'raises ArgumentError for non-positive parameters' do expect do SCrypt::Engine.send(:__sc_crypt, 'secret', 'salt', 0, 1, 1, 32) end.to raise_error(ArgumentError, 'cpu_cost must be positive') expect do SCrypt::Engine.send(:__sc_crypt, 'secret', 'salt', 16, 0, 1, 32) end.to raise_error(ArgumentError, 'memory_cost must be positive') expect do SCrypt::Engine.send(:__sc_crypt, 'secret', 'salt', 16, 1, 0, 32) end.to raise_error(ArgumentError, 'parallelization must be positive') expect do SCrypt::Engine.send(:__sc_crypt, 'secret', 'salt', 16, 1, 1, 0) end.to raise_error(ArgumentError, 'key_len must be positive') end end end describe 'Memory usage calculation' do it 'calculates memory usage correctly' do cost = '400$8$1$' memory = SCrypt::Engine.memory_use(cost) n = 0x400 r = 8 p = 1 expected = (128 * r * p) + (256 * r) + (128 * r * n) expect(memory).to eq(expected) end end describe 'Calibrated cost management' do after do # Reset calibrated cost after each test SCrypt::Engine.calibrated_cost = nil end it 'initializes have no calibrated cost' do SCrypt::Engine.calibrated_cost = nil expect(SCrypt::Engine.calibrated_cost).to be_nil end it 'stores and retrieve calibrated cost' do cost = SCrypt::Engine.calibrate!(max_time: 0.01) expect(SCrypt::Engine.calibrated_cost).to eq(cost) end it 'uses calibrated cost in generate_salt when available' do cost = SCrypt::Engine.calibrate!(max_time: 0.01) salt = SCrypt::Engine.generate_salt expect(salt).to start_with(cost) end end ================================================ FILE: spec/scrypt/ffi_spec.rb ================================================ # frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper')) describe 'SCrypt FFI Library Loading' do describe 'Extension loading' do it 'loads the scrypt extension successfully' do # This test verifies that the FFI library loads without error # If we get here, the library loaded successfully during require expect(SCrypt::Ext).to be_a(Module) expect(SCrypt::Ext).to respond_to(:sc_calibrate) expect(SCrypt::Ext).to respond_to(:crypto_scrypt) end it 'has proper FFI function signatures' do # Verify that the FFI functions are properly bound expect(SCrypt::Ext.method(:sc_calibrate)).to be_a(Method) expect(SCrypt::Ext.method(:crypto_scrypt)).to be_a(Method) end end describe 'FFI function behavior' do it 'handles basic calibration calls' do # Test that the FFI functions are callable expect { SCrypt::Engine.calibrate(max_time: 0.01) }.not_to raise_error end it 'handles basic scrypt calls' do salt = SCrypt::Engine.generate_salt(max_time: 0.01) expect { SCrypt::Engine.hash_secret('test', salt) }.not_to raise_error end end end ================================================ FILE: spec/scrypt/integration_spec.rb ================================================ # frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper')) describe 'SCrypt Integration Tests' do describe 'Full password lifecycle' do let(:secret) { 'my_super_secret_password' } let(:options) { { max_time: 0.1, max_mem: 8 * 1024 * 1024 } } it 'create,s store, and verify passwords correctly' do # Create password password = SCrypt::Password.create(secret, options) expect(password).to be_a(SCrypt::Password) expect(password.to_s).to match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]+\$[A-Za-z0-9]+$/) # Verify password expect(password == secret).to be(true) expect(password == 'wrong_password').to be(false) # Re-instantiate from stored hash stored_hash = password.to_s recovered_password = SCrypt::Password.new(stored_hash) expect(recovered_password == secret).to be(true) expect(recovered_password == 'wrong_password').to be(false) end it 'handles calibration workflow correctly' do # Calibrate for fast testing cost = SCrypt::Engine.calibrate!(max_time: 0.05) expect(cost).to match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$$/) # Generate salt using calibrated cost salt = SCrypt::Engine.generate_salt expect(salt).to start_with(cost) # Hash secret with calibrated parameters hash = SCrypt::Engine.hash_secret(secret, salt) expect(hash).to be_a(String) expect(hash).to include(salt) # Verify the hash password = SCrypt::Password.new(hash) expect(password == secret).to be(true) # Reset calibration SCrypt::Engine.calibrated_cost = nil end end describe 'Cross-compatibility tests' do it 'is compatible between Engine and Password classes' do # Create using Password class password1 = SCrypt::Password.create('test_secret', max_time: 0.05) # Extract components and recreate using Engine cost = password1.cost salt_with_cost = cost + password1.salt hash2 = SCrypt::Engine.hash_secret('test_secret', salt_with_cost, password1.digest.length / 2) # Both should verify the same secret password2 = SCrypt::Password.new(hash2) expect(password1 == 'test_secret').to be(true) expect(password2 == 'test_secret').to be(true) end end describe 'Edge cases and error conditions' do it 'handles various secret types' do # String secret password1 = SCrypt::Password.create('string_secret', max_time: 0.05) expect(password1 == 'string_secret').to be(true) # Symbol secret password2 = SCrypt::Password.create(:symbol_secret, max_time: 0.05) expect(password2 == 'symbol_secret').to be(true) # Numeric secret password3 = SCrypt::Password.create(12_345, max_time: 0.05) expect(password3 == '12345').to be(true) # Boolean secret password4 = SCrypt::Password.create(false, max_time: 0.05) expect(password4 == 'false').to be(true) end it 'handles empty and nil secrets safely' do # Empty string password1 = SCrypt::Password.create('', max_time: 0.05) expect(password1 == '').to be(true) # Nil (converts to empty string) password2 = SCrypt::Password.create(nil, max_time: 0.05) expect(password2 == '').to be(true) end it 'validates input parameters' do # Invalid hash format expect { SCrypt::Password.new('invalid_hash') }.to raise_error(SCrypt::Errors::InvalidHash) # Invalid salt expect { SCrypt::Engine.hash_secret('secret', 'invalid_salt') }.to raise_error(SCrypt::Errors::InvalidSalt) end end describe 'Performance and memory tests' do it 'respects memory and time constraints' do start_time = Time.now # Use very low constraints for fast testing password = SCrypt::Password.create('test', max_time: 0.01, max_mem: 1024 * 1024) elapsed_time = Time.now - start_time # Should complete reasonably quickly (allowing some overhead) expect(elapsed_time).to be < 1.0 expect(password == 'test').to be(true) end it 'calculates memory usage correctly' do cost = SCrypt::Engine.calibrate(max_time: 0.05) memory_usage = SCrypt::Engine.memory_use(cost) # Memory usage should be a reasonable number expect(memory_usage).to be > 0 expect(memory_usage).to be < 100 * 1024 * 1024 # Less than 100MB end end end ================================================ FILE: spec/scrypt/password_spec.rb ================================================ # frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper')) describe 'Creating a hashed password' do before do @password = SCrypt::Password.create('s3cr3t', max_time: 0.25) end it 'returns a SCrypt::Password' do expect(@password).to be_an_instance_of(SCrypt::Password) end it 'returns a valid password' do expect { SCrypt::Password.new(@password) }.not_to raise_error end it 'behaves normally if the secret is not a string' do expect { SCrypt::Password.create(nil) }.not_to raise_error expect { SCrypt::Password.create(false) }.not_to raise_error expect { SCrypt::Password.create(42) }.not_to raise_error end it 'tolerates empty string secrets' do expect { SCrypt::Password.create('') }.not_to raise_error expect { SCrypt::Password.create('', max_time: 0.01) }.not_to raise_error expect(SCrypt::Password.create('')).to be_an_instance_of(SCrypt::Password) end end describe 'Reading a hashed password' do before do @secret = 'my secret' @hash = '400$8$d$173a8189751c095a29b933789560b73bf17b2e01$9bf66d74bd6f3ebcf99da3b379b689b89db1cb07' end it 'reads the cost, salt, and hash' do password = SCrypt::Password.new(@hash) expect(password.cost).to eq('400$8$d$') expect(password.salt).to eq('173a8189751c095a29b933789560b73bf17b2e01') expect(password.digest).to eq('9bf66d74bd6f3ebcf99da3b379b689b89db1cb07') end it 'raises an InvalidHashError when given an invalid hash' do expect { SCrypt::Password.new('invalid') }.to raise_error(SCrypt::Errors::InvalidHash) end end describe 'Comparing a hashed password with a secret' do before do @secret = 's3cr3t' @password = SCrypt::Password.create(@secret, max_time: 0.01) end it 'compares successfully to the original secret' do expect(@password == @secret).to be true end it 'compares unsuccessfully to anything besides original secret' do expect(@password == 'different').to be false end end describe 'non-default salt sizes' do before do @secret = 's3cret' end it 'enforces a minimum salt of 8 bytes' do @password = SCrypt::Password.create(@secret, salt_size: 4, max_time: 0.01) expect(@password.salt.length).to eq(16) # 8 bytes * 2 (hex encoding) end it 'allows a salt of 32 bytes' do @password = SCrypt::Password.create(@secret, salt_size: 32, max_time: 0.01) expect(@password.salt.length).to eq(64) # 32 bytes * 2 (hex encoding) end it 'enforces a maximum salt of 32 bytes' do @password = SCrypt::Password.create(@secret, salt_size: 64, max_time: 0.01) expect(@password.salt.length).to eq(64) # 32 bytes * 2 (hex encoding) end it 'pads a 20-byte salt to not look like a 20-byte SHA1' do @password = SCrypt::Password.create(@secret, salt_size: 20) expect(@password.salt.length).to eq(41) end it 'properly compares a non-standard salt hash' do @password = SCrypt::Password.create(@secret, salt_size: 16, max_time: 0.01) expect(@password == @secret).to be true end end describe 'non-default key lengths' do before do @secret = 's3cret' end it 'enforces a minimum keylength of 16 bytes' do @password = SCrypt::Password.create(@secret, key_len: 8, max_time: 0.01) expect(@password.digest.length).to eq(32) # 16 bytes * 2 (hex encoding) end it 'allows a keylength of 512 bytes' do @password = SCrypt::Password.create(@secret, key_len: 512, max_time: 0.01) expect(@password.digest.length).to eq(1024) # 512 bytes * 2 (hex encoding) end it 'enforces a maximum keylength of 512 bytes' do @password = SCrypt::Password.create(@secret, key_len: 1024, max_time: 0.01) expect(@password.digest.length).to eq(1024) # 512 bytes * 2 (hex encoding) end it 'properly compares a non-standard hash' do @password = SCrypt::Password.create(@secret, key_len: 64, max_time: 0.01) expect(@password == @secret).to be true end end describe 'Old-style hashes' do before do @secret = 'my secret' @hash = '400$8$d$173a8189751c095a29b933789560b73bf17b2e01$9bf66d74bd6f3ebcf99da3b379b689b89db1cb07' end it 'compares successfully' do expect(SCrypt::Password.new(@hash) == @secret).to be true end end describe 'Respecting standard ruby behaviors' do it 'hashes as an integer' do password = SCrypt::Password.create('secret', max_time: 0.01) expect(password.hash).to be_an(Integer) end end describe 'Password validation and parsing' do it 'correctly parses hash components' do password = SCrypt::Password.new('400$8$d$173a8189751c095a29b933789560b73bf17b2e01$9bf66d74bd6f3ebcf99da3b379b689b89db1cb07') expect(password.cost).to eq('400$8$d$') expect(password.salt).to eq('173a8189751c095a29b933789560b73bf17b2e01') expect(password.digest).to eq('9bf66d74bd6f3ebcf99da3b379b689b89db1cb07') end it 'validates hash format strictly' do valid_hash = '400$8$d$173a8189751c095a29b933789560b73bf17b2e01$9bf66d74bd6f3ebcf99da3b379b689b89db1cb07' expect { SCrypt::Password.new(valid_hash) }.not_to raise_error expect { SCrypt::Password.new('invalid') }.to raise_error(SCrypt::Errors::InvalidHash) expect { SCrypt::Password.new('') }.to raise_error(SCrypt::Errors::InvalidHash) expect { SCrypt::Password.new('400$8$d$') }.to raise_error(SCrypt::Errors::InvalidHash) end it 'handles alias method correctly' do password = SCrypt::Password.create('secret', max_time: 0.01) expect(password.is_password?('secret')).to be true expect(password.is_password?('wrong')).to be false end end describe 'Parameter boundary testing' do it 'enforces minimum and maximum key lengths correctly' do # Test minimum key length (should be clamped to 16) password_min = SCrypt::Password.create('secret', key_len: 8, max_time: 0.01) expect(password_min.digest.length).to eq(32) # 16 bytes * 2 (hex encoding) # Test maximum key length (should be clamped to 512) password_max = SCrypt::Password.create('secret', key_len: 1024, max_time: 0.01) expect(password_max.digest.length).to eq(1024) # 512 bytes * 2 (hex encoding) end it 'enforces minimum and maximum salt sizes correctly' do # Test minimum salt size (should be clamped to 8) password_min = SCrypt::Password.create('secret', salt_size: 4, max_time: 0.01) expect(password_min.salt.length).to eq(16) # 8 bytes * 2 (hex encoding) # Test maximum salt size (should be clamped to 32) password_max = SCrypt::Password.create('secret', salt_size: 64, max_time: 0.01) expect(password_max.salt.length).to eq(64) # 32 bytes * 2 (hex encoding) end end ================================================ FILE: spec/scrypt/utils_spec.rb ================================================ # frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper')) describe 'Security Utils' do describe '.secure_compare' do it 'performs a string comparison correctly' do expect(SCrypt::SecurityUtils.secure_compare('a', 'a')).to equal(true) expect(SCrypt::SecurityUtils.secure_compare('a', 'b')).to equal(false) expect(SCrypt::SecurityUtils.secure_compare('aa', 'aa')).to equal(true) expect(SCrypt::SecurityUtils.secure_compare('aa', 'ab')).to equal(false) end it 'returns false for different length strings' do expect(SCrypt::SecurityUtils.secure_compare('aa', 'aaa')).to equal(false) expect(SCrypt::SecurityUtils.secure_compare('aaa', 'aa')).to equal(false) expect(SCrypt::SecurityUtils.secure_compare('', 'a')).to equal(false) expect(SCrypt::SecurityUtils.secure_compare('a', '')).to equal(false) end it 'handles empty strings correctly' do expect(SCrypt::SecurityUtils.secure_compare('', '')).to equal(true) end it 'handles binary data correctly' do binary1 = "\x00\x01\x02\x03" binary2 = "\x00\x01\x02\x03" binary3 = "\x00\x01\x02\x04" expect(SCrypt::SecurityUtils.secure_compare(binary1, binary2)).to equal(true) expect(SCrypt::SecurityUtils.secure_compare(binary1, binary3)).to equal(false) end it 'handles unicode strings correctly' do unicode1 = 'héllo' unicode2 = 'héllo' unicode3 = 'hello' expect(SCrypt::SecurityUtils.secure_compare(unicode1, unicode2)).to equal(true) expect(SCrypt::SecurityUtils.secure_compare(unicode1, unicode3)).to equal(false) end it 'is resistant to timing attacks' do # This test ensures the function takes constant time regardless of where differences occur long_string1 = ('a' * 1000) + 'x' long_string2 = ('a' * 1000) + 'y' long_string3 = 'x' + ('a' * 1000) long_string4 = 'y' + ('a' * 1000) # All of these should return false and take similar time expect(SCrypt::SecurityUtils.secure_compare(long_string1, long_string2)).to equal(false) expect(SCrypt::SecurityUtils.secure_compare(long_string3, long_string4)).to equal(false) end end end ================================================ FILE: spec/spec_helper.rb ================================================ # frozen_string_literal: true $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib')) require 'rubygems' require 'rspec' require 'yaml' require 'scrypt' # Load shared examples Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f } # Load test fixtures TEST_VECTORS = YAML.load_file(File.expand_path('fixtures/test_vectors.yml', __dir__)).freeze RSpec.configure do |config| # Use documentation format for better output config.default_formatter = 'doc' if config.files_to_run.one? # Run specs in random order to surface order dependencies config.order = :random # Seed global randomization in this process using the `--seed` CLI option Kernel.srand config.seed # Allow more verbose output when running a single file config.filter_run_when_matching :focus # Enable expect syntax (recommended) config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true # Disable deprecated should syntax expectations.syntax = :expect end # Configure mocks config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end # Enable shared context metadata behavior config.shared_context_metadata_behavior = :apply_to_host_groups # Configure warnings and deprecations config.warnings = true config.raise_errors_for_deprecations! end ================================================ FILE: spec/support/shared_examples.rb ================================================ # frozen_string_literal: true # Shared examples for SCrypt tests RSpec.shared_examples 'a valid scrypt hash' do it 'has the correct format' do expect(subject).to match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]+\$[A-Za-z0-9]+$/) end it 'is a string' do expect(subject).to be_a(String) end end RSpec.shared_examples 'a valid cost string' do it 'has the correct format' do expect(subject).to match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$$/) end it 'is valid according to Engine.valid_cost?' do expect(SCrypt::Engine.valid_cost?(subject)).to be(true) end end RSpec.shared_examples 'a valid salt string' do it 'has the correct format' do expect(subject).to match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]{16,64}$/) end it 'is valid according to Engine.valid_salt?' do expect(SCrypt::Engine.valid_salt?(subject)).to be(true) end end RSpec.shared_examples 'proper input validation' do |method, args, error_class, error_message| it "raises #{error_class} for invalid input" do expect { subject.send(method, *args) }.to raise_error(error_class, error_message) end end RSpec.shared_examples 'deterministic output' do |method, args| it 'produces the same output for the same input' do result1 = subject.send(method, *args) result2 = subject.send(method, *args) expect(result1).to eq(result2) end end ================================================ FILE: spec/support/test_helpers.rb ================================================ # frozen_string_literal: true module TestHelpers # Common test data VALID_SECRETS = [ 'simple_password', 'complex_password_123!@#', '', 'unicode_tésting', '🔒secure🔑' ].freeze INVALID_HASH_FORMATS = [ '', 'invalid', '400$8$d$invalid', '400$8$d$173a8189751c095a29b933789560b73bf17b2e01', '400$8$d$173a8189751c095a29b933789560b73bf17b2e01$' ].freeze INVALID_SALT_FORMATS = [ '', 'invalid', 'nino', '400$8$d$' ].freeze # Helper methods def self.generate_test_password(secret = 'test_secret', options = {}) default_options = { max_time: 0.05 } SCrypt::Password.create(secret, default_options.merge(options)) end def self.generate_test_salt(options = {}) default_options = { max_time: 0.05 } SCrypt::Engine.generate_salt(default_options.merge(options)) end def self.reset_calibration SCrypt::Engine.calibrated_cost = nil end end # Include helper methods in RSpec RSpec.configure do |config| config.include TestHelpers end