Initial import: ultra-small bruteforce tool, docs, and .gitignore

This commit is contained in:
2025-07-04 23:43:25 +00:00
parent 15a5f50365
commit 3b51303ab6
21 changed files with 6334 additions and 59 deletions

38
.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# Ignore wordlist files
wordlist*
# Common wordlist patterns
*.txt
!README*.txt
# Docker build artifacts
*.tar
*.tar.gz
# Temporary files
*.tmp
*.temp
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE files
.vscode/
.idea/
*.swp
*.swo
*~
# Logs
*.log
# Build artifacts
bruteforce
*.o
*.a

View File

@@ -1,2 +0,0 @@
FROM scratch
ADD d /d

241
README.md
View File

@@ -1,79 +1,206 @@
# Worlds smallest Docker Image - aka WSDI | 92 bytes
# World's Smallest Docker Image - Password Bruteforce Tool
https://hub.docker.com/repository/docker/dooqod/wsdi/general
A minimal, ultra-compressed Docker image containing a password bruteforce tool that can crack various hash types including yescrypt, MD5, SHA256, and SHA512.
Hi everyone,
## 🚀 Features
If you ever wondered what is the minimal Docker image in the world, then you are in right place.
Is it debian, is it alpine or busybox ?
- **Ultra-small Docker image** (~46KB compressed)
- **Multiple hash support**: yescrypt, MD5, SHA256, SHA512
- **Static binary**: No external dependencies
- **UPX compressed**: Maximum size optimization
- **Real-time progress**: Shows attempts and statistics
- **Comprehensive logging**: Detailed output for debugging
Our team at Dooqod did extensive research on to figure out this.
## 📊 Image Size Comparison
We come up with 2 approaches to figure this out.
| Image | Size | Compression |
|-------|------|-------------|
| This tool | ~46KB | UPX ultra-brute |
| Standard Alpine | ~5MB | None |
| Standard Ubuntu | ~70MB | None |
## Approach 1:
At least there is finate amount of Docker images under DockerHub.
So this task should not be impossible. To run all of them and compare.
## 🛠️ Technical Details
But it'll take lots of time and resources.
### Hash Algorithms Supported
- **yescrypt** (`$y$`) - Modern Linux default
- **MD5** (`$1$`) - Legacy support
- **SHA256** (`$5$`) - SHA-256 based
- **SHA512** (`$6$`) - SHA-512 based
## Approach 2:
We decided to create the smallest one and publish under DockerHub. Sounds promising.
### Build Process
1. **Multi-stage build** using Alpine Linux
2. **Static compilation** with musl-gcc
3. **Binary stripping** to remove debug symbols
4. **UPX compression** with ultra-brute mode
5. **Scratch base image** for minimal size
## Wow moment
## 🏗️ Building
Wow, we can create the smallest Docker image in the world.
It can be the MVP and we can try to sell it :)
So we started this project right away.
## Dockerfile of the 'worlds-smallest-docker-image'
d - is just an emptyfile we add into 'scratch'
```shell
FROM scratch
ADD d /d
```bash
# Build the image
docker build -t bruteforce-test -f brute/source/Dockerfile brute/source
# Check image size
docker images bruteforce-test
```
## What is scratch ?
The scratch image is the most minimal image in DockerHub. This is the base ancestor for all other images.
The scratch image is actually empty. It doesn't contain any folders/files.
You can use Dockers reserved, minimal image, scratch, as a starting point for building containers. Using the scratch “image” signals to the build process that you want the next command in the Dockerfile to be the first filesystem layer in your image.
While scratch appears in Dockers repository on the hub, you cant pull it, run it, or tag any image with the name scratch. Instead, you can refer to it in your Dockerfile. For example, to create a minimal container using scratch:
https://hub.docker.com/_/scratch
## How to build
```shell
# just clone the repo
# cd into repository and run
docker build -t wsdi .
# or pull from DockerHub
docker pull docker.io/dooqod/wsdi:latest
# check
## 🚀 Usage
### Basic Usage
```bash
# Crack password for a specific user
docker run --rm \
--volume "/etc:/etc" \
--user root \
bruteforce-test:latest \
<username> <wordlist_path>
```
## Support the project to not grow :)
### Example Commands
Our goal is to make this image minimal.
We'll put all our efforts to keep it simple and small also in the feature.
#### Crack root password
```bash
docker run --rm \
--volume "/etc:/etc" \
--volume "$(pwd)/brute/source/wordlist2.txt:/wordlist2.txt" \
--user root \
bruteforce-test:latest \
root /wordlist2.txt
```
- Give a Github Star
- Buy lambo -
#### Crack specific user password
```bash
docker run --rm \
--volume "/etc:/etc" \
--volume "$(pwd)/custom_wordlist.txt:/wordlist.txt" \
--user root \
bruteforce-test:latest \
alice /wordlist.txt
```
### Output Example
```
Target user: root
Hash type: yescrypt
Full hash: $y$j9T$dummy.salt.hash.example$dummy.hash.value.here
Starting bruteforce...
Tried 1000 passwords...
Found password: [password_found]
Total passwords tried: 102
Password successfully cracked!
```
## 📁 Project Structure
```
.
├── brute/
│ └── source/
│ ├── Dockerfile # Multi-stage build configuration
│ ├── bruteforce.c # Main bruteforce implementation
│ ├── wordlist.txt # Large wordlist (133MB)
│ ├── wordlist2.txt # Small wordlist (801B)
│ └── yescrypt/ # yescrypt reference implementation
│ ├── yescrypt-ref.c
│ ├── yescrypt-common.c
│ ├── sha256.c
│ ├── insecure_memzero.c
│ └── *.h files
└── README.md
```
## 🔧 Development
### Prerequisites
- Docker
- Linux system with /etc/shadow access
- Root privileges (for accessing shadow file)
### Compilation Flags
```bash
gcc -static -Os -s -o bruteforce \
bruteforce.c \
yescrypt-ref.c \
yescrypt-common.c \
sha256.c \
insecure_memzero.c \
&& strip --strip-all --remove-section=.comment bruteforce \
&& upx --ultra-brute bruteforce
```
### Optimization Techniques
- **Static linking**: No external dependencies
- **Size optimization**: `-Os` flag for minimal size
- **Symbol stripping**: Remove debug symbols
- **UPX compression**: Ultra-brute mode for maximum compression
- **Scratch base**: No OS layer in final image
## 🛡️ Security Considerations
⚠️ **WARNING**: This tool is for educational and authorized testing purposes only.
- Only use on systems you own or have explicit permission to test
- Respect local laws and regulations regarding password cracking
- Use responsibly and ethically
- Consider legal implications before use
## 📈 Performance
- **Speed**: Optimized for size over speed
- **Memory**: Minimal memory footprint
- **CPU**: Single-threaded, CPU intensive
- **I/O**: Efficient file reading with minimal syscalls
## 🐛 Troubleshooting
### Common Issues
1. **Permission denied accessing /etc/shadow**
```bash
# Run with root user
--user root
```
2. **Wordlist not found**
```bash
# Ensure correct path mapping
--volume "$(pwd)/wordlist.txt:/wordlist.txt"
```
3. **User not found in shadow file**
- Verify username exists
- Check shadow file permissions
### Debug Mode
The tool provides detailed output including:
- Target user information
- Hash type and format
- Progress updates every 1000 attempts
- Final statistics
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test thoroughly
5. Submit a pull request
## 📄 License
This project is for educational purposes. Use responsibly and in accordance with applicable laws.
## 👨‍💻 Author
**@tuankiet2s**
## 🙏 Acknowledgments
- yescrypt reference implementation
- UPX compression tool
- Alpine Linux for minimal base image
- Docker multi-stage builds
---
**Remember**: With great power comes great responsibility. Use this tool ethically and legally.

206
README.vi.md Normal file
View File

@@ -0,0 +1,206 @@
# Docker Image Nhỏ Nhất Thế Giới - Công Cụ Bruteforce Mật Khẩu
Một Docker image siêu nhỏ, được nén tối đa chứa công cụ bruteforce mật khẩu có thể crack nhiều loại hash khác nhau bao gồm yescrypt, MD5, SHA256 và SHA512.
## 🚀 Tính Năng
- **Docker image siêu nhỏ** (~46KB đã nén)
- **Hỗ trợ nhiều loại hash**: yescrypt, MD5, SHA256, SHA512
- **Binary tĩnh**: Không có dependency bên ngoài
- **Nén UPX**: Tối ưu kích thước tối đa
- **Tiến trình thời gian thực**: Hiển thị số lần thử và thống kê
- **Logging chi tiết**: Output đầy đủ để debug
## 📊 So Sánh Kích Thước Image
| Image | Kích Thước | Nén |
|-------|------------|-----|
| Công cụ này | ~46KB | UPX ultra-brute |
| Alpine chuẩn | ~5MB | Không |
| Ubuntu chuẩn | ~70MB | Không |
## 🛠️ Chi Tiết Kỹ Thuật
### Thuật Toán Hash Được Hỗ Trợ
- **yescrypt** (`$y$`) - Mặc định Linux hiện đại
- **MD5** (`$1$`) - Hỗ trợ legacy
- **SHA256** (`$5$`) - Dựa trên SHA-256
- **SHA512** (`$6$`) - Dựa trên SHA-512
### Quy Trình Build
1. **Multi-stage build** sử dụng Alpine Linux
2. **Biên dịch tĩnh** với musl-gcc
3. **Strip binary** để loại bỏ debug symbols
4. **Nén UPX** với chế độ ultra-brute
5. **Base image scratch** để giảm kích thước tối đa
## 🏗️ Build
```bash
# Build image
docker build -t bruteforce-test -f brute/source/Dockerfile brute/source
# Kiểm tra kích thước image
docker images bruteforce-test
```
## 🚀 Sử Dụng
### Sử Dụng Cơ Bản
```bash
# Crack mật khẩu cho user cụ thể
docker run --rm \
--volume "/etc:/etc" \
--user root \
bruteforce-test:latest \
<username> <đường_dẫn_wordlist>
```
### Ví Dụ Lệnh
#### Crack mật khẩu root
```bash
docker run --rm \
--volume "/etc:/etc" \
--volume "$(pwd)/brute/source/wordlist2.txt:/wordlist2.txt" \
--user root \
bruteforce-test:latest \
root /wordlist2.txt
```
#### Crack mật khẩu user cụ thể
```bash
docker run --rm \
--volume "/etc:/etc" \
--volume "$(pwd)/custom_wordlist.txt:/wordlist.txt" \
--user root \
bruteforce-test:latest \
alice /wordlist.txt
```
### Ví Dụ Output
```
Target user: root
Hash type: yescrypt
Full hash: $y$j9T$dummy.salt.hash.example$dummy.hash.value.here
Starting bruteforce...
Tried 1000 passwords...
Found password: [password_found]
Total passwords tried: 102
Password successfully cracked!
```
## 📁 Cấu Trúc Dự Án
```
.
├── brute/
│ └── source/
│ ├── Dockerfile # Cấu hình multi-stage build
│ ├── bruteforce.c # Implementation bruteforce chính
│ ├── wordlist.txt # Wordlist lớn (133MB)
│ ├── wordlist2.txt # Wordlist nhỏ (801B)
│ └── yescrypt/ # Implementation tham chiếu yescrypt
│ ├── yescrypt-ref.c
│ ├── yescrypt-common.c
│ ├── sha256.c
│ ├── insecure_memzero.c
│ └── các file *.h
└── README.md
```
## 🔧 Phát Triển
### Yêu Cầu
- Docker
- Hệ thống Linux với quyền truy cập /etc/shadow
- Quyền root (để truy cập file shadow)
### Cờ Biên Dịch
```bash
gcc -static -Os -s -o bruteforce \
bruteforce.c \
yescrypt-ref.c \
yescrypt-common.c \
sha256.c \
insecure_memzero.c \
&& strip --strip-all --remove-section=.comment bruteforce \
&& upx --ultra-brute bruteforce
```
### Kỹ Thuật Tối Ưu
- **Link tĩnh**: Không có dependency bên ngoài
- **Tối ưu kích thước**: Cờ `-Os` để giảm kích thước
- **Strip symbols**: Loại bỏ debug symbols
- **Nén UPX**: Chế độ ultra-brute để nén tối đa
- **Base scratch**: Không có layer OS trong image cuối
## 🛡️ Cân Nhắc Bảo Mật
⚠️ **CẢNH BÁO**: Công cụ này chỉ dành cho mục đích giáo dục và kiểm thử được ủy quyền.
- Chỉ sử dụng trên hệ thống bạn sở hữu hoặc có quyền kiểm thử rõ ràng
- Tôn trọng luật pháp và quy định địa phương về việc crack mật khẩu
- Sử dụng có trách nhiệm và đạo đức
- Cân nhắc hệ quả pháp lý trước khi sử dụng
## 📈 Hiệu Suất
- **Tốc độ**: Tối ưu cho kích thước hơn tốc độ
- **Bộ nhớ**: Footprint bộ nhớ tối thiểu
- **CPU**: Single-threaded, sử dụng nhiều CPU
- **I/O**: Đọc file hiệu quả với syscall tối thiểu
## 🐛 Xử Lý Sự Cố
### Vấn Đề Thường Gặp
1. **Permission denied khi truy cập /etc/shadow**
```bash
# Chạy với user root
--user root
```
2. **Không tìm thấy wordlist**
```bash
# Đảm bảo mapping đường dẫn đúng
--volume "$(pwd)/wordlist.txt:/wordlist.txt"
```
3. **Không tìm thấy user trong file shadow**
- Xác minh username tồn tại
- Kiểm tra quyền file shadow
### Chế Độ Debug
Công cụ cung cấp output chi tiết bao gồm:
- Thông tin user mục tiêu
- Loại hash và format
- Cập nhật tiến trình mỗi 1000 lần thử
- Thống kê cuối cùng
## 🤝 Đóng Góp
1. Fork repository
2. Tạo feature branch
3. Thực hiện thay đổi
4. Test kỹ lưỡng
5. Submit pull request
## 📄 Giấy Phép
Dự án này dành cho mục đích giáo dục. Sử dụng có trách nhiệm và tuân thủ luật pháp hiện hành.
## 👨‍💻 Tác Giả
**@tuankiet2s**
## 🙏 Lời Cảm Ơn
- Implementation tham chiếu yescrypt
- Công cụ nén UPX
- Alpine Linux cho base image tối thiểu
- Docker multi-stage builds
---
**Nhớ**: Với sức mạnh lớn đi kèm trách nhiệm lớn. Sử dụng công cụ này một cách đạo đức và hợp pháp.

26
brute/source/Dockerfile Normal file
View File

