/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#pragma once
#include
#include
#include
#include
#include
namespace ams::crypto::impl {
class BigNum {
NON_COPYABLE(BigNum);
NON_MOVEABLE(BigNum);
public:
using HalfWord = u16;
using Word = u32;
using DoubleWord = u64;
static constexpr size_t MaxBits = 4096;
static constexpr size_t BitsPerWord = sizeof(Word) * CHAR_BIT;
static constexpr Word MaxWord = std::numeric_limits::max();
static constexpr Word MaxHalfWord = std::numeric_limits::max();
class WordAllocator {
NON_COPYABLE(WordAllocator);
NON_MOVEABLE(WordAllocator);
public:
class Allocation {
NON_COPYABLE(Allocation);
NON_MOVEABLE(Allocation);
private:
friend class WordAllocator;
private:
WordAllocator *m_allocator;
Word *m_buffer;
size_t m_count;
private:
constexpr ALWAYS_INLINE Allocation(WordAllocator *a, Word *w, size_t c) : m_allocator(a), m_buffer(w), m_count(c) { /* ... */ }
public:
ALWAYS_INLINE ~Allocation() { if (m_allocator) { m_allocator->Free(m_buffer, m_count); } }
constexpr ALWAYS_INLINE Word *GetBuffer() const { return m_buffer; }
constexpr ALWAYS_INLINE size_t GetCount() const { return m_count; }
constexpr ALWAYS_INLINE bool IsValid() const { return m_buffer != nullptr; }
};
friend class Allocation;
private:
Word *m_buffer;
size_t m_count;
size_t m_max_count;
size_t m_min_count;
private:
ALWAYS_INLINE void Free(void *words, size_t num) {
m_buffer -= num;
m_count += num;
AMS_ASSERT(words == m_buffer);
AMS_UNUSED(words);
}
public:
constexpr ALWAYS_INLINE WordAllocator(Word *buf, size_t c) : m_buffer(buf), m_count(c), m_max_count(c), m_min_count(c) { /* ... */ }
ALWAYS_INLINE Allocation Allocate(size_t num) {
if (num <= m_count) {
Word *allocated = m_buffer;
m_buffer += num;
m_count -= num;
m_min_count = std::min(m_count, m_min_count);
return Allocation(this, allocated, num);
} else {
return Allocation(nullptr, nullptr, 0);
}
}
constexpr ALWAYS_INLINE size_t GetMaxUsedSize() const {
return (m_max_count - m_min_count) * sizeof(Word);
}
};
private:
Word *m_words;
size_t m_num_words;
size_t m_max_words;
private:
static void ImportImpl(Word *out, size_t out_size, const u8 *src, size_t src_size);
static void ExportImpl(u8 *out, size_t out_size, const Word *src, size_t src_size);
public:
constexpr BigNum() : m_words(), m_num_words(), m_max_words() { /* ... */ }
~BigNum() { /* ... */ }
constexpr void ReserveStatic(Word *buf, size_t capacity) {
m_words = buf;
m_max_words = capacity;
}
bool Import(const void *src, size_t src_size);
void Export(void *dst, size_t dst_size);
size_t GetSize() const;
bool IsZero() const {
return m_num_words == 0;
}
bool ExpMod(void *dst, const void *src, size_t size, const BigNum &exp, u32 *work_buf, size_t work_buf_size) const;
void ClearToZero();
void UpdateCount();
public:
/* Utility. */
static bool IsZero(const Word *w, size_t num_words);
static int Compare(const Word *lhs, const Word *rhs, size_t num_words);
static size_t CountWords(const Word *w, size_t num_words);
static size_t CountSignificantBits(Word w);
static void ClearToZero(Word *w, size_t num_words);
static void SetToWord(Word *w, size_t num_words, Word v);
static void Copy(Word *dst, const Word *src, size_t num_words);
/* Arithmetic. */
static bool ExpMod(Word *dst, const Word *src, const Word *exp, size_t exp_num_words, const Word *mod, size_t mod_num_words, WordAllocator *allocator);
static bool MultMod(Word *dst, const Word *src, const Word *mult, const Word *mod, size_t num_words, WordAllocator *allocator);
static bool Mod(Word *dst, const Word *src, size_t src_words, const Word *mod, size_t mod_words, WordAllocator *allocator);
static bool DivMod(Word *quot, Word *rem, const Word *top, size_t top_words, const Word *bot, size_t bot_words, WordAllocator *allocator);
static bool Mult(Word *dst, const Word *lhs, const Word *rhs, size_t num_words, WordAllocator *allocator);
static Word LeftShift(Word *dst, const Word *w, size_t num_words, const size_t shift);
static Word RightShift(Word *dst, const Word *w, size_t num_words, const size_t shift);
static Word Add(Word *dst, const Word *lhs, const Word *rhs, size_t num_words);
static Word Sub(Word *dst, const Word *lhs, const Word *rhs, size_t num_words);
static Word MultAdd(Word *dst, const Word *w, size_t num_words, Word mult);
static Word MultSub(Word *dst, const Word *w, const Word *v, size_t num_words, Word mult);
};
template
class StaticBigNum : public BigNum {
public:
static constexpr size_t NumBits = Bits;
static constexpr size_t NumWords = util::AlignUp(NumBits, BitsPerWord) / BitsPerWord;
static constexpr size_t NumBytes = NumWords * sizeof(Word);
private:
Word m_word_buf[NumWords];
public:
constexpr StaticBigNum() : m_word_buf() {
this->ReserveStatic(m_word_buf, NumWords);
}
};
}