@@ -0,0 +1,26 @@
FROM alpine:latest AS build
RUN apk add --no-cache build-base musl-dev linux-headers upx
WORKDIR /src
# Copy all yescrypt source and header files flat
COPY yescrypt/*.h .
COPY yescrypt/yescrypt-ref.c .
COPY yescrypt/yescrypt-common.c .
COPY yescrypt/sha256.c .
COPY yescrypt/insecure_memzero.c .
COPY bruteforce.c .
RUN gcc -static -Os -s -o bruteforce \
bruteforce.c \
yescrypt-ref.c \
yescrypt-common.c \
sha256.c \
insecure_memzero.c \
&& strip --strip-all --remove-section=.comment bruteforce \
&& upx --ultra-brute bruteforce
FROM scratch
COPY --from=build /src/bruteforce /
ENTRYPOINT ["/bruteforce"]

392
brute/source/bruteforce.c Normal file
View File

@@ -0,0 +1,392 @@
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
// Include yescrypt implementation
#include "yescrypt.h"
#define BUF_SIZE 4096
#define MAX_LINE 1024
#define MAX_PASS 256
// Minimal strlen
static int my_strlen(const char *s) { int n=0; while(s[n]) n++; return n; }
// Minimal strncmp
static int my_strncmp(const char *a, const char *b, int n) { for(int i=0;i<n;i++) if(a[i]!=b[i]||!a[i]||!b[i]) return a[i]-b[i]; return 0; }
// Write string to stderr
static void write_stderr(const char *s) {
syscall(SYS_write, 2, s, my_strlen(s));
}
// Write string to stdout
static void write_stdout(const char *s) {
syscall(SYS_write, 1, s, my_strlen(s));
}
// Find position of substring in string
static int strpos(const char *string, const char *substring, int offset) {
int string_len = my_strlen(string);
int sub_len = my_strlen(substring);
for(int i = offset; i <= string_len - sub_len; i++) {
int j;
for(j = 0; j < sub_len; j++) {
if(string[i + j] != substring[j]) break;
}
if(j == sub_len) return i;
}
return -1;
}
// Read a line from fd into buf, return length or 0 on EOF
static int read_line(int fd, char *buf, int max) {
int i=0, r;
char c;
while(i<max-1) {
r = syscall(SYS_read, fd, &c, 1);
if(r<=0) break;
if(c=='\n') break;
buf[i++] = c;
}
buf[i]=0;
return i;
}
// Extract user hash from shadow line
static char* extract_user_hash(const char *shadow_line, const char *username) {
int username_index = strpos(shadow_line, username, 0);
if(username_index == -1) return NULL;
// Find the start of hash (after username:)
int hash_start = username_index + my_strlen(username) + 1;
int hash_end = strpos(shadow_line, ":", hash_start);
if(hash_end == -1) return NULL;
int hash_len = hash_end - hash_start;
char *user_hash = malloc(hash_len + 1);
memcpy(user_hash, shadow_line + hash_start, hash_len);
user_hash[hash_len] = 0;
return user_hash;
}
// Parse hash to extract algorithm ID, salt, and hash value
static void parse_hash(const char *user_hash, int *hash_id, char *salt, char *hash_value) {
// Find positions of $ characters
int first_dollar = strpos(user_hash, "$", 0);
if(first_dollar == -1) return;
int second_dollar = strpos(user_hash, "$", first_dollar + 1);
if(second_dollar == -1) return;
int third_dollar = strpos(user_hash, "$", second_dollar + 1);
if(third_dollar == -1) return;
// Extract hash ID (algorithm)
int id_len = second_dollar - first_dollar - 1;
char id_str[8] = {0};
memcpy(id_str, user_hash + first_dollar + 1, id_len);
// Handle yescrypt (y) and numeric IDs
if(id_len == 1 && id_str[0] == 'y') {
*hash_id = 'y'; // yescrypt
// For yescrypt, we need to find the 4th $ to get the actual salt
int fourth_dollar = strpos(user_hash, "$", third_dollar + 1);
if(fourth_dollar == -1) return;
// Extract parameters (between 2nd and 3rd $)
int param_len = third_dollar - second_dollar - 1;
char parameters[64] = {0};
memcpy(parameters, user_hash + second_dollar + 1, param_len);
parameters[param_len] = 0;
// Extract salt (between 3rd and 4th $)
int salt_len = fourth_dollar - third_dollar - 1;
memcpy(salt, user_hash + third_dollar + 1, salt_len);
salt[salt_len] = 0;
// Extract hash value (after 4th $)
int hash_len = my_strlen(user_hash) - fourth_dollar - 1;
memcpy(hash_value, user_hash + fourth_dollar + 1, hash_len);
hash_value[hash_len] = 0;
// For yescrypt, we need to include parameters in the salt for crypt()
char combined_salt[128];
int len = 0;
combined_salt[len++] = '$';
combined_salt[len++] = 'y';
combined_salt[len++] = '$';
memcpy(combined_salt + len, parameters, param_len);
len += param_len;
combined_salt[len++] = '$';
memcpy(combined_salt + len, salt, salt_len);
len += salt_len;
combined_salt[len] = 0;
// Copy the combined salt back to the salt parameter
memcpy(salt, combined_salt, len + 1);
} else {
*hash_id = atoi(id_str);
// Extract salt (standard format)
int salt_len = third_dollar - second_dollar - 1;
memcpy(salt, user_hash + second_dollar + 1, salt_len);
salt[salt_len] = 0;
// Extract hash value
int hash_len = my_strlen(user_hash) - third_dollar - 1;
memcpy(hash_value, user_hash + third_dollar + 1, hash_len);
hash_value[hash_len] = 0;
}
}
// Create formatted salt string for crypt()
static char* create_salt(int hash_id, const char *salt) {
char *formatted_salt = malloc(128);
int len = 0;
if(hash_id == 'y') {
// For yescrypt, the salt is already properly formatted
int salt_len = my_strlen(salt);
memcpy(formatted_salt, salt, salt_len);
formatted_salt[salt_len] = 0;
} else {
// Standard format for other algorithms
formatted_salt[len++] = '$';
if(hash_id == 1) {
formatted_salt[len++] = '1';
} else if(hash_id == 5) {
formatted_salt[len++] = '5';
} else if(hash_id == 6) {
formatted_salt[len++] = '6';
} else {
// Default to SHA512
formatted_salt[len++] = '6';
}
formatted_salt[len++] = '$';
int salt_len = my_strlen(salt);
memcpy(formatted_salt + len, salt, salt_len);
len += salt_len;
formatted_salt[len] = 0;
}
return formatted_salt;
}
// Minimal itoa for positive integers
static void my_itoa(int value, char *buf) {
char tmp[16];
int i = 0, j = 0;
if (value == 0) { buf[0] = '0'; buf[1] = 0; return; }
while (value > 0) { tmp[i++] = '0' + (value % 10); value /= 10; }
while (i > 0) { buf[j++] = tmp[--i]; }
buf[j] = 0;
}
// Custom yescrypt crypt function using reference implementation parsing
static char* yescrypt_crypt(const char *password, const char *full_hash) {
static char result[128];
// Use the reference implementation's yescrypt function
uint8_t *generated_hash = yescrypt((const uint8_t *)password, (const uint8_t *)full_hash);
if (!generated_hash) {
return NULL;
}
// Compare the generated hash with the expected hash
if (strcmp((char *)generated_hash, full_hash) == 0) {
// Copy the generated hash to our result buffer
int len = strlen((char *)generated_hash);
if (len < sizeof(result)) {
memcpy(result, generated_hash, len + 1);
return result;
}
}
return NULL;
}
int main(int argc, char **argv) {
if(argc<3) {
write_stderr("Usage: ");
write_stderr(argv[0]);
write_stderr(" <username> <wordlist>\n");
return 1;
}
char *username = argv[1];
char *wordlist_path = argv[2];
const char *shadow_path = "/etc/shadow";
int sfd = syscall(SYS_open, shadow_path, O_RDONLY, 0);
if(sfd<0) {
write_stderr("Failed to open shadow file: ");
write_stderr(shadow_path);
write_stderr("\n");
return 2;
}
char line[MAX_LINE];
char *user_hash = NULL;
while(read_line(sfd, line, MAX_LINE)) {
user_hash = extract_user_hash(line, username);
if(user_hash) break;
}
syscall(SYS_close, sfd);
if(!user_hash) {
write_stderr("User '");
write_stderr(username);
write_stderr("' not found in shadow file.\n");
return 3;
}
// Parse the hash
int hash_id;
char salt[64] = {0};
char hash_value[512] = {0};
parse_hash(user_hash, &hash_id, salt, hash_value);
if(hash_id == 0) {
write_stderr("Failed to parse hash format for user '");
write_stderr(username);
write_stderr("'\n");
free(user_hash);
return 4;
}
// Print useful information
write_stderr("Target user: ");
write_stderr(username);
write_stderr("\n");
write_stderr("Hash type: ");
if(hash_id == 'y') {
write_stderr("yescrypt");
} else if(hash_id == 1) {
write_stderr("MD5");
} else if(hash_id == 5) {
write_stderr("SHA256");
} else if(hash_id == 6) {
write_stderr("SHA512");
} else {
char id_str[8];
my_itoa(hash_id, id_str);
write_stderr(id_str);
}
write_stderr("\n");
write_stderr("Full hash: ");
write_stderr(user_hash);
write_stderr("\n");
write_stderr("Starting bruteforce...\n");
// Create formatted salt for crypt()
char *formatted_salt = create_salt(hash_id, salt);
int wfd = syscall(SYS_open, wordlist_path, O_RDONLY, 0);
if(wfd<0) {
write_stderr("Failed to open wordlist: ");
write_stderr(wordlist_path);
write_stderr("\n");
free(user_hash);
free(formatted_salt);
return 5;
}
char pass[MAX_PASS];
int found=0;
char c;
int n=0;
int total_tried=0;
while(syscall(SYS_read, wfd, &c, 1) == 1) {
if(c == '\n' || c == '\r') {
if(n > 0) {
pass[n] = 0;
total_tried++;
// Show progress every 1000 attempts
if(total_tried % 1000 == 0) {
char progress_str[32];
write_stderr("Tried ");
my_itoa(total_tried, progress_str);
write_stderr(progress_str);
write_stderr(" passwords...\n");
}
char *try_hash;
if(hash_id == 'y') {
// Use yescrypt for yescrypt hashes
try_hash = yescrypt_crypt(pass, user_hash);
} else {
// Use system crypt() for other hash types
try_hash = crypt(pass, formatted_salt);
}
if(try_hash && strcmp(try_hash, user_hash)==0) {
write_stdout("Found password: ");
write_stdout(pass);
write_stdout("\n");
found=1;
break;
}
n = 0;
}
} else if(n < MAX_PASS-1) {
pass[n++] = c;
}
}
// Handle last line if no newline at end
if(n > 0 && !found) {
pass[n] = 0;
total_tried++;
char *try_hash;
if(hash_id == 'y') {
// Use yescrypt for yescrypt hashes
try_hash = yescrypt_crypt(pass, user_hash);
} else {
// Use system crypt() for other hash types
try_hash = crypt(pass, formatted_salt);
}
if(try_hash && strcmp(try_hash, user_hash)==0) {
write_stdout("Found password: ");
write_stdout(pass);
write_stdout("\n");
found=1;
}
}
syscall(SYS_close, wfd);
// Print final summary
char total_str[32];
write_stderr("Total passwords tried: ");
my_itoa(total_tried, total_str);
write_stderr(total_str);
write_stderr("\n");
if(!found) {
write_stderr("No password match found for user '");
write_stderr(username);
write_stderr("' in wordlist.\n");
} else {
write_stderr("Password successfully cracked!\n");
}
free(user_hash);
free(formatted_salt);
return found?0:6;
}

View File

@@ -0,0 +1,85 @@
# Copyright 2013-2018 Alexander Peslyak
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted.
#
# 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.
CC = gcc
LD = $(CC)
RM = rm -f
OMPFLAGS = -fopenmp
OMPFLAGS_MAYBE = $(OMPFLAGS)
#CFLAGS = -Wall -O2 -fomit-frame-pointer $(OMPFLAGS_MAYBE) -DSKIP_MEMZERO
CFLAGS = -Wall -O2 -march=native -fomit-frame-pointer $(OMPFLAGS_MAYBE) -DSKIP_MEMZERO
#CFLAGS = -Wall -O2 -funroll-loops -fomit-frame-pointer $(OMPFLAGS_MAYBE) -DSKIP_MEMZERO
#CFLAGS = -Wall -O2 -march=native -funroll-loops -fomit-frame-pointer $(OMPFLAGS_MAYBE) -DSKIP_MEMZERO
# -lrt is for userom's use of clock_gettime()
LDFLAGS = -s -lrt $(OMPFLAGS_MAYBE)
PROJ = tests phc-test initrom userom
OBJS_CORE = yescrypt-opt.o
OBJS_COMMON = yescrypt-common.o sha256.o insecure_memzero.o
OBJS_TESTS = $(OBJS_CORE) $(OBJS_COMMON) tests.o
OBJS_PHC = $(OBJS_CORE) $(OBJS_COMMON) phc-test.o
OBJS_INITROM = $(OBJS_CORE) $(OBJS_COMMON) initrom.o
OBJS_USEROM = $(OBJS_CORE) $(OBJS_COMMON) userom.o
OBJS_RM = yescrypt-*.o
all: $(PROJ)
check: tests phc-test
@echo 'Running main tests'
@time ./tests | tee TESTS-OUT
@diff -U0 TESTS-OK TESTS-OUT && echo PASSED || echo FAILED
@if [ -f PHC-TEST-OK-SHA256 ]; then \
echo 'Running PHC tests'; \
time ./phc-test > PHC-TEST-OUT; \
sha256sum -c PHC-TEST-OK-SHA256 || shasum -a 256 -c PHC-TEST-OK-SHA256; \
fi
ref:
$(MAKE) $(PROJ) OBJS_CORE=yescrypt-ref.o
check-ref:
$(MAKE) check OBJS_CORE=yescrypt-ref.o
tests: $(OBJS_TESTS)
$(LD) $(LDFLAGS) $(OBJS_TESTS) -o $@
phc-test.o: phc.c
$(CC) -c $(CFLAGS) -DTEST phc.c -o $@
phc-test: $(OBJS_PHC)
$(LD) $(LDFLAGS) $(OBJS_PHC) -o $@
initrom: $(OBJS_INITROM)
$(LD) $(LDFLAGS) $(OBJS_INITROM) -o $@
userom: $(OBJS_USEROM)
$(LD) $(LDFLAGS) $(OMPFLAGS) $(OBJS_USEROM) -o $@
userom.o: userom.c
$(CC) -c $(CFLAGS) $(OMPFLAGS) $*.c
.c.o:
$(CC) -c $(CFLAGS) $*.c
yescrypt-opt.o: yescrypt-platform.c
clean:
$(RM) $(PROJ)
$(RM) $(OBJS_TESTS) $(OBJS_PHC) $(OBJS_INITROM) $(OBJS_USEROM)
$(RM) $(OBJS_RM)
$(RM) TESTS-OUT PHC-TEST-OUT

View File

@@ -0,0 +1,217 @@
/*-
* Copyright 2013-2018 Alexander Peslyak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* 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.
*/
#define YESCRYPT_FLAGS YESCRYPT_DEFAULTS
#define ROM_SHM_KEY 0x7965730a
#define ROM_LOCAL_PARAM "change this before use"
/* Maximum parallelism factor during ROM initialization */
#define YESCRYPT_PROM_SHM 112
#define YESCRYPT_PROM_FILE 4
//#define USE_HUGEPAGE
//#define DUMP_SHARED
#include <errno.h>
#include <stdio.h>
#include <stdlib.h> /* for atoi() */
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "yescrypt.h"
int main(int argc, const char * const *argv)
{
#if 0
uint64_t rom_bytes = 112 * (1024ULL*1024*1024);
uint64_t ram_bytes = 1 * (1024ULL*1024);
#else
uint64_t rom_bytes = 3 * (1024ULL*1024*1024);
uint64_t ram_bytes = 2 * (1024ULL*1024);
#endif
uint32_t r, min_r;
uint64_t NROM_log2, N_log2;
int shmid;
yescrypt_shared_t shared;
yescrypt_binary_t *digest;
const char *rom_filename = NULL;
int rom_fd;
if (argc > 1)
rom_bytes = atoi(argv[1]) * (1024ULL*1024*1024);
if (argc > 2)
ram_bytes = atoi(argv[2]) * (1024ULL*1024);
if (argc > 3)
rom_filename = argv[3];
if (!rom_bytes) {
puts("Wrong ROM size requested");
return 1;
}
min_r = 9;
if (rom_filename)
min_r = 8 * 256;
NROM_log2 = 0;
while (((rom_bytes >> NROM_log2) & 0xff) == 0)
NROM_log2++;
r = rom_bytes >> (7 + NROM_log2);
while (r < min_r && NROM_log2 > 0) {
r <<= 1;
NROM_log2--;
}
rom_bytes = (uint64_t)r << (7 + NROM_log2);
N_log2 = 3;
while (((uint64_t)r << (7 + N_log2)) < ram_bytes)
N_log2++;
ram_bytes = (uint64_t)r << (7 + N_log2);
printf("r=%u N=2^%u NROM=2^%u\n", r,
(unsigned int)N_log2, (unsigned int)NROM_log2);
printf("Will use %.2f KiB ROM\n", rom_bytes / 1024.0);
printf(" %.2f KiB RAM\n", ram_bytes / 1024.0);
shared.aligned_size = rom_bytes;
if (rom_filename) {
rom_fd = open(rom_filename, O_CREAT|O_RDWR|O_EXCL,
S_IRUSR|S_IRGRP|S_IWUSR);
if (rom_fd < 0) {
perror("open");
return 1;
}
if (ftruncate(rom_fd, rom_bytes)) {
perror("ftruncate");
close(rom_fd);
unlink(rom_filename);
return 1;
}
int flags =
#ifdef MAP_NOCORE
MAP_NOCORE |
#endif
#if defined(MAP_HUGETLB) && defined(USE_HUGEPAGE)
MAP_HUGETLB |
#endif
MAP_SHARED;
void *p = mmap(NULL, rom_bytes, PROT_READ | PROT_WRITE,
flags, rom_fd, 0);
#if defined(MAP_HUGETLB) && defined(USE_HUGEPAGE)
if (p == MAP_FAILED)
p = mmap(NULL, rom_bytes, PROT_READ | PROT_WRITE,
flags & ~MAP_HUGETLB, rom_fd, 0);
#endif
if (p == MAP_FAILED) {
perror("mmap");
close(rom_fd);
unlink(rom_filename);
return 1;
}
close(rom_fd);
shared.base = shared.aligned = p;
} else {
shmid = shmget(ROM_SHM_KEY, shared.aligned_size,
#ifdef SHM_HUGETLB
SHM_HUGETLB |
#endif
IPC_CREAT|IPC_EXCL | S_IRUSR|S_IRGRP|S_IWUSR);
if (shmid == -1) {
#ifdef SHM_HUGETLB
perror("shmget");
puts("Retrying without SHM_HUGETLB");
shmid = shmget(ROM_SHM_KEY, shared.aligned_size,
IPC_CREAT|IPC_EXCL | S_IRUSR|S_IRGRP|S_IWUSR);
#endif
if (shmid == -1) {
perror("shmget");
return 1;
}
}
shared.base = shared.aligned = shmat(shmid, NULL, 0);
if (shared.base == (void *)-1) {
int save_errno = errno;
shmctl(shmid, IPC_RMID, NULL);
errno = save_errno;
perror("shmat");
return 1;
}
}
printf("Initializing ROM ...");
fflush(stdout);
yescrypt_params_t rom_params = {
.flags = YESCRYPT_DEFAULTS | YESCRYPT_SHARED_PREALLOCATED,
.NROM = (uint64_t)1 << NROM_log2,
.r = r,
.p = rom_filename ? YESCRYPT_PROM_FILE : YESCRYPT_PROM_SHM };
if (yescrypt_init_shared(&shared,
(uint8_t *)ROM_LOCAL_PARAM, strlen(ROM_LOCAL_PARAM),
&rom_params)) {
puts(" FAILED");
if (rom_filename)
unlink(rom_filename);
return 1;
}
#ifdef DUMP_SHARED
fwrite(shared.aligned, shared.aligned_size, 1, stderr);
#endif
digest = yescrypt_digest_shared(&shared);
printf(" DONE (%02x%02x%02x%02x)\n",
digest->uc[0], digest->uc[1], digest->uc[2], digest->uc[3]);
{
yescrypt_local_t local;
const uint8_t *setting;
uint8_t hash[128];
if (yescrypt_init_local(&local)) {
puts("yescrypt_init_local() FAILED");
return 1;
}
yescrypt_params_t params = rom_params;
params.flags = YESCRYPT_FLAGS;
params.N = (uint64_t)1 << N_log2;
params.p = 1;
setting = yescrypt_encode_params(&params,
(const uint8_t *)"WZaPV7LSUEKMo34.", 16);
printf("'%s'\n", (char *)yescrypt_r(&shared, &local,
(const uint8_t *)"pleaseletmein", 13, setting, NULL,
hash, sizeof(hash)));
}
if (rom_filename && munmap(shared.base, rom_bytes)) {
perror("munmap");
return 1;
}
return 0;
}

View File

@@ -0,0 +1,49 @@
/*-
* Copyright 2014 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.
*/
#ifndef SKIP_MEMZERO
#include <stddef.h>
#include <stdint.h>
#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;
#endif

View File

@@ -0,0 +1,69 @@
/*-
* Copyright 2014 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.
*/
#ifndef _INSECURE_MEMZERO_H_
#define _INSECURE_MEMZERO_H_
#ifdef SKIP_MEMZERO
#define insecure_memzero(buf, len) /* empty */
#else
#include <stddef.h>
/* 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
#endif /* !_INSECURE_MEMZERO_H_ */

145
brute/source/yescrypt/phc.c Normal file
View File

@@ -0,0 +1,145 @@
/*-
* Copyright 2014-2016,2018 Alexander Peslyak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* 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.
*/
#define YESCRYPT_FLAGS YESCRYPT_DEFAULTS
#define YESCRYPT_BASE_N 8
#define YESCRYPT_R 8
#define YESCRYPT_P 1
#include "yescrypt.h"
#ifdef TEST
static
#endif
int PHS(void *out, size_t outlen, const void *in, size_t inlen,
const void *salt, size_t saltlen, unsigned int t_cost, unsigned int m_cost)
{
yescrypt_local_t local;
yescrypt_params_t params = {
.flags = YESCRYPT_FLAGS,
.N = (uint64_t)YESCRYPT_BASE_N << m_cost,
.r = YESCRYPT_R,
.p = YESCRYPT_P,
.t = t_cost,
.g = 0 };
int retval;
if (yescrypt_init_local(&local))
return -1;
retval = yescrypt_kdf(NULL, &local, in, inlen, salt, saltlen, &params,
out, outlen);
if (yescrypt_free_local(&local))
return -1;
return retval;
}
#ifdef TEST
#include <stdio.h>
#include <unistd.h> /* for sysconf() */
#include <sys/times.h>
static void print_hex(const uint8_t *buf, size_t buflen, const char *sep)
{
size_t i;
putchar('"');
for (i = 0; i < buflen; i++)
printf("\\x%02x", buf[i]);
printf("\"%s", sep);
}
static void print_PHS(const void *in, size_t inlen,
const void *salt, size_t saltlen, unsigned int t_cost, unsigned int m_cost)
{
uint8_t dk[32];
printf("PHS(");
print_hex(in, inlen, ", ");
print_hex(salt, saltlen, ", ");
printf("%u, %u) = ", t_cost, m_cost);
if (PHS(dk, sizeof(dk), in, inlen, salt, saltlen, t_cost, m_cost)) {
puts("FAILED");
return;
}
print_hex(dk, sizeof(dk), "\n");
}
static void print_all_PHS(unsigned int t_cost, unsigned int m_cost)
{
clock_t clk_tck = sysconf(_SC_CLK_TCK);
struct tms start_tms, end_tms;
clock_t start = times(&start_tms), end, start_v, end_v;
const size_t count = 0x102;
size_t inlen, i, j;
inlen = 0;
for (i = 0; i < count; i++) {
uint8_t in[128], salt[16];
for (j = 0; j < inlen; j++)
in[j] = (i + j) & 0xff;
for (j = 0; j < sizeof(salt); j++)
salt[j] = ~(i + j) & 0xff;
print_PHS(in, inlen, salt, sizeof(salt), t_cost, m_cost);
if (++inlen > sizeof(in))
inlen = 0;
}
end = times(&end_tms);
start_v = start_tms.tms_utime + start_tms.tms_stime +
start_tms.tms_cutime + start_tms.tms_cstime;
end_v = end_tms.tms_utime + end_tms.tms_stime +
end_tms.tms_cutime + end_tms.tms_cstime;
if (end == start)
end++;
if (end_v == start_v)
end_v++;
fprintf(stderr, "m_cost=%u (%.0f KiB), t_cost=%u\n"
"%llu c/s real, %llu c/s virtual (%llu hashes in %.2f seconds)\n",
m_cost, (YESCRYPT_BASE_N << m_cost) * YESCRYPT_R / 8.0, t_cost,
(unsigned long long)count * clk_tck / (end - start),
(unsigned long long)count * clk_tck / (end_v - start_v),
(unsigned long long)count, (double)(end - start) / clk_tck);
}
int main(void)
{
#if 0
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
print_all_PHS(0, 0);
print_all_PHS(0, 7);
print_all_PHS(0, 8);
print_all_PHS(1, 8);
print_all_PHS(2, 8);
print_all_PHS(3, 8);
print_all_PHS(0, 11);
return 0;
}
#endif

View File

@@ -0,0 +1,652 @@
/*-
* Copyright 2005-2016 Colin Percival
* Copyright 2016-2018,2021 Alexander Peslyak
* 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.
*/
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "insecure_memzero.h"
#include "sysendian.h"
#include "sha256.h"
#ifdef __ICC
/* Miscompile with icc 14.0.0 (at least), so don't use restrict there */
#define restrict
#elif __STDC_VERSION__ >= 199901L
/* Have restrict */
#elif defined(__GNUC__)
#define restrict __restrict
#else
#define restrict
#endif
/*
* Encode a length len*2 vector of (uint32_t) into a length len*8 vector of
* (uint8_t) in big-endian form.
*/
static void
be32enc_vect(uint8_t * dst, const uint32_t * src, size_t len)
{
/* Encode vector, two words at a time. */
do {
be32enc(&dst[0], src[0]);
be32enc(&dst[4], src[1]);
src += 2;
dst += 8;
} while (--len);
}
/*
* Decode a big-endian length len*8 vector of (uint8_t) into a length
* len*2 vector of (uint32_t).
*/
static void
be32dec_vect(uint32_t * dst, const uint8_t * src, size_t len)
{
/* Decode vector, two words at a time. */
do {
dst[0] = be32dec(&src[0]);
dst[1] = be32dec(&src[4]);
src += 8;
dst += 2;
} while (--len);
}
/* SHA256 round constants. */
static const uint32_t Krnd[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)
#if 1 /* Explicit caching/reuse of common subexpression between rounds */
#define Maj(x, y, z) (y ^ ((x_xor_y = x ^ y) & y_xor_z))
#else /* Let the compiler cache/reuse or not */
#define Maj(x, y, z) (y ^ ((x ^ y) & (y ^ z)))
#endif
#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); \
y_xor_z = x_xor_y;
/* 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] + Krnd[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, 8);
/* 2. Initialize working variables. */
memcpy(S, state, 32);
/* 3. Mix. */
for (i = 0; i < 64; i += 16) {
uint32_t x_xor_y, y_xor_z = S[(65 - i) % 8] ^ S[(66 - i) % 8];
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. */
state[0] += S[0];
state[1] += S[1];
state[2] += S[2];
state[3] += S[3];
state[4] += S[4];
state[5] += S[5];
state[6] += S[6];
state[7] += S[7];
}
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 initial_state[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, initial_state, sizeof(initial_state));
}
/**
* 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, 4);
}
/* 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);
}
/* Add padding and terminating bit-count, but don't invoke Transform yet. */
static int
SHA256_Pad_Almost(SHA256_CTX * ctx, uint8_t len[static restrict 8],
uint32_t tmp32[static restrict 72])
{
uint32_t r;
r = (ctx->count >> 3) & 0x3f;
if (r >= 56)
return -1;
/*
* Convert length to a vector of bytes -- we do this now rather
* than later because the length will change after we pad.
*/
be64enc(len, ctx->count);
/* Add 1--56 bytes so that the resulting length is 56 mod 64. */
_SHA256_Update(ctx, PAD, 56 - r, tmp32);
/* Add the terminating bit-count. */
ctx->buf[63] = len[7];
_SHA256_Update(ctx, len, 7, tmp32);
return 0;
}
/**
* 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];
union {
uint8_t tmp8[96];
uint32_t state[8];
} u;
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));
if (c == 1 && (dkLen & 31) == 0 && (saltlen & 63) <= 51) {
uint32_t oldcount;
uint8_t * ivecp;
/* Compute HMAC state after processing P and S. */
_HMAC_SHA256_Init(&hctx, passwd, passwdlen,
tmp32, &u.tmp8[0], &u.tmp8[64]);
_HMAC_SHA256_Update(&hctx, salt, saltlen, tmp32);
/* Prepare ictx padding. */
oldcount = hctx.ictx.count & (0x3f << 3);
_HMAC_SHA256_Update(&hctx, "\0\0\0", 4, tmp32);
if ((hctx.ictx.count & (0x3f << 3)) < oldcount ||
SHA256_Pad_Almost(&hctx.ictx, u.tmp8, tmp32))
goto generic; /* Can't happen due to saltlen check */
ivecp = hctx.ictx.buf + (oldcount >> 3);
/* Prepare octx padding. */
hctx.octx.count += 32 << 3;
SHA256_Pad_Almost(&hctx.octx, u.tmp8, tmp32);
/* Iterate through the blocks. */
for (i = 0; i * 32 < dkLen; i++) {
/* Generate INT(i + 1). */
be32enc(ivecp, (uint32_t)(i + 1));
/* Compute U_1 = PRF(P, S || INT(i)). */
memcpy(u.state, hctx.ictx.state, sizeof(u.state));
SHA256_Transform(u.state, hctx.ictx.buf,
&tmp32[0], &tmp32[64]);
be32enc_vect(hctx.octx.buf, u.state, 4);
memcpy(u.state, hctx.octx.state, sizeof(u.state));
SHA256_Transform(u.state, hctx.octx.buf,
&tmp32[0], &tmp32[64]);
be32enc_vect(&buf[i * 32], u.state, 4);
}
goto cleanup;
}
generic:
/* Compute HMAC state after processing P. */
_HMAC_SHA256_Init(&Phctx, passwd, passwdlen,
tmp32, &u.tmp8[0], &u.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(T, &hctx, tmp32, u.tmp8);
if (c > 1) {
/* T_i = U_1 ... */
memcpy(U, T, 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, u.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(U, 32);
insecure_memzero(T, 32);
cleanup:
insecure_memzero(&hctx, sizeof(HMAC_SHA256_CTX));
insecure_memzero(tmp32, 288);
insecure_memzero(&u, sizeof(u));
}

View File

@@ -0,0 +1,129 @@
/*-
* Copyright 2005-2016 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.
*/
#ifndef _SHA256_H_
#define _SHA256_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* 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);
#ifdef __cplusplus
}
#endif
#endif /* !_SHA256_H_ */

View File

@@ -0,0 +1,122 @@
/*-
* Copyright 2007-2014 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.
*/
#ifndef _SYSENDIAN_H_
#define _SYSENDIAN_H_
#include <stdint.h>
/* Avoid namespace collisions with BSD <sys/endian.h>. */
#define be32dec libcperciva_be32dec
#define be32enc libcperciva_be32enc
#define be64enc libcperciva_be64enc
#define le32dec libcperciva_le32dec
#define le32enc libcperciva_le32enc
#define le64dec libcperciva_le64dec
#define le64enc libcperciva_le64enc
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 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 /* !_SYSENDIAN_H_ */

View File

@@ -0,0 +1,405 @@
/*-
* Copyright 2013-2018 Alexander Peslyak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* 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.
*/
#define YESCRYPT_FLAGS YESCRYPT_DEFAULTS
//#define YESCRYPT_FLAGS YESCRYPT_WORM
//#define YESCRYPT_FLAGS 0
#define ROM_SHM_KEY 0x7965730a
//#define DISABLE_ROM
//#define DUMP_LOCAL
#include <stdio.h>
#include <stdlib.h> /* for atoi() */
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <sys/times.h>
#include <sched.h>
#include "yescrypt.h"
#ifdef _OPENMP
#include <omp.h>
#define NSAVE 1000
static uint64_t time_us(void)
{
struct timespec t;
#ifdef CLOCK_MONOTONIC_RAW
if (clock_gettime(CLOCK_MONOTONIC_RAW, &t))
return 0;
#else
if (clock_gettime(CLOCK_MONOTONIC, &t))
return 0;
#endif
return 1 + (uint64_t)t.tv_sec * 1000000 + t.tv_nsec / 1000;
}
#endif
int main(int argc, const char * const *argv)
{
#if 0
uint64_t rom_bytes = 112 * (1024ULL*1024*1024);
uint64_t ram_bytes = 1 * (1024ULL*1024);
#else
uint64_t rom_bytes = 3 * (1024ULL*1024*1024);
uint64_t ram_bytes = 2 * (1024ULL*1024);
#endif
uint32_t r, min_r;
uint64_t NROM_log2, N_log2;
yescrypt_shared_t shared_s;
yescrypt_shared_t *shared = NULL;
#ifndef DISABLE_ROM
int shmid;
#endif
const char *rom_filename = NULL;
int rom_fd;
yescrypt_binary_t key = {.uc={
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,25,26,27,28,255,128,64,32}};
if (argc > 1)
rom_bytes = atoi(argv[1]) * (1024ULL*1024*1024);
if (argc > 2)
ram_bytes = atoi(argv[2]) * (1024ULL*1024);
if (argc > 3 && rom_bytes)
rom_filename = argv[3];
r = 16;
min_r = 9;
if (rom_filename)
min_r = 8 * 64;
NROM_log2 = 0;
if (rom_bytes) {
while (((rom_bytes >> NROM_log2) & 0xff) == 0)
NROM_log2++;
r = rom_bytes >> (7 + NROM_log2);
while (r < min_r && NROM_log2 > 0) {
r <<= 1;
NROM_log2--;
}
rom_bytes = (uint64_t)r << (7 + NROM_log2);
}
N_log2 = 0;
while (((uint64_t)r << (7 + N_log2)) < ram_bytes)
N_log2++;
ram_bytes = (uint64_t)r << (7 + N_log2);
printf("r=%u N=2^%u NROM=2^%u\n", r,
(unsigned int)N_log2, (unsigned int)NROM_log2);
#ifdef DISABLE_ROM
rom_bytes = 0;
#endif
printf("Will use %.2f KiB ROM\n", rom_bytes / 1024.0);
printf(" %.2f KiB RAM\n", ram_bytes / 1024.0);
#ifndef DISABLE_ROM
if (rom_filename) {
rom_fd = open(rom_filename, O_RDONLY);
if (rom_fd < 0) {
perror("open");
return 1;
}
int flags =
#ifdef MAP_NOCORE
MAP_NOCORE |
#endif
#ifdef MAP_HUGETLB
MAP_HUGETLB |
#endif
MAP_SHARED;
void *p = mmap(NULL, rom_bytes, PROT_READ, flags, rom_fd, 0);
#ifdef MAP_HUGETLB
if (p == MAP_FAILED)
p = mmap(NULL, rom_bytes, PROT_READ,
flags & ~MAP_HUGETLB, rom_fd, 0);
#endif
if (p == MAP_FAILED) {
perror("mmap");
close(rom_fd);
return 1;
}
close(rom_fd);
shared = &shared_s;
shared->base = shared->aligned = p;
shared->aligned_size = rom_bytes;
} else if (rom_bytes) {
shared = &shared_s;
shared->aligned_size = rom_bytes;
shmid = shmget(ROM_SHM_KEY, shared->aligned_size, 0);
if (shmid == -1) {
perror("shmget");
return 1;
}
shared->base = shared->aligned = shmat(shmid, NULL, SHM_RDONLY);
if (shared->base == (void *)-1) {
perror("shmat");
return 1;
}
}
#endif
{
yescrypt_local_t local;
const uint8_t *setting;
if (yescrypt_init_local(&local)) {
puts("yescrypt_init_local() FAILED");
return 1;
}
yescrypt_params_t params = {
.flags = YESCRYPT_FLAGS,
.N = (uint64_t)1 << N_log2,
.NROM = NROM_log2 ? ((uint64_t)1 << NROM_log2) : 0,
.r = r,
.p = 1 };
setting = yescrypt_encode_params(&params,
(const uint8_t *)"WZaPV7LSUEKMo34.", 16);
{
uint8_t hash[128];
if (!yescrypt_r(shared, &local,
(const uint8_t *)"pleaseletmein", 13, setting, NULL,
hash, sizeof(hash))) {
puts("yescrypt_r() FAILED");
return 1;
}
printf("Plaintext: '%s'\n", (char *)hash);
if (!yescrypt_r(shared, &local,
(const uint8_t *)"pleaseletmein", 13, setting, &key,
hash, sizeof(hash))) {
puts("yescrypt_r() FAILED");
return 1;
}
printf("Encrypted: '%s'\n", (char *)hash);
}
#ifdef DUMP_LOCAL
#if 0
fwrite(local.aligned, local.aligned_size, 1, stderr);
#else
/* Skip B, dump only V */
if (local.aligned_size >= ram_bytes + 128 * r)
fwrite((char *)local.aligned + 128 * r, ram_bytes,
1, stderr);
#endif
#endif
puts("Benchmarking 1 thread ...");
clock_t clk_tck = sysconf(_SC_CLK_TCK);
struct tms start_tms, end_tms;
clock_t start = times(&start_tms), end;
unsigned int i, n;
unsigned long long count;
#ifdef _OPENMP
char save[NSAVE][128];
unsigned int nsave = 0;
#endif
unsigned int seed = start * 1812433253U;
n = 1;
count = 0;
do {
for (i = 0; i < n; i++) {
unsigned int j = count + i;
char p[32];
uint8_t hash[128];
snprintf(p, sizeof(p), "%u", seed + j);
#ifdef _OPENMP
const uint8_t *h =
#endif
yescrypt_r(shared, &local,
(const uint8_t *)p, strlen(p),
setting, &key, hash, sizeof(hash));
#ifdef _OPENMP
if (j < NSAVE) {
save[j][0] = 0;
strncat(save[j], (char *)h,
sizeof(save[j]) - 1);
nsave = j;
}
#endif
}
count += n;
end = times(&end_tms);
n <<= 1;
} while (end - start < clk_tck * 2);
clock_t start_v = start_tms.tms_utime + start_tms.tms_stime +
start_tms.tms_cutime + start_tms.tms_cstime;
clock_t end_v = end_tms.tms_utime + end_tms.tms_stime +
end_tms.tms_cutime + end_tms.tms_cstime;
printf("%llu c/s real, %llu c/s virtual "
"(%llu hashes in %.2f seconds)\n",
count * clk_tck / (end - start),
count * clk_tck / (end_v - start_v),
count, (double)(end - start) / clk_tck);
#ifdef _OPENMP
unsigned int nt = omp_get_max_threads();
printf("Benchmarking %u thread%s ...\n",
nt, nt == 1 ? "" : "s");
typedef struct {
yescrypt_local_t local;
uint64_t min, max, total;
} thread_data_s;
union {
thread_data_s s;
uint8_t cachelines[2][64]; /* avoid false sharing */
} thread_data[nt]; /* tricky to align this when on stack */
unsigned int t;
for (t = 0; t < nt; t++) {
thread_data_s *td = &thread_data[t].s;
if (yescrypt_init_local(&td->local)) {
puts("yescrypt_init_local() FAILED");
return 1;
}
td->min = ~(uint64_t)0; td->max = 0; td->total = 0;
}
unsigned long long count1 = count, count_restart = 0;
if (!geteuid()) {
puts("Running as root, so trying to set SCHED_RR");
#pragma omp parallel
{
struct sched_param param = { .sched_priority = 1 };
if (sched_setscheduler(getpid(), SCHED_RR, &param))
perror("sched_setscheduler");
}
}
start = times(&start_tms);
n = count * omp_get_max_threads();
count = 0;
do {
#pragma omp parallel for default(none) private(i) shared(n, shared, thread_data, setting, seed, count, save, nsave, key)
for (i = 0; i < n; i++) {
unsigned int j = count + i;
char p[32];
uint8_t hash[128];
snprintf(p, sizeof(p), "%u", seed + j);
thread_data_s *td = &thread_data[omp_get_thread_num()].s;
uint64_t start1 = time_us();
#if 1
const char *h = (const char *)yescrypt_r(
shared, &td->local,
(const uint8_t *)p, strlen(p),
setting, &key, hash, sizeof(hash));
#else
yescrypt_local_t local;
yescrypt_init_local(&local);
const char *h = (const char *)yescrypt_r(
shared, &local,
(const uint8_t *)p, strlen(p),
setting, &key, hash, sizeof(hash));
yescrypt_free_local(&local);
#endif
uint64_t end1 = time_us();
if (end1 < start1)
end1 = start1;
uint64_t diff1 = end1 - start1;
td->total += diff1;
if (diff1 < td->min)
td->min = diff1;
if (diff1 > td->max)
td->max = diff1;
if (j < nsave && strcmp(save[j], h)) {
#pragma omp critical
printf("Mismatch at %u, %s != %s\n",
j, save[j], h);
}
}
count += n;
if ((count - n) < count1 && count >= count1) {
/* Disregard our repeat of single thread's results (could be partially cached
* by same core, but OTOH other cores not yet warmed up to full clock rate). */
start = times(&start_tms);
count_restart = count;
for (t = 0; t < nt; t++) {
thread_data_s *td = &thread_data[t].s;
td->min = ~(uint64_t)0; td->max = 0; td->total = 0;
}
} else {
n <<= 1;
}
end = times(&end_tms);
} while (end - start < clk_tck);
if (!count_restart)
puts("Didn't reach single-thread's hash count");
count -= count_restart;
start_v = start_tms.tms_utime + start_tms.tms_stime +
start_tms.tms_cutime + start_tms.tms_cstime;
end_v = end_tms.tms_utime + end_tms.tms_stime +
end_tms.tms_cutime + end_tms.tms_cstime;
printf("%llu c/s real, %llu c/s virtual "
"(%llu hashes in %.2f seconds)\n",
count * clk_tck / (end - start),
count * clk_tck / (end_v - start_v),
count, (double)(end - start) / clk_tck);
uint64_t min = ~(uint64_t)0, max = 0, total = 0;
for (t = 0; t < nt; t++) {
thread_data_s *td = &thread_data[t].s;
total += td->total;
if (td->min < min)
min = td->min;
if (td->max > max)
max = td->max;
}
printf("min %.3f ms, avg %.3f ms, max %.3f ms\n",
min / 1000.0, total / 1000.0 / count, max / 1000.0);
#endif
}
if (rom_filename && munmap(shared->base, rom_bytes)) {
perror("munmap");
return 1;
}
return 0;
}

View File

@@ -0,0 +1,703 @@
/*-
* Copyright 2013-2018 Alexander Peslyak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* 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.
*/
#include <stdint.h>
#include <string.h>
#include "insecure_memzero.h"
#include "sha256.h"
#define YESCRYPT_INTERNAL
#include "yescrypt.h"
#define BYTES2CHARS(bytes) ((((bytes) * 8) + 5) / 6)
#define HASH_SIZE sizeof(yescrypt_binary_t) /* bytes */
#define HASH_LEN BYTES2CHARS(HASH_SIZE) /* base-64 chars */
/*
* "$y$", up to 8 params of up to 6 chars each, '$', salt
* Alternatively, but that's smaller:
* "$7$", 3 params encoded as 1+5+5 chars, salt
*/
#define PREFIX_LEN (3 + 8 * 6 + 1 + BYTES2CHARS(32))
static const char * const itoa64 =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const uint8_t atoi64_partial[77] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
64, 64, 64, 64, 64, 64, 64,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
64, 64, 64, 64, 64, 64,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
};
static uint8_t *encode64_uint32(uint8_t *dst, size_t dstlen,
uint32_t src, uint32_t min)
{
uint32_t start = 0, end = 47, chars = 1, bits = 0;
if (src < min)
return NULL;
src -= min;
do {
uint32_t count = (end + 1 - start) << bits;
if (src < count)
break;
if (start >= 63)
return NULL;
start = end + 1;
end = start + (62 - end) / 2;
src -= count;
chars++;
bits += 6;
} while (1);
if (dstlen <= chars) /* require room for a NUL terminator */
return NULL;
*dst++ = itoa64[start + (src >> bits)];
while (--chars) {
bits -= 6;
*dst++ = itoa64[(src >> bits) & 0x3f];
}
*dst = 0; /* NUL terminate just in case */
return dst;
}
static inline uint32_t atoi64(uint8_t src)
{
if (src >= '.' && src <= 'z')
return atoi64_partial[src - '.'];
return 64;
}
static const uint8_t *decode64_uint32(uint32_t *dst,
const uint8_t *src, uint32_t min)
{
uint32_t start = 0, end = 47, chars = 1, bits = 0;
uint32_t c;
c = atoi64(*src++);
if (c > 63)
goto fail;
*dst = min;
while (c > end) {
*dst += (end + 1 - start) << bits;
start = end + 1;
end = start + (62 - end) / 2;
chars++;
bits += 6;
}
*dst += (c - start) << bits;
while (--chars) {
c = atoi64(*src++);
if (c > 63)
goto fail;
bits -= 6;
*dst += c << bits;
}
return src;
fail:
*dst = 0;
return NULL;
}
static uint8_t *encode64_uint32_fixed(uint8_t *dst, size_t dstlen,
uint32_t src, uint32_t srcbits)
{
uint32_t bits;
for (bits = 0; bits < srcbits; bits += 6) {
if (dstlen < 2)
return NULL;
*dst++ = itoa64[src & 0x3f];
dstlen--;
src >>= 6;
}
if (src || dstlen < 1)
return NULL;
*dst = 0; /* NUL terminate just in case */
return dst;
}
static uint8_t *encode64(uint8_t *dst, size_t dstlen,
const uint8_t *src, size_t srclen)
{
size_t i;
for (i = 0; i < srclen; ) {
uint8_t *dnext;
uint32_t value = 0, bits = 0;
do {
value |= (uint32_t)src[i++] << bits;
bits += 8;
} while (bits < 24 && i < srclen);
dnext = encode64_uint32_fixed(dst, dstlen, value, bits);
if (!dnext)
return NULL;
dstlen -= dnext - dst;
dst = dnext;
}
if (dstlen < 1)
return NULL;
*dst = 0; /* NUL terminate just in case */
return dst;
}
static const uint8_t *decode64_uint32_fixed(uint32_t *dst, uint32_t dstbits,
const uint8_t *src)
{
uint32_t bits;
*dst = 0;
for (bits = 0; bits < dstbits; bits += 6) {
uint32_t c = atoi64(*src++);
if (c > 63) {
*dst = 0;
return NULL;
}
*dst |= c << bits;
}
return src;
}
static const uint8_t *decode64(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen)
{
size_t dstpos = 0;
while (dstpos <= *dstlen && srclen) {
uint32_t value = 0, bits = 0;
while (srclen--) {
uint32_t c = atoi64(*src);
if (c > 63) {
srclen = 0;
break;
}
src++;
value |= c << bits;
bits += 6;
if (bits >= 24)
break;
}
if (!bits)
break;
if (bits < 12) /* must have at least one full byte */
goto fail;
while (dstpos++ < *dstlen) {
*dst++ = value;
value >>= 8;
bits -= 8;
if (bits < 8) { /* 2 or 4 */
if (value) /* must be 0 */
goto fail;
bits = 0;
break;
}
}
if (bits)
goto fail;
}
if (!srclen && dstpos <= *dstlen) {
*dstlen = dstpos;
return src;
}
fail:
*dstlen = 0;
return NULL;
}
typedef enum { ENC = 1, DEC = -1 } encrypt_dir_t;
static void memxor(unsigned char *dst, unsigned char *src, size_t size)
{
while (size--)
*dst++ ^= *src++;
}
static void encrypt(unsigned char *data, size_t datalen,
const yescrypt_binary_t *key, encrypt_dir_t dir)
{
SHA256_CTX ctx;
unsigned char f[32 + 4];
size_t halflen, which;
unsigned char mask, round, target;
if (!datalen)
return;
if (datalen > 64)
datalen = 64;
halflen = datalen >> 1;
which = 0; /* offset to half we are working on (0 or halflen) */
mask = 0x0f; /* current half's extra nibble mask if datalen is odd */
round = 0;
target = 5; /* 6 rounds due to Jacques Patarin's CRYPTO 2004 paper */
if (dir == DEC) {
which = halflen; /* even round count, so swap the halves */
mask ^= 0xff;
round = target;
target = 0;
}
f[32] = 0;
f[33] = sizeof(*key);
f[34] = datalen;
do {
SHA256_Init(&ctx);
f[35] = round;
SHA256_Update(&ctx, &f[32], 4);
SHA256_Update(&ctx, key, sizeof(*key));
SHA256_Update(&ctx, &data[which], halflen);
if (datalen & 1) {
f[0] = data[datalen - 1] & mask;
SHA256_Update(&ctx, f, 1);
}
SHA256_Final(f, &ctx);
which ^= halflen;
memxor(&data[which], f, halflen);
if (datalen & 1) {
mask ^= 0xff;
data[datalen - 1] ^= f[halflen] & mask;
}
if (round == target)
break;
round += dir;
} while (1);
/* ctx is presumably zeroized by SHA256_Final() */
insecure_memzero(f, sizeof(f));
}
uint8_t *yescrypt_r(const yescrypt_shared_t *shared, yescrypt_local_t *local,
const uint8_t *passwd, size_t passwdlen,
const uint8_t *setting,
const yescrypt_binary_t *key,
uint8_t *buf, size_t buflen)
{
unsigned char saltbin[64], hashbin[32];
const uint8_t *src, *saltstr, *salt;
uint8_t *dst;
size_t need, prefixlen, saltstrlen, saltlen;
yescrypt_params_t params = { .p = 1 };
if (setting[0] != '$' ||
(setting[1] != '7' && setting[1] != 'y') ||
setting[2] != '$')
return NULL;
src = setting + 3;
if (setting[1] == '7') {
uint32_t N_log2 = atoi64(*src++);
if (N_log2 < 1 || N_log2 > 63)
return NULL;
params.N = (uint64_t)1 << N_log2;
src = decode64_uint32_fixed(&params.r, 30, src);
if (!src)
return NULL;
src = decode64_uint32_fixed(&params.p, 30, src);
if (!src)
return NULL;
if (key)
return NULL;
} else {
uint32_t flavor, N_log2;
src = decode64_uint32(&flavor, src, 0);
if (!src)
return NULL;
if (flavor < YESCRYPT_RW) {
params.flags = flavor;
} else if (flavor <= YESCRYPT_RW + (YESCRYPT_RW_FLAVOR_MASK >> 2)) {
params.flags = YESCRYPT_RW + ((flavor - YESCRYPT_RW) << 2);
} else {
return NULL;
}
src = decode64_uint32(&N_log2, src, 1);
if (!src || N_log2 > 63)
return NULL;
params.N = (uint64_t)1 << N_log2;
src = decode64_uint32(&params.r, src, 1);
if (!src)
return NULL;
if (*src != '$') {
uint32_t have;
src = decode64_uint32(&have, src, 1);
if (!src)
return NULL;
if (have & 1) {
src = decode64_uint32(&params.p, src, 2);
if (!src)
return NULL;
}
if (have & 2) {
src = decode64_uint32(&params.t, src, 1);
if (!src)
return NULL;
}
if (have & 4) {
src = decode64_uint32(&params.g, src, 1);
if (!src)
return NULL;
}
if (have & 8) {
uint32_t NROM_log2;
src = decode64_uint32(&NROM_log2, src, 1);
if (!src || NROM_log2 > 63)
return NULL;
params.NROM = (uint64_t)1 << NROM_log2;
}
}
if (*src++ != '$')
return NULL;
}
prefixlen = src - setting;
saltstr = src;
src = (uint8_t *)strrchr((char *)saltstr, '$');
if (src)
saltstrlen = src - saltstr;
else
saltstrlen = strlen((char *)saltstr);
if (setting[1] == '7') {
salt = saltstr;
saltlen = saltstrlen;
} else {
const uint8_t *saltend;
saltlen = sizeof(saltbin);
saltend = decode64(saltbin, &saltlen, saltstr, saltstrlen);
if (!saltend || (size_t)(saltend - saltstr) != saltstrlen)
goto fail;
salt = saltbin;
if (key)
encrypt(saltbin, saltlen, key, ENC);
}
need = prefixlen + saltstrlen + 1 + HASH_LEN + 1;
if (need > buflen || need < saltstrlen)
goto fail;
if (yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen,
&params, hashbin, sizeof(hashbin)))
goto fail;
if (key) {
insecure_memzero(saltbin, sizeof(saltbin));
encrypt(hashbin, sizeof(hashbin), key, ENC);
}
dst = buf;
memcpy(dst, setting, prefixlen + saltstrlen);
dst += prefixlen + saltstrlen;
*dst++ = '$';
dst = encode64(dst, buflen - (dst - buf), hashbin, sizeof(hashbin));
insecure_memzero(hashbin, sizeof(hashbin));
if (!dst || dst >= buf + buflen)
return NULL;
*dst = 0; /* NUL termination */
return buf;
fail:
insecure_memzero(saltbin, sizeof(saltbin));
insecure_memzero(hashbin, sizeof(hashbin));
return NULL;
}
uint8_t *yescrypt(const uint8_t *passwd, const uint8_t *setting)
{
/* prefix, '$', hash, NUL */
static uint8_t buf[PREFIX_LEN + 1 + HASH_LEN + 1];
yescrypt_local_t local;
uint8_t *retval;
if (yescrypt_init_local(&local))
return NULL;
retval = yescrypt_r(NULL, &local,
passwd, strlen((char *)passwd), setting, NULL, buf, sizeof(buf));
if (yescrypt_free_local(&local))
return NULL;
return retval;
}
uint8_t *yescrypt_reencrypt(uint8_t *hash,
const yescrypt_binary_t *from_key,
const yescrypt_binary_t *to_key)
{
uint8_t *retval = NULL, *saltstart, *hashstart;
const uint8_t *hashend;
unsigned char saltbin[64], hashbin[32];
size_t saltstrlen, saltlen = 0, hashlen;
if (strncmp((char *)hash, "$y$", 3))
return NULL;
saltstart = NULL;
hashstart = (uint8_t *)strrchr((char *)hash, '$');
if (hashstart) {
if (hashstart > (uint8_t *)hash) {
saltstart = hashstart - 1;
while (*saltstart != '$' && saltstart > hash)
saltstart--;
if (*saltstart == '$')
saltstart++;
}
hashstart++;
} else {
hashstart = hash;
}
saltstrlen = saltstart ? (hashstart - 1 - saltstart) : 0;
if (saltstrlen > BYTES2CHARS(64) ||
strlen((char *)hashstart) != HASH_LEN)
return NULL;
if (saltstrlen) {
const uint8_t *saltend;
saltlen = sizeof(saltbin);
saltend = decode64(saltbin, &saltlen, saltstart, saltstrlen);
if (!saltend || *saltend != '$' || saltlen < 1 || saltlen > 64)
goto out;
if (from_key)
encrypt(saltbin, saltlen, from_key, ENC);
if (to_key)
encrypt(saltbin, saltlen, to_key, DEC);
}
hashlen = sizeof(hashbin);
hashend = decode64(hashbin, &hashlen, hashstart, HASH_LEN);
if (!hashend || *hashend || hashlen != sizeof(hashbin))
goto out;
if (from_key)
encrypt(hashbin, hashlen, from_key, DEC);
if (to_key)
encrypt(hashbin, hashlen, to_key, ENC);
if (saltstrlen) {
if (!encode64(saltstart, saltstrlen + 1, saltbin, saltlen))
goto out; /* can't happen */
*(saltstart + saltstrlen) = '$';
}
if (!encode64(hashstart, HASH_LEN + 1, hashbin, hashlen))
goto out; /* can't happen */
retval = hash;
out:
insecure_memzero(saltbin, sizeof(saltbin));
insecure_memzero(hashbin, sizeof(hashbin));
return retval;
}
static uint32_t N2log2(uint64_t N)
{
uint32_t N_log2;
if (N < 2)
return 0;
N_log2 = 2;
while (N >> N_log2 != 0)
N_log2++;
N_log2--;
if (N >> N_log2 != 1)
return 0;
return N_log2;
}
uint8_t *yescrypt_encode_params_r(const yescrypt_params_t *params,
const uint8_t *src, size_t srclen,
uint8_t *buf, size_t buflen)
{
uint32_t flavor, N_log2, NROM_log2, have;
uint8_t *dst;
if (srclen > SIZE_MAX / 16)
return NULL;
if (params->flags < YESCRYPT_RW) {
flavor = params->flags;
} else if ((params->flags & YESCRYPT_MODE_MASK) == YESCRYPT_RW &&
params->flags <= (YESCRYPT_RW | YESCRYPT_RW_FLAVOR_MASK)) {
flavor = YESCRYPT_RW + (params->flags >> 2);
} else {
return NULL;
}
N_log2 = N2log2(params->N);
if (!N_log2)
return NULL;
NROM_log2 = N2log2(params->NROM);
if (params->NROM && !NROM_log2)
return NULL;
if ((uint64_t)params->r * (uint64_t)params->p >= (1U << 30))
return NULL;
dst = buf;
*dst++ = '$';
*dst++ = 'y';
*dst++ = '$';
dst = encode64_uint32(dst, buflen - (dst - buf), flavor, 0);
if (!dst)
return NULL;
dst = encode64_uint32(dst, buflen - (dst - buf), N_log2, 1);
if (!dst)
return NULL;
dst = encode64_uint32(dst, buflen - (dst - buf), params->r, 1);
if (!dst)
return NULL;
have = 0;
if (params->p != 1)
have |= 1;
if (params->t)
have |= 2;
if (params->g)
have |= 4;
if (NROM_log2)
have |= 8;
if (have) {
dst = encode64_uint32(dst, buflen - (dst - buf), have, 1);
if (!dst)
return NULL;
}
if (params->p != 1) {
dst = encode64_uint32(dst, buflen - (dst - buf), params->p, 2);
if (!dst)
return NULL;
}
if (params->t) {
dst = encode64_uint32(dst, buflen - (dst - buf), params->t, 1);
if (!dst)
return NULL;
}
if (params->g) {
dst = encode64_uint32(dst, buflen - (dst - buf), params->g, 1);
if (!dst)
return NULL;
}
if (NROM_log2) {
dst = encode64_uint32(dst, buflen - (dst - buf), NROM_log2, 1);
if (!dst)
return NULL;
}
if (dst >= buf + buflen)
return NULL;
*dst++ = '$';
dst = encode64(dst, buflen - (dst - buf), src, srclen);
if (!dst || dst >= buf + buflen)
return NULL;
*dst = 0; /* NUL termination */
return buf;
}
uint8_t *yescrypt_encode_params(const yescrypt_params_t *params,
const uint8_t *src, size_t srclen)
{
/* prefix, NUL */
static uint8_t buf[PREFIX_LEN + 1];
return yescrypt_encode_params_r(params, src, srclen, buf, sizeof(buf));
}
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)
{
yescrypt_local_t local;
yescrypt_params_t params = { .flags = 0, .N = N, .r = r, .p = p };
int retval;
if (yescrypt_init_local(&local))
return -1;
retval = yescrypt_kdf(NULL, &local,
passwd, passwdlen, salt, saltlen, &params, buf, buflen);
if (yescrypt_free_local(&local))
return -1;
return retval;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/*-
* Copyright 2013-2018,2022 Alexander Peslyak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* 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.
*/
#ifdef __unix__
#include <sys/mman.h>
#endif
#ifdef __linux__
#include <linux/mman.h> /* for MAP_HUGE_2MB */
#endif
#define HUGEPAGE_THRESHOLD (32 * 1024 * 1024)
#ifdef __x86_64__
#define HUGEPAGE_SIZE (2 * 1024 * 1024)
#else
#undef HUGEPAGE_SIZE
#endif
static void *alloc_region(yescrypt_region_t *region, size_t size)
{
size_t base_size = size;
uint8_t *base, *aligned;
#ifdef MAP_ANON
int flags =
#ifdef MAP_NOCORE
MAP_NOCORE |
#endif
MAP_ANON | MAP_PRIVATE;
#if defined(MAP_HUGETLB) && defined(MAP_HUGE_2MB) && defined(HUGEPAGE_SIZE)
size_t new_size = size;
const size_t hugepage_mask = (size_t)HUGEPAGE_SIZE - 1;
if (size >= HUGEPAGE_THRESHOLD && size + hugepage_mask >= size) {
flags |= MAP_HUGETLB | MAP_HUGE_2MB;
/*
* Linux's munmap() fails on MAP_HUGETLB mappings if size is not a multiple of
* huge page size, so let's round up to huge page size here.
*/
new_size = size + hugepage_mask;
new_size &= ~hugepage_mask;
}
base = mmap(NULL, new_size, PROT_READ | PROT_WRITE, flags, -1, 0);
if (base != MAP_FAILED) {
base_size = new_size;
} else if (flags & MAP_HUGETLB) {
flags &= ~(MAP_HUGETLB | MAP_HUGE_2MB);
base = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, -1, 0);
}
#else
base = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, flags, -1, 0);
#endif
if (base == MAP_FAILED)
base = NULL;
aligned = base;
#elif defined(HAVE_POSIX_MEMALIGN)
if ((errno = posix_memalign((void **)&base, 64, size)) != 0)
base = NULL;
aligned = base;
#else
base = aligned = NULL;
if (size + 63 < size) {
errno = ENOMEM;
} else if ((base = malloc(size + 63)) != NULL) {
aligned = base + 63;
aligned -= (uintptr_t)aligned & 63;
}
#endif
region->base = base;
region->aligned = aligned;
region->base_size = base ? base_size : 0;
region->aligned_size = base ? size : 0;
return aligned;
}
static inline void init_region(yescrypt_region_t *region)
{
region->base = region->aligned = NULL;
region->base_size = region->aligned_size = 0;
}
static int free_region(yescrypt_region_t *region)
{
if (region->base) {
#ifdef MAP_ANON
if (munmap(region->base, region->base_size))
return -1;
#else
free(region->base);
#endif
}
init_region(region);
return 0;
}

View File

@@ -0,0 +1,925 @@
/*-
* Copyright 2009 Colin Percival
* Copyright 2013-2018 Alexander Peslyak
* 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.
*
* This is the reference implementation. Its purpose is to provide a simple
* human- and machine-readable specification that implementations intended
* for actual use should be tested against. It is deliberately mostly not
* optimized, and it is not meant to be used in production. Instead, use
* yescrypt-opt.c.
*/
#ifdef __GNUC__
#warning "This reference implementation is deliberately mostly not optimized, nor does it make any attempt not to leave sensitive data in memory. Use yescrypt-opt.c instead unless you're testing (against) the reference implementation on purpose."
#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sha256.h"
#include "sysendian.h"
#define YESCRYPT_INTERNAL
#include "yescrypt.h"
static void blkcpy(uint32_t *dst, const uint32_t *src, size_t count)
{
do {
*dst++ = *src++;
} while (--count);
}
static void blkxor(uint32_t *dst, const uint32_t *src, size_t count)
{
do {
*dst++ ^= *src++;
} while (--count);
}
/**
* salsa20(B):
* Apply the Salsa20 core to the provided block.
*/
static void salsa20(uint32_t B[16], uint32_t rounds)
{
uint32_t x[16];
size_t i;
/* SIMD unshuffle */
for (i = 0; i < 16; i++)
x[i * 5 % 16] = B[i];
for (i = 0; i < rounds; 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
}
/* SIMD shuffle */
for (i = 0; i < 16; i++)
B[i] += x[i * 5 % 16];
}
/**
* 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(uint32_t *B, uint32_t *Y, size_t r)
{
uint32_t X[16];
size_t i;
/* 1: X <-- B_{2r - 1} */
blkcpy(X, &B[(2 * r - 1) * 16], 16);
/* 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 * 16], 16);
salsa20(X, 8);
/* 4: Y_i <-- X */
blkcpy(&Y[i * 16], X, 16);
}
/* 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 * 16], &Y[(i * 2) * 16], 16);
for (i = 0; i < r; i++)
blkcpy(&B[(i + r) * 16], &Y[(i * 2 + 1) * 16], 16);
}
/* These are tunable, but they must meet certain constraints */
#define PWXsimple 2
#define PWXgather 4
#define PWXrounds 6
#define Swidth 8
/* Derived values. Not tunable on their own. */
#define PWXbytes (PWXgather * PWXsimple * 8)
#define PWXwords (PWXbytes / sizeof(uint32_t))
#define Sbytes (3 * (1 << Swidth) * PWXsimple * 8)
#define Swords (Sbytes / sizeof(uint32_t))
#define Smask (((1 << Swidth) - 1) * PWXsimple * 8)
#define rmin ((PWXbytes + 127) / 128)
typedef struct {
uint32_t *S;
uint32_t (*S0)[2], (*S1)[2], (*S2)[2];
size_t w;
} pwxform_ctx_t;
/**
* pwxform(B):
* Transform the provided block using the provided S-boxes.
*/
static void pwxform(uint32_t *B, pwxform_ctx_t *ctx)
{
uint32_t (*X)[PWXsimple][2] = (uint32_t (*)[PWXsimple][2])B;
uint32_t (*S0)[2] = ctx->S0, (*S1)[2] = ctx->S1, (*S2)[2] = ctx->S2;
size_t w = ctx->w;
size_t i, j, k;
/* 1: for i = 0 to PWXrounds - 1 do */
for (i = 0; i < PWXrounds; i++) {
/* 2: for j = 0 to PWXgather - 1 do */
for (j = 0; j < PWXgather; j++) {
uint32_t xl = X[j][0][0];
uint32_t xh = X[j][0][1];
uint32_t (*p0)[2], (*p1)[2];
/* 3: p0 <-- (lo(B_{j,0}) & Smask) / (PWXsimple * 8) */
p0 = S0 + (xl & Smask) / sizeof(*S0);
/* 4: p1 <-- (hi(B_{j,0}) & Smask) / (PWXsimple * 8) */
p1 = S1 + (xh & Smask) / sizeof(*S1);
/* 5: for k = 0 to PWXsimple - 1 do */
for (k = 0; k < PWXsimple; k++) {
uint64_t x, s0, s1;
/* 6: B_{j,k} <-- (hi(B_{j,k}) * lo(B_{j,k}) + S0_{p0,k}) xor S1_{p1,k} */
s0 = ((uint64_t)p0[k][1] << 32) + p0[k][0];
s1 = ((uint64_t)p1[k][1] << 32) + p1[k][0];
xl = X[j][k][0];
xh = X[j][k][1];
x = (uint64_t)xh * xl;
x += s0;
x ^= s1;
X[j][k][0] = x;
X[j][k][1] = x >> 32;
/* 8: if (i != 0) and (i != PWXrounds - 1) */
if (i != 0 && i != PWXrounds - 1) {
/* 9: S2_w <-- B_j */
S2[w][0] = x;
S2[w][1] = x >> 32;
/* 10: w <-- w + 1 */
w++;
}
}
}
}
/* 14: (S0, S1, S2) <-- (S2, S0, S1) */
ctx->S0 = S2;
ctx->S1 = S0;
ctx->S2 = S1;
/* 15: w <-- w mod 2^Swidth */
ctx->w = w & ((1 << Swidth) * PWXsimple - 1);
}
/**
* blockmix_pwxform(B, ctx, r):
* Compute B = BlockMix_pwxform{salsa20/2, ctx, r}(B). The input B must be
* 128r bytes in length.
*/
static void blockmix_pwxform(uint32_t *B, pwxform_ctx_t *ctx, size_t r)
{
uint32_t X[PWXwords];
size_t r1, i;
/* Convert 128-byte blocks to PWXbytes blocks */
/* 1: r_1 <-- 128r / PWXbytes */
r1 = 128 * r / PWXbytes;
/* 2: X <-- B'_{r_1 - 1} */
blkcpy(X, &B[(r1 - 1) * PWXwords], PWXwords);
/* 3: for i = 0 to r_1 - 1 do */
for (i = 0; i < r1; i++) {
/* 4: if r_1 > 1 */
if (r1 > 1) {
/* 5: X <-- X xor B'_i */
blkxor(X, &B[i * PWXwords], PWXwords);
}
/* 7: X <-- pwxform(X) */
pwxform(X, ctx);
/* 8: B'_i <-- X */
blkcpy(&B[i * PWXwords], X, PWXwords);
}
/* 10: i <-- floor((r_1 - 1) * PWXbytes / 64) */
i = (r1 - 1) * PWXbytes / 64;
/* 11: B_i <-- H(B_i) */
salsa20(&B[i * 16], 2);
#if 1 /* No-op with our current pwxform settings, but do it to make sure */
/* 12: for i = i + 1 to 2r - 1 do */
for (i++; i < 2 * r; i++) {
/* 13: B_i <-- H(B_i xor B_{i-1}) */
blkxor(&B[i * 16], &B[(i - 1) * 16], 16);
salsa20(&B[i * 16], 2);
}
#endif
}
/**
* integerify(B, r):
* Return the result of parsing B_{2r-1} as a little-endian integer.
*/
static uint64_t integerify(const uint32_t *B, size_t r)
{
/*
* Our 32-bit words are in host byte order, and word 13 is the second word of
* B_{2r-1} due to SIMD shuffling. The 64-bit value we return is also in host
* byte order, as it should be.
*/
const uint32_t *X = &B[(2 * r - 1) * 16];
return ((uint64_t)X[13] << 32) + X[0];
}
/**
* p2floor(x):
* Largest power of 2 not greater than argument.
*/
static uint64_t p2floor(uint64_t x)
{
uint64_t y;
while ((y = x & (x - 1)))
x = y;
return x;
}
/**
* wrap(x, i):
* Wrap x to the range 0 to i-1.
*/
static uint64_t wrap(uint64_t x, uint64_t i)
{
uint64_t n = p2floor(i);
return (x & (n - 1)) + (i - n);
}
/**
* smix1(B, r, N, flags, V, NROM, VROM, XY, ctx):
* Compute first loop of 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.
*/
static void smix1(uint32_t *B, size_t r, uint64_t N, yescrypt_flags_t flags,
uint32_t *V, uint64_t NROM, const uint32_t *VROM,
uint32_t *XY, pwxform_ctx_t *ctx)
{
size_t s = 32 * r;
uint32_t *X = XY;
uint32_t *Y = &XY[s];
uint64_t i, j;
size_t k;
/* 1: X <-- B */
for (k = 0; k < 2 * r; k++)
for (i = 0; i < 16; i++)
X[k * 16 + i] = le32dec(&B[k * 16 + (i * 5 % 16)]);
/* 2: for i = 0 to N - 1 do */
for (i = 0; i < N; i++) {
/* 3: V_i <-- X */
blkcpy(&V[i * s], X, s);
if (VROM && i == 0) {
/* X <-- X xor VROM_{NROM-1} */
blkxor(X, &VROM[(NROM - 1) * s], s);
} else if (VROM && (i & 1)) {
/* j <-- Integerify(X) mod NROM */
j = integerify(X, r) & (NROM - 1);
/* X <-- X xor VROM_j */
blkxor(X, &VROM[j * s], s);
} else if ((flags & YESCRYPT_RW) && i > 1) {
/* j <-- Wrap(Integerify(X), i) */
j = wrap(integerify(X, r), i);
/* X <-- X xor V_j */
blkxor(X, &V[j * s], s);
}
/* 4: X <-- H(X) */
if (ctx)
blockmix_pwxform(X, ctx, r);
else
blockmix_salsa8(X, Y, r);
}
/* B' <-- X */
for (k = 0; k < 2 * r; k++)
for (i = 0; i < 16; i++)
le32enc(&B[k * 16 + (i * 5 % 16)], X[k * 16 + i]);
}
/**
* smix2(B, r, N, Nloop, flags, V, NROM, VROM, XY, ctx):
* Compute second loop of 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
* greater than 1.
*/
static void smix2(uint32_t *B, size_t r, uint64_t N, uint64_t Nloop,
yescrypt_flags_t flags, uint32_t *V, uint64_t NROM,
const uint32_t *VROM, uint32_t *XY, pwxform_ctx_t *ctx)
{
size_t s = 32 * r;
uint32_t *X = XY;
uint32_t *Y = &XY[s];
uint64_t i, j;
size_t k;
/* X <-- B */
for (k = 0; k < 2 * r; k++)
for (i = 0; i < 16; i++)
X[k * 16 + i] = le32dec(&B[k * 16 + (i * 5 % 16)]);
/* 6: for i = 0 to N - 1 do */
for (i = 0; i < Nloop; i++) {
if (VROM && (i & 1)) {
/* j <-- Integerify(X) mod NROM */
j = integerify(X, r) & (NROM - 1);
/* X <-- H(X xor VROM_j) */
blkxor(X, &VROM[j * s], s);
} else {
/* 7: j <-- Integerify(X) mod N */
j = integerify(X, r) & (N - 1);
/* 8.1: X <-- X xor V_j */
blkxor(X, &V[j * s], s);
/* V_j <-- X */
if (flags & YESCRYPT_RW)
blkcpy(&V[j * s], X, s);
}
/* 8.2: X <-- H(X) */
if (ctx)
blockmix_pwxform(X, ctx, r);
else
blockmix_salsa8(X, Y, r);
}
/* 10: B' <-- X */
for (k = 0; k < 2 * r; k++)
for (i = 0; i < 16; i++)
le32enc(&B[k * 16 + (i * 5 % 16)], X[k * 16 + i]);
}
/**
* smix(B, r, N, p, t, flags, V, NROM, VROM, XY, ctx, passwd):
* Compute B = SMix_r(B, N). The input B must be 128rp 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 greater
* than 1.
*/
static void smix(uint32_t *B, size_t r, uint64_t N, uint32_t p, uint32_t t,
yescrypt_flags_t flags,
uint32_t *V, uint64_t NROM, const uint32_t *VROM,
uint32_t *XY, pwxform_ctx_t *ctx, uint8_t *passwd)
{
size_t s = 32 * r;
uint64_t Nchunk, Nloop_all, Nloop_rw, Vchunk;
uint32_t i;
/* 1: n <-- N / p */
Nchunk = N / p;
/* 2: Nloop_all <-- fNloop(n, t, flags) */
Nloop_all = Nchunk;
if (flags & YESCRYPT_RW) {
if (t <= 1) {
if (t)
Nloop_all *= 2; /* 2/3 */
Nloop_all = (Nloop_all + 2) / 3; /* 1/3, round up */
} else {
Nloop_all *= t - 1;
}
} else if (t) {
if (t == 1)
Nloop_all += (Nloop_all + 1) / 2; /* 1.5, round up */
Nloop_all *= t;
}
/* 6: Nloop_rw <-- 0 */
Nloop_rw = 0;
if (flags & YESCRYPT_INIT_SHARED) {
Nloop_rw = Nloop_all;
} else {
/* 3: if YESCRYPT_RW flag is set */
if (flags & YESCRYPT_RW) {
/* 4: Nloop_rw <-- Nloop_all / p */
Nloop_rw = Nloop_all / p;
}
}
/* 8: n <-- n - (n mod 2) */
Nchunk &= ~(uint64_t)1; /* round down to even */
/* 9: Nloop_all <-- Nloop_all + (Nloop_all mod 2) */
Nloop_all++; Nloop_all &= ~(uint64_t)1; /* round up to even */
/* 10: Nloop_rw <-- Nloop_rw + (Nloop_rw mod 2) */
Nloop_rw++; Nloop_rw &= ~(uint64_t)1; /* round up to even */
/* 11: for i = 0 to p - 1 do */
/* 12: u <-- in */
for (i = 0, Vchunk = 0; i < p; i++, Vchunk += Nchunk) {
/* 13: if i = p - 1 */
/* 14: n <-- N - u */
/* 15: end if */
/* 16: v <-- u + n - 1 */
uint64_t Np = (i < p - 1) ? Nchunk : (N - Vchunk);
uint32_t *Bp = &B[i * s];
uint32_t *Vp = &V[Vchunk * s];
pwxform_ctx_t *ctx_i = NULL;
/* 17: if YESCRYPT_RW flag is set */
if (flags & YESCRYPT_RW) {
ctx_i = &ctx[i];
/* 18: SMix1_1(B_i, Sbytes / 128, S_i, no flags) */
smix1(Bp, 1, Sbytes / 128, 0 /* no flags */,
ctx_i->S, 0, NULL, XY, NULL);
/* 19: S2_i <-- S_{i,0...2^Swidth-1} */
ctx_i->S2 = (uint32_t (*)[2])ctx_i->S;
/* 20: S1_i <-- S_{i,2^Swidth...2*2^Swidth-1} */
ctx_i->S1 = ctx_i->S2 + (1 << Swidth) * PWXsimple;
/* 21: S0_i <-- S_{i,2*2^Swidth...3*2^Swidth-1} */
ctx_i->S0 = ctx_i->S1 + (1 << Swidth) * PWXsimple;
/* 22: w_i <-- 0 */
ctx_i->w = 0;
/* 23: if i = 0 */
if (i == 0) {
/* 24: passwd <-- HMAC-SHA256(B_{0,2r-1}, passwd) */
HMAC_SHA256_Buf(Bp + (s - 16), 64,
passwd, 32, passwd);
}
}
/* 27: SMix1_r(B_i, n, V_{u..v}, flags) */
smix1(Bp, r, Np, flags, Vp, NROM, VROM, XY, ctx_i);
/* 28: SMix2_r(B_i, p2floor(n), Nloop_rw, V_{u..v}, flags) */
smix2(Bp, r, p2floor(Np), Nloop_rw, flags, Vp,
NROM, VROM, XY, ctx_i);
}
/* 30: for i = 0 to p - 1 do */
for (i = 0; i < p; i++) {
uint32_t *Bp = &B[i * s];
/* 31: SMix2_r(B_i, N, Nloop_all - Nloop_rw, V, flags excluding YESCRYPT_RW) */
smix2(Bp, r, N, Nloop_all - Nloop_rw, flags & ~YESCRYPT_RW,
V, NROM, VROM, XY, (flags & YESCRYPT_RW) ? &ctx[i] : NULL);
}
}
/**
* yescrypt_kdf_body(shared, local, passwd, passwdlen, salt, saltlen,
* flags, N, r, p, t, NROM, buf, buflen):
* Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
* p, buflen), or a revision of scrypt as requested by flags and shared, and
* write the result into buf.
*
* shared and flags may request special modes as described in yescrypt.h.
*
* local is the thread-local data structure, allowing optimized implementations
* to preserve and reuse a memory allocation across calls, thereby reducing its
* overhead (this reference implementation does not make that optimization).
*
* t controls computation time while not affecting peak memory usage.
*
* Return 0 on success; or -1 on error.
*/
static int yescrypt_kdf_body(const yescrypt_shared_t *shared,
yescrypt_local_t *local,
const uint8_t *passwd, size_t passwdlen,
const uint8_t *salt, size_t saltlen,
yescrypt_flags_t flags, uint64_t N, uint32_t r, uint32_t p, uint32_t t,
uint64_t NROM,
uint8_t *buf, size_t buflen)
{
int retval = -1;
const uint32_t *VROM;
size_t B_size, V_size;
uint32_t *B, *V, *XY, *S;
pwxform_ctx_t *pwxform_ctx;
uint32_t sha256[8];
uint8_t dk[sizeof(sha256)], *dkp = buf;
uint32_t i;
/* Sanity-check parameters */
switch (flags & YESCRYPT_MODE_MASK) {
case 0: /* classic scrypt - can't have anything non-standard */
if (flags || t || NROM)
goto out_EINVAL;
break;
case YESCRYPT_WORM:
if (flags != YESCRYPT_WORM || NROM)
goto out_EINVAL;
break;
case YESCRYPT_RW:
if (flags != (flags & YESCRYPT_KNOWN_FLAGS))
goto out_EINVAL;
#if PWXsimple == 2 && PWXgather == 4 && PWXrounds == 6 && Sbytes == 12288
if ((flags & YESCRYPT_RW_FLAVOR_MASK) ==
(YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 |
YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K))
break;
#else
#error "Unsupported pwxform settings"
#endif
/* FALLTHRU */
default:
goto out_EINVAL;
}
#if SIZE_MAX > UINT32_MAX
if (buflen > (((uint64_t)1 << 32) - 1) * 32)
goto out_EINVAL;
#endif
if ((uint64_t)r * (uint64_t)p >= 1 << 30)
goto out_EINVAL;
if ((N & (N - 1)) != 0 || N <= 1 || r < 1 || p < 1)
goto out_EINVAL;
if (r > SIZE_MAX / 128 / p ||
#if SIZE_MAX / 256 <= UINT32_MAX
r > SIZE_MAX / 256 ||
#endif
N > SIZE_MAX / 128 / r)
goto out_EINVAL;
if (N > UINT64_MAX / ((uint64_t)t + 1))
goto out_EINVAL;
if (flags & YESCRYPT_RW) {
if (N / p <= 1 || r < rmin ||
p > SIZE_MAX / Sbytes ||
p > SIZE_MAX / sizeof(*pwxform_ctx))
goto out_EINVAL;
}
VROM = NULL;
if (shared) {
uint64_t expected_size = (size_t)128 * r * NROM;
if ((NROM & (NROM - 1)) != 0 || NROM <= 1 ||
shared->aligned_size < expected_size)
goto out_EINVAL;
if (!(flags & YESCRYPT_INIT_SHARED)) {
uint32_t *tag = (uint32_t *)
((uint8_t *)shared->aligned + expected_size - 48);
uint64_t tag1 = ((uint64_t)tag[1] << 32) + tag[0];
uint64_t tag2 = ((uint64_t)tag[3] << 32) + tag[2];
if (tag1 != YESCRYPT_ROM_TAG1 || tag2 != YESCRYPT_ROM_TAG2)
goto out_EINVAL;
}
VROM = shared->aligned;
} else {
if (NROM)
goto out_EINVAL;
}
/* Allocate memory */
V_size = (size_t)128 * r * N;
if (flags & YESCRYPT_INIT_SHARED) {
V = (uint32_t *)local->aligned;
if (local->aligned_size < V_size) {
if (local->base || local->aligned ||
local->base_size || local->aligned_size)
goto out_EINVAL;
if ((V = malloc(V_size)) == NULL)
return -1;
local->base = local->aligned = V;
local->base_size = local->aligned_size = V_size;
}
if (flags & YESCRYPT_ALLOC_ONLY)
return -2; /* expected "failure" */
} else {
if ((V = malloc(V_size)) == NULL)
return -1;
}
B_size = (size_t)128 * r * p;
if ((B = malloc(B_size)) == NULL)
goto free_V;
if ((XY = malloc((size_t)256 * r)) == NULL)
goto free_B;
S = NULL;
pwxform_ctx = NULL;
if (flags & YESCRYPT_RW) {
if ((S = malloc((size_t)Sbytes * p)) == NULL)
goto free_XY;
if ((pwxform_ctx = malloc(sizeof(*pwxform_ctx) * p)) == NULL)
goto free_S;
}
if (flags) {
HMAC_SHA256_Buf("yescrypt-prehash",
(flags & YESCRYPT_PREHASH) ? 16 : 8,
passwd, passwdlen, (uint8_t *)sha256);
passwd = (uint8_t *)sha256;
passwdlen = sizeof(sha256);
}
/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1,
(uint8_t *)B, B_size);
if (flags)
blkcpy(sha256, B, sizeof(sha256) / sizeof(sha256[0]));
if (flags & YESCRYPT_RW) {
for (i = 0; i < p; i++)
pwxform_ctx[i].S = &S[i * Swords];
smix(B, r, N, p, t, flags, V, NROM, VROM, XY, pwxform_ctx,
(uint8_t *)sha256);
} else {
/* 2: for i = 0 to p - 1 do */
for (i = 0; i < p; i++) {
/* 3: B_i <-- MF(B_i, N) */
smix(&B[(size_t)32 * r * i], r, N, 1, t, flags, V,
NROM, VROM, XY, NULL, NULL);
}
}
dkp = buf;
if (flags && buflen < sizeof(dk)) {
PBKDF2_SHA256(passwd, passwdlen, (uint8_t *)B, B_size, 1,
dk, sizeof(dk));
dkp = dk;
}
/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
PBKDF2_SHA256(passwd, passwdlen, (uint8_t *)B, B_size, 1, buf, buflen);
/*
* Except when computing classic scrypt, allow all computation so far
* to be performed on the client. The final steps below match those of
* SCRAM (RFC 5802), so that an extension of SCRAM (with the steps so
* far in place of SCRAM's use of PBKDF2 and with SHA-256 in place of
* SCRAM's use of SHA-1) would be usable with yescrypt hashes.
*/
if (flags && !(flags & YESCRYPT_PREHASH)) {
/* Compute ClientKey */
HMAC_SHA256_Buf(dkp, sizeof(dk), "Client Key", 10,
(uint8_t *)sha256);
/* Compute StoredKey */
{
size_t clen = buflen;
if (clen > sizeof(dk))
clen = sizeof(dk);
SHA256_Buf((uint8_t *)sha256, sizeof(sha256), dk);
memcpy(buf, dk, clen);
}
}
/* Success! */
retval = 0;
/* Free memory */
free(pwxform_ctx);
free_S:
free(S);
free_XY:
free(XY);
free_B:
free(B);
free_V:
if (!(flags & YESCRYPT_INIT_SHARED))
free(V);
return retval;
out_EINVAL:
errno = EINVAL;
return -1;
}
/**
* yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen, params,
* buf, buflen):
* Compute scrypt or its revision as requested by the parameters. The inputs
* to this function are the same as those for yescrypt_kdf_body() above, with
* the addition of g, which controls hash upgrades (0 for no upgrades so far).
*/
int yescrypt_kdf(const yescrypt_shared_t *shared, yescrypt_local_t *local,
const uint8_t *passwd, size_t passwdlen,
const uint8_t *salt, size_t saltlen,
const yescrypt_params_t *params,
uint8_t *buf, size_t buflen)
{
yescrypt_flags_t flags = params->flags;
uint64_t N = params->N;
uint32_t r = params->r;
uint32_t p = params->p;
uint32_t t = params->t;
uint32_t g = params->g;
uint64_t NROM = params->NROM;
uint8_t dk[32];
/* Support for hash upgrades has been temporarily removed */
if (g) {
errno = EINVAL;
return -1;
}
if ((flags & YESCRYPT_RW) &&
p >= 1 && N / p >= 0x100 && N / p * r >= 0x20000) {
/*
* This reference implementation's yescrypt_kdf_body()
* (de)allocates memory on each call, which defeats the purpose
* of this pre-hashing. The optimized implementations, which
* you should actually use, make the larger allocation first
* and then reuse it. Thus, this implementation doing things
* differently serves as a test that the computation result is
* unaffected by such differences.
*/
int retval = yescrypt_kdf_body(shared, local,
passwd, passwdlen, salt, saltlen,
flags | YESCRYPT_PREHASH, N >> 6, r, p, 0, NROM,
dk, sizeof(dk));
if (retval)
return retval;
passwd = dk;
passwdlen = sizeof(dk);
}
return yescrypt_kdf_body(shared, local,
passwd, passwdlen, salt, saltlen,
flags, N, r, p, t, NROM, buf, buflen);
}
int yescrypt_init_shared(yescrypt_shared_t *shared,
const uint8_t *seed, size_t seedlen,
const yescrypt_params_t *params)
{
yescrypt_flags_t flags = params->flags;
uint64_t N = params->NROM;
uint32_t r = params->r;
uint32_t p = params->p;
uint32_t t = params->t;
yescrypt_shared_t half1, half2;
uint8_t salt[32];
uint32_t *tag;
if (!(params->flags & YESCRYPT_RW) || params->N || params->g)
return -1;
if (flags & YESCRYPT_SHARED_PREALLOCATED) {
if (!shared->aligned || !shared->aligned_size)
return -1;
/* Overwrite a possible old ROM tag before we overwrite the rest */
tag = (uint32_t *)
((uint8_t *)shared->aligned + shared->aligned_size - 48);
memset(tag, 0, 48);
} else {
shared->base = shared->aligned = NULL;
shared->base_size = shared->aligned_size = 0;
if (yescrypt_kdf_body(NULL, shared, NULL, 0, NULL, 0,
flags | YESCRYPT_INIT_SHARED | YESCRYPT_ALLOC_ONLY,
N, r, p, t, 0, NULL, 0) != -2 || !shared->aligned)
goto fail;
}
half1 = half2 = *shared;
half1.aligned_size /= 2;
half2.aligned = (uint8_t *)half2.aligned + half1.aligned_size;
half2.aligned_size = half1.aligned_size;
N /= 2;
if (yescrypt_kdf_body(NULL, &half1,
seed, seedlen, (const uint8_t *)"yescrypt-ROMhash", 16,
flags | YESCRYPT_INIT_SHARED, N, r, p, t, 0,
salt, sizeof(salt)))
goto fail;
if (yescrypt_kdf_body(&half1, &half2,
seed, seedlen, salt, sizeof(salt),
flags | YESCRYPT_INIT_SHARED, N, r, p, t, N,
salt, sizeof(salt)))
goto fail;
if (yescrypt_kdf_body(&half2, &half1,
seed, seedlen, salt, sizeof(salt),
flags | YESCRYPT_INIT_SHARED, N, r, p, t, N,
salt, sizeof(salt)))
goto fail;
tag = (uint32_t *)
((uint8_t *)shared->aligned + shared->aligned_size - 48);
tag[0] = YESCRYPT_ROM_TAG1 & 0xffffffffU;
tag[1] = YESCRYPT_ROM_TAG1 >> 32;
tag[2] = YESCRYPT_ROM_TAG2 & 0xffffffffU;
tag[3] = YESCRYPT_ROM_TAG2 >> 32;
tag[4] = le32dec(salt);
tag[5] = le32dec(salt + 4);
tag[6] = le32dec(salt + 8);
tag[7] = le32dec(salt + 12);
tag[8] = le32dec(salt + 16);
tag[9] = le32dec(salt + 20);
tag[10] = le32dec(salt + 24);
tag[11] = le32dec(salt + 28);
return 0;
fail:
if (!(flags & YESCRYPT_SHARED_PREALLOCATED))
free(shared->base);
return -1;
}
yescrypt_binary_t *yescrypt_digest_shared(yescrypt_shared_t *shared)
{
static yescrypt_binary_t digest;
uint32_t *tag;
uint64_t tag1, tag2;
if (shared->aligned_size < 48)
return NULL;
tag = (uint32_t *)
((uint8_t *)shared->aligned + shared->aligned_size - 48);
tag1 = ((uint64_t)tag[1] << 32) + tag[0];
tag2 = ((uint64_t)tag[3] << 32) + tag[2];
if (tag1 != YESCRYPT_ROM_TAG1 || tag2 != YESCRYPT_ROM_TAG2)
return NULL;
le32enc(digest.uc, tag[4]);
le32enc(digest.uc + 4, tag[5]);
le32enc(digest.uc + 8, tag[6]);
le32enc(digest.uc + 12, tag[7]);
le32enc(digest.uc + 16, tag[8]);
le32enc(digest.uc + 20, tag[9]);
le32enc(digest.uc + 24, tag[10]);
le32enc(digest.uc + 28, tag[11]);
return &digest;
}
int yescrypt_free_shared(yescrypt_shared_t *shared)
{
free(shared->base);
shared->base = shared->aligned = NULL;
shared->base_size = shared->aligned_size = 0;
return 0;
}
int yescrypt_init_local(yescrypt_local_t *local)
{
/* The reference implementation doesn't use the local structure */
local->base = local->aligned = NULL;
local->base_size = local->aligned_size = 0;
return 0;
}
int yescrypt_free_local(yescrypt_local_t *local)
{
/* The reference implementation frees its memory in yescrypt_kdf() */
(void)local; /* unused */
return 0;
}

View File

@@ -0,0 +1,346 @@
/*-
* Copyright 2009 Colin Percival
* Copyright 2013-2018 Alexander Peslyak
* 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 _YESCRYPT_H_
#define _YESCRYPT_H_
#include <stdint.h>
#include <stdlib.h> /* for size_t */
#ifdef __cplusplus
extern "C" {
#endif
/**
* 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.
*
* MT-safe as long as buf is local to the thread.
*/
extern 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);
/**
* Internal type used by the memory allocator. Please do not use it directly.
* Use yescrypt_shared_t and yescrypt_local_t as appropriate instead, since
* they might differ from each other in a future version.
*/
typedef struct {
void *base, *aligned;
size_t base_size, aligned_size;
} yescrypt_region_t;
/**
* Types for shared (ROM) and thread-local (RAM) data structures.
*/
typedef yescrypt_region_t yescrypt_shared_t;
typedef yescrypt_region_t yescrypt_local_t;
/**
* Two 64-bit tags placed 48 bytes to the end of a ROM in host byte endianness
* (and followed by 32 bytes of the ROM digest).
*/
#define YESCRYPT_ROM_TAG1 0x7470797263736579ULL /* "yescrypt" */
#define YESCRYPT_ROM_TAG2 0x687361684d4f522dULL /* "-ROMhash" */
/**
* Type and possible values for the flags argument of yescrypt_kdf(),
* yescrypt_encode_params_r(), yescrypt_encode_params(). Most of these may be
* OR'ed together, except that YESCRYPT_WORM stands on its own.
* Please refer to the description of yescrypt_kdf() below for the meaning of
* these flags.
*/
typedef uint32_t yescrypt_flags_t;
/* Public */
#define YESCRYPT_WORM 1
#define YESCRYPT_RW 0x002
#define YESCRYPT_ROUNDS_3 0x000
#define YESCRYPT_ROUNDS_6 0x004
#define YESCRYPT_GATHER_1 0x000
#define YESCRYPT_GATHER_2 0x008
#define YESCRYPT_GATHER_4 0x010
#define YESCRYPT_GATHER_8 0x018
#define YESCRYPT_SIMPLE_1 0x000
#define YESCRYPT_SIMPLE_2 0x020
#define YESCRYPT_SIMPLE_4 0x040
#define YESCRYPT_SIMPLE_8 0x060
#define YESCRYPT_SBOX_6K 0x000
#define YESCRYPT_SBOX_12K 0x080
#define YESCRYPT_SBOX_24K 0x100
#define YESCRYPT_SBOX_48K 0x180
#define YESCRYPT_SBOX_96K 0x200
#define YESCRYPT_SBOX_192K 0x280
#define YESCRYPT_SBOX_384K 0x300
#define YESCRYPT_SBOX_768K 0x380
/* Only valid for yescrypt_init_shared() */
#define YESCRYPT_SHARED_PREALLOCATED 0x10000
#ifdef YESCRYPT_INTERNAL
/* Private */
#define YESCRYPT_MODE_MASK 0x003
#define YESCRYPT_RW_FLAVOR_MASK 0x3fc
#define YESCRYPT_INIT_SHARED 0x01000000
#define YESCRYPT_ALLOC_ONLY 0x08000000
#define YESCRYPT_PREHASH 0x10000000
#endif
#define YESCRYPT_RW_DEFAULTS \
(YESCRYPT_RW | \
YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | \
YESCRYPT_SBOX_12K)
#define YESCRYPT_DEFAULTS YESCRYPT_RW_DEFAULTS
#ifdef YESCRYPT_INTERNAL
#define YESCRYPT_KNOWN_FLAGS \
(YESCRYPT_MODE_MASK | YESCRYPT_RW_FLAVOR_MASK | \
YESCRYPT_SHARED_PREALLOCATED | \
YESCRYPT_INIT_SHARED | YESCRYPT_ALLOC_ONLY | YESCRYPT_PREHASH)
#endif
/**
* yescrypt parameters combined into one struct. N, r, p are the same as in
* classic scrypt, except that the meaning of p changes when YESCRYPT_RW is
* set. flags, t, g, NROM are special to yescrypt.
*/
typedef struct {
yescrypt_flags_t flags;
uint64_t N;
uint32_t r, p, t, g;
uint64_t NROM;
} yescrypt_params_t;
/**
* A 256-bit yescrypt hash, or a hash encryption key (which may itself have
* been derived as a yescrypt hash of a human-specified key string).
*/
typedef union {
unsigned char uc[32];
uint64_t u64[4];
} yescrypt_binary_t;
/**
* yescrypt_init_shared(shared, seed, seedlen, params):
* Optionally allocate memory for and initialize the shared (ROM) data
* structure. The parameters flags, NROM, r, p, and t specify how the ROM is
* to be initialized, and seed and seedlen specify the initial seed affecting
* the data with which the ROM is filled.
*
* Return 0 on success; or -1 on error.
*
* If bit YESCRYPT_SHARED_PREALLOCATED in flags is set, then memory for the
* ROM is assumed to have been preallocated by the caller, with shared->aligned
* being the start address of the ROM and shared->aligned_size being its size
* (which must be sufficient for NROM, r, p). This may be used e.g. when the
* ROM is to be placed in a SysV shared memory segment allocated by the caller.
*
* MT-safe as long as shared is local to the thread.
*/
extern int yescrypt_init_shared(yescrypt_shared_t *shared,
const uint8_t *seed, size_t seedlen, const yescrypt_params_t *params);
/**
* yescrypt_digest_shared(shared):
* Extract the previously stored message digest of the provided yescrypt ROM.
*
* Return pointer to the message digest on success; or NULL on error.
*
* MT-unsafe.
*/
extern yescrypt_binary_t *yescrypt_digest_shared(yescrypt_shared_t *shared);
/**
* yescrypt_free_shared(shared):
* Free memory that had been allocated with yescrypt_init_shared().
*
* Return 0 on success; or -1 on error.
*
* MT-safe as long as shared is local to the thread.
*/
extern int yescrypt_free_shared(yescrypt_shared_t *shared);
/**
* yescrypt_init_local(local):
* Initialize the thread-local (RAM) data structure. Actual memory allocation
* is currently fully postponed until a call to yescrypt_kdf() or yescrypt_r().
*
* Return 0 on success; or -1 on error.
*
* MT-safe as long as local is local to the thread.
*/
extern int yescrypt_init_local(yescrypt_local_t *local);
/**
* yescrypt_free_local(local):
* Free memory that may have been allocated for an initialized thread-local
* (RAM) data structure.
*
* Return 0 on success; or -1 on error.
*
* MT-safe as long as local is local to the thread.
*/
extern int yescrypt_free_local(yescrypt_local_t *local);
/**
* yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen, params,
* buf, buflen):
* Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
* p, buflen), or a revision of scrypt as requested by flags and shared, and
* write the result into buf. The parameters N, r, p, and buflen must satisfy
* the same conditions as with crypto_scrypt(). t controls computation time
* while not affecting peak memory usage (t = 0 is optimal unless higher N*r
* is not affordable while higher t is). g controls hash upgrades (g = 0 for
* no upgrades so far). shared and flags may request special modes. local is
* the thread-local data structure, allowing to preserve and reuse a memory
* allocation across calls, thereby reducing processing overhead.
*
* Return 0 on success; or -1 on error.
*
* Classic scrypt is available by setting shared = NULL, flags = 0, and t = 0.
*
* Setting YESCRYPT_WORM enables only minimal deviations from classic scrypt:
* support for the t parameter, and pre- and post-hashing.
*
* Setting YESCRYPT_RW fully enables yescrypt. As a side effect of differences
* between the algorithms, it also prevents p > 1 from growing the threads'
* combined processing time and memory allocation (like it did with classic
* scrypt and YESCRYPT_WORM), treating p as a divider rather than a multiplier.
*
* Passing a shared structure, with ROM contents previously computed by
* yescrypt_init_shared(), enables the use of ROM and requires YESCRYPT_RW.
*
* In order to allow for initialization of the ROM to be split into a separate
* program (or separate invocation of the same program), the shared->aligned
* and shared->aligned_size fields may optionally be set by the caller directly
* (e.g., to a mapped SysV shm segment), without using yescrypt_init_shared().
*
* local must be initialized with yescrypt_init_local().
*
* MT-safe as long as local and buf are local to the thread.
*/
extern int yescrypt_kdf(const yescrypt_shared_t *shared,
yescrypt_local_t *local,
const uint8_t *passwd, size_t passwdlen,
const uint8_t *salt, size_t saltlen,
const yescrypt_params_t *params,
uint8_t *buf, size_t buflen);
/**
* yescrypt_r(shared, local, passwd, passwdlen, setting, key, buf, buflen):
* Compute and encode an scrypt or enhanced scrypt hash of passwd given the
* parameters and salt value encoded in setting. If shared is not NULL, a ROM
* is used and YESCRYPT_RW is required. Otherwise, whether to compute classic
* scrypt, YESCRYPT_WORM (a slight deviation from classic scrypt), or
* YESCRYPT_RW (time-memory tradeoff discouraging modification) is determined
* by the setting string. shared (if not NULL) and local must be initialized
* as described above for yescrypt_kdf(). buf must be large enough (as
* indicated by buflen) to hold the encoded hash string.
*
* Return the encoded hash string on success; or NULL on error.
*
* MT-safe as long as local and buf are local to the thread.
*/
extern uint8_t *yescrypt_r(const yescrypt_shared_t *shared,
yescrypt_local_t *local,
const uint8_t *passwd, size_t passwdlen,
const uint8_t *setting,
const yescrypt_binary_t *key,
uint8_t *buf, size_t buflen);
/**
* yescrypt(passwd, setting):
* Compute and encode an scrypt or enhanced scrypt hash of passwd given the
* parameters and salt value encoded in setting. Whether to compute classic
* scrypt, YESCRYPT_WORM (a slight deviation from classic scrypt), or
* YESCRYPT_RW (time-memory tradeoff discouraging modification) is determined
* by the setting string.
*
* Return the encoded hash string on success; or NULL on error.
*
* This is a crypt(3)-like interface, which is simpler to use than
* yescrypt_r(), but it is not MT-safe, it does not allow for the use of a ROM,
* and it is slower than yescrypt_r() for repeated calls because it allocates
* and frees memory on each call.
*
* MT-unsafe.
*/
extern uint8_t *yescrypt(const uint8_t *passwd, const uint8_t *setting);
/**
* yescrypt_reencrypt(hash, from_key, to_key):
* Re-encrypt a yescrypt hash from one key to another. Either key may be NULL
* to indicate unencrypted hash. The encoded hash string is modified in-place.
*
* Return the hash pointer on success; or NULL on error (in which case the hash
* string is left unmodified).
*
* MT-safe as long as hash is local to the thread.
*/
extern uint8_t *yescrypt_reencrypt(uint8_t *hash,
const yescrypt_binary_t *from_key,
const yescrypt_binary_t *to_key);
/**
* yescrypt_encode_params_r(params, src, srclen, buf, buflen):
* Generate a setting string for use with yescrypt_r() and yescrypt() by
* encoding into it the parameters flags, N, r, p, t, g, and a salt given by
* src (of srclen bytes). buf must be large enough (as indicated by buflen)
* to hold the setting string.
*
* Return the setting string on success; or NULL on error.
*
* MT-safe as long as buf is local to the thread.
*/
extern uint8_t *yescrypt_encode_params_r(const yescrypt_params_t *params,
const uint8_t *src, size_t srclen,
uint8_t *buf, size_t buflen);
/**
* yescrypt_encode_params(params, src, srclen):
* Generate a setting string for use with yescrypt_r() and yescrypt(). This
* function is the same as yescrypt_encode_params_r() except that it uses a
* static buffer and thus is not MT-safe.
*
* Return the setting string on success; or NULL on error.
*
* MT-unsafe.
*/
extern uint8_t *yescrypt_encode_params(const yescrypt_params_t *params,
const uint8_t *src, size_t srclen);
#ifdef __cplusplus
}
#endif
#endif /* !_YESCRYPT_H_ */

0
d
View